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