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 input_fields[row_i][block_indices[row_i][col_j]] = diagonal_fields[col_j];
261 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
262 FEFieldPtr primal_field_row_i = diagonal_fields[row_i];
268 std::vector<std::vector<std::unique_ptr<mfem::HypreParMatrix>>> jacobians(num_rows);
269 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
270 std::vector<FEFieldPtr> row_field_inputs = input_fields[row_i];
271 std::vector<double> tangent_weights(row_field_inputs.size(), 0.0);
272 for (
size_t col_j = 0; col_j < num_rows; ++col_j) {
273 size_t field_index_to_diff = block_indices[row_i][col_j];
274 if (field_index_to_diff != invalid_block_index) {
275 tangent_weights[field_index_to_diff] = 1.0;
276 auto jac_ij = residual_evals[row_i]->jacobian(time_info, shape_disp_ptr.get(),
278 jacobians[row_i].emplace_back(std::move(jac_ij));
279 tangent_weights[field_index_to_diff] = 0.0;
281 jacobians[row_i].emplace_back(
nullptr);
287 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
288 if (!bc_managers[row_i]) {
291 s_dual[row_i]->SetSubVector(bc_managers[row_i]->allEssentialTrueDofs(), 0.0);
293 mfem::HypreParMatrix* Jii =
294 jacobians[row_i][row_i]->EliminateRowsCols(bc_managers[row_i]->allEssentialTrueDofs());
296 for (
size_t col_j = 0; col_j < num_rows; ++col_j) {
297 if (col_j != row_i) {
298 if (jacobians[row_i][col_j]) {
299 jacobians[row_i][col_j]->EliminateRows(bc_managers[row_i]->allEssentialTrueDofs());
301 if (jacobians[col_j][row_i]) {
302 mfem::HypreParMatrix* Jji =
303 jacobians[col_j][row_i]->EliminateCols(bc_managers[row_i]->allEssentialTrueDofs());
311 std::vector<std::vector<std::unique_ptr<mfem::HypreParMatrix>>> jacobians_T(num_rows);
312 for (
size_t col_j = 0; col_j < num_rows; ++col_j) {
313 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
314 if (jacobians[row_i][col_j]) {
315 jacobians_T[col_j].emplace_back(std::unique_ptr<mfem::HypreParMatrix>(jacobians[row_i][col_j]->Transpose()));
317 jacobians_T[col_j].emplace_back(
nullptr);
321 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
322 for (
size_t col_j = 0; col_j < num_rows; ++col_j) {
323 jacobians[row_i][col_j].reset();
327 std::vector<FEFieldPtr> adjoint_fields(num_rows);
328 adjoint_fields = solver->
solveAdjoint(s_dual, jacobians_T);
329 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
330 *adjoint_fields[row_i] *= -1.0;
334 std::vector<std::vector<FEDualPtr>> field_sensitivities(num_rows);
336 size_t dual_index = 0;
337 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
338 for (
size_t state_i = 0; state_i < num_state_inputs[row_i]; ++state_i) {
339 field_sensitivities[row_i].push_back(upstreams[dual_index++].get_dual<FEDualPtr, FEFieldPtr>());
342 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
343 for (
size_t param_i = 0; param_i < num_param_inputs[row_i]; ++param_i) {
344 field_sensitivities[row_i].push_back(upstreams[dual_index++].get_dual<FEDualPtr, FEFieldPtr>());
347 SLIC_ERROR_IF(field_count != dual_index,
"Number of sensitivities must equal to number of upstreams");
350 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
351 for (
size_t col_j = 0; col_j < num_rows; ++col_j) {
352 field_sensitivities[row_i][block_indices[row_i][col_j]] =
nullptr;
356 for (
size_t row_i = 0; row_i < num_rows; ++row_i) {
357 residual_evals[row_i]->vjp(time_info, shape_disp_ptr.get(),
getConstFieldPointers(input_fields[row_i]), {},
358 adjoint_fields[row_i].get(), shape_disp_sensitivity.get(),
367 std::vector<FieldState> results;
368 for (
size_t i = 0; i < num_rows_; ++i) {
369 FieldState s = gretl::create_state<FEFieldPtr, FEDualPtr>(
371 [i](
const std::vector<FEFieldPtr>& sols) {
372 auto state_copy = std::make_shared<FiniteElementState>(sols[i]->
space(), sols[i]->name());
373 *state_copy = *sols[i];
376 [i](
const std::vector<FEFieldPtr>&,
const FEFieldPtr&, std::vector<FEDualPtr>& sols_,
377 const FEDualPtr& output_) { *sols_[i] += *output_; },
380 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<...