PolyFEM
Loading...
Searching...
No Matches
GenericProblem.cpp
Go to the documentation of this file.
1#include "GenericProblem.hpp"
2
6
7namespace polyfem
8{
9 using namespace utils;
10
11 namespace assembler
12 {
13 namespace
14 {
15 bool matches_fe_space(const int entry_fe_space_id, const int fe_space_id)
16 {
17 return entry_fe_space_id < 0 || fe_space_id < 0 || entry_fe_space_id == fe_space_id;
18 }
19
20 int fe_space_id(const json &entry)
21 {
22 return entry.value("fe_space", -1);
23 }
24
25 bool is_body_value_entry(const json &entry)
26 {
27 if (!entry.is_object() || !entry.contains("id") || !entry.contains("value"))
28 return false;
29
30 const json &id = entry["id"];
31 if (id.is_array())
32 {
33 for (const json &value : id)
34 if (value.is_number_integer() && value.get<int>() != -1)
35 return true;
36 return false;
37 }
38
39 return id.is_number_integer() && id.get<int>() != -1;
40 }
41
42 bool has_body_value_entries(const json &value)
43 {
44 return is_body_value_entry(value) || (value.is_array() && !value.empty() && is_body_value_entry(value.front()));
45 }
46
47 template <typename Map>
48 const typename Map::mapped_type *find_for_fe_space(const Map &values, const int fe_space_id)
49 {
50 if (const auto it = values.find(fe_space_id); it != values.end())
51 return &it->second;
52 if (const auto it = values.find(-1); it != values.end())
53 return &it->second;
54 if (fe_space_id < 0 && values.size() == 1)
55 return &values.begin()->second;
56 return nullptr;
57 }
58
59 std::vector<json> flatten_ids(const json &p_j_boundary_tmp)
60 {
61 const std::vector<json> j_boundary_tmp = utils::json_as_array(p_j_boundary_tmp);
62
63 std::vector<json> j_boundary;
64
65 for (size_t i = 0; i < j_boundary_tmp.size(); ++i)
66 {
67 const auto &tmp = j_boundary_tmp[i];
68
69 if (tmp.is_string())
70 {
71 j_boundary.push_back(tmp);
72 continue;
73 }
74 if (!tmp.contains("id"))
75 continue;
76
77 if (tmp["id"].is_array())
78 {
79 for (size_t j = 0; j < tmp["id"].size(); ++j)
80 {
81 json newj = tmp;
82 newj["id"] = tmp["id"][j].get<int>();
83 j_boundary.push_back(newj);
84 }
85 }
86 else
87 j_boundary.push_back(tmp);
88 }
89
90 return j_boundary;
91 }
92 } // namespace
93
94 double TensorBCValue::eval(const RowVectorNd &pts, const int dim, const double t, const int el_id) const
95 {
96 double x = pts(0);
97 double y = pts(1);
98 double z = pts.size() == 2 ? 0 : pts(2);
99
100 double val = value[dim](x, y, z, t, el_id);
101
102 if (interpolation.empty())
103 {
104 }
105 else if (interpolation.size() == 1)
106 val *= interpolation[0]->eval(t);
107 else
108 {
109 assert(dim < interpolation.size());
110 val *= interpolation[dim]->eval(t);
111 }
112
113 return val;
114 }
115
116 double ScalarBCValue::eval(const RowVectorNd &pts, const double t) const
117 {
118 assert(pts.size() == 2 || pts.size() == 3);
119 double x = pts(0), y = pts(1), z = pts.size() == 3 ? pts(2) : 0.0;
120 return value(x, y, z, t) * interpolation->eval(t);
121 }
122
124 : Problem(name), is_all_(false)
125 {
126 }
127
128 void GenericTensorProblem::set_units(const assembler::Assembler &assembler, const Units &units)
129 {
130 if (assembler.is_fluid())
131 {
132 for (int i = 0; i < 3; ++i)
133 {
134 for (auto &[fe_space_id, rhs] : rhs_)
135 rhs[i].set_unit_type(units.force());
136 exact_[i].set_unit_type(units.velocity());
137 }
138 for (int i = 0; i < 3; ++i)
139 exact_grad_[i].set_unit_type("");
140
141 for (auto &v : displacements_)
142 v.set_unit_type(units.velocity());
143
144 for (auto &v : forces_)
145 v.set_unit_type(units.force());
146
147 for (auto &v : normal_aligned_forces_)
148 v.set_unit_type(units.pressure());
149
150 for (auto &v : pressures_)
151 v.set_unit_type(units.pressure());
152
153 for (auto &v : cavity_pressures_)
154 v.second.set_unit_type(units.pressure());
155
156 for (auto &v : initial_position_)
157 for (int i = 0; i < 3; ++i)
158 v.value[i].set_unit_type(units.velocity());
159
160 for (auto &v : initial_velocity_)
161 for (int i = 0; i < 3; ++i)
162 v.value[i].set_unit_type(units.velocity());
163
164 for (auto &v : initial_acceleration_)
165 for (int i = 0; i < 3; ++i)
166 v.value[i].set_unit_type(units.acceleration());
167
168 for (auto &v : nodal_dirichlet_)
169 v.second.set_unit_type(units.velocity());
170
171 for (auto &v : nodal_neumann_)
172 v.second.set_unit_type(units.force());
173 }
174 else
175 {
176 for (int i = 0; i < 3; ++i)
177 {
178 for (auto &[fe_space_id, rhs] : rhs_)
179 rhs[i].set_unit_type(units.acceleration());
180 exact_[i].set_unit_type(units.length());
181 }
182 for (int i = 0; i < 3; ++i)
183 exact_grad_[i].set_unit_type("");
184
185 for (auto &v : displacements_)
186 v.set_unit_type(units.length());
187
188 for (auto &v : forces_)
189 v.set_unit_type(units.force());
190
191 for (auto &v : normal_aligned_forces_)
192 v.set_unit_type(units.pressure());
193
194 for (auto &v : pressures_)
195 v.set_unit_type(units.pressure());
196
197 for (auto &v : cavity_pressures_)
198 v.second.set_unit_type(units.pressure());
199
200 for (auto &v : initial_position_)
201 for (int i = 0; i < 3; ++i)
202 v.value[i].set_unit_type(units.length());
203
204 for (auto &v : initial_velocity_)
205 for (int i = 0; i < 3; ++i)
206 v.value[i].set_unit_type(units.velocity());
207
208 for (auto &v : initial_acceleration_)
209 for (int i = 0; i < 3; ++i)
210 v.value[i].set_unit_type(units.acceleration());
211
212 for (auto &v : nodal_dirichlet_)
213 v.second.set_unit_type(units.length());
214
215 for (auto &v : nodal_neumann_)
216 v.second.set_unit_type(units.force());
217 }
218 }
219
220 void GenericTensorProblem::rhs(const assembler::Assembler &assembler, const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val, const int fe_space_id) const
221 {
222 const auto *rhs = find_for_fe_space(rhs_, fe_space_id);
223 const auto *rhs_size = find_for_fe_space(rhs_size_, fe_space_id);
224 const int size = rhs_size == nullptr ? pts.cols() : *rhs_size;
225 val.resize(pts.rows(), size);
226
227 if (rhs == nullptr || is_rhs_zero(fe_space_id))
228 {
229 val.setZero();
230 return;
231 }
232
233 const bool planar = pts.cols() == 2;
234 for (int i = 0; i < pts.rows(); ++i)
235 {
236 for (int j = 0; j < size; ++j)
237 {
238 double x = pts(i, 0), y = pts(i, 1), z = planar ? 0 : pts(i, 2);
239 val(i, j) = (*rhs)[j](x, y, z, t);
240 }
241 }
242 }
243
244 void GenericTensorProblem::rhs(const assembler::Assembler &assembler, const mesh::Mesh &mesh, const int element_id, const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val, const int fe_space_id) const
245 {
246 if (body_rhs_.empty())
247 {
248 rhs(assembler, pts, t, val, fe_space_id);
249 return;
250 }
251
252 int value_size = pts.cols();
253 for (const TensorInitialValue &entry : body_rhs_)
254 {
255 if (matches_fe_space(entry.fe_space_id, fe_space_id))
256 {
257 value_size = entry.size;
258 break;
259 }
260 }
261
262 val.resize(pts.rows(), value_size);
263 val.setZero();
264
265 const int body_id = mesh.get_body_id(element_id);
266 const TensorInitialValue *body_rhs = nullptr;
267 for (const TensorInitialValue &entry : body_rhs_)
268 {
269 if (entry.body_id == body_id && matches_fe_space(entry.fe_space_id, fe_space_id))
270 {
271 body_rhs = &entry;
272 break;
273 }
274 }
275
276 if (body_rhs == nullptr)
277 return;
278
279 const bool planar = pts.cols() == 2;
280 for (int i = 0; i < pts.rows(); ++i)
281 {
282 for (int j = 0; j < val.cols(); ++j)
283 {
284 const double x = pts(i, 0), y = pts(i, 1), z = planar ? 0 : pts(i, 2);
285 val(i, j) = body_rhs->value[j](x, y, z, t);
286 }
287 }
288 }
289
290 bool GenericTensorProblem::is_rhs_zero(const int fe_space_id) const
291 {
292 const auto *rhs = find_for_fe_space(rhs_, fe_space_id);
293 const auto *rhs_size = find_for_fe_space(rhs_size_, fe_space_id);
294 if (rhs != nullptr && rhs_size != nullptr)
295 {
296 for (int i = 0; i < *rhs_size; ++i)
297 if (!(*rhs)[i].is_zero())
298 return false;
299 }
300
301 for (const TensorInitialValue &entry : body_rhs_)
302 {
303 if (!matches_fe_space(entry.fe_space_id, fe_space_id))
304 continue;
305 for (int i = 0; i < entry.size; ++i)
306 if (!entry.value[i].is_zero())
307 return false;
308 }
309
310 return true;
311 }
312
313 bool GenericTensorProblem::has_boundary(const BoundaryKind kind, const int tag, const int fe_space_id)
314 {
315 if (kind != BoundaryKind::Dirichlet && kind != BoundaryKind::Neumann)
316 return Problem::has_boundary(kind, tag, fe_space_id);
317
318 if (tag <= 0)
319 return false;
320
321 if (kind == BoundaryKind::Dirichlet)
322 {
323 for (size_t i = 0; i < boundary_ids_.size(); ++i)
324 if ((boundary_ids_[i] < 0 || boundary_ids_[i] == tag) && matches_fe_space(displacements_[i].fe_space_id, fe_space_id))
325 return true;
326 return false;
327 }
328
329 for (size_t i = 0; i < neumann_boundary_ids_.size(); ++i)
330 if (neumann_boundary_ids_[i] == tag && matches_fe_space(forces_[i].fe_space_id, fe_space_id))
331 return true;
332 for (size_t i = 0; i < normal_aligned_neumann_boundary_ids_.size(); ++i)
333 if (normal_aligned_neumann_boundary_ids_[i] == tag && matches_fe_space(normal_aligned_forces_[i].fe_space_id, fe_space_id))
334 return true;
335 return false;
336 }
337
338 bool GenericTensorProblem::is_dimension_dirichet(const int tag, const int dim, const int fe_space_id) const
339 {
340 if (all_dimensions_dirichlet(fe_space_id))
341 return true;
342
343 for (size_t b = 0; b < boundary_ids_.size(); ++b)
344 {
345 if ((boundary_ids_[b] < 0 || tag == boundary_ids_[b]) && matches_fe_space(displacements_[b].fe_space_id, fe_space_id))
346 {
347 auto &tmp = displacements_[b].dirichlet_dimension;
348 return tmp[dim];
349 }
350 }
351
352 assert(false);
353 return true;
354 }
355
356 bool GenericTensorProblem::all_dimensions_dirichlet(const int fe_space_id) const
357 {
358 for (const TensorBCValue &displacement : displacements_)
359 {
360 if (!matches_fe_space(displacement.fe_space_id, fe_space_id))
361 continue;
362
363 for (int d = 0; d < displacement.dirichlet_dimension.size(); ++d)
364 {
365 if (!displacement.dirichlet_dimension(d))
366 return false;
367 }
368 }
369
370 for (const auto &n_dirichlet : nodal_dirichlet_mat_)
371 {
372 for (int i = 0; i < n_dirichlet.rows(); ++i)
373 {
374 for (int d = 1; d < n_dirichlet.cols(); ++d)
375 {
376 if (std::isnan(n_dirichlet(i, d)))
377 return false;
378 }
379 }
380 }
381
382 return true;
383 }
384
385 void GenericTensorProblem::dirichlet_bc(const mesh::Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &uv, const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val, const int fe_space_id) const
386 {
387 int value_size = mesh.dimension();
388 for (const TensorBCValue &displacement : displacements_)
389 {
390 if (displacement.size > 0 && matches_fe_space(displacement.fe_space_id, fe_space_id))
391 {
392 value_size = displacement.size;
393 break;
394 }
395 }
396 val = Eigen::MatrixXd::Zero(pts.rows(), value_size);
397
398 for (long i = 0; i < pts.rows(); ++i)
399 {
400 const int id = mesh.get_boundary_id(global_ids(i));
401 for (size_t b = 0; b < boundary_ids_.size(); ++b)
402 {
403 if ((boundary_ids_[b] < 0 || id == boundary_ids_[b]) && matches_fe_space(displacements_[b].fe_space_id, fe_space_id))
404 {
405 for (int d = 0; d < std::min<int>(val.cols(), displacements_[b].size); ++d)
406 {
407 val(i, d) = displacements_[b].eval(pts.row(i), d, t);
408 }
409 break;
410 }
411 }
412 }
413 }
414
415 void GenericTensorProblem::neumann_bc(const mesh::Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &uv, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &normals, const double t, Eigen::MatrixXd &val, const int fe_space_id) const
416 {
417 val = Eigen::MatrixXd::Zero(pts.rows(), mesh.dimension());
418
419 for (long i = 0; i < pts.rows(); ++i)
420 {
421 const int id = mesh.get_boundary_id(global_ids(i));
422
423 for (size_t b = 0; b < neumann_boundary_ids_.size(); ++b)
424 {
425 if (id == neumann_boundary_ids_[b] && matches_fe_space(forces_[b].fe_space_id, fe_space_id))
426 {
427 for (int d = 0; d < std::min<int>(val.cols(), forces_[b].size); ++d)
428 {
429 val(i, d) = forces_[b].eval(pts.row(i), d, t);
430 }
431
432 break;
433 }
434 }
435
436 for (size_t b = 0; b < normal_aligned_neumann_boundary_ids_.size(); ++b)
437 {
438 if (id == normal_aligned_neumann_boundary_ids_[b] && matches_fe_space(normal_aligned_forces_[b].fe_space_id, fe_space_id))
439 {
440 for (int d = 0; d < val.cols(); ++d)
441 {
442 val(i, d) = normal_aligned_forces_[b].eval(pts.row(i), t) * normals(i, d);
443 }
444 break;
445 }
446 }
447 }
448 }
449
450 void GenericTensorProblem::pressure_bc(const mesh::Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &uv, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &normals, const double t, Eigen::MatrixXd &val) const
451 {
452 val = Eigen::MatrixXd::Zero(pts.rows(), 1);
453
454 for (long i = 0; i < pts.rows(); ++i)
455 {
456 const int id = mesh.get_boundary_id(global_ids(i));
457
458 for (size_t b = 0; b < pressure_boundary_ids_.size(); ++b)
459 {
460 if (id == pressure_boundary_ids_[b])
461 {
462 val(i) = pressures_[b].eval(pts.row(i), t);
463 break;
464 }
465 }
466 }
467 }
468
469 double GenericTensorProblem::pressure_cavity_bc(const int boundary_id, const double t) const
470 {
471 Eigen::VectorXd pt;
472 pt.setZero(3);
473 return cavity_pressures_.at(boundary_id).eval(pt, t);
474 }
475
476 void GenericTensorProblem::exact(const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val) const
477 {
478 assert(has_exact_sol());
479 const bool planar = pts.cols() == 2;
480 val.resize(pts.rows(), pts.cols());
481
482 for (int i = 0; i < pts.rows(); ++i)
483 {
484 for (int j = 0; j < pts.cols(); ++j)
485 {
486 double x = pts(i, 0), y = pts(i, 1), z = planar ? 0 : pts(i, 2);
487 val(i, j) = exact_[j](x, y, z, t);
488 }
489 }
490 }
491
492 void GenericTensorProblem::exact_grad(const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val) const
493 {
494 const int size = pts.cols();
495 val.resize(pts.rows(), pts.cols() * size);
496 if (!has_exact_grad_)
497 return;
498
499 const bool planar = size == 2;
500 for (int i = 0; i < pts.rows(); ++i)
501 {
502 for (int j = 0; j < pts.cols() * size; ++j)
503 {
504 double x = pts(i, 0), y = pts(i, 1), z = planar ? 0 : pts(i, 2);
505 val(i, j) = exact_grad_[j](x, y, z, t);
506 }
507 }
508 }
509
510 void GenericTensorProblem::dirichlet_nodal_value(const mesh::Mesh &mesh, const int node_id, const RowVectorNd &pt, const double t, Eigen::MatrixXd &val, const int fe_space_id) const
511 {
512 val = Eigen::MatrixXd::Zero(1, mesh.dimension());
513 const int tag = mesh.get_node_id(node_id);
514
515 for (size_t i = 0; i < boundary_ids_.size(); ++i)
516 {
517 if ((boundary_ids_[i] < 0 || boundary_ids_[i] == tag) && matches_fe_space(displacements_[i].fe_space_id, fe_space_id))
518 {
519 val = Eigen::MatrixXd::Zero(1, displacements_[i].size);
520 for (int d = 0; d < val.cols(); ++d)
521 val(d) = displacements_[i].eval(pt, d, t);
522 return;
523 }
524 }
525
526 for (const auto &n_dirichlet : nodal_dirichlet_mat_)
527 {
528 for (int i = 0; i < n_dirichlet.rows(); ++i)
529 {
530 if (n_dirichlet(i, 0) == node_id)
531 {
532 for (int d = 0; d < val.cols(); ++d)
533 {
534 val(d) = n_dirichlet(i, d + 1);
535 }
536
537 return;
538 }
539 }
540 }
541
542 assert(false);
543 }
544
545 void GenericTensorProblem::neumann_nodal_value(const mesh::Mesh &mesh, const int node_id, const RowVectorNd &pt, const Eigen::MatrixXd &normal, const double t, Eigen::MatrixXd &val, const int fe_space_id) const
546 {
547 val = Eigen::MatrixXd::Zero(1, mesh.dimension());
548 const int tag = mesh.get_node_id(node_id);
549
550 if (const auto it = nodal_neumann_.find(tag); it != nodal_neumann_.end() && matches_fe_space(it->second.fe_space_id, fe_space_id))
551 {
552 val = Eigen::MatrixXd::Zero(1, it->second.size);
553 for (int d = 0; d < val.cols(); ++d)
554 val(d) = it->second.eval(pt, d, t);
555 return;
556 }
557
558 for (const auto &n_neumann : nodal_neumann_mat_)
559 {
560 for (int i = 0; i < n_neumann.rows(); ++i)
561 {
562 if (n_neumann(i, 0) == node_id)
563 {
564 val.resize(1, n_neumann.cols() - 1);
565 for (int d = 0; d < val.cols(); ++d)
566 val(d) = n_neumann(i, d + 1);
567 return;
568 }
569 }
570 }
571
572 assert(false);
573 }
574
575 bool GenericTensorProblem::is_nodal_dirichlet_boundary(const int n_id, const int tag, const int fe_space_id)
576 {
577 for (size_t i = 0; i < boundary_ids_.size(); ++i)
578 if ((boundary_ids_[i] < 0 || boundary_ids_[i] == tag) && matches_fe_space(displacements_[i].fe_space_id, fe_space_id))
579 return true;
580
581 for (const auto &n_dirichlet : nodal_dirichlet_mat_)
582 {
583 for (int i = 0; i < n_dirichlet.rows(); ++i)
584 {
585 if (n_dirichlet(i, 0) == n_id)
586 return true;
587 }
588 }
589
590 return false;
591 }
592
593 bool GenericTensorProblem::is_nodal_neumann_boundary(const int n_id, const int tag, const int fe_space_id)
594 {
595 if (const auto it = nodal_neumann_.find(tag); it != nodal_neumann_.end())
596 return matches_fe_space(it->second.fe_space_id, fe_space_id);
597
598 for (const auto &n_neumann : nodal_neumann_mat_)
599 {
600 for (int i = 0; i < n_neumann.rows(); ++i)
601 {
602 if (n_neumann(i, 0) == n_id)
603 return true;
604 }
605 }
606
607 return false;
608 }
609
611 {
612 return !nodal_dirichlet_mat_.empty();
613 }
614
615 bool GenericTensorProblem::has_nodal_neumann(const int fe_space_id)
616 {
617 if (!nodal_neumann_mat_.empty())
618 return true;
619
620 for (const auto &[id, force] : nodal_neumann_)
621 {
622 (void)id;
623 if (matches_fe_space(force.fe_space_id, fe_space_id))
624 return true;
625 }
626 return false;
627 }
628
629 bool GenericTensorProblem::is_nodal_dimension_dirichlet(const int n_id, const int tag, const int dim, const int fe_space_id) const
630 {
631 for (size_t i = 0; i < boundary_ids_.size(); ++i)
632 if ((boundary_ids_[i] < 0 || boundary_ids_[i] == tag) && matches_fe_space(displacements_[i].fe_space_id, fe_space_id))
633 return displacements_[i].dirichlet_dimension(dim);
634
635 for (const auto &n_dirichlet : nodal_dirichlet_mat_)
636 {
637 for (int i = 0; i < n_dirichlet.rows(); ++i)
638 {
639 if (n_dirichlet(i, 0) == n_id)
640 {
641 return !std::isnan(n_dirichlet(i, dim + 1));
642 }
643 }
644 }
645
646 assert(false);
647 return true;
648 }
649
650 void GenericTensorProblem::update_nodes(const Eigen::VectorXi &in_node_to_node)
651 {
653 {
654 if (!nodal_dirichlet_mat_.empty() || !nodal_neumann_mat_.empty())
655 logger().debug("Skipping updating in nodes to nodes in problem, already done once...");
656 return;
657 }
658 for (auto &n_dirichlet : nodal_dirichlet_mat_)
659 {
660 for (int n = 0; n < n_dirichlet.rows(); ++n)
661 {
662 const int node_id = in_node_to_node[n_dirichlet(n, 0)];
663 n_dirichlet(n, 0) = node_id;
664 }
665 }
666 for (auto &n_neumann : nodal_neumann_mat_)
667 {
668 for (int n = 0; n < n_neumann.rows(); ++n)
669 {
670 const int node_id = in_node_to_node[n_neumann(n, 0)];
671 n_neumann(n, 0) = node_id;
672 }
673 }
675 }
676
677 void GenericTensorProblem::set_parameters(const json &params, const io::ResourceIO &resources)
678 {
679 if (is_param_valid(params, "is_time_dependent"))
680 {
681 is_time_dept_ = params["is_time_dependent"];
682 }
683
684 if (is_param_valid(params, "rhs"))
685 {
686 const json &rr = params["rhs"];
687 const bool has_fe_spaces = rr.is_array() && !rr.empty() && rr.front().is_object() && rr.front().contains("fe_space") && rr.front().contains("value");
688 const bool has_body_ids = has_body_value_entries(rr);
689 if (has_body_ids)
690 {
691 const std::vector<json> entries = flatten_ids(rr);
692 body_rhs_.resize(entries.size());
693 for (size_t k = 0; k < entries.size(); ++k)
694 {
695 body_rhs_[k].body_id = entries[k]["id"];
696 body_rhs_[k].fe_space_id = fe_space_id(entries[k]);
697 const auto &value = entries[k]["value"];
698 body_rhs_[k].size = value.is_array() ? int(value.size()) : 1;
699 if (body_rhs_[k].size > 3)
700 log_and_throw_error("RHS for body {} and FE space {} has {} components; at most 3 are supported.", body_rhs_[k].body_id, body_rhs_[k].fe_space_id, body_rhs_[k].size);
701 for (int d = 0; d < body_rhs_[k].size; ++d)
702 body_rhs_[k].value[d].init(value.is_array() ? value[d] : value, resources);
703 }
704 }
705 else if (has_fe_spaces)
706 {
707 for (const json &entry : rr)
708 {
709 const int id = fe_space_id(entry);
710 const json &value = entry["value"];
711 const int size = value.is_array() ? int(value.size()) : 1;
712 if (size > 3)
713 log_and_throw_error("RHS for FE space {} has {} components; at most 3 are supported.", id, size);
714 rhs_size_[id] = size;
715 for (int k = 0; k < size; ++k)
716 rhs_[id][k].init(value.is_array() ? value[k] : value, resources);
717 }
718 }
719 else if (rr.is_array() && !rr.empty())
720 {
721 if (rr.size() > 3)
722 log_and_throw_error("RHS has {} components; at most 3 are supported.", rr.size());
723 rhs_size_[-1] = int(rr.size());
724 for (size_t k = 0; k < rr.size(); ++k)
725 rhs_[-1][k].init(rr[k], resources);
726 }
727 else if (!rr.is_array())
728 log_and_throw_error("Invalid tensor problem RHS: expected an array.");
729 }
730
731 if (is_param_valid(params, "reference") && is_param_valid(params["reference"], "solution"))
732 {
733 auto ex = params["reference"]["solution"];
734 has_exact_ = ex.size() > 0;
735 if (ex.is_array())
736 {
737 for (size_t k = 0; k < ex.size(); ++k)
738 exact_[k].init(ex[k], resources);
739 }
740 else
741 {
742 assert(false);
743 }
744 }
745
746 if (is_param_valid(params, "reference") && is_param_valid(params["reference"], "gradient"))
747 {
748 auto ex = params["reference"]["gradient"];
749 has_exact_grad_ = ex.size() > 0;
750 if (ex.is_array())
751 {
752 for (size_t k = 0; k < ex.size(); ++k)
753 exact_grad_[k].init(ex[k], resources);
754 }
755 else
756 {
757 assert(false);
758 }
759 }
760
761 if (is_param_valid(params, "dirichlet_boundary"))
762 {
763 // boundary_ids_.clear();
764 int offset = boundary_ids_.size();
765 std::vector<json> j_boundary = flatten_ids(params["dirichlet_boundary"]);
766
767 boundary_ids_.resize(offset + j_boundary.size());
768 displacements_.resize(offset + j_boundary.size());
769
770 for (size_t i = offset; i < boundary_ids_.size(); ++i)
771 {
772 if (j_boundary[i - offset].is_string())
773 {
774 const std::string path = j_boundary[i - offset].get<std::string>();
775 nodal_dirichlet_mat_.emplace_back(resources.read_matrix(path));
776
777 continue;
778 }
779
780 int current_id = -1;
781 displacements_[i].fe_space_id = fe_space_id(j_boundary[i - offset]);
782
783 if (j_boundary[i - offset]["id"] == "all")
784 {
785 boundary_ids_[i] = -1;
786 is_all_ = true;
787 nodal_dirichlet_[current_id] = TensorBCValue();
788 }
789 else
790 {
791 boundary_ids_[i] = j_boundary[i - offset]["id"];
792 current_id = boundary_ids_[i];
793 nodal_dirichlet_[current_id] = TensorBCValue();
794 }
795 nodal_dirichlet_[current_id].fe_space_id = displacements_[i].fe_space_id;
796
797 auto ff = j_boundary[i - offset]["value"];
798 if (ff.is_array())
799 {
800 if (ff.size() > 3)
801 log_and_throw_error("Dirichlet condition for FE space {} has {} components; at most 3 are supported.", displacements_[i].fe_space_id, ff.size());
802 displacements_[i].size = int(ff.size());
803 for (size_t k = 0; k < ff.size(); ++k)
804 {
805 displacements_[i].value[k].init(ff[k], resources);
806 if (j_boundary[i - offset].contains("time_reference") && j_boundary[i - offset]["time_reference"].size() > 0)
807 displacements_[i].value[k].set_t(j_boundary[i - offset]["time_reference"]);
808 nodal_dirichlet_[current_id].value[k].init(ff[k], resources);
809 }
810 }
811 else
812 {
813 displacements_[i].size = 1;
814 displacements_[i].value[0].init(ff, resources);
815 nodal_dirichlet_[current_id].value[0].init(ff, resources);
816 }
817 nodal_dirichlet_[current_id].size = displacements_[i].size;
818
819 displacements_[i].dirichlet_dimension.setConstant(true);
820 nodal_dirichlet_[current_id].dirichlet_dimension.setConstant(true);
821 if (j_boundary[i - offset].contains("dimension"))
822 {
823 auto &tmp = j_boundary[i - offset]["dimension"];
824 assert(tmp.is_array());
825 for (size_t k = 0; k < tmp.size(); ++k)
826 {
827 displacements_[i].dirichlet_dimension[k] = tmp[k];
828 nodal_dirichlet_[current_id].dirichlet_dimension[k] = tmp[k];
829 }
830 }
831
832 if (j_boundary[i - offset]["interpolation"].is_array())
833 {
834 for (int ii = 0; ii < j_boundary[i - offset]["interpolation"].size(); ++ii)
835 displacements_[i].interpolation.push_back(Interpolation::build(j_boundary[i - offset]["interpolation"][ii]));
836 }
837 else
838 displacements_[i].interpolation.push_back(Interpolation::build(j_boundary[i - offset]["interpolation"]));
839
840 nodal_dirichlet_[current_id].interpolation = displacements_[i].interpolation;
841 }
842 }
843
844 if (is_param_valid(params, "neumann_boundary"))
845 {
846 // neumann_boundary_ids_.clear();
847 const int offset = neumann_boundary_ids_.size();
848
849 auto j_boundary_tmp = params["neumann_boundary"];
850 std::vector<json> j_boundary = flatten_ids(j_boundary_tmp);
851
852 neumann_boundary_ids_.resize(offset + j_boundary.size());
853 forces_.resize(offset + j_boundary.size());
854
855 for (size_t i = offset; i < neumann_boundary_ids_.size(); ++i)
856 {
857 neumann_boundary_ids_[i] = j_boundary[i - offset]["id"];
858 forces_[i].fe_space_id = fe_space_id(j_boundary[i - offset]);
859
860 auto ff = j_boundary[i - offset]["value"];
861 forces_[i].size = ff.is_array() ? int(ff.size()) : 1;
862 if (forces_[i].size > 3)
863 log_and_throw_error("Neumann condition for FE space {} has {} components; at most 3 are supported.", forces_[i].fe_space_id, forces_[i].size);
864 for (int k = 0; k < forces_[i].size; ++k)
865 forces_[i].value[k].init(ff.is_array() ? ff[k] : ff, resources);
866
867 if (j_boundary[i - offset]["interpolation"].is_array())
868 {
869 for (int ii = 0; ii < j_boundary[i - offset]["interpolation"].size(); ++ii)
870 forces_[i].interpolation.push_back(Interpolation::build(j_boundary[i - offset]["interpolation"][ii]));
871 }
872 else
873 {
874 forces_[i].interpolation.push_back(Interpolation::build(j_boundary[i - offset]["interpolation"]));
875 }
876 }
877 }
878
879 if (is_param_valid(params, "nodal_neumann_boundary"))
880 {
881 std::vector<json> j_boundary = flatten_ids(params["nodal_neumann_boundary"]);
882
883 for (size_t i = 0; i < j_boundary.size(); ++i)
884 {
885 if (j_boundary[i].is_string())
886 {
887 const std::string path = j_boundary[i].get<std::string>();
888 nodal_neumann_mat_.emplace_back(resources.read_matrix(path));
889
890 continue;
891 }
892
893 const int id = j_boundary[i]["id"];
894 TensorBCValue nodal_neumann;
895 nodal_neumann.fe_space_id = fe_space_id(j_boundary[i]);
896
897 auto ff = j_boundary[i]["value"];
898 nodal_neumann.size = ff.is_array() ? int(ff.size()) : 1;
899 if (nodal_neumann.size > 3)
900 log_and_throw_error("Nodal Neumann condition for FE space {} has {} components; at most 3 are supported.", nodal_neumann.fe_space_id, nodal_neumann.size);
901 for (int k = 0; k < nodal_neumann.size; ++k)
902 nodal_neumann.value[k].init(ff.is_array() ? ff[k] : ff, resources);
903
904 if (j_boundary[i]["interpolation"].is_array())
905 {
906 for (int ii = 0; ii < j_boundary[i]["interpolation"].size(); ++ii)
907 nodal_neumann.interpolation.push_back(Interpolation::build(j_boundary[i]["interpolation"][ii]));
908 }
909 else
910 {
911 nodal_neumann.interpolation.push_back(Interpolation::build(j_boundary[i]["interpolation"]));
912 }
913
914 nodal_neumann_[id] = nodal_neumann;
915 }
916 }
917
918 if (is_param_valid(params, "normal_aligned_neumann_boundary"))
919 {
920 const int offset = normal_aligned_neumann_boundary_ids_.size();
921
922 auto j_boundary_tmp = params["normal_aligned_neumann_boundary"];
923 std::vector<json> j_boundary = flatten_ids(j_boundary_tmp);
924
925 normal_aligned_neumann_boundary_ids_.resize(offset + j_boundary.size());
926 normal_aligned_forces_.resize(offset + j_boundary.size());
927
928 for (size_t i = offset; i < normal_aligned_neumann_boundary_ids_.size(); ++i)
929 {
930 normal_aligned_neumann_boundary_ids_[i] = j_boundary[i - offset]["id"];
931 normal_aligned_forces_[i].fe_space_id = fe_space_id(j_boundary[i - offset]);
932
933 auto ff = j_boundary[i - offset]["value"];
934 normal_aligned_forces_[i].value.init(ff, resources);
935
936 if (j_boundary[i - offset].contains("interpolation"))
937 normal_aligned_forces_[i].interpolation = Interpolation::build(j_boundary[i - offset]["interpolation"]);
938 else
939 normal_aligned_forces_[i].interpolation = std::make_shared<NoInterpolation>();
940 }
941 }
942
943 if (is_param_valid(params, "pressure_boundary"))
944 {
945 // pressure_boundary_ids_.clear();
946 const int offset = pressure_boundary_ids_.size();
947
948 auto j_boundary_tmp = params["pressure_boundary"];
949 std::vector<json> j_boundary = flatten_ids(j_boundary_tmp);
950
951 pressure_boundary_ids_.resize(offset + j_boundary.size());
952 pressures_.resize(offset + j_boundary.size());
953
954 for (size_t i = offset; i < pressure_boundary_ids_.size(); ++i)
955 {
956 pressure_boundary_ids_[i] = j_boundary[i - offset]["id"];
957
958 auto ff = j_boundary[i - offset]["value"];
959 pressures_[i].value.init(ff, resources);
960 if (j_boundary[i - offset].contains("time_reference") && j_boundary[i - offset]["time_reference"].size() > 0)
961 pressures_[i].value.set_t(j_boundary[i - offset]["time_reference"]);
962
963 pressures_[i].interpolation = std::make_shared<NoInterpolation>();
964 }
965 }
966
967 if (is_param_valid(params, "pressure_cavity"))
968 {
969 const int offset = pressure_cavity_ids_.size();
970
971 auto j_boundary_tmp = params["pressure_cavity"];
972 std::vector<json> j_boundary = flatten_ids(j_boundary_tmp);
973
974 pressure_cavity_ids_.resize(offset + j_boundary.size());
975
976 for (size_t i = offset; i < pressure_cavity_ids_.size(); ++i)
977 {
978 int boundary_id = j_boundary[i - offset]["id"];
979 pressure_cavity_ids_[i] = boundary_id;
980
981 if (cavity_pressures_.find(boundary_id) == cavity_pressures_.end())
982 {
983 cavity_pressures_[boundary_id] = ScalarBCValue();
984
985 auto ff = j_boundary[i - offset]["value"];
986 cavity_pressures_[boundary_id].value.init(ff, resources);
987
988 cavity_pressures_[boundary_id].interpolation = std::make_shared<NoInterpolation>();
989 }
990 }
991 }
992
993 if (is_param_valid(params, "solution"))
994 {
995 auto rr = params["solution"];
996 initial_position_.resize(rr.size());
997 assert(rr.is_array());
998
999 for (size_t k = 0; k < rr.size(); ++k)
1000 {
1001 initial_position_[k].body_id = rr[k]["id"];
1002 initial_position_[k].fe_space_id = fe_space_id(rr[k]);
1003 const auto v = rr[k]["value"];
1004 initial_position_[k].size = v.is_array() ? int(v.size()) : 1;
1005 if (initial_position_[k].size > 3)
1006 log_and_throw_error("Initial solution for FE space {} has {} components; at most 3 are supported.", initial_position_[k].fe_space_id, initial_position_[k].size);
1007 for (int d = 0; d < initial_position_[k].size; ++d)
1008 initial_position_[k].value[d].init(v.is_array() ? v[d] : v, resources);
1009 }
1010 }
1011
1012 if (is_param_valid(params, "velocity"))
1013 {
1014 auto rr = params["velocity"];
1015 initial_velocity_.resize(rr.size());
1016 assert(rr.is_array());
1017
1018 for (size_t k = 0; k < rr.size(); ++k)
1019 {
1020 initial_velocity_[k].body_id = rr[k]["id"];
1021 initial_velocity_[k].fe_space_id = fe_space_id(rr[k]);
1022 const auto v = rr[k]["value"];
1023 initial_velocity_[k].size = v.is_array() ? int(v.size()) : 1;
1024 if (initial_velocity_[k].size > 3)
1025 log_and_throw_error("Initial velocity for FE space {} has {} components; at most 3 are supported.", initial_velocity_[k].fe_space_id, initial_velocity_[k].size);
1026 for (int d = 0; d < initial_velocity_[k].size; ++d)
1027 initial_velocity_[k].value[d].init(v.is_array() ? v[d] : v, resources);
1028 }
1029 }
1030
1031 if (is_param_valid(params, "acceleration"))
1032 {
1033 auto rr = params["acceleration"];
1034 initial_acceleration_.resize(rr.size());
1035 assert(rr.is_array());
1036
1037 for (size_t k = 0; k < rr.size(); ++k)
1038 {
1039 initial_acceleration_[k].body_id = rr[k]["id"];
1040 initial_acceleration_[k].fe_space_id = fe_space_id(rr[k]);
1041 const auto v = rr[k]["value"];
1042 initial_acceleration_[k].size = v.is_array() ? int(v.size()) : 1;
1043 if (initial_acceleration_[k].size > 3)
1044 log_and_throw_error("Initial acceleration for FE space {} has {} components; at most 3 are supported.", initial_acceleration_[k].fe_space_id, initial_acceleration_[k].size);
1045 for (int d = 0; d < initial_acceleration_[k].size; ++d)
1046 initial_acceleration_[k].value[d].init(v.is_array() ? v[d] : v, resources);
1047 }
1048 }
1049 }
1050
1051 void GenericTensorProblem::initial_solution(const mesh::Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &pts, Eigen::MatrixXd &val, const int fe_space_id) const
1052 {
1053 int value_size = pts.cols();
1054 for (const TensorInitialValue &entry : initial_position_)
1055 if (matches_fe_space(entry.fe_space_id, fe_space_id))
1056 {
1057 value_size = entry.size;
1058 break;
1059 }
1060 val.resize(pts.rows(), value_size);
1061 if (initial_position_.empty())
1062 {
1063 val.setZero();
1064 return;
1065 }
1066
1067 const bool planar = pts.cols() == 2;
1068 for (int i = 0; i < pts.rows(); ++i)
1069 {
1070 const int id = mesh.get_body_id(global_ids(i));
1071 int index = -1;
1072 for (int j = 0; j < initial_position_.size(); ++j)
1073 {
1074 if (initial_position_[j].body_id == id && matches_fe_space(initial_position_[j].fe_space_id, fe_space_id))
1075 {
1076 index = j;
1077 break;
1078 }
1079 }
1080 if (index < 0)
1081 {
1082 val.row(i).setZero();
1083 continue;
1084 }
1085
1086 for (int j = 0; j < val.cols(); ++j)
1087 val(i, j) = planar ? initial_position_[index].value[j](pts(i, 0), pts(i, 1)) : initial_position_[index].value[j](pts(i, 0), pts(i, 1), pts(i, 2));
1088 }
1089 }
1090
1091 void GenericTensorProblem::initial_velocity(const mesh::Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &pts, Eigen::MatrixXd &val, const int fe_space_id) const
1092 {
1093 int value_size = pts.cols();
1094 for (const TensorInitialValue &entry : initial_velocity_)
1095 if (matches_fe_space(entry.fe_space_id, fe_space_id))
1096 {
1097 value_size = entry.size;
1098 break;
1099 }
1100 val.resize(pts.rows(), value_size);
1101 if (initial_velocity_.empty())
1102 {
1103 val.setZero();
1104 return;
1105 }
1106
1107 const bool planar = pts.cols() == 2;
1108 for (int i = 0; i < pts.rows(); ++i)
1109 {
1110 const int id = mesh.get_body_id(global_ids(i));
1111 int index = -1;
1112 for (int j = 0; j < initial_velocity_.size(); ++j)
1113 {
1114 if (initial_velocity_[j].body_id == id && matches_fe_space(initial_velocity_[j].fe_space_id, fe_space_id))
1115 {
1116 index = j;
1117 break;
1118 }
1119 }
1120 if (index < 0)
1121 {
1122 val.row(i).setZero();
1123 continue;
1124 }
1125
1126 for (int j = 0; j < val.cols(); ++j)
1127 val(i, j) = planar ? initial_velocity_[index].value[j](pts(i, 0), pts(i, 1)) : initial_velocity_[index].value[j](pts(i, 0), pts(i, 1), pts(i, 2));
1128 }
1129 }
1130
1131 void GenericTensorProblem::initial_acceleration(const mesh::Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &pts, Eigen::MatrixXd &val, const int fe_space_id) const
1132 {
1133 int value_size = pts.cols();
1134 for (const TensorInitialValue &entry : initial_acceleration_)
1135 if (matches_fe_space(entry.fe_space_id, fe_space_id))
1136 {
1137 value_size = entry.size;
1138 break;
1139 }
1140 val.resize(pts.rows(), value_size);
1141 if (initial_acceleration_.empty())
1142 {
1143 val.setZero();
1144 return;
1145 }
1146
1147 const bool planar = pts.cols() == 2;
1148 for (int i = 0; i < pts.rows(); ++i)
1149 {
1150 const int id = mesh.get_body_id(global_ids(i));
1151 int index = -1;
1152 for (int j = 0; j < initial_acceleration_.size(); ++j)
1153 {
1154 if (initial_acceleration_[j].body_id == id && matches_fe_space(initial_acceleration_[j].fe_space_id, fe_space_id))
1155 {
1156 index = j;
1157 break;
1158 }
1159 }
1160 if (index < 0)
1161 {
1162 val.row(i).setZero();
1163 continue;
1164 }
1165
1166 for (int j = 0; j < val.cols(); ++j)
1167 val(i, j) = planar ? initial_acceleration_[index].value[j](pts(i, 0), pts(i, 1)) : initial_acceleration_[index].value[j](pts(i, 0), pts(i, 1), pts(i, 2));
1168 }
1169 }
1170
1172 {
1173 has_exact_ = false;
1174 has_exact_grad_ = false;
1175 is_time_dept_ = false;
1176
1177 forces_.clear();
1178 displacements_.clear();
1179 normal_aligned_forces_.clear();
1180 pressures_.clear();
1181 cavity_pressures_.clear();
1182
1183 nodal_dirichlet_.clear();
1184 nodal_neumann_.clear();
1185 nodal_dirichlet_mat_.clear();
1186 nodal_neumann_mat_.clear();
1187
1188 initial_position_.clear();
1189 initial_velocity_.clear();
1190 initial_acceleration_.clear();
1191
1192 rhs_.clear();
1193 rhs_size_.clear();
1194 body_rhs_.clear();
1195 for (int i = 0; i < exact_.size(); ++i)
1196 exact_[i].clear();
1197 for (int i = 0; i < exact_grad_.size(); ++i)
1198 exact_grad_[i].clear();
1199 is_all_ = false;
1201 }
1202
1204 : Problem(name), is_all_(false)
1205 {
1206 }
1207
1209 {
1210 // TODO?
1211
1212 for (auto &v : neumann_)
1213 v.set_unit_type("");
1214
1215 for (auto &v : dirichlet_)
1216 v.set_unit_type("");
1217
1218 for (auto &v : nodal_dirichlet_)
1219 v.second.set_unit_type("");
1220
1221 for (auto &v : nodal_neumann_)
1222 v.second.set_unit_type("");
1223
1224 for (auto &v : initial_solution_)
1225 v.value.set_unit_type("");
1226
1227 for (auto &[fe_space_id, rhs] : rhs_)
1228 rhs.set_unit_type("");
1230
1231 for (int i = 0; i < 3; ++i)
1232 exact_grad_[i].set_unit_type("");
1233 }
1234
1235 void GenericScalarProblem::rhs(const assembler::Assembler &assembler, const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val, const int fe_space_id) const
1236 {
1237 val.resize(pts.rows(), 1);
1238 const auto *rhs = find_for_fe_space(rhs_, fe_space_id);
1239 if (rhs == nullptr || rhs->is_zero())
1240 {
1241 val.setZero();
1242 return;
1243 }
1244 const bool planar = pts.cols() == 2;
1245 for (int i = 0; i < pts.rows(); ++i)
1246 {
1247 double x = pts(i, 0), y = pts(i, 1), z = planar ? 0 : pts(i, 2);
1248 val(i) = (*rhs)(x, y, z, t);
1249 }
1250 }
1251
1252 void GenericScalarProblem::rhs(const assembler::Assembler &assembler, const mesh::Mesh &mesh, const int element_id, const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val, const int fe_space_id) const
1253 {
1254 if (body_rhs_.empty())
1255 {
1256 rhs(assembler, pts, t, val, fe_space_id);
1257 return;
1258 }
1259
1260 val.resize(pts.rows(), 1);
1261 val.setZero();
1262
1263 const int body_id = mesh.get_body_id(element_id);
1264 const ScalarInitialValue *body_rhs = nullptr;
1265 for (const ScalarInitialValue &entry : body_rhs_)
1266 {
1267 if (entry.body_id == body_id && matches_fe_space(entry.fe_space_id, fe_space_id))
1268 {
1269 body_rhs = &entry;
1270 break;
1271 }
1272 }
1273
1274 if (body_rhs == nullptr)
1275 return;
1276
1277 const bool planar = pts.cols() == 2;
1278 for (int i = 0; i < pts.rows(); ++i)
1279 {
1280 const double x = pts(i, 0), y = pts(i, 1), z = planar ? 0 : pts(i, 2);
1281 val(i) = body_rhs->value(x, y, z, t);
1282 }
1283 }
1284
1285 bool GenericScalarProblem::is_rhs_zero(const int fe_space_id) const
1286 {
1287 const auto *rhs = find_for_fe_space(rhs_, fe_space_id);
1288 if (rhs != nullptr && !rhs->is_zero())
1289 return false;
1290
1291 for (const ScalarInitialValue &entry : body_rhs_)
1292 if (matches_fe_space(entry.fe_space_id, fe_space_id) && !entry.value.is_zero())
1293 return false;
1294
1295 return true;
1296 }
1297
1298 bool GenericScalarProblem::has_boundary(const BoundaryKind kind, const int tag, const int fe_space_id)
1299 {
1300 if (kind != BoundaryKind::Dirichlet && kind != BoundaryKind::Neumann)
1301 return Problem::has_boundary(kind, tag, fe_space_id);
1302
1303 if (tag <= 0)
1304 return false;
1305 if (kind == BoundaryKind::Dirichlet)
1306 {
1307 for (size_t i = 0; i < boundary_ids_.size(); ++i)
1308 if ((boundary_ids_[i] < 0 || boundary_ids_[i] == tag) && matches_fe_space(dirichlet_[i].fe_space_id, fe_space_id))
1309 return true;
1310 return false;
1311 }
1312 for (size_t i = 0; i < neumann_boundary_ids_.size(); ++i)
1313 if (neumann_boundary_ids_[i] == tag && matches_fe_space(neumann_[i].fe_space_id, fe_space_id))
1314 return true;
1315 return false;
1316 }
1317
1318 void GenericScalarProblem::dirichlet_bc(const mesh::Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &uv, const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val, const int fe_space_id) const
1319 {
1320 val = Eigen::MatrixXd::Zero(pts.rows(), 1);
1321
1322 for (long i = 0; i < pts.rows(); ++i)
1323 {
1324 const int id = mesh.get_boundary_id(global_ids(i));
1325 for (size_t b = 0; b < boundary_ids_.size(); ++b)
1326 {
1327 if ((boundary_ids_[b] < 0 || id == boundary_ids_[b]) && matches_fe_space(dirichlet_[b].fe_space_id, fe_space_id))
1328 {
1329 val(i) = dirichlet_[b].eval(pts.row(i), t);
1330 break;
1331 }
1332 }
1333 }
1334 }
1335
1336 void GenericScalarProblem::neumann_bc(const mesh::Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &uv, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &normals, const double t, Eigen::MatrixXd &val, const int fe_space_id) const
1337 {
1338 val = Eigen::MatrixXd::Zero(pts.rows(), 1);
1339
1340 for (long i = 0; i < pts.rows(); ++i)
1341 {
1342 const int id = mesh.get_boundary_id(global_ids(i));
1343
1344 for (size_t b = 0; b < neumann_boundary_ids_.size(); ++b)
1345 {
1346 if (id == neumann_boundary_ids_[b] && matches_fe_space(neumann_[b].fe_space_id, fe_space_id))
1347 {
1348 double x = pts(i, 0), y = pts(i, 1), z = pts.cols() == 2 ? 0 : pts(i, 2);
1349 val(i) = neumann_[b].eval(pts.row(i), t);
1350 break;
1351 }
1352 }
1353 }
1354 }
1355
1356 void GenericScalarProblem::initial_solution(const mesh::Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &pts, Eigen::MatrixXd &val, const int fe_space_id) const
1357 {
1358 val.resize(pts.rows(), 1);
1359 if (initial_solution_.empty())
1360 {
1361 val.setZero();
1362 return;
1363 }
1364
1365 const bool planar = pts.cols() == 2;
1366 for (int i = 0; i < pts.rows(); ++i)
1367 {
1368 const int id = mesh.get_body_id(global_ids(i));
1369 int index = -1;
1370 for (int j = 0; j < initial_solution_.size(); ++j)
1371 {
1372 if (initial_solution_[j].body_id == id && matches_fe_space(initial_solution_[j].fe_space_id, fe_space_id))
1373 {
1374 index = j;
1375 break;
1376 }
1377 }
1378 if (index < 0)
1379 {
1380 val(i) = 0;
1381 continue;
1382 }
1383
1384 val(i) = planar ? initial_solution_[index].value(pts(i, 0), pts(i, 1)) : initial_solution_[index].value(pts(i, 0), pts(i, 1), pts(i, 2));
1385 }
1386 }
1387
1388 void GenericScalarProblem::exact(const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val) const
1389 {
1390 assert(has_exact_sol());
1391 const bool planar = pts.cols() == 2;
1392 val.resize(pts.rows(), 1);
1393
1394 for (int i = 0; i < pts.rows(); ++i)
1395 {
1396 double x = pts(i, 0), y = pts(i, 1), z = pts.cols() == 2 ? 0 : pts(i, 2);
1397 val(i) = exact_(x, y, z, t);
1398 }
1399 }
1400
1401 void GenericScalarProblem::exact_grad(const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val) const
1402 {
1403 val.resize(pts.rows(), pts.cols());
1404 if (!has_exact_grad_)
1405 return;
1406
1407 const bool planar = pts.cols() == 2;
1408 for (int i = 0; i < pts.rows(); ++i)
1409 {
1410 for (int j = 0; j < pts.cols(); ++j)
1411 {
1412 double x = pts(i, 0), y = pts(i, 1), z = pts.cols() == 2 ? 0 : pts(i, 2);
1413 val(i, j) = exact_grad_[j](x, y, z, t);
1414 }
1415 }
1416 }
1417
1418 void GenericTensorProblem::update_pressure_boundary(const int id, const int time_step, const double val)
1419 {
1420 int index = -1;
1421 for (int i = 0; i < pressure_boundary_ids_.size(); ++i)
1422 {
1423 if (pressure_boundary_ids_[i] == id)
1424 {
1425 index = i;
1426 break;
1427 }
1428 }
1429 if (index == -1)
1430 {
1431 throw "Invalid boundary id for pressure update";
1432 }
1433
1434 if (pressures_[index].value.is_mat())
1435 {
1436 Eigen::MatrixXd curr_val = pressures_[index].value.get_mat();
1437 assert(time_step <= curr_val.size());
1438 assert(curr_val.cols() == 1);
1439 curr_val(time_step) = val;
1440 pressures_[index].value.set_mat(curr_val);
1441 }
1442 else
1443 {
1444 pressures_[index].value.init(val);
1445 }
1446 }
1447
1448 void GenericTensorProblem::update_dirichlet_boundary(const int id, const int time_step, const Eigen::VectorXd &val)
1449 {
1450 int index = -1;
1451 for (int i = 0; i < boundary_ids_.size(); ++i)
1452 {
1453 if (boundary_ids_[i] == id)
1454 {
1455 index = i;
1456 break;
1457 }
1458 }
1459 if (index == -1)
1460 {
1461 throw "Invalid boundary id for dirichlet update";
1462 }
1463
1464 for (int i = 0; i < val.size(); ++i)
1465 {
1466 if (displacements_[index].value[i].is_mat())
1467 {
1468 Eigen::MatrixXd curr_val = displacements_[index].value[i].get_mat();
1469 assert(time_step <= curr_val.size());
1470 assert(curr_val.cols() == 1);
1471 curr_val(time_step) = val(i);
1472 displacements_[index].value[i].set_mat(curr_val);
1473 }
1474 else
1475 {
1476 displacements_[index].value[i].init(val(i));
1477 }
1478 }
1479 }
1480
1481 void GenericTensorProblem::update_dirichlet_nodes(const Eigen::VectorXi &in_node_to_node, const Eigen::VectorXi &node_ids, const Eigen::MatrixXd &nodal_dirichlet)
1482 {
1483 assert(node_ids.size() == nodal_dirichlet.rows());
1484 // NOTE!!! Update nodes called in `State::build_basis()` so the first row of this mat
1485 // must be set to input node ordering if `build_basis` will be called, like in optimization
1486 for (auto &n_dirichlet : nodal_dirichlet_mat_)
1487 for (int i = 0; i < node_ids.size(); ++i)
1488 {
1489 int mapped_node_id = in_node_to_node(node_ids(i));
1490 for (int j = 0; j < n_dirichlet.rows(); ++j)
1491 if (mapped_node_id == n_dirichlet(j, 0))
1492 for (int k = 0; k < n_dirichlet.cols() - 1; ++k)
1493 n_dirichlet(j, k + 1) = nodal_dirichlet(i, k);
1494 }
1495 }
1496
1497 void GenericScalarProblem::dirichlet_nodal_value(const mesh::Mesh &mesh, const int node_id, const RowVectorNd &pt, const double t, Eigen::MatrixXd &val, const int fe_space_id) const
1498 {
1499 val = Eigen::MatrixXd::Zero(1, 1);
1500 const int tag = mesh.get_node_id(node_id);
1501
1502 for (size_t i = 0; i < boundary_ids_.size(); ++i)
1503 {
1504 if ((boundary_ids_[i] < 0 || boundary_ids_[i] == tag) && matches_fe_space(dirichlet_[i].fe_space_id, fe_space_id))
1505 {
1506 val(0) = dirichlet_[i].eval(pt, t);
1507 return;
1508 }
1509 }
1510
1511 for (const auto &n_dirichlet : nodal_dirichlet_mat_)
1512 {
1513 for (int i = 0; i < n_dirichlet.rows(); ++i)
1514 {
1515 if (n_dirichlet(i, 0) == node_id)
1516 {
1517 val(0) = n_dirichlet(i, 1);
1518 return;
1519 }
1520 }
1521 }
1522
1523 assert(false);
1524 }
1525
1526 void GenericScalarProblem::neumann_nodal_value(const mesh::Mesh &mesh, const int node_id, const RowVectorNd &pt, const Eigen::MatrixXd &normal, const double t, Eigen::MatrixXd &val, const int fe_space_id) const
1527 {
1528 val = Eigen::MatrixXd::Zero(1, 1);
1529 const int tag = mesh.get_node_id(node_id);
1530
1531 if (const auto it = nodal_neumann_.find(tag); it != nodal_neumann_.end() && matches_fe_space(it->second.fe_space_id, fe_space_id))
1532 {
1533 val(0) = it->second.eval(pt, t);
1534 return;
1535 }
1536
1537 for (const auto &n_neumann : nodal_neumann_mat_)
1538 {
1539 for (int i = 0; i < n_neumann.rows(); ++i)
1540 {
1541 if (n_neumann(i, 0) == node_id)
1542 {
1543 val(0) = n_neumann(i, 1);
1544 return;
1545 }
1546 }
1547 }
1548
1549 assert(false);
1550 }
1551
1552 bool GenericScalarProblem::is_nodal_dirichlet_boundary(const int n_id, const int tag, const int fe_space_id)
1553 {
1554 for (size_t i = 0; i < boundary_ids_.size(); ++i)
1555 if ((boundary_ids_[i] < 0 || boundary_ids_[i] == tag) && matches_fe_space(dirichlet_[i].fe_space_id, fe_space_id))
1556 return true;
1557
1558 for (const auto &n_dirichlet : nodal_dirichlet_mat_)
1559 {
1560 for (int i = 0; i < n_dirichlet.rows(); ++i)
1561 {
1562 if (n_dirichlet(i, 0) == n_id)
1563 return true;
1564 }
1565 }
1566
1567 return false;
1568 }
1569
1570 bool GenericScalarProblem::is_nodal_neumann_boundary(const int n_id, const int tag, const int fe_space_id)
1571 {
1572 if (const auto it = nodal_neumann_.find(tag); it != nodal_neumann_.end())
1573 return matches_fe_space(it->second.fe_space_id, fe_space_id);
1574
1575 for (const auto &n_neumann : nodal_neumann_mat_)
1576 {
1577 for (int i = 0; i < n_neumann.rows(); ++i)
1578 {
1579 if (n_neumann(i, 0) == n_id)
1580 return true;
1581 }
1582 }
1583
1584 return false;
1585 }
1586
1588 {
1589 return nodal_dirichlet_mat_.size() > 0;
1590 }
1591
1593 {
1594 if (!nodal_neumann_mat_.empty())
1595 return true;
1596
1597 for (const auto &[id, neumann] : nodal_neumann_)
1598 {
1599 (void)id;
1600 if (matches_fe_space(neumann.fe_space_id, fe_space_id))
1601 return true;
1602 }
1603 return false;
1604 }
1605
1606 void GenericScalarProblem::update_nodes(const Eigen::VectorXi &in_node_to_node)
1607 {
1609 return;
1610 for (auto &n_dirichlet : nodal_dirichlet_mat_)
1611 {
1612 for (int n = 0; n < n_dirichlet.rows(); ++n)
1613 {
1614 const int node_id = in_node_to_node[n_dirichlet(n, 0)];
1615 n_dirichlet(n, 0) = node_id;
1616 }
1617 }
1618 for (auto &n_neumann : nodal_neumann_mat_)
1619 {
1620 for (int n = 0; n < n_neumann.rows(); ++n)
1621 {
1622 const int node_id = in_node_to_node[n_neumann(n, 0)];
1623 n_neumann(n, 0) = node_id;
1624 }
1625 }
1627 }
1628
1629 void GenericScalarProblem::set_parameters(const json &params, const io::ResourceIO &resources)
1630 {
1631 if (is_param_valid(params, "is_time_dependent"))
1632 {
1633 is_time_dept_ = params["is_time_dependent"];
1634 }
1635
1636 if (is_param_valid(params, "rhs"))
1637 {
1638 const json &rr = params["rhs"];
1639 const bool has_fe_spaces = rr.is_array() && !rr.empty() && rr.front().is_object() && rr.front().contains("fe_space") && rr.front().contains("value");
1640 const bool has_body_ids = has_body_value_entries(rr);
1641 if (has_body_ids)
1642 {
1643 const std::vector<json> entries = flatten_ids(rr);
1644 body_rhs_.resize(entries.size());
1645 for (size_t k = 0; k < entries.size(); ++k)
1646 {
1647 body_rhs_[k].body_id = entries[k]["id"];
1648 body_rhs_[k].fe_space_id = fe_space_id(entries[k]);
1649 body_rhs_[k].value.init(entries[k]["value"], resources);
1650 }
1651 }
1652 else if (has_fe_spaces)
1653 {
1654 for (const json &entry : rr)
1655 rhs_[fe_space_id(entry)].init(entry["value"], resources);
1656 }
1657 else if (!rr.is_array() || !rr.empty())
1658 rhs_[-1].init(rr, resources);
1659 }
1660
1661 if (is_param_valid(params, "reference") && is_param_valid(params["reference"], "solution"))
1662 {
1663 has_exact_ = !params["reference"]["solution"].empty();
1664 exact_.init(params["reference"]["solution"], resources);
1665 }
1666
1667 if (is_param_valid(params, "reference") && is_param_valid(params["reference"], "gradient"))
1668 {
1669 auto ex = params["reference"]["gradient"];
1670 has_exact_grad_ = ex.size() > 0;
1671 if (ex.is_array())
1672 {
1673 for (size_t k = 0; k < ex.size(); ++k)
1674 exact_grad_[k].init(ex[k], resources);
1675 }
1676 else
1677 {
1678 assert(false);
1679 }
1680 }
1681
1682 if (is_param_valid(params, "dirichlet_boundary"))
1683 {
1684 // boundary_ids_.clear();
1685 const int offset = boundary_ids_.size();
1686 std::vector<json> j_boundary = flatten_ids(params["dirichlet_boundary"]);
1687
1688 boundary_ids_.resize(offset + j_boundary.size());
1689 dirichlet_.resize(offset + j_boundary.size());
1690
1691 for (size_t i = offset; i < boundary_ids_.size(); ++i)
1692 {
1693 if (j_boundary[i - offset].is_string())
1694 {
1695 const std::string path = j_boundary[i - offset].get<std::string>();
1696 nodal_dirichlet_mat_.emplace_back(resources.read_matrix(path));
1697
1698 continue;
1699 }
1700
1701 int current_id = -1;
1702 dirichlet_[i].fe_space_id = fe_space_id(j_boundary[i - offset]);
1703
1704 if (j_boundary[i - offset]["id"] == "all")
1705 {
1706 boundary_ids_[i] = -1;
1707 is_all_ = true;
1708 nodal_dirichlet_[current_id] = ScalarBCValue();
1709 }
1710 else
1711 {
1712 boundary_ids_[i] = j_boundary[i - offset]["id"];
1713 current_id = boundary_ids_[i];
1714 nodal_dirichlet_[current_id] = ScalarBCValue();
1715 }
1716 nodal_dirichlet_[current_id].fe_space_id = dirichlet_[i].fe_space_id;
1717
1718 auto ff = j_boundary[i - offset]["value"];
1719 dirichlet_[i].value.init(ff, resources);
1720 nodal_dirichlet_[current_id].value.init(ff, resources);
1721
1722 if (j_boundary[i - offset]["interpolation"].is_array())
1723 {
1724 if (j_boundary[i - offset]["interpolation"].size() == 0)
1725 dirichlet_[i].interpolation = std::make_shared<NoInterpolation>();
1726 else if (j_boundary[i - offset]["interpolation"].size() == 1)
1727 dirichlet_[i].interpolation = Interpolation::build(j_boundary[i - offset]["interpolation"][0]);
1728 else
1729 log_and_throw_error("Only one Dirichlet interpolation supported");
1730 }
1731 else
1732 dirichlet_[i].interpolation = Interpolation::build(j_boundary[i - offset]["interpolation"]);
1733
1734 nodal_dirichlet_[current_id].interpolation = dirichlet_[i].interpolation;
1735 }
1736 }
1737
1738 if (is_param_valid(params, "neumann_boundary"))
1739 {
1740 // neumann_boundary_ids_.clear();
1741 const int offset = neumann_boundary_ids_.size();
1742 auto j_boundary_tmp = params["neumann_boundary"];
1743 std::vector<json> j_boundary = flatten_ids(j_boundary_tmp);
1744
1745 neumann_boundary_ids_.resize(offset + j_boundary.size());
1746 neumann_.resize(offset + j_boundary.size());
1747
1748 for (size_t i = offset; i < neumann_boundary_ids_.size(); ++i)
1749 {
1750 neumann_boundary_ids_[i] = j_boundary[i - offset]["id"];
1751 neumann_[i].fe_space_id = fe_space_id(j_boundary[i - offset]);
1752
1753 auto ff = j_boundary[i - offset]["value"];
1754 neumann_[i].value.init(ff, resources);
1755
1756 if (j_boundary[i - offset]["interpolation"].is_array())
1757 {
1758 if (j_boundary[i - offset]["interpolation"].size() == 0)
1759 neumann_[i].interpolation = std::make_shared<NoInterpolation>();
1760 else if (j_boundary[i - offset]["interpolation"].size() == 1)
1761 neumann_[i].interpolation = Interpolation::build(j_boundary[i - offset]["interpolation"][0]);
1762 else
1763 log_and_throw_error("Only one Neumann interpolation supported");
1764 }
1765 else
1766 neumann_[i].interpolation = Interpolation::build(j_boundary[i - offset]["interpolation"]);
1767 }
1768 }
1769
1770 if (is_param_valid(params, "nodal_neumann_boundary"))
1771 {
1772 std::vector<json> j_boundary = flatten_ids(params["nodal_neumann_boundary"]);
1773
1774 for (size_t i = 0; i < j_boundary.size(); ++i)
1775 {
1776 if (j_boundary[i].is_string())
1777 {
1778 const std::string path = j_boundary[i].get<std::string>();
1779 nodal_neumann_mat_.emplace_back(resources.read_matrix(path));
1780
1781 continue;
1782 }
1783
1784 const int id = j_boundary[i]["id"];
1785 ScalarBCValue nodal_neumann;
1786 nodal_neumann.fe_space_id = fe_space_id(j_boundary[i]);
1787 nodal_neumann.value.init(j_boundary[i]["value"], resources);
1788
1789 if (j_boundary[i]["interpolation"].is_array())
1790 {
1791 if (j_boundary[i]["interpolation"].size() == 0)
1792 nodal_neumann.interpolation = std::make_shared<NoInterpolation>();
1793 else if (j_boundary[i]["interpolation"].size() == 1)
1794 nodal_neumann.interpolation = Interpolation::build(j_boundary[i]["interpolation"][0]);
1795 else
1796 log_and_throw_error("Only one nodal Neumann interpolation supported");
1797 }
1798 else
1799 nodal_neumann.interpolation = Interpolation::build(j_boundary[i]["interpolation"]);
1800
1801 nodal_neumann_[id] = nodal_neumann;
1802 }
1803 }
1804
1805 if (is_param_valid(params, "solution"))
1806 {
1807 auto rr = params["solution"];
1808 initial_solution_.resize(rr.size());
1809 assert(rr.is_array());
1810
1811 for (size_t k = 0; k < rr.size(); ++k)
1812 {
1813 initial_solution_[k].body_id = rr[k]["id"];
1814 initial_solution_[k].fe_space_id = fe_space_id(rr[k]);
1815 initial_solution_[k].value.init(rr[k]["value"], resources);
1816 }
1817 }
1818 }
1819
1821 {
1822 neumann_.clear();
1823 dirichlet_.clear();
1824
1825 nodal_dirichlet_.clear();
1826 nodal_neumann_.clear();
1827 nodal_dirichlet_mat_.clear();
1828 nodal_neumann_mat_.clear();
1829
1830 rhs_.clear();
1831 body_rhs_.clear();
1832 exact_.clear();
1833 for (int i = 0; i < exact_grad_.size(); ++i)
1834 exact_grad_[i].clear();
1835 is_all_ = false;
1836 has_exact_ = false;
1837 has_exact_grad_ = false;
1838 is_time_dept_ = false;
1840 }
1841 } // namespace assembler
1842} // namespace polyfem
double val
Definition Assembler.cpp:90
std::vector< Eigen::Triplet< double > > entries
int y
int z
int x
std::string force() const
Definition Units.hpp:31
std::string pressure() const
Definition Units.hpp:32
std::string velocity() const
Definition Units.hpp:29
std::string acceleration() const
Definition Units.hpp:30
const std::string & length() const
Definition Units.hpp:19
virtual bool is_fluid() const
void neumann_bc(const mesh::Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &uv, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &normals, const double t, Eigen::MatrixXd &val, const int fe_space_id=-1) const override
bool is_nodal_neumann_boundary(const int n_id, const int tag, const int fe_space_id=-1) override
std::vector< ScalarBCValue > neumann_
void initial_solution(const mesh::Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &pts, Eigen::MatrixXd &val, const int fe_space_id=-1) const override
std::vector< Eigen::MatrixXd > nodal_neumann_mat_
void update_nodes(const Eigen::VectorXi &in_node_to_node) override
void dirichlet_nodal_value(const mesh::Mesh &mesh, const int node_id, const RowVectorNd &pt, const double t, Eigen::MatrixXd &val, const int fe_space_id=-1) const override
bool is_nodal_dirichlet_boundary(const int n_id, const int tag, const int fe_space_id=-1) override
std::vector< ScalarInitialValue > body_rhs_
std::array< utils::ExpressionValue, 3 > exact_grad_
bool has_nodal_dirichlet(const int fe_space_id=-1) override
void exact_grad(const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val) const override
bool has_nodal_neumann(const int fe_space_id=-1) override
std::map< int, utils::ExpressionValue > rhs_
void set_parameters(const json &params, const io::ResourceIO &resources) override
void neumann_nodal_value(const mesh::Mesh &mesh, const int node_id, const RowVectorNd &pt, const Eigen::MatrixXd &normal, const double t, Eigen::MatrixXd &val, const int fe_space_id=-1) const override
std::map< int, ScalarBCValue > nodal_dirichlet_
void rhs(const assembler::Assembler &assembler, const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val, const int fe_space_id=-1) const override
std::vector< ScalarInitialValue > initial_solution_
bool is_rhs_zero(const int fe_space_id=-1) const override
void set_units(const assembler::Assembler &assembler, const Units &units) override
std::map< int, ScalarBCValue > nodal_neumann_
std::vector< ScalarBCValue > dirichlet_
bool has_boundary(const BoundaryKind kind, const int tag, const int fe_space_id) override
void exact(const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val) const override
void dirichlet_bc(const mesh::Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &uv, const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val, const int fe_space_id=-1) const override
std::vector< Eigen::MatrixXd > nodal_dirichlet_mat_
void update_pressure_boundary(const int id, const int time_step, const double val)
bool has_boundary(const BoundaryKind kind, const int tag, const int fe_space_id) override
std::map< int, TensorBCValue > nodal_neumann_
std::vector< TensorInitialValue > initial_acceleration_
void initial_acceleration(const mesh::Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &pts, Eigen::MatrixXd &val, const int fe_space_id=-1) const override
void initial_solution(const mesh::Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &pts, Eigen::MatrixXd &val, const int fe_space_id=-1) const override
std::unordered_map< int, ScalarBCValue > cavity_pressures_
std::vector< TensorInitialValue > initial_position_
void rhs(const assembler::Assembler &assembler, const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val, const int fe_space_id=-1) const override
void update_nodes(const Eigen::VectorXi &in_node_to_node) override
GenericTensorProblem(const std::string &name)
std::vector< TensorInitialValue > body_rhs_
std::vector< TensorInitialValue > initial_velocity_
bool all_dimensions_dirichlet(const int fe_space_id) const override
void update_dirichlet_nodes(const Eigen::VectorXi &in_node_to_node, const Eigen::VectorXi &node_ids, const Eigen::MatrixXd &nodal_dirichlet)
std::vector< TensorBCValue > displacements_
void neumann_bc(const mesh::Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &uv, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &normals, const double t, Eigen::MatrixXd &val, const int fe_space_id=-1) const override
std::vector< ScalarBCValue > normal_aligned_forces_
std::map< int, std::array< utils::ExpressionValue, 3 > > rhs_
bool is_nodal_dimension_dirichlet(const int n_id, const int tag, const int dim, const int fe_space_id=-1) const override
void dirichlet_bc(const mesh::Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &uv, const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val, const int fe_space_id=-1) const override
std::vector< Eigen::MatrixXd > nodal_neumann_mat_
bool is_nodal_neumann_boundary(const int n_id, const int tag, const int fe_space_id=-1) override
bool is_dimension_dirichet(const int tag, const int dim, const int fe_space_id=-1) const override
void pressure_bc(const mesh::Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &uv, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &normals, const double t, Eigen::MatrixXd &val) const override
void exact(const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val) const override
std::map< int, TensorBCValue > nodal_dirichlet_
bool has_nodal_neumann(const int fe_space_id=-1) override
std::array< utils::ExpressionValue, 9 > exact_grad_
void set_parameters(const json &params, const io::ResourceIO &resources) override
bool is_rhs_zero(const int fe_space_id=-1) const override
void dirichlet_nodal_value(const mesh::Mesh &mesh, const int node_id, const RowVectorNd &pt, const double t, Eigen::MatrixXd &val, const int fe_space_id=-1) const override
void update_dirichlet_boundary(const int id, const int time_step, const Eigen::VectorXd &val)
double pressure_cavity_bc(const int boundary_id, const double t) const override
std::vector< ScalarBCValue > pressures_
bool is_nodal_dirichlet_boundary(const int n_id, const int tag, const int fe_space_id=-1) override
void neumann_nodal_value(const mesh::Mesh &mesh, const int node_id, const RowVectorNd &pt, const Eigen::MatrixXd &normal, const double t, Eigen::MatrixXd &val, const int fe_space_id=-1) const override
void exact_grad(const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val) const override
bool has_nodal_dirichlet(const int fe_space_id=-1) override
void set_units(const assembler::Assembler &assembler, const Units &units) override
std::array< utils::ExpressionValue, 3 > exact_
std::vector< Eigen::MatrixXd > nodal_dirichlet_mat_
void initial_velocity(const mesh::Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &pts, Eigen::MatrixXd &val, const int fe_space_id=-1) const override
std::vector< int > normal_aligned_neumann_boundary_ids_
Definition Problem.hpp:126
virtual bool has_boundary(const BoundaryKind kind, const int tag, const int fe_space_id)
Definition Problem.cpp:16
std::vector< int > pressure_boundary_ids_
Definition Problem.hpp:127
std::vector< int > boundary_ids_
Definition Problem.hpp:124
virtual void init(const mesh::Mesh &mesh)
Definition Problem.hpp:30
std::vector< int > neumann_boundary_ids_
Definition Problem.hpp:125
std::vector< int > pressure_cavity_ids_
Definition Problem.hpp:128
Read-only interface to a hierarchical collection of simulation resources.
virtual Eigen::MatrixXd read_matrix(const std::string &path) const =0
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
Definition Mesh.hpp:50
virtual int get_body_id(const int primitive) const
Get the volume selection of an element (cell in 3d, face in 2d)
Definition Mesh.hpp:492
virtual int get_boundary_id(const int primitive) const
Get the boundary selection of an element (face in 3d, edge in 2d)
Definition Mesh.hpp:466
int dimension() const
utily for dimension
Definition Mesh.hpp:138
virtual int get_node_id(const int node_id) const
Get the boundary selection of a node.
Definition Mesh.hpp:475
void set_unit_type(const std::string &unit_type)
void init(const json &vals, const io::ResourceIO &resources)
static std::shared_ptr< Interpolation > build(const json &params)
list tmp
Definition p_bases.py:366
std::vector< T > json_as_array(const json &j)
Return the value of a json object as an array.
Definition JSONUtils.hpp:49
bool is_param_valid(const json &params, const std::string &key)
Determine if a key exists and is non-null in a json object.
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:44
nlohmann::json json
Definition Common.hpp:9
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
Definition Types.hpp:13
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:73
std::shared_ptr< utils::Interpolation > interpolation
double eval(const RowVectorNd &pts, const double t) const
std::vector< std::shared_ptr< utils::Interpolation > > interpolation
std::array< utils::ExpressionValue, 3 > value
double eval(const RowVectorNd &pts, const int dim, const double t, const int el_id=-1) const
std::array< utils::ExpressionValue, 3 > value