18 for (
int i = 0; i < constrained_dofs.Size(); i++) {
19 int j = constrained_dofs[i];
20 (*primal_field)[j] = (*bc_field_ptr)(j);
24 bc.setDofs(*primal_field, time);
29 std::vector<FieldState>
block_solve(
const std::vector<WeakForm*>& residual_evals,
30 const std::vector<std::vector<size_t>> block_indices,
const FieldState& shape_disp,
31 const std::vector<std::vector<FieldState>>& states,
32 const std::vector<std::vector<FieldState>>& params,
const TimeInfo& time_info,
34 const std::vector<const BoundaryConditionManager*>& bc_managers)
37 size_t num_rows_ = residual_evals.size();
39 SLIC_ERROR_IF(num_rows_ != block_indices.size(),
"Block indices size not consistent with number of residual rows");
40 SLIC_ERROR_IF(num_rows_ != states.size(),
41 "Number of state input vectors not consistent with number of residual rows");
42 SLIC_ERROR_IF(num_rows_ != params.size(),
43 "Number of parameter input vectors not consistent with number of residual rows");
44 SLIC_ERROR_IF(num_rows_ != bc_managers.size(),
45 "Number of boundary condition manager not consistent with number of residual rows");
47 for (
size_t r = 0; r < num_rows_; ++r) {
48 SLIC_ERROR_IF(num_rows_ != block_indices[r].
size(),
"All block index rows must have the same number of columns");
52 for (
size_t row = 0; row < num_rows_; ++row) {
53 for (
size_t col = 0; col < num_rows_; ++col) {
54 size_t idx = block_indices[row][col];
55 if (idx != invalid_block_index) {
56 SLIC_ERROR_IF(idx >= states[row].
size(),
57 axom::fmt::format(
"block_indices[{}][{}] = {} is out of bounds (states[{}].size() = {})", row,
58 col, idx, row, states[row].
size()));
63 block_indices[row][row] == invalid_block_index,
64 axom::fmt::format(
"block_indices[{}][{}] (diagonal entry) must not be invalid_block_index", row, row));
67 std::vector<size_t> num_state_inputs;
68 std::vector<gretl::StateBase> allFields;
69 for (
auto& ss : states) {
70 num_state_inputs.push_back(ss.size());
72 allFields.push_back(s);
75 std::vector<size_t> num_param_inputs;
76 for (
auto& ps : params) {
77 num_param_inputs.push_back(ps.size());
79 allFields.push_back(p);
82 allFields.push_back(shape_disp);
83 struct ZeroDualVectors {
84 std::vector<FEDualPtr> operator()(
const std::vector<FEFieldPtr>& fs)
86 std::vector<FEDualPtr> ds(fs.size());
87 for (
size_t i = 0; i < fs.size(); ++i) {
88 ds[i] = std::make_shared<FiniteElementDual>(fs[i]->
space(), fs[i]->name() +
"_dual");
95 shape_disp.create_state<std::vector<FEFieldPtr>, std::vector<FEDualPtr>>(allFields, ZeroDualVectors());
96 sol.set_eval([=](
const gretl::UpstreamStates& upstreams, gretl::DownstreamState& downstream) {
98 const size_t num_rows = num_state_inputs.size();
99 std::vector<std::vector<FEFieldPtr>> input_fields(num_rows);
100 SLIC_ERROR_IF(num_rows != num_param_inputs.size(),
"row count for params and states are inconsistent");
104 size_t field_count = 0;
105 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
106 for (
size_t state_i = 0; state_i < num_state_inputs[row_i]; ++state_i) {
107 input_fields[row_i].push_back(upstreams[field_count++].get<FEFieldPtr>());
110 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
111 for (
size_t param_i = 0; param_i < num_param_inputs[row_i]; ++param_i) {
112 input_fields[row_i].push_back(upstreams[field_count++].get<FEFieldPtr>());
116 std::vector<FEFieldPtr> diagonal_fields(num_rows);
117 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
118 size_t prime_unknown_row_i = block_indices[row_i][row_i];
119 SLIC_ERROR_IF(prime_unknown_row_i == invalid_block_index,
120 "The primary unknown field (field index for block_index[n][n], must not be invalid)");
121 diagonal_fields[row_i] = std::make_shared<FiniteElementState>(*input_fields[row_i][prime_unknown_row_i]);
124 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
125 FEFieldPtr primal_field_row_i = diagonal_fields[row_i];
129 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
130 for (
size_t col_j = 0; col_j < num_rows; ++col_j) {
131 size_t prime_unknown_ij = block_indices[row_i][col_j];
132 if (prime_unknown_ij != invalid_block_index) {
133 input_fields[row_i][block_indices[row_i][col_j]] = diagonal_fields[col_j];
140 auto eval_residuals = [=](
const std::vector<FEFieldPtr>& unknowns) {
141 SLIC_ERROR_IF(unknowns.size() != num_rows,
142 "block solver unknowns size must match the number or residuals in block_solve");
143 std::vector<mfem::Vector> residuals(num_rows);
145 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
146 FEFieldPtr primal_field_row_i = diagonal_fields[row_i];
147 *primal_field_row_i = *unknowns[row_i];
150 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
151 residuals[row_i] = residual_evals[row_i]->residual(time_info, shape_disp_ptr.get(),
153 if (bc_managers[row_i]) {
154 residuals[row_i].SetSubVector(bc_managers[row_i]->allEssentialTrueDofs(), 0.0);
160 auto eval_jacobians = [=](
const std::vector<FEFieldPtr>& unknowns) {
161 SLIC_ERROR_IF(unknowns.size() != num_rows,
162 "block solver unknown size must match the number or residuals in block_solve");
163 std::vector<std::vector<std::unique_ptr<mfem::HypreParMatrix>>> jacobians(num_rows);
165 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
166 FEFieldPtr primal_field_row_i = diagonal_fields[row_i];
167 *primal_field_row_i = *unknowns[row_i];
171 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
172 std::vector<FEFieldPtr> row_field_inputs = input_fields[row_i];
173 std::vector<double> tangent_weights(row_field_inputs.size(), 0.0);
174 for (
size_t col_j = 0; col_j < num_rows; ++col_j) {
175 size_t field_index_to_diff = block_indices[row_i][col_j];
176 if (field_index_to_diff != invalid_block_index) {
177 tangent_weights[field_index_to_diff] = 1.0;
178 auto jac_ij = residual_evals[row_i]->jacobian(time_info, shape_disp_ptr.get(),
180 jacobians[row_i].emplace_back(std::move(jac_ij));
181 tangent_weights[field_index_to_diff] = 0.0;
183 jacobians[row_i].emplace_back(
nullptr);
189 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
190 if (!bc_managers[row_i]) {
193 if (jacobians[row_i][row_i]) {
194 jacobians[row_i][row_i]->EliminateBC(bc_managers[row_i]->allEssentialTrueDofs(),
195 mfem::Operator::DiagonalPolicy::DIAG_ONE);
197 for (
size_t col_j = 0; col_j < num_rows; ++col_j) {
198 if (col_j != row_i) {
199 if (jacobians[row_i][col_j]) {
200 jacobians[row_i][col_j]->EliminateRows(bc_managers[row_i]->allEssentialTrueDofs());
202 if (jacobians[col_j][row_i]) {
203 mfem::HypreParMatrix* Jji =
204 jacobians[col_j][row_i]->EliminateCols(bc_managers[row_i]->allEssentialTrueDofs());
213 diagonal_fields = solver->
solve(diagonal_fields, eval_residuals, eval_jacobians);
215 downstream.set<std::vector<FEFieldPtr>, std::vector<FEDualPtr>>(diagonal_fields);
220 sol.set_vjp([=](gretl::UpstreamStates& upstreams,
const gretl::DownstreamState& downstream) {
222 const std::vector<FEFieldPtr> s = downstream.get<std::vector<FEFieldPtr>>();
223 const std::vector<FEDualPtr> s_dual =
224 downstream.get_dual<std::vector<FEDualPtr>, std::vector<FEFieldPtr>>();
226 const size_t num_rows = num_state_inputs.size();
227 SLIC_ERROR_IF(s_dual.size() != num_rows,
228 "block solver vjp downstream size must match the number or residuals in block_solve");
230 std::vector<std::vector<FEFieldPtr>> input_fields(num_rows);
231 size_t field_count = 0;
232 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
233 for (
size_t state_i = 0; state_i < num_state_inputs[row_i]; ++state_i) {
234 input_fields[row_i].push_back(upstreams[field_count++].get<FEFieldPtr>());
237 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
238 for (
size_t param_i = 0; param_i < num_param_inputs[row_i]; ++param_i) {
239 input_fields[row_i].push_back(upstreams[field_count++].get<FEFieldPtr>());
245 std::vector<FEFieldPtr> diagonal_fields(num_rows);
246 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
247 diagonal_fields[row_i] = std::make_shared<FiniteElementState>(*input_fields[row_i][block_indices[row_i][row_i]]);
248 *diagonal_fields[row_i] = *s[row_i];
251 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
252 for (
size_t col_j = 0; col_j < num_rows; ++col_j) {
253 if (block_indices[row_i][col_j] != invalid_block_index) {
254 input_fields[row_i][block_indices[row_i][col_j]] = diagonal_fields[col_j];
263 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
264 FEFieldPtr primal_field_row_i = diagonal_fields[row_i];
270 std::vector<std::vector<std::unique_ptr<mfem::HypreParMatrix>>> jacobians(num_rows);
271 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
272 std::vector<FEFieldPtr> row_field_inputs = input_fields[row_i];
273 std::vector<double> tangent_weights(row_field_inputs.size(), 0.0);
274 for (
size_t col_j = 0; col_j < num_rows; ++col_j) {
275 size_t field_index_to_diff = block_indices[row_i][col_j];
276 if (field_index_to_diff != invalid_block_index) {
277 tangent_weights[field_index_to_diff] = 1.0;
278 auto jac_ij = residual_evals[row_i]->jacobian(time_info, shape_disp_ptr.get(),
280 jacobians[row_i].emplace_back(std::move(jac_ij));
281 tangent_weights[field_index_to_diff] = 0.0;
283 jacobians[row_i].emplace_back(
nullptr);
289 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
290 if (!bc_managers[row_i]) {
293 s_dual[row_i]->SetSubVector(bc_managers[row_i]->allEssentialTrueDofs(), 0.0);
295 mfem::HypreParMatrix* Jii =
296 jacobians[row_i][row_i]->EliminateRowsCols(bc_managers[row_i]->allEssentialTrueDofs());
298 for (
size_t col_j = 0; col_j < num_rows; ++col_j) {
299 if (col_j != row_i) {
300 if (jacobians[row_i][col_j]) {
301 jacobians[row_i][col_j]->EliminateRows(bc_managers[row_i]->allEssentialTrueDofs());
303 if (jacobians[col_j][row_i]) {
304 mfem::HypreParMatrix* Jji =
305 jacobians[col_j][row_i]->EliminateCols(bc_managers[row_i]->allEssentialTrueDofs());
313 std::vector<std::vector<std::unique_ptr<mfem::HypreParMatrix>>> jacobians_T(num_rows);
314 for (
size_t col_j = 0; col_j < num_rows; ++col_j) {
315 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
316 if (jacobians[row_i][col_j]) {
317 jacobians_T[col_j].emplace_back(std::unique_ptr<mfem::HypreParMatrix>(jacobians[row_i][col_j]->Transpose()));
319 jacobians_T[col_j].emplace_back(
nullptr);
323 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
324 for (
size_t col_j = 0; col_j < num_rows; ++col_j) {
325 jacobians[row_i][col_j].reset();
329 std::vector<FEFieldPtr> adjoint_fields(num_rows);
330 adjoint_fields = solver->
solveAdjoint(s_dual, jacobians_T);
331 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
332 *adjoint_fields[row_i] *= -1.0;
336 std::vector<std::vector<FEDualPtr>> field_sensitivities(num_rows);
338 size_t dual_index = 0;
339 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
340 for (
size_t state_i = 0; state_i < num_state_inputs[row_i]; ++state_i) {
341 field_sensitivities[row_i].push_back(upstreams[dual_index++].get_dual<FEDualPtr, FEFieldPtr>());
344 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
345 for (
size_t param_i = 0; param_i < num_param_inputs[row_i]; ++param_i) {
346 field_sensitivities[row_i].push_back(upstreams[dual_index++].get_dual<FEDualPtr, FEFieldPtr>());
349 SLIC_ERROR_IF(field_count != dual_index,
"Number of sensitivities must equal to number of upstreams");
352 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
353 for (
size_t col_j = 0; col_j < num_rows; ++col_j) {
354 if (block_indices[row_i][col_j] != invalid_block_index) {
355 field_sensitivities[row_i][block_indices[row_i][col_j]] =
nullptr;
360 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
361 residual_evals[row_i]->vjp(time_info, shape_disp_ptr.get(),
getConstFieldPointers(input_fields[row_i]), {},
362 adjoint_fields[row_i].get(), shape_disp_sensitivity.get(),
371 std::vector<FieldState> results;
372 for (
size_t i = 0; i < num_rows_; ++i) {
373 FieldState s = gretl::create_state<FEFieldPtr, FEDualPtr>(
375 [i](
const std::vector<FEFieldPtr>& sols) {
376 auto state_copy = std::make_shared<FiniteElementState>(sols[i]->
space(), sols[i]->name());
377 *state_copy = *sols[i];
380 [i](
const std::vector<FEFieldPtr>&,
const FEFieldPtr&, std::vector<FEDualPtr>& sols_,
381 const FEDualPtr& output_) { *sols_[i] += *output_; },
384 results.emplace_back(s);
This file contains the declaration of the boundary condition manager class.
A container for the boundary condition information relating to a specific physics module.
const mfem::Array< int > & allEssentialTrueDofs() const
Returns all the true degrees of freedom associated with all the essential BCs.
std::vector< BoundaryCondition > & essentials()
Accessor for the essential BC objects.
Abstract interface for nonlinear block solvers that provide both forward and adjoint solves.
virtual std::vector< FieldPtr > solveAdjoint(const std::vector< DualPtr > &u_bars, std::vector< std::vector< MatrixPtr >> &jacobian_transposed) const =0
Solve the (linear) adjoint set of equations with a vector of FiniteElementState as unknown.
virtual void clearMemory() const
Interface option to clear memory between solves to avoid high-water mark memory usage.
virtual std::vector< FieldPtr > solve(const std::vector< FieldPtr > &u_guesses, std::function< std::vector< mfem::Vector >(const std::vector< FieldPtr > &)> residuals, std::function< std::vector< std::vector< MatrixPtr >>(const std::vector< FieldPtr > &)> jacobians) const =0
Solve a set of equations with a vector of FiniteElementState as unknown.
Defines common types and helper functions for using the residual and scalar_objective classes.
Accelerator functionality.
std::shared_ptr< FiniteElementState > FEFieldPtr
typedef
gretl::State< std::vector< FEFieldPtr >, std::vector< FEDualPtr > > FieldVecState
typedef
void applyBoundaryConditions(double time, const smith::BoundaryConditionManager *bc_manager, smith::FEFieldPtr &primal_field, const smith::FEFieldPtr &bc_field_ptr)
apply boundary conditions
std::vector< FiniteElementState * > getFieldPointers(std::vector< FieldState > &states, std::vector< FieldState > params={})
Get a vector of FieldPtr or DualFieldPtr from a vector of FieldState.
gretl::State< FEFieldPtr, FEDualPtr > FieldState
typedef
constexpr SMITH_HOST_DEVICE int size(const tensor< T, n... > &)
returns the total number of stored values in a tensor
std::shared_ptr< FiniteElementDual > FEDualPtr
typedef
std::vector< const FiniteElementState * > getConstFieldPointers(const std::vector< FieldState > &states, const std::vector< FieldState > ¶ms={})
Get a vector of ConstFieldPtr or ConstDualFieldPtr from a vector of FieldState.
mfem::ParFiniteElementSpace & space(FieldState field)
Get the space from the primal field of a field states.
std::vector< FieldState > block_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 >> ¶ms, const TimeInfo &time_info, const NonlinearBlockSolverBase *solver, const std::vector< const BoundaryConditionManager * > &bc_managers)
Solve a block nonlinear system of equations as defined by the vector of weak form.
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 ...
#define SMITH_MARK_FUNCTION
#define SMITH_MARK_BEGIN(name)
#define SMITH_MARK_END(name)
struct storing time and timestep information
double time() const
accessor for the current time
functor which takes a std::shared_ptr<FiniteElementState>, and returns a zero-valued std::shared_ptr<...