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