Loading [MathJax]/extensions/tex2jax.js
libMesh
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
diff_system.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 
19 // libMesh includes
20 #include "libmesh/diff_solver.h"
21 #include "libmesh/diff_system.h"
22 #include "libmesh/time_solver.h"
23 #include "libmesh/unsteady_solver.h"
24 #include "libmesh/dirichlet_boundaries.h"
25 #include "libmesh/dof_map.h"
26 #include "libmesh/zero_function.h"
27 
28 // C++ includes
29 #include <utility> // std::swap
30 
31 namespace libMesh
32 {
33 
34 
35 
37  const std::string & name_in,
38  const unsigned int number_in) :
39  Parent (es, name_in, number_in),
40  time_solver (),
41  deltat(1.),
42  postprocess_sides(false),
43  print_solution_norms(false),
44  print_solutions(false),
45  print_residual_norms(false),
46  print_residuals(false),
47  print_jacobian_norms(false),
48  print_jacobians(false),
49  print_element_solutions(false),
50  print_element_residuals(false),
51  print_element_jacobians(false),
52  _constrain_in_solver(true),
53  _diff_physics(),
54  _diff_qoi()
55 {
56 }
57 
58 
59 
61 
62 
63 
65 {
66  // If we had no attached Physics object, clear our own Physics data
67  if (this->_diff_physics.empty())
68  this->clear_physics();
69 
70  this->_diff_physics = {}; // No stack::clear
71  this->_diff_qoi = {};
72 
73  // If we had no attached QoI object, clear our own QoI data
74  if (this->_diff_qoi.empty())
75  this->clear_qoi();
76 
77  use_fixed_solution = false;
78 }
79 
80 
81 
83 {
85 
87  libmesh_assert_equal_to (&(time_solver->system()), this);
88 
89  time_solver->reinit();
90 }
91 
92 
93 
95 {
96  // If it isn't a separate initialized-upon-attachment object, do any
97  // initialization our physics needs.
98  if (this->_diff_physics.empty())
99  this->init_physics(*this);
100 
101  // Do any initialization our solvers need
103  libmesh_assert_equal_to (&(time_solver->system()), this);
104 
105  // Now check for second order variables and add their velocities to the System.
106  if (!time_solver->is_steady())
107  {
108  const UnsteadySolver & unsteady_solver =
109  cast_ref<const UnsteadySolver &>(*(time_solver.get()));
110 
111  if (unsteady_solver.time_order() == 1)
113  }
114 
115  time_solver->init();
116 
117  // Next initialize ImplicitSystem data
119 
120  time_solver->init_data();
121 }
122 
123 std::unique_ptr<DiffContext> DifferentiableSystem::build_context ()
124 {
125  auto context = std::make_unique<DiffContext>(*this);
126  context->set_deltat_pointer( &this->deltat );
127  return context;
128 }
129 
130 
132 {
133  this->assembly(true, true);
134 }
135 
136 
137 
139 {
140  // Get the time solver object associated with the system, and tell it that
141  // we are not solving the adjoint problem
142  this->get_time_solver().set_is_adjoint(false);
143 
144  libmesh_assert_equal_to (&(time_solver->system()), this);
145  time_solver->solve();
146 }
147 
148 
149 
150 std::pair<unsigned int, Real> DifferentiableSystem::adjoint_solve (const QoISet & qoi_indices)
151 {
152  // Get the time solver object associated with the system, and tell it that
153  // we are solving the adjoint problem
154  this->get_time_solver().set_is_adjoint(true);
155 
156  return time_solver->adjoint_solve(qoi_indices);
157 
158  //return this->ImplicitSystem::adjoint_solve(qoi_indices);
159 }
160 
161 
162 
164 {
166  libmesh_assert_equal_to (&(time_solver->system()), this);
167  return this->time_solver->linear_solver().get();
168 }
169 
170 
171 
172 std::pair<unsigned int, Real> DifferentiableSystem::get_linear_solve_parameters() const
173 {
175  libmesh_assert_equal_to (&(time_solver->system()), this);
176  return std::make_pair(this->time_solver->diff_solver()->max_linear_iterations,
177  this->time_solver->diff_solver()->relative_residual_tolerance);
178 }
179 
180 
181 
183 {
184  const std::set<unsigned int> & second_order_vars = this->get_second_order_vars();
185  if (!second_order_vars.empty())
186  {
187  for (const auto & var_id : second_order_vars)
188  {
189  const Variable & var = this->variable(var_id);
190  std::string new_var_name = std::string("dot_")+var.name();
191 
192  unsigned int v_var_idx;
193 
194  if (var.active_subdomains().empty())
195  v_var_idx = this->add_variable( new_var_name, var.type() );
196  else
197  v_var_idx = this->add_variable( new_var_name, var.type(), &var.active_subdomains() );
198 
199  _second_order_dot_vars.insert(std::pair<unsigned int, unsigned int>(var_id, v_var_idx));
200 
201  // The new velocities are time evolving variables of first order
202  this->time_evolving( v_var_idx, 1 );
203 
204 #ifdef LIBMESH_ENABLE_DIRICHLET
205  // And if there are any boundary conditions set on the second order
206  // variable, we also need to set it on its velocity variable.
207  this->add_dot_var_dirichlet_bcs(var_id, v_var_idx);
208 #endif
209  }
210  }
211 }
212 
213 #ifdef LIBMESH_ENABLE_DIRICHLET
215  unsigned int dot_var_idx )
216 {
217  // We're assuming that there could be a lot more variables than
218  // boundary conditions, so we search each of the boundary conditions
219  // for this variable rather than looping over boundary conditions
220  // in a separate loop and searching through all the variables.
221  const DirichletBoundaries * all_dbcs =
223 
224  if (all_dbcs)
225  {
226  // We need to cache the DBCs to be added so that we add them
227  // after looping over the existing DBCs. Otherwise, we're polluting
228  // the thing we're looping over.
229  std::vector<DirichletBoundary> new_dbcs;
230 
231  for (const auto & dbc : *all_dbcs)
232  {
233  libmesh_assert(dbc);
234 
235  // Look for second order variable in the current
236  // DirichletBoundary object
237  std::vector<unsigned int>::const_iterator dbc_var_it =
238  std::find( dbc->variables.begin(), dbc->variables.end(), var_idx );
239 
240  // If we found it, then we also need to add it's corresponding
241  // "dot" variable to a DirichletBoundary
242  std::vector<unsigned int> vars_to_add;
243  if (dbc_var_it != dbc->variables.end())
244  vars_to_add.push_back(dot_var_idx);
245 
246  if (!vars_to_add.empty())
247  {
248  // We need to check if the boundary condition is time-dependent.
249  // Currently, we cannot automatically differentiate w.r.t. time
250  // so if the user supplies a time-dependent Dirichlet BC, then
251  // we can't automatically support the Dirichlet BC for the
252  // "velocity" boundary condition, so we error. Otherwise,
253  // the "velocity boundary condition will just be zero.
254  bool is_time_evolving_bc = false;
255  if (dbc->f)
256  is_time_evolving_bc = dbc->f->is_time_dependent();
257  else if (dbc->f_fem)
258  // We it's a FEMFunctionBase object, it will be implicitly
259  // time-dependent since it is assumed to depend on the solution.
260  is_time_evolving_bc = true;
261  else
262  libmesh_error_msg("Could not find valid boundary function!");
263 
264  libmesh_error_msg_if(is_time_evolving_bc, "Cannot currently support time-dependent Dirichlet BC for dot variables!");
265  libmesh_error_msg_if(!dbc->f, "Expected valid DirichletBoundary function");
266 
267  new_dbcs.emplace_back(dbc->b, vars_to_add, ZeroFunction<Number>());
268  }
269  }
270 
271  // Let the DofMap make its own deep copy of the DirichletBC objects
272  for (const auto & dbc : new_dbcs)
273  this->get_dof_map().add_dirichlet_boundary(dbc);
274 
275  } // if (all_dbcs)
276 }
277 #endif // LIBMESH_ENABLE_DIRICHLET
278 
280 {
281  this->_diff_qoi = {};
282  this->_diff_qoi.push(qoi_in->clone());
283 
284  auto & dq = this->_diff_qoi.top();
285  // User needs to resize qoi system qoi accordingly
286 #ifdef LIBMESH_ENABLE_DEPRECATED
287  // Call the old API for backwards compatibility
288  dq->init_qoi( this->qoi );
289 
290  // Then the new API for forwards compatibility
291  dq->init_qoi_count( *this );
292 #else
293 #ifndef NDEBUG
294  // Make sure the user has updated their QoI subclass - call the old
295  // API and make sure it does nothing
296  std::vector<Number> deprecated_vector;
297  dq->init_qoi( deprecated_vector );
298  libmesh_assert(deprecated_vector.empty());
299 #endif
300 
301  // Then the new API
302  dq->init_qoi_count( *this );
303 #endif
304 }
305 
306 unsigned int DifferentiableSystem::get_second_order_dot_var( unsigned int var ) const
307 {
308  // For SteadySolver or SecondOrderUnsteadySolvers, we just give back var
309  unsigned int dot_var = var;
310 
311  if (!time_solver->is_steady())
312  {
313  const UnsteadySolver & unsteady_solver =
314  cast_ref<const UnsteadySolver &>(*(time_solver.get()));
315 
316  if (unsteady_solver.time_order() == 1)
317  dot_var = this->_second_order_dot_vars.find(var)->second;
318  }
319 
320  return dot_var;
321 }
322 
324 {
325  bool have_first_order_scalar_vars = false;
326 
327  if (this->have_first_order_vars())
328  for (const auto & var : this->get_first_order_vars())
329  if (this->variable(var).type().family == SCALAR)
331 
333 }
334 
336 {
337  bool have_second_order_scalar_vars = false;
338 
339  if (this->have_second_order_vars())
340  for (const auto & var : this->get_second_order_vars())
341  if (this->variable(var).type().family == SCALAR)
343 
345 }
346 
347 
348 
349 #ifdef LIBMESH_ENABLE_DEPRECATED
351 {
352  // This isn't safe if users aren't very careful about memory
353  // management and they don't (or aren't able to due to an exception)
354  // swap back.
355  libmesh_deprecated();
356 
357  // A mess of code for backwards compatibility
358  if (this->_diff_physics.empty())
359  {
360  // Swap-something-else-for-self
361  std::unique_ptr<DifferentiablePhysics> scary_hack(swap_physics);
362  this->_diff_physics.push(std::move(scary_hack));
363  swap_physics = this;
364  }
365  else if (swap_physics == this)
366  {
367  // The user must be cleaning up after a previous
368  // swap-something-else-for-self
369  libmesh_assert(!this->_diff_physics.empty());
370 
371  // So we don't want to delete what got swapped in, but we do
372  // want to put it back into their pointer
373  DifferentiablePhysics * old_p = this->_diff_physics.top().release();
374  this->_diff_physics.pop();
375  swap_physics = old_p;
376 
377  // And if the user is doing anything more sophisticated than
378  // that then the user is sophisticated enough to upgrade to
379  // push/pop.
380  libmesh_assert(this->_diff_physics.empty());
381  }
382  else
383  {
384  // Swapping one external physics for another
385  DifferentiablePhysics * old_p = this->_diff_physics.top().release();
386  std::swap(old_p, swap_physics);
387  this->_diff_physics.top().reset(old_p);
388  }
389 
390  // If the physics has been swapped, we will reassemble
391  // the matrix from scratch before doing an adjoint solve
392  // rather than just transposing
393  this->disable_cache();
394 }
395 #endif // LIBMESH_ENABLE_DEPRECATED
396 
397 
398 
400 {
401  this->_diff_physics.push(new_physics.clone_physics());
402 
403  // If the physics has been changed, we will reassemble
404  // the matrix from scratch before doing an adjoint solve
405  // rather than just transposing
406  this->disable_cache();
407 }
408 
409 
410 
412 {
413  libmesh_assert(!this->_diff_physics.empty());
414 
415  this->_diff_physics.pop();
416 
417  // If the physics has been changed, we will reassemble
418  // the matrix from scratch before doing an adjoint solve
419  // rather than just transposing
420  this->disable_cache();
421 }
422 
423 
425 {
426  _constrain_in_solver = enable;
427  this->time_solver->diff_solver()->set_exact_constraint_enforcement(enable);
428 }
429 
430 
431 } // namespace libMesh
This is the EquationSystems class.
virtual void clear_physics()
Clear any data structures associated with the physics.
Definition: diff_physics.C:28
virtual void assemble() override
Prepares matrix and rhs for matrix assembly.
Definition: diff_system.C:131
const Variable & variable(unsigned int var) const
Return a constant reference to Variable var.
Definition: system.h:2454
bool _constrain_in_solver
_constrain_in_solver defaults to true; if false then we apply constraints only via residual terms in ...
Definition: diff_system.h:432
ConstFunction that simply returns 0.
Definition: zero_function.h:38
virtual void assembly(bool get_residual, bool get_jacobian, bool apply_heterogeneous_constraints=false, bool apply_no_constraints=false) override=0
Assembles a residual in rhs and/or a jacobian in matrix, as requested.
virtual void set_constrain_in_solver(bool enable)
set_constrain_in_solver to false to apply constraints only via residual terms in the systems to be so...
Definition: diff_system.C:424
Data structure for specifying which Quantities of Interest should be calculated in an adjoint or a pa...
Definition: qoi_set.h:45
virtual void reinit()
Reinitializes degrees of freedom and other required data on the current mesh.
Definition: system.C:445
virtual void init_data()
Initializes the data for the system.
Definition: system.C:205
std::unique_ptr< TimeSolver > time_solver
A pointer to the solver object we&#39;re going to use.
Definition: diff_system.h:253
void add_second_order_dot_vars()
Helper function to add "velocity" variables that are cousins to second order-in-time variables in the...
Definition: diff_system.C:182
void add_dot_var_dirichlet_bcs(unsigned int var_idx, unsigned int dot_var_idx)
Helper function to and Dirichlet boundary conditions to "dot" variable cousins of second order variab...
Definition: diff_system.C:214
The libMesh namespace provides an interface to certain functionality in the library.
virtual void time_evolving(unsigned int var, unsigned int order)
Tells the DiffSystem that variable var is evolving with respect to time.
Definition: diff_physics.C:41
virtual std::unique_ptr< DiffContext > build_context()
Builds a DiffContext object with enough information to do evaluations on each element.
Definition: diff_system.C:123
std::map< unsigned int, unsigned int > _second_order_dot_vars
If the user adds any second order variables, then we need to also cache the map to their correspondin...
Definition: diff_physics.h:560
void pop_physics()
Pop a physics object off of our stack.
Definition: diff_system.C:411
virtual void reinit() override
Reinitializes the member data fields associated with the system, so that, e.g., assemble() may be use...
Definition: diff_system.C:82
virtual void init_physics(const System &sys)
Initialize any data structures associated with the physics.
Definition: diff_physics.C:35
void swap_physics(DifferentiablePhysics *&swap_physics)
Swap current physics object with external object.
Definition: diff_system.C:350
void set_is_adjoint(bool _is_adjoint_value)
Accessor for setting whether we need to do a primal or adjoint solve.
Definition: time_solver.h:284
virtual unsigned int time_order() const =0
const std::set< unsigned int > & get_second_order_vars() const
Definition: diff_physics.h:519
This class defines the notion of a variable in the system.
Definition: variable.h:49
const std::set< subdomain_id_type > & active_subdomains() const
Definition: variable.h:171
unsigned int get_second_order_dot_var(unsigned int var) const
For a given second order (in time) variable var, this method will return the index to the correspondi...
Definition: diff_system.C:306
const std::set< unsigned int > & get_first_order_vars() const
Definition: diff_physics.h:506
std::vector< Number > qoi
Values of the quantities of interest.
Definition: system.h:1637
Real deltat
For time-dependent problems, this is the amount delta t to advance the solution in time...
Definition: diff_system.h:280
bool use_fixed_solution
A boolean to be set to true by systems using elem_fixed_solution, for optional use by e...
Definition: system.h:1569
std::stack< std::unique_ptr< DifferentiablePhysics >, std::vector< std::unique_ptr< DifferentiablePhysics > > > _diff_physics
Stack of pointers to objects to use for physics assembly evaluations.
Definition: diff_system.h:442
std::stack< std::unique_ptr< DifferentiableQoI >, std::vector< std::unique_ptr< DifferentiableQoI > > > _diff_qoi
Pointer to object to use for quantity of interest assembly evaluations.
Definition: diff_system.h:450
We&#39;re using a class instead of a typedef to allow forward declarations and future flexibility...
libmesh_assert(ctx)
virtual void clear_qoi()
Clear all the data structures associated with the QoI.
Definition: diff_qoi.h:91
unsigned int add_variable(std::string_view var, const FEType &type, const std::set< subdomain_id_type > *const active_subdomains=nullptr)
Adds the variable var to the list of variables for this system.
Definition: system.C:1335
void attach_qoi(DifferentiableQoI *qoi_in)
Attach external QoI object.
Definition: diff_system.C:279
virtual void disable_cache() override
Avoids use of any cached data that might affect any solve result.
const DirichletBoundaries * get_dirichlet_boundaries() const
Definition: dof_map.h:1573
bool have_second_order_scalar_vars() const
Check for any second order vars that are also belong to FEFamily::SCALAR.
Definition: diff_system.C:335
virtual std::unique_ptr< DifferentiableQoI > clone()=0
Copy of this object.
This class provides a specific system class.
Definition: diff_physics.h:76
DifferentiableSystem(EquationSystems &es, const std::string &name, const unsigned int number)
Constructor.
Definition: diff_system.C:36
This class provides a specific system class.
Definition: diff_qoi.h:52
virtual void init_data() override
Initializes the member data fields associated with the system, so that, e.g., assemble() may be used...
Definition: diff_system.C:94
This is a generic class that defines a solver to handle time integration of DifferentiableSystems.
virtual LinearSolver< Number > * get_linear_solver() const override
Definition: diff_system.C:163
virtual std::pair< unsigned int, Real > get_linear_solve_parameters() const override
Definition: diff_system.C:172
virtual std::unique_ptr< DifferentiablePhysics > clone_physics()=0
Copy of this object.
void add_dirichlet_boundary(const DirichletBoundary &dirichlet_boundary)
Adds a copy of the specified Dirichlet boundary to the system.
void push_physics(DifferentiablePhysics &new_physics)
Push a clone of a new physics object onto our stack, overriding the current physics until the new phy...
Definition: diff_system.C:399
virtual void solve() override
Invokes the solver associated with the system.
Definition: diff_system.C:138
virtual void clear() override
Clear all the data structures associated with the system.
Definition: diff_system.C:64
const std::string & name() const
Definition: variable.h:121
bool have_first_order_scalar_vars() const
Check for any first order vars that are also belong to FEFamily::SCALAR.
Definition: diff_system.C:323
const DofMap & get_dof_map() const
Definition: system.h:2370
virtual std::pair< unsigned int, Real > adjoint_solve(const QoISet &qoi_indices=QoISet()) override
This function sets the _is_adjoint boolean member of TimeSolver to true and then calls the adjoint_so...
Definition: diff_system.C:150
TimeSolver & get_time_solver()
Definition: diff_system.h:456
const FEType & type() const
Definition: variable.h:140
Manages consistently variables, degrees of freedom, coefficient vectors, and matrices for implicit sy...