libMesh
file_solution_history.C
Go to the documentation of this file.
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 
37  : SolutionHistory(), _system(system)
38 {
41 
42  libmesh_experimental();
43 }
44 
45 
47 
48 std::unique_ptr<SolutionHistory>
50 {
51  return std::make_unique<FileSolutionHistory>(_system);
52 }
53 
54 // This functions writes the solution at the current system time to disk
55 void FileSolutionHistory::store(bool is_adjoint_solve, Real time)
56 {
57  // This will map the stored_datum iterator to the current time
58  this->find_stored_entry(time, true);
59 
60  // In an empty history we create the first entry
61  if (stored_data.begin() == stored_data.end())
62  {
63  stored_data[time] = std::make_unique<FileHistoryData>(_system);
64  stored_datum = stored_data.begin();
65  }
66 
67  // If we're past the end we can create a new entry
68  if (time - stored_datum->first > TOLERANCE )
69  {
70 #ifndef NDEBUG
71  ++stored_datum;
73 #endif
74  stored_data[time] = std::make_unique<FileHistoryData>(_system);
75  stored_datum = stored_data.end();
76  --stored_datum;
77  }
78 
79  // If we're before the beginning we can create a new entry
80  else if (stored_datum->first - time > TOLERANCE)
81  {
83  stored_data[time] = std::make_unique<FileHistoryData>(_system);
84  stored_datum = stored_data.begin();
85  }
86 
87  // We don't support inserting entries elsewhere
88  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  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  if(stored_data.size() == 1)
97  {
98  (stored_datum->second)->store_initial_solution();
99  }
100  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  (stored_datum->second)->store_primal_solution(stored_datum);
103  }
104  else
105  {
106  (stored_datum->second)->rewrite_stored_solution();
107  }
108  }
109  else // We are in the adjoint time stepping loop
110  {
111  (stored_datum->second)->store_adjoint_solution();
112  }
113 
114 }
115 
116 void FileSolutionHistory::retrieve(bool is_adjoint_solve, Real time)
117 {
118  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  if(is_adjoint_solve)
124  {
125  if( stored_datum != stored_data.begin() )
126  {
127  stored_data_iterator stored_datum_past = stored_datum;
128  stored_datum_past--;
129 
130  _system.deltat = (stored_datum_past->second)->get_deltat_at();
131  }
132  else
133  {
134  _system.deltat = (stored_datum->second)->get_deltat_at();
135  }
136  }
137  else
138  {
139  if( stored_datum != std::prev(stored_data.end()) )
140  _system.deltat = (stored_datum->second)->get_deltat_at();
141  else
142  {
143  stored_data_iterator stored_datum_past = stored_datum;
144  stored_datum_past--;
145 
146  _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  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  if (stored_datum == stored_data.end() ||
157  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  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  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  for (auto j : make_range(_system.n_qois()))
173  {
175 
176  std::string old_adjoint_solution_name = "_old_adjoint_solution";
177  old_adjoint_solution_name+= std::to_string(j);
178  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  (stored_datum->second)->retrieve_primal_solution();
183 
184  // Swap back the copy of adjoint and old adjoint vectors back in place
185  for (auto j : make_range(_system.n_qois()))
186  {
188 
189  std::string old_adjoint_solution_name = "_old_adjoint_solution";
190  old_adjoint_solution_name+= std::to_string(j);
191  (_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  if(dynamic_cast<FileHistoryData &>(*(stored_datum->second)).get_adjoint_filename().empty())
200  (stored_datum->second)->retrieve_primal_solution();
201  else
202  (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  _system.update();
208 
209 }
210 
211 }
212 // End namespace libMesh
std::vector< std::unique_ptr< NumericVector< Number > > > dual_solution_copies
A vector of pointers to adjoint and old adjoint solutions at the last time step.
~FileSolutionHistory()
Destructor, defaulted out-of-line.
static constexpr Real TOLERANCE
unsigned int n_qois() const
Number of currently active quantities of interest.
Definition: system.h:2621
static const unsigned int prev[3]
A lookup table for the decrement modulo 3 operation, for iterating through the three nodes per elemen...
virtual std::unique_ptr< NumericVector< T > > clone() const =0
The libMesh namespace provides an interface to certain functionality in the library.
A SolutionHistory class that enables the storage and retrieval of timesteps and (in the future) adapt...
This class provides a specific system class.
Definition: diff_system.h:54
Real deltat
For time-dependent problems, this is the amount delta t to advance the solution in time...
Definition: diff_system.h:280
libmesh_assert(ctx)
virtual void retrieve(bool is_adjoint_solve, Real time) override
Virtual function retrieve which we will be overriding to retrieve timesteps.
DifferentiableSystem & _system
virtual void update()
Update the local values to reflect the solution on neighboring processors.
Definition: system.C:510
map_type::iterator stored_data_iterator
FileSolutionHistory(DifferentiableSystem &system)
Constructor, reference to system to be passed by user, set the stored_sols iterator to some initial v...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual std::unique_ptr< SolutionHistory > clone() const override
Definition of the clone function needed for the setter function.
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:140
NumericVector< Number > & get_adjoint_solution(unsigned int i=0)
Definition: system.C:1245
stored_data_iterator stored_datum
virtual void store(bool is_adjoint_solve, Real time) override
Virtual function store which we will be overriding to store timesteps.
std::vector< std::unique_ptr< NumericVector< Number > > > old_dual_solution_copies
const NumericVector< Number > & get_vector(std::string_view vec_name) const
Definition: system.C:943
void find_stored_entry(Real time, bool storing=false)