Smith  0.1
Smith is an implicit thermal structural mechanics simulation code.
multiphysics_time_integrator.cpp
1 // Copyright (c) Lawrence Livermore National Security, LLC and
2 // other Smith Project Developers. See the top-level LICENSE file for
3 // details.
4 //
5 // SPDX-License-Identifier: (BSD-3-Clause)
6 
7 #include "smith/differentiable_numerics/multiphysics_time_integrator.hpp"
10 #include "smith/differentiable_numerics/coupled_system_solver.hpp"
13 
14 #include <algorithm>
15 #include <stdexcept>
16 
17 namespace smith {
18 
19 MultiphysicsTimeIntegrator::MultiphysicsTimeIntegrator(std::shared_ptr<FieldStore> field_store,
20  const std::vector<std::shared_ptr<WeakForm>>& weak_forms,
21  std::shared_ptr<smith::CoupledSystemSolver> solver,
22  std::shared_ptr<WeakForm> cycle_zero_weak_form,
23  std::shared_ptr<smith::CoupledSystemSolver> cycle_zero_solver)
24  : field_store_(field_store),
25  weak_forms_(weak_forms),
26  solver_(solver),
27  cycle_zero_weak_form_(cycle_zero_weak_form),
28  cycle_zero_solver_(cycle_zero_solver)
29 {
30 }
31 
32 std::pair<std::vector<FieldState>, std::vector<ReactionState>> MultiphysicsTimeIntegrator::advanceState(
33  const TimeInfo& time_info, const FieldState& shape_disp, const std::vector<FieldState>& states,
34  const std::vector<FieldState>& params) const
35 {
36  std::vector<FieldState> current_states = states;
37 
38  // Handle initial acceleration solve at cycle 0
39  const bool requires_cycle_zero_solve =
40  std::any_of(field_store_->getTimeIntegrationRules().begin(), field_store_->getTimeIntegrationRules().end(),
41  [](const auto& rule_and_mapping) {
42  return rule_and_mapping.first && rule_and_mapping.first->requiresInitialAccelerationSolve();
43  });
44  if (time_info.cycle() == 0 && cycle_zero_weak_form_ && requires_cycle_zero_solve) {
45  for (size_t i = 0; i < current_states.size(); ++i) {
46  field_store_->setField(i, current_states[i]);
47  }
48 
49  std::string test_field_name = field_store_->getWeakFormReaction(cycle_zero_weak_form_->name());
50  std::vector<FieldState> wf_fields = field_store_->getStates(cycle_zero_weak_form_->name());
51 
52  FieldState test_field = field_store_->getField(test_field_name);
53  size_t test_field_idx_in_wf = invalid_block_index;
54  for (size_t j = 0; j < wf_fields.size(); ++j) {
55  if (wf_fields[j].get() == test_field.get()) {
56  test_field_idx_in_wf = j;
57  break;
58  }
59  }
60  SLIC_ERROR_IF(test_field_idx_in_wf == invalid_block_index, "Test field '" << test_field_name
61  << "' not found in cycle-zero weak form '"
62  << cycle_zero_weak_form_->name() << "'");
63 
64  std::vector<WeakForm*> wf_ptrs = {cycle_zero_weak_form_.get()};
65  std::vector<std::vector<size_t>> block_indices = {{test_field_idx_in_wf}};
66 
67  std::vector<const BoundaryConditionManager*> bcs;
68  auto all_bcs = field_store_->getBoundaryConditionManagers();
69  size_t test_field_unknown_idx = invalid_block_index;
70  try {
71  test_field_unknown_idx = field_store_->getUnknownIndex(test_field_name);
72  } catch (const std::out_of_range&) {
73  for (const auto& [rule, mapping] : field_store_->getTimeIntegrationRules()) {
74  static_cast<void>(rule);
75  if (mapping.primary_name == test_field_name || mapping.history_name == test_field_name ||
76  mapping.dot_name == test_field_name || mapping.ddot_name == test_field_name) {
77  test_field_unknown_idx = field_store_->getUnknownIndex(mapping.primary_name);
78  break;
79  }
80  }
81  }
82  SLIC_ERROR_IF(test_field_unknown_idx == invalid_block_index,
83  "Could not map cycle-zero test field '" << test_field_name << "' to an independent unknown.");
84  SLIC_ERROR_IF(test_field_unknown_idx >= all_bcs.size(),
85  "Cycle-zero test field '" << test_field_name << "' has unknown index " << test_field_unknown_idx
86  << ", but only " << all_bcs.size() << " BC managers are registered.");
87  bcs.push_back(all_bcs[test_field_unknown_idx]);
88 
89  std::vector<std::vector<FieldState>> states_vec = {wf_fields};
90  std::vector<std::vector<FieldState>> params_vec = {params};
91 
92  auto& cz_solver = cycle_zero_solver_ ? cycle_zero_solver_ : solver_;
93  auto result = cz_solver->solve(wf_ptrs, block_indices, shape_disp, states_vec, params_vec, time_info, bcs);
94 
95  size_t test_field_state_idx = field_store_->getFieldIndex(test_field_name);
96  current_states[test_field_state_idx] = result[0];
97  }
98 
99  // Sync FieldStore with (possibly updated) states
100  for (size_t i = 0; i < current_states.size(); ++i) {
101  field_store_->setField(i, current_states[i]);
102  }
103 
104  std::vector<FieldState> primary_unknowns = solve(weak_forms_, *field_store_, solver_.get(), time_info, params);
105 
106  // Create states for reaction computation: newly solved primary unknowns + current states
107  std::vector<FieldState> states_for_reactions = current_states;
108  for (const auto& [rule, mapping] : field_store_->getTimeIntegrationRules()) {
109  size_t u_idx = field_store_->getFieldIndex(mapping.primary_name);
110  size_t unknown_idx = field_store_->getUnknownIndex(mapping.primary_name);
111  FieldState u_new = primary_unknowns[unknown_idx];
112  states_for_reactions[u_idx] = u_new;
113  }
114 
115  // Compute reactions using newly solved unknowns but BEFORE time integration state updates
116  std::vector<ReactionState> reactions;
117  for (const auto& wf : weak_forms_) {
118  std::vector<FieldState> wf_fields = field_store_->getStatesFromVectors(wf->name(), states_for_reactions, params);
119  std::string test_field_name = field_store_->getWeakFormReaction(wf->name());
120  size_t test_field_idx = field_store_->getFieldIndex(test_field_name);
121  FieldState test_field = states_for_reactions[test_field_idx];
122  reactions.push_back(smith::evaluateWeakForm(wf, time_info, shape_disp, wf_fields, test_field));
123  }
124 
125  // Now do time integration to compute corrected velocities/accelerations and update all states
126  const auto& all_current_states = field_store_->getAllFields();
127  std::vector<FieldState> new_states = current_states;
128  for (size_t i = 0; i < current_states.size(); ++i) {
129  new_states[i] = all_current_states[i];
130  }
131 
132  for (const auto& [rule, mapping] : field_store_->getTimeIntegrationRules()) {
133  size_t u_idx = field_store_->getFieldIndex(mapping.primary_name);
134  size_t unknown_idx = field_store_->getUnknownIndex(mapping.primary_name);
135  FieldState u_new = primary_unknowns[unknown_idx];
136  new_states[u_idx] = u_new;
137 
138  std::vector<FieldState> rule_inputs;
139  rule_inputs.push_back(u_new); // u_{n+1}
140  if (rule->num_args() >= 2) {
141  rule_inputs.push_back(current_states[u_idx]); // u_n
142  }
143 
144  if (rule->num_args() >= 3 && !mapping.dot_name.empty()) {
145  size_t v_idx = field_store_->getFieldIndex(mapping.dot_name);
146  rule_inputs.push_back(current_states[v_idx]);
147  }
148 
149  if (rule->num_args() >= 4 && !mapping.ddot_name.empty()) {
150  size_t a_idx = field_store_->getFieldIndex(mapping.ddot_name);
151  rule_inputs.push_back(current_states[a_idx]);
152  }
153 
154  if (!mapping.dot_name.empty()) {
155  size_t v_idx = field_store_->getFieldIndex(mapping.dot_name);
156  new_states[v_idx] = rule->corrected_dot(time_info, rule_inputs);
157  }
158 
159  if (!mapping.ddot_name.empty()) {
160  size_t a_idx = field_store_->getFieldIndex(mapping.ddot_name);
161  new_states[a_idx] = rule->corrected_ddot(time_info, rule_inputs);
162  }
163 
164  if (!mapping.history_name.empty()) {
165  size_t hist_idx = field_store_->getFieldIndex(mapping.history_name);
166  new_states[hist_idx] = u_new;
167  }
168  }
169 
170  return {new_states, reactions};
171 }
172 
173 std::vector<FieldState> solve(const std::vector<std::shared_ptr<WeakForm>>& weak_forms, const FieldStore& field_store,
174  const CoupledSystemSolver* solver, const TimeInfo& time_info,
175  const std::vector<FieldState>& params)
176 {
177  std::vector<std::string> weak_form_names;
178  for (const auto& wf : weak_forms) {
179  weak_form_names.push_back(wf->name());
180  }
181  std::vector<std::vector<size_t>> index_map = field_store.indexMap(weak_form_names);
182 
183  std::vector<std::vector<FieldState>> inputs;
184  for (size_t i = 0; i < weak_forms.size(); ++i) {
185  std::string wf_name = weak_forms[i]->name();
186  std::vector<FieldState> fields_for_wk = field_store.getStates(wf_name);
187  inputs.push_back(fields_for_wk);
188  }
189  std::vector<std::vector<FieldState>> wk_params(weak_forms.size(), params);
190 
191  std::vector<WeakForm*> weak_form_ptrs;
192  for (auto& p : weak_forms) {
193  weak_form_ptrs.push_back(p.get());
194  }
195  return solver->solve(weak_form_ptrs, index_map, field_store.getShapeDisp(), inputs, wk_params, time_info,
196  field_store.getBoundaryConditionManagers());
197 }
198 
199 } // namespace smith
Orchestrates staggered solution for multiphysics systems.
std::vector< FieldState > solve(const std::vector< WeakForm * > &residual_evals, const std::vector< std::vector< size_t >> &block_indices, const FieldState &shape_disp, const std::vector< std::vector< FieldState >> &states, const std::vector< std::vector< FieldState >> &params, const TimeInfo &time_info, const std::vector< const BoundaryConditionManager * > &bc_managers) const
Solves the multiphysics system using staggered iterations.
std::pair< std::vector< FieldState >, std::vector< ReactionState > > advanceState(const TimeInfo &time_info, const FieldState &shape_disp, const std::vector< FieldState > &states, const std::vector< FieldState > &params) const override
Advance the multiphysics state by one time step.
MultiphysicsTimeIntegrator(std::shared_ptr< FieldStore > field_store, const std::vector< std::shared_ptr< WeakForm >> &weak_forms, std::shared_ptr< smith::CoupledSystemSolver > solver, std::shared_ptr< WeakForm > cycle_zero_weak_form=nullptr, std::shared_ptr< smith::CoupledSystemSolver > cycle_zero_solver=nullptr)
Construct a new MultiphysicsTimeIntegrator object.
Contains DirichletBoundaryConditions class for interaction with the differentiable solve interfaces.
Accelerator functionality.
Definition: smith.cpp:36
constexpr T & get(variant< T0, T1 > &v)
Returns the variant member of specified type.
Definition: variant.hpp:338
auto evaluateWeakForm(const std::shared_ptr< WeakForm > &weak_form, const TimeInfo &time_info, FieldState shape_disp, const std::vector< FieldState > &field_states, FieldState field_for_residual_space)
gretl-function implementation which evaluates the residual force (which is minus the mechanical force...
Definition: reaction.hpp:26
gretl::State< FEFieldPtr, FEDualPtr > FieldState
typedef
Definition: field_state.hpp:22
std::vector< FieldState > solve(const std::vector< std::shared_ptr< WeakForm >> &weak_forms, const FieldStore &field_store, const CoupledSystemSolver *solver, const TimeInfo &time_info, const std::vector< FieldState > &params)
Solve a set of weak forms.
This file contains nonlinear block solver interfaces and helpers.
Methods for solving systems of equations as given by WeakForms. Tracks these operations on the gretl ...
Reaction class which is a names combination of a weak form and a set of dirichlet constrained nodes.
Manages storage and metadata for fields, parameters, and weak forms.
Definition: field_store.hpp:44
std::vector< std::vector< size_t > > indexMap(const std::vector< std::string > &residual_names) const
Generate an index map for the residuals.
Definition: field_store.cpp:59
std::vector< const BoundaryConditionManager * > getBoundaryConditionManagers() const
Get the boundary condition managers for all independent fields.
Definition: field_store.cpp:80
std::vector< FieldState > getStates(const std::string &weak_form_name) const
Get the state fields associated with a weak form.
FieldState getShapeDisp() const
Get the shape displacement field.
struct storing time and timestep information
Definition: common.hpp:18
size_t cycle() const
accessor for cycle
Definition: common.hpp:32