PolyFEM
Loading...
Searching...
No Matches
PressureBoundaryVariableToSimulation.cpp
Go to the documentation of this file.
2
3#include <polyfem/Common.hpp>
11
12#include <Eigen/Core>
13
14#include <cassert>
15#include <string>
16#include <utility>
17#include <vector>
18#include <algorithm>
19
20namespace polyfem::solver
21{
22
24 VarFormPtrs varforms,
25 DiffCachePtrs diff_caches,
26 CompositeParametrization parametrizations,
27 Eigen::VectorXi active_boundary_ids,
28 Eigen::VectorXi active_time_slices)
29 : is_transient_(varforms[0]->get_problem().is_time_dependent()),
30 time_steps_(0),
31 varforms_(std::move(varforms)),
32 diff_caches_(std::move(diff_caches)),
33 parametrization_(std::move(parametrizations)),
34 active_boundary_ids_(std::move(active_boundary_ids)),
35 active_time_slices_(std::move(active_time_slices))
36 {
37 assert(!varforms_.empty());
38 assert(varforms_.size() == diff_caches_.size());
39
40 for (auto &varform : varforms_)
41 {
42 if (varform->get_problem().is_time_dependent() != is_transient_)
43 {
44 log_and_throw_adjoint_error("Fail to construct pressure boundary variable to simulation. Reason: inconsistent transient/static varforms.");
45 }
46 }
47
48 // time_step field might not be populated for static problem.
49 if (is_transient_)
50 {
51 time_steps_ = varforms_[0]->get_args()["time"]["time_steps"].get<int>();
52 }
53
54 // Expand implicit all-active boundary id selection (keep JSON order; no sort/unique pass).
55 if (active_boundary_ids_.size() == 0)
56 {
57 json boundary_json = varforms_[0]->get_args()["boundary_conditions"]["pressure_boundary"];
58 std::vector<int> tmp;
59 for (const json &bc : utils::json_as_array(boundary_json))
60 {
61 tmp.push_back(bc["id"].get<int>());
62 }
63
64 if (tmp.empty())
65 {
66 log_and_throw_adjoint_error("Fail to construct pressure boundary variable to simulation. Reason: No pressure boundary");
67 }
68
69 active_boundary_ids_ = Eigen::Map<Eigen::VectorXi>(tmp.data(), tmp.size());
70 }
71
72 // Expand implicit all-active time slice selection (transient only).
73 if (is_transient_ && active_time_slices_.size() == 0)
74 {
75 active_time_slices_ = Eigen::VectorXi::LinSpaced(time_steps_, 0, time_steps_ - 1);
76 }
77
78 // Validate expanded selections against every varform.
79 std::string reason;
81 {
82 log_and_throw_adjoint_error("Fail to construct pressure boundary variable to simulation. Reason: {}", reason);
83 }
85 {
86 log_and_throw_adjoint_error("Fail to construct pressure boundary variable to simulation. Reason: {}", reason);
87 }
88 }
89
91 {
92 return "pressure";
93 }
94
99
101 {
102 for (auto &varform : varforms_)
103 {
104 if (varform.get() == &target)
105 {
106 return true;
107 }
108 }
109 return false;
110 }
111
113 {
114 Eigen::VectorXd y = parametrization_.eval(x);
115 assert(y.size() == para_out_dof());
116
117 for (auto &varform : varforms_)
118 {
119 if (is_transient_)
120 {
121 for (int ti = 0; ti < active_time_slices_.size(); ++ti)
122 {
123 int time_step = active_time_slices_(ti) + 1;
124 for (int bi = 0; bi < active_boundary_ids_.size(); ++bi)
125 {
126 int boundary_id = active_boundary_ids_(bi);
127 varform->set_pressure_boundary(boundary_id, time_step, y(ti * active_boundary_ids_.size() + bi));
128 }
129 }
130 }
131 else
132 {
133 for (int bi = 0; bi < active_boundary_ids_.size(); ++bi)
134 {
135 int boundary_id = active_boundary_ids_(bi);
136 varform->set_pressure_boundary(boundary_id, /*time_step=*/1, y(bi));
137 }
138 }
139 }
140 }
141
142 void PressureBoundaryVariableToSimulation::update_state_variables(const Eigen::VectorXd &x, Eigen::VectorXd &state_variables) const
143 {
144 assert(state_variables.size() == para_out_dof());
145 state_variables = parametrization_.eval(x);
146 }
147
148 Eigen::VectorXd PressureBoundaryVariableToSimulation::compute_adjoint_term(const Eigen::VectorXd &x) const
149 {
150 Eigen::VectorXd term = Eigen::VectorXd::Zero(para_out_dof());
151 int bnum = active_boundary_ids_.size();
152
153 // AdjointTool helps take std::vector<int> instead of Eigen::Vector.
154 // Create temp to workaround this.
155 std::vector<int> tmp(active_boundary_ids_.data(), active_boundary_ids_.data() + bnum);
156
157 for (int si = 0; si < varforms_.size(); ++si)
158 {
159 auto &varform = varforms_[si];
160 auto &diff_cache = diff_caches_[si];
161
162 if (is_transient_)
163 {
164 Eigen::MatrixXd adjoint_p = get_adjoint_mat(*varform, *diff_cache, 0);
165 Eigen::MatrixXd adjoint_nu = get_adjoint_mat(*varform, *diff_cache, 1);
166
167 Eigen::VectorXd cur_term;
168 AdjointTools::dJ_pressure_transient_adjoint_term(*varform, *diff_cache, tmp, adjoint_nu, adjoint_p, cur_term);
169
170 assert(cur_term.size() == time_steps_ * bnum);
171 for (int ti = 0; ti < active_time_slices_.size(); ++ti)
172 {
173 int slice = active_time_slices_(ti);
174 term.segment(ti * bnum, bnum) += cur_term.segment(slice * bnum, bnum);
175 }
176 }
177 else
178 {
179 Eigen::MatrixXd adjoint_p = get_adjoint_mat(*varform, *diff_cache, 0);
180 Eigen::VectorXd cur_term;
181 AdjointTools::dJ_pressure_static_adjoint_term(*varform, tmp, diff_cache->u(0), adjoint_p, cur_term);
182 assert(cur_term.size() == bnum);
183 term += cur_term;
184 }
185 }
186
187 assert(term.size() == para_out_dof());
188 return parametrization_.apply_jacobian(term, x);
189 }
190
195
197 {
198 Eigen::VectorXd y = Eigen::VectorXd::Zero(para_out_dof());
199 int bnum = active_boundary_ids_.size();
200
201 json boundary_json = varforms_[0]->get_args()["boundary_conditions"]["pressure_boundary"];
202 std::vector<json> boundaries = utils::json_as_array(boundary_json);
203
204 for (int bi = 0; bi < bnum; ++bi)
205 {
206 int boundary_id = active_boundary_ids_(bi);
207
208 auto pred = [boundary_id](const json &bc) { return bc["id"].get<int>() == boundary_id; };
209 auto iter = std::find_if(boundaries.begin(), boundaries.end(), pred);
210 if (iter == boundaries.end())
211 {
212 logger().warn("Cannot find pressure boundary id {} in JSON; falling back to zero.", boundary_id);
213 continue;
214 }
215
216 const json &value = (*iter)["value"];
217 if (is_transient_)
218 {
219 Eigen::VectorXd pressures;
220 try
221 {
222 pressures = value;
223 }
224 catch (std::exception &err)
225 {
226 }
227
228 int required = time_steps_ + 1;
229 if (pressures.size() != required)
230 {
231 logger().warn("Unsupported initial value spec for pressure boundary id {}; falling back to zero.", boundary_id);
232 pressures = Eigen::VectorXd::Zero(required);
233 }
234
235 for (int ti = 0; ti < active_time_slices_.size(); ++ti)
236 {
237 int slice = active_time_slices_(ti);
238 y(ti * bnum + bi) = pressures(slice + 1);
239 }
240 }
241 else
242 {
243 if (value.is_number())
244 {
245 y(bi) = value.get<double>();
246 }
247 else
248 {
249 logger().warn("Unsupported initial value spec for pressure boundary id {}; falling back to zero.", boundary_id);
250 }
251 }
252 }
253
255 }
256
257 Eigen::VectorXd PressureBoundaryVariableToSimulation::apply_parametrization_jacobian(const Eigen::VectorXd &, const Eigen::VectorXd &) const
258 {
259 // Not implemented because there's no user
260 log_and_throw_adjoint_error("apply_parametrization_jacobian is not implemented in {} variable to simulation.", name());
261 }
262
267
268} // namespace polyfem::solver
int y
int x
Eigen::VectorXd apply_jacobian(const Eigen::VectorXd &grad_full, const Eigen::VectorXd &x) const override
Apply jacobian for chain rule.
Eigen::VectorXd inverse_eval(const Eigen::VectorXd &y) const override
Eval x = f^-1 (y).
int inverse_size(int y_size) const override
Compute DOF of x given DOF of y.
Eigen::VectorXd eval(const Eigen::VectorXd &x) const override
Eval y = f(x).
PressureBoundaryVariableToSimulation(VarFormPtrs varforms, DiffCachePtrs diff_caches, CompositeParametrization parametrizations, Eigen::VectorXi active_boundary_ids, Eigen::VectorXi active_time_slices)
Construct ShapeVariableToSimulation.
void update(const Eigen::VectorXd &x) override
Update forward simulation varforms from optimization variables.
Eigen::VectorXd compute_adjoint_term(const Eigen::VectorXd &x) const override
Compute adjoint contribution of objective gradient.
int inverse_dof() const override
Compute optimization variables dof.
Eigen::VectorXd apply_parametrization_jacobian(const Eigen::VectorXd &term, const Eigen::VectorXd &x) const override
Apply parametrization jacobian to compute the gradient w.r.t.
std::vector< std::shared_ptr< varform::DifferentiableVarForm > > VarFormPtrs
bool affects_varform(const varform::DifferentiableVarForm &target) const override
Return true if current var2sim maps to target varform.
void update_state_variables(const Eigen::VectorXd &x, Eigen::VectorXd &state_variables) const override
Update varform variables from optimization variables.
Eigen::VectorXd inverse_eval() const override
Compute optimization variables from forward simulation varform::DifferentiableVarForm.
Optimization-facing interface implemented by differentiated VarForm adapters.
void dJ_pressure_transient_adjoint_term(const varform::DifferentiableVarForm &varform, const DiffCache &diff_cache, const std::vector< int > &boundary_ids, const Eigen::MatrixXd &adjoint_nu, const Eigen::MatrixXd &adjoint_p, Eigen::VectorXd &one_form)
void dJ_pressure_static_adjoint_term(const varform::DifferentiableVarForm &varform, const std::vector< int > &boundary_ids, const Eigen::MatrixXd &sol, const Eigen::MatrixXd &adjoint, Eigen::VectorXd &one_form)
bool is_active_time_slices_valid(const Eigen::VectorXi &active_time_slices, const std::vector< std::shared_ptr< varform::DifferentiableVarForm > > &varforms, std::string &reason)
Validate active time slices selection given varforms.
bool is_active_pressure_boundary_ids_valid(const Eigen::VectorXi &active_boundary_ids, const std::vector< std::shared_ptr< varform::DifferentiableVarForm > > &varforms, std::string &reason)
Validate active pressure boundary ids selection given varforms.
std::vector< T > json_as_array(const json &j)
Return the value of a json object as an array.
Definition JSONUtils.hpp:41
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:44
nlohmann::json json
Definition Common.hpp:9
void log_and_throw_adjoint_error(const std::string &msg)
Definition Logger.cpp:79
Eigen::MatrixXd get_adjoint_mat(const varform::DifferentiableVarForm &varform, const DiffCache &diff_cache, int type)
Get adjoint parameter nu or p.