Line data Source code
1 : // The libMesh Finite Element Library. 2 : // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner 3 : 4 : // This library is free software; you can redistribute it and/or 5 : // modify it under the terms of the GNU Lesser General Public 6 : // License as published by the Free Software Foundation; either 7 : // version 2.1 of the License, or (at your option) any later version. 8 : 9 : // This library is distributed in the hope that it will be useful, 10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 : // Lesser General Public License for more details. 13 : 14 : // You should have received a copy of the GNU Lesser General Public 15 : // License along with this library; if not, write to the Free Software 16 : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 : 18 : // libMesh includes 19 : #include "libmesh/file_solution_history.h" 20 : #include "libmesh/file_history_data.h" 21 : #include "libmesh/diff_system.h" 22 : #include "libmesh/numeric_vector.h" 23 : 24 : // C++ includes 25 : #include <iostream> 26 : #include <cmath> 27 : #include <iterator> 28 : 29 : namespace libMesh 30 : { 31 : 32 : /** 33 : * Constructor, reference to system to be passed by user, set the 34 : * stored_datum iterator to some initial value 35 : */ 36 700 : FileSolutionHistory::FileSolutionHistory(DifferentiableSystem & system) 37 720 : : SolutionHistory(), _system(system) 38 : { 39 700 : dual_solution_copies.resize(_system.n_qois()); 40 720 : old_dual_solution_copies.resize(_system.n_qois()); 41 : 42 : libmesh_experimental(); 43 700 : } 44 : 45 : 46 1056 : FileSolutionHistory::~FileSolutionHistory () = default; 47 : 48 : std::unique_ptr<SolutionHistory> 49 420 : FileSolutionHistory::clone() const 50 : { 51 420 : return std::make_unique<FileSolutionHistory>(_system); 52 : } 53 : 54 : // This functions writes the solution at the current system time to disk 55 8120 : void FileSolutionHistory::store(bool is_adjoint_solve, Real time) 56 : { 57 : // This will map the stored_datum iterator to the current time 58 8120 : this->find_stored_entry(time, true); 59 : 60 : // In an empty history we create the first entry 61 8120 : if (stored_data.begin() == stored_data.end()) 62 : { 63 288 : stored_data[time] = std::make_unique<FileHistoryData>(_system); 64 280 : stored_datum = stored_data.begin(); 65 : } 66 : 67 : // If we're past the end we can create a new entry 68 8120 : if (time - stored_datum->first > TOLERANCE ) 69 : { 70 : #ifndef NDEBUG 71 108 : ++stored_datum; 72 108 : libmesh_assert (stored_datum == stored_data.end()); 73 : #endif 74 3780 : stored_data[time] = std::make_unique<FileHistoryData>(_system); 75 3780 : stored_datum = stored_data.end(); 76 108 : --stored_datum; 77 : } 78 : 79 : // If we're before the beginning we can create a new entry 80 4340 : else if (stored_datum->first - time > TOLERANCE) 81 : { 82 0 : libmesh_assert (stored_datum == stored_data.begin()); 83 0 : stored_data[time] = std::make_unique<FileHistoryData>(_system); 84 0 : stored_datum = stored_data.begin(); 85 : } 86 : 87 : // We don't support inserting entries elsewhere 88 232 : libmesh_assert(std::abs(stored_datum->first - time) < TOLERANCE); 89 : 90 : // If we are in the primal loop, either reuse an existing timestamp if a write has been done for 91 : // this time earlier. Else, we are at a new time and need to make a new entry in the timeTotimestamp map. 92 8120 : if(!is_adjoint_solve) 93 : { 94 : // First we handle the case of the initial data, this is the only case in which 95 : // stored_data will have size one 96 4060 : if(stored_data.size() == 1) 97 : { 98 288 : (stored_datum->second)->store_initial_solution(); 99 : } 100 3888 : else if((stored_datum->second)->get_previously_stored() == false) // If we are not at the initial time, we are either creating a new entry or overwriting an existing one 101 : { 102 3780 : (stored_datum->second)->store_primal_solution(stored_datum); 103 : } 104 : else 105 : { 106 0 : (stored_datum->second)->rewrite_stored_solution(); 107 : } 108 : } 109 : else // We are in the adjoint time stepping loop 110 : { 111 4176 : (stored_datum->second)->store_adjoint_solution(); 112 : } 113 : 114 8120 : } 115 : 116 10360 : void FileSolutionHistory::retrieve(bool is_adjoint_solve, Real time) 117 : { 118 10360 : this->find_stored_entry(time, false); 119 : 120 : // If we are solving the adjoint, the timestep we need to move to the past step 121 : // is the one taken at that step to get to the current time. 122 : // At the initial time, be ready for the primal time march again. 123 10360 : if(is_adjoint_solve) 124 : { 125 4060 : if( stored_datum != stored_data.begin() ) 126 : { 127 100 : stored_data_iterator stored_datum_past = stored_datum; 128 100 : stored_datum_past--; 129 : 130 3500 : _system.deltat = (stored_datum_past->second)->get_deltat_at(); 131 : } 132 : else 133 : { 134 560 : _system.deltat = (stored_datum->second)->get_deltat_at(); 135 : } 136 : } 137 : else 138 : { 139 6300 : if( stored_datum != std::prev(stored_data.end()) ) 140 5880 : _system.deltat = (stored_datum->second)->get_deltat_at(); 141 : else 142 : { 143 12 : stored_data_iterator stored_datum_past = stored_datum; 144 12 : stored_datum_past--; 145 : 146 420 : _system.deltat = (stored_datum_past->second)->get_deltat_at(); 147 : } 148 : 149 : } 150 : 151 : // Get the time at which we are recovering the solution vectors 152 10360 : Real recovery_time = stored_datum->first; 153 : 154 : // Do we not have a solution for this time? Then 155 : // there's nothing to do. 156 10656 : if (stored_datum == stored_data.end() || 157 10360 : std::abs(recovery_time - time) > TOLERANCE) 158 : { 159 : //libMesh::out << "No more solutions to recover ! We are at time t = " << 160 : // _system.time << std::endl; 161 8 : return; 162 : } 163 : 164 : 165 : // If we are doing an adjoint solve, we read in the primal solution, 166 : // but this overwrites the adjoint solution with zero, so we swap 167 : // the last adjoint solution out to prevent this zeroing 168 10080 : if(is_adjoint_solve) 169 : { 170 : // Reading in the primal xdas overwrites the adjoint solution with zero 171 : // So swap to retain the adjoint and old adjoint vectors 172 9660 : for (auto j : make_range(_system.n_qois())) 173 : { 174 5880 : dual_solution_copies[j] = _system.get_adjoint_solution(j).clone(); 175 : 176 5880 : std::string old_adjoint_solution_name = "_old_adjoint_solution"; 177 5712 : old_adjoint_solution_name+= std::to_string(j); 178 11760 : old_dual_solution_copies[j] = _system.get_vector(old_adjoint_solution_name).clone(); 179 : } 180 : 181 : // Read in the primal solution stored at the current recovery time from the disk 182 3888 : (stored_datum->second)->retrieve_primal_solution(); 183 : 184 : // Swap back the copy of adjoint and old adjoint vectors back in place 185 9660 : for (auto j : make_range(_system.n_qois())) 186 : { 187 5880 : (_system.get_adjoint_solution(j)).swap(*dual_solution_copies[j]); 188 : 189 6048 : std::string old_adjoint_solution_name = "_old_adjoint_solution"; 190 5712 : old_adjoint_solution_name+= std::to_string(j); 191 6048 : (_system.get_vector(old_adjoint_solution_name)).swap(*old_dual_solution_copies[j]); 192 : } 193 : } 194 : else 195 : { 196 : // // If we are not in the adjoint loop, we could be in the primal loop again having solved 197 : // // only the primal problem, or in a primal postprocessing stage to evaluate a QoI for example. 198 : // // If we can find an adjoint solution file, read that in, else read in the primal file 199 6300 : if(dynamic_cast<FileHistoryData &>(*(stored_datum->second)).get_adjoint_filename().empty()) 200 2240 : (stored_datum->second)->retrieve_primal_solution(); 201 : else 202 4060 : (stored_datum->second)->retrieve_adjoint_solution(); 203 : } 204 : 205 : // We need to call update to put system in a consistent state 206 : // with the solution that was read in 207 10080 : _system.update(); 208 : 209 : } 210 : 211 : } 212 : // End namespace libMesh