PolyFEM
Loading...
Searching...
No Matches
MatParams.cpp
Go to the documentation of this file.
1#include "MatParams.hpp"
2
5#include <polyfem/utils/StringUtils.hpp> // utils::resolve_path
6#include <iostream>
7#include <fstream>
8#include <sstream>
9
10namespace polyfem::assembler
11{
12 namespace
13 {
14 double convert_to_lambda(const bool is_volume, const double E, const double nu)
15 {
16 if (is_volume)
17 return (E * nu) / ((1.0 + nu) * (1.0 - 2.0 * nu));
18
19 return (nu * E) / (1.0 - nu * nu);
20 }
21
22 // Reads a named VECTORS array under CELL_DATA from a legacy ASCII VTK
23 // file. Returns one Eigen::Vector3d per cell, in file order.
24 std::vector<Eigen::Vector3d> read_cell_vectors_legacy_vtk(
25 const std::string &path, const std::string &field_name)
26 {
27 std::ifstream in(path);
28 if (!in)
29 log_and_throw_error(fmt::format("Cannot open fiber file: {}", path));
30
31 std::vector<Eigen::Vector3d> out;
32 std::string line;
33 int n_cell_data = -1;
34 bool found = false;
35
36 while (std::getline(in, line))
37 {
38 std::istringstream ss(line);
39 std::string tok;
40 ss >> tok;
41 if (tok == "CELL_DATA")
42 {
43 ss >> n_cell_data;
44 }
45 else if (tok == "VECTORS")
46 {
47 std::string name;
48 ss >> name;
49 if (name != field_name)
50 continue;
51 if (n_cell_data < 0)
52 log_and_throw_error("VECTORS encountered before CELL_DATA in fiber VTK");
53 out.resize(n_cell_data);
54 for (int i = 0; i < n_cell_data; ++i)
55 {
56 if (!(in >> out[i].x() >> out[i].y() >> out[i].z()))
57 log_and_throw_error(fmt::format(
58 "Fiber VTK '{}' ended early: expected {} vectors", path, n_cell_data));
59 }
60 found = true;
61 break;
62 }
63 }
64 if (!found)
65 log_and_throw_error(fmt::format(
66 "VECTORS '{}' not found under CELL_DATA in {}", field_name, path));
67 return out;
68 }
69
70 double convert_to_mu(const double E, const double nu)
71 {
72 return E / (2.0 * (1.0 + nu));
73 }
74 } // namespace
75
76 GenericMatParam::GenericMatParam(const std::string &param_name)
77 : param_name_(param_name)
78 {
79 }
80
81 void GenericMatParam::add_multimaterial(const int index, const json &params, const std::string &unit_type, const std::string &root_path)
82 {
83 for (int i = param_.size(); i <= index; ++i)
84 {
85 param_.emplace_back();
86 param_.back().set_unit_type(unit_type);
87 }
88
89 if (params.count(param_name_))
90 {
91 param_[index].init(params[param_name_], root_path);
92 if (params.contains(MATERIAL_ELEMENT_INDEX))
93 param_[index].set_index(params[MATERIAL_ELEMENT_INDEX]);
94 }
95 }
96
97 double GenericMatParam::operator()(const RowVectorNd &p, double t, int index) const
98 {
99 const double x = p(0);
100 const double y = p(1);
101 const double z = p.size() == 3 ? p(2) : 0;
102
103 return (*this)(x, y, z, t, index);
104 }
105
106 double GenericMatParam::operator()(double x, double y, double z, double t, int index) const
107 {
108 assert(param_.size() == 1 || index < param_.size());
109
110 const auto &tmp_param = param_.size() == 1 ? param_[0] : param_[index];
111
112 return tmp_param(x, y, z, t, index);
113 }
114
115 GenericMatParams::GenericMatParams(const std::string &param_name)
116 : param_name_(param_name)
117 {
118 }
119
120 void GenericMatParams::add_multimaterial(const int index, const json &params, const std::string &unit_type, const std::string &root_path)
121 {
122 if (!params.contains(param_name_))
123 return;
124
125 std::vector<json> params_array = utils::json_as_array(params[param_name_]);
126 assert(params_array.size() == params_.size() || params_.empty());
127
128 if (params_.empty())
129 for (int i = 0; i < params_array.size(); ++i)
130 params_.emplace_back(param_name_ + "_" + std::to_string(i));
131
132 for (int i = 0; i < params_.size(); ++i)
133 {
134 for (int j = params_.at(i).param_.size(); j <= index; ++j)
135 {
136 params_.at(i).param_.emplace_back();
137 params_.at(i).param_.back().set_unit_type(unit_type);
138 }
139
140 params_.at(i).param_[index].init(params_array[i], root_path);
141 if (params.contains(MATERIAL_ELEMENT_INDEX))
142 params_.at(i).param_[index].set_index(params[MATERIAL_ELEMENT_INDEX]);
143 }
144 }
145
146 void ElasticityTensor::resize(const int size)
147 {
148 if (size == 2)
149 stiffness_tensor_.resize(3, 3);
150 else
151 stiffness_tensor_.resize(6, 6);
152
153 stiffness_tensor_.setZero();
154
155 size_ = size;
156 }
157
158 double ElasticityTensor::operator()(int i, int j) const
159 {
160 if (j < i)
161 {
162 std::swap(i, j);
163 }
164
165 assert(j >= i);
166 return stiffness_tensor_(i, j);
167 }
168
169 double &ElasticityTensor::operator()(int i, int j)
170 {
171 if (j < i)
172 {
173 std::swap(i, j);
174 }
175
176 assert(j >= i);
177 return stiffness_tensor_(i, j);
178 }
179
180 void ElasticityTensor::set_from_entries(const std::vector<double> &entries, const std::string &stress_units, const std::string &root_path)
181 {
182 if (size_ == 2)
183 {
184 if (entries.size() == 4)
185 {
187 entries[0],
188 entries[1],
189 entries[2],
190 entries[3], stress_units, root_path);
191
192 return;
193 }
194
195 assert(entries.size() >= 6);
196
197 (*this)(0, 0) = entries[0];
198 (*this)(0, 1) = entries[1];
199 (*this)(0, 2) = entries[2];
200
201 (*this)(1, 1) = entries[3];
202 (*this)(1, 2) = entries[4];
203
204 (*this)(2, 2) = entries[5];
205 }
206 else
207 {
208 if (entries.size() == 5)
209 {
211 entries[0],
212 entries[1],
213 entries[2],
214 entries[3],
215 entries[4],
216 stress_units, root_path);
217
218 return;
219 }
220 else if (entries.size() == 9)
221 {
223 entries[0],
224 entries[1],
225 entries[2],
226 entries[3],
227 entries[4],
228 entries[5],
229 entries[6],
230 entries[7],
231 entries[8], stress_units, root_path);
232
233 return;
234 }
235 assert(entries.size() >= 21);
236
237 (*this)(0, 0) = entries[0];
238 (*this)(0, 1) = entries[1];
239 (*this)(0, 2) = entries[2];
240 (*this)(0, 3) = entries[3];
241 (*this)(0, 4) = entries[4];
242 (*this)(0, 5) = entries[5];
243
244 (*this)(1, 1) = entries[6];
245 (*this)(1, 2) = entries[7];
246 (*this)(1, 3) = entries[8];
247 (*this)(1, 4) = entries[9];
248 (*this)(1, 5) = entries[10];
249
250 (*this)(2, 2) = entries[11];
251 (*this)(2, 3) = entries[12];
252 (*this)(2, 4) = entries[13];
253 (*this)(2, 5) = entries[14];
254
255 (*this)(3, 3) = entries[15];
256 (*this)(3, 4) = entries[16];
257 (*this)(3, 5) = entries[17];
258
259 (*this)(4, 4) = entries[18];
260 (*this)(4, 5) = entries[19];
261
262 (*this)(5, 5) = entries[20];
263 }
264 }
265
266 void ElasticityTensor::set_from_lambda_mu(const double lambda, const double mu, const std::string &stress_units, const std::string &root_path)
267 {
268 if (size_ == 2)
269 {
270 (*this)(0, 0) = 2 * mu + lambda;
271 (*this)(0, 1) = lambda;
272 (*this)(0, 2) = 0;
273
274 (*this)(1, 1) = 2 * mu + lambda;
275 (*this)(1, 2) = 0;
276
277 (*this)(2, 2) = mu;
278 }
279 else
280 {
281 (*this)(0, 0) = 2 * mu + lambda;
282 (*this)(0, 1) = lambda;
283 (*this)(0, 2) = lambda;
284 (*this)(0, 3) = 0;
285 (*this)(0, 4) = 0;
286 (*this)(0, 5) = 0;
287
288 (*this)(1, 1) = 2 * mu + lambda;
289 (*this)(1, 2) = lambda;
290 (*this)(1, 3) = 0;
291 (*this)(1, 4) = 0;
292 (*this)(1, 5) = 0;
293
294 (*this)(2, 2) = 2 * mu + lambda;
295 (*this)(2, 3) = 0;
296 (*this)(2, 4) = 0;
297 (*this)(2, 5) = 0;
298
299 (*this)(3, 3) = mu;
300 (*this)(3, 4) = 0;
301 (*this)(3, 5) = 0;
302
303 (*this)(4, 4) = mu;
304 (*this)(4, 5) = 0;
305
306 (*this)(5, 5) = mu;
307 }
308 }
309
310 void ElasticityTensor::set_from_young_poisson(const double young, const double nu, const std::string &stress_units, const std::string &root_path)
311 {
312 if (size_ == 2)
313 {
314 stiffness_tensor_ << 1.0, nu, 0.0,
315 nu, 1.0, 0.0,
316 0.0, 0.0, (1.0 - nu) / 2.0;
317 stiffness_tensor_ *= young / (1.0 - nu * nu);
318 }
319 else
320 {
321 assert(size_ == 3);
322 const double v = nu;
323 stiffness_tensor_ << 1. - v, v, v, 0, 0, 0,
324 v, 1. - v, v, 0, 0, 0,
325 v, v, 1. - v, 0, 0, 0,
326 0, 0, 0, (1. - 2. * v) / 2., 0, 0,
327 0, 0, 0, 0, (1. - 2. * v) / 2., 0,
328 0, 0, 0, 0, 0, (1. - 2. * v) / 2.;
329 stiffness_tensor_ *= young / ((1. + v) * (1. - 2. * v));
330 }
331 }
332
334 double Ex, double Ey, double Ez,
335 double nuXY, double nuXZ, double nuYZ,
336 double muYZ, double muZX, double muXY, const std::string &stress_units, const std::string &root_path)
337 {
338 assert(size_ == 3);
339
340 // from https://www.efunda.com/formulae/solid_mechanics/mat_mechanics/hooke_orthotropic.cfm
341 double nuYX = nuXY * Ey / Ex;
342 double nuZX = nuXZ * Ez / Ex;
343 double nuZY = nuYZ * Ez / Ey;
344
345 Eigen::MatrixXd compliance;
346 compliance.setZero(6, 6);
347 compliance << 1 / Ex, -nuYX / Ey, -nuZX / Ez, 0, 0, 0,
348 -nuXY / Ex, 1 / Ey, -nuZY / Ez, 0, 0, 0,
349 -nuXZ / Ex, -nuYZ / Ey, 1 / Ez, 0, 0, 0,
350 0, 0, 0, 1 / (2 * muYZ), 0, 0,
351 0, 0, 0, 0, 1 / (2 * muZX), 0,
352 0, 0, 0, 0, 0, 1 / (2 * muXY);
353 stiffness_tensor_ = compliance.inverse();
354 }
355
356 void ElasticityTensor::set_orthotropic(double Ex, double Ey, double nuXY, double muXY, const std::string &stress_units, const std::string &root_path)
357 {
358 assert(size_ == 2);
359
360 // from https://www.efunda.com/formulae/solid_mechanics/mat_mechanics/hooke_orthotropic.cfm
361 double nuYX = nuXY * Ey / Ex;
362
363 Eigen::MatrixXd compliance;
364 compliance.setZero(3, 3);
365 compliance << 1.0 / Ex, -nuYX / Ey, 0.0,
366 -nuXY / Ex, 1.0 / Ey, 0.0,
367 0.0, 0.0, 1.0 / (2 * muXY);
368 stiffness_tensor_ = compliance.inverse();
369 }
370
372 double Et, double Ea,
373 double nu_t, double nu_a,
374 double Ga, const std::string &stress_units, const std::string &root_path)
375 {
376 assert(size_ == 3);
377
378 // from https://osupdocs.forestry.oregonstate.edu/index.php/Transversely_Isotropic_Material
379 Eigen::MatrixXd compliance;
380 compliance.setZero(6, 6);
381 compliance << 1 / Et, -nu_t / Et, -nu_a / Ea, 0, 0, 0,
382 -nu_t / Et, 1 / Et, -nu_a / Ea, 0, 0, 0,
383 -nu_a / Ea, -nu_a / Ea, 1 / Ea, 0, 0, 0,
384 0, 0, 0, 1 / Ga, 0, 0,
385 0, 0, 0, 0, 1 / Ga, 0,
386 0, 0, 0, 0, 0, (2 * (1 + nu_t)) / Et;
387 stiffness_tensor_ = compliance.inverse();
388 }
389
390 template <int DIM>
391 double ElasticityTensor::compute_stress(const std::array<double, DIM> &strain, const int j) const
392 {
393 double res = 0;
394
395 for (int k = 0; k < DIM; ++k)
396 res += (*this)(j, k) * strain[k];
397
398 return res;
399 }
400
401 void ElasticityTensor::rotate_stiffness(const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, 0, 6, 6> &rotation_mtx_voigt)
402 {
404 stiffness_tensor_ = rotation_mtx_voigt * stiffness_tensor_ * rotation_mtx_voigt.transpose();
405 }
406
411
413 {
414 lambda_or_E_.emplace_back();
415 lambda_or_E_.back().init(1.0);
416
417 mu_or_nu_.emplace_back();
418 mu_or_nu_.back().init(1.0);
419 size_ = -1;
420 is_lambda_mu_ = true;
421 }
422
423 void LameParameters::lambda_mu(double px, double py, double pz, double x, double y, double z, double t, int el_id, double &lambda, double &mu) const
424 {
425 assert(lambda_or_E_.size() == 1 || el_id < lambda_or_E_.size());
426 assert(mu_or_nu_.size() == 1 || el_id < mu_or_nu_.size());
427 assert(size_ == 2 || size_ == 3);
428
429 const auto &tmp1 = lambda_or_E_.size() == 1 ? lambda_or_E_[0] : lambda_or_E_[el_id];
430 const auto &tmp2 = mu_or_nu_.size() == 1 ? mu_or_nu_[0] : mu_or_nu_[el_id];
431
432 double llambda = tmp1(x, y, z, t, el_id);
433 double mmu = tmp2(x, y, z, t, el_id);
434
435 if (!is_lambda_mu_)
436 {
437 lambda = convert_to_lambda(size_ == 3, llambda, mmu);
438 mu = convert_to_mu(llambda, mmu);
439 }
440 else
441 {
442 lambda = llambda;
443 mu = mmu;
444 }
445
446 if (lambda_mat_.size() > el_id && mu_mat_.size() > el_id)
447 {
448 lambda = lambda_mat_(el_id);
449 mu = mu_mat_(el_id);
450 }
451
452 assert(!std::isnan(lambda));
453 assert(!std::isnan(mu));
454 assert(!std::isinf(lambda));
455 assert(!std::isinf(mu));
456 }
457
458 void LameParameters::add_multimaterial(const int index, const json &params, const bool is_volume, const std::string &stress_unit, const std::string &root_path)
459 {
460 const int size = is_volume ? 3 : 2;
461 assert(size_ == -1 || size == size_);
462 size_ = size;
463
464 for (int i = lambda_or_E_.size(); i <= index; ++i)
465 {
466 lambda_or_E_.emplace_back();
467 lambda_or_E_.back().set_unit_type(stress_unit);
468 mu_or_nu_.emplace_back();
469 mu_or_nu_.back().set_unit_type("");
470 }
471
472 if (params.count("young"))
473 {
474 set_e_nu(index, params["young"], params["nu"], stress_unit, root_path);
475 }
476 else if (params.count("E"))
477 {
478 set_e_nu(index, params["E"], params["nu"], stress_unit, root_path);
479 }
480 else if (params.count("lambda"))
481 {
482 lambda_or_E_[index].init(params["lambda"], root_path);
483 mu_or_nu_[index].init(params["mu"], root_path);
484
485 lambda_or_E_[index].set_unit_type(stress_unit);
486 mu_or_nu_[index].set_unit_type(stress_unit);
487 is_lambda_mu_ = true;
488 }
489
490 if (params.contains(MATERIAL_ELEMENT_INDEX))
491 {
492 lambda_or_E_[index].set_index(params[MATERIAL_ELEMENT_INDEX]);
493 mu_or_nu_[index].set_index(params[MATERIAL_ELEMENT_INDEX]);
494 }
495 }
496
497 void LameParameters::set_e_nu(const int index, const json &E, const json &nu, const std::string &stress_unit, const std::string &root_path)
498 {
499 // TODO: conversion is always called
500 is_lambda_mu_ = false;
501 lambda_or_E_[index].init(E, root_path);
502 mu_or_nu_[index].init(nu, root_path);
503
504 lambda_or_E_[index].set_unit_type(stress_unit);
505 // nu has no unit
506 mu_or_nu_[index].set_unit_type("");
507 }
508
510 {
511 rho_.emplace_back();
512 rho_.back().init(1.0);
513 }
514
515 double Density::operator()(double px, double py, double pz, double x, double y, double z, double t, int el_id) const
516 {
517 assert(rho_.size() == 1 || el_id < rho_.size());
518
519 const auto &tmp = rho_.size() == 1 ? rho_[0] : rho_[el_id];
520 const double res = tmp(x, y, z, t, el_id);
521 assert(!std::isnan(res));
522 assert(!std::isinf(res));
523 return res;
524 }
525
526 void Density::add_multimaterial(const int index, const json &params, const std::string &density_unit, const std::string &root_path)
527 {
528 for (int i = rho_.size(); i <= index; ++i)
529 {
530 rho_.emplace_back();
531 }
532
533 if (params.count("rho"))
534 {
535 rho_[index].init(params["rho"], root_path);
536 }
537 else if (params.count("density"))
538 {
539 rho_[index].init(params["density"], root_path);
540 }
541 if (params.contains(MATERIAL_ELEMENT_INDEX))
542 rho_[index].set_index(params[MATERIAL_ELEMENT_INDEX]);
543
544 rho_[index].set_unit_type(density_unit);
545 }
546
548 : rho_("rho"), heat_capacity_("heat_capacity")
549 {
550 }
551
552 void ThermalMassDensity::add_multimaterial(const int index, const json &params, const std::string &density_unit, const std::string &root_path)
553 {
554 add_multimaterial(index, params, density_unit, "", root_path);
555 }
556
557 void ThermalMassDensity::add_multimaterial(const int index, const json &params, const std::string &density_unit, const std::string &heat_capacity_unit, const std::string &root_path)
558 {
559 rho_.add_multimaterial(index, params, density_unit, root_path);
560 heat_capacity_.add_multimaterial(index, params, heat_capacity_unit, root_path);
561 }
562
563 double ThermalMassDensity::operator()(double px, double py, double pz, double x, double y, double z, double t, int el_id) const
564 {
565 const double rho = rho_(x, y, z, t, el_id);
566 const double heat_capacity = heat_capacity_(x, y, z, t, el_id);
567 const double res = rho * heat_capacity;
568 assert(!std::isnan(res));
569 assert(!std::isinf(res));
570 return res;
571 }
572
573 double ThermalMassDensity::rho(const RowVectorNd &p, double t, int el_id) const
574 {
575 return rho_(p, t, el_id);
576 }
577
578 double ThermalMassDensity::heat_capacity(const RowVectorNd &p, double t, int el_id) const
579 {
580 return heat_capacity_(p, t, el_id);
581 }
582
586
587 void FiberDirection::resize(const int size)
588 {
589 assert(size == 2 || size == 3);
590 size_ = size;
591 if (!dir_.empty())
592 {
593 for (const auto &m : dir_)
594 {
595 assert((m.rows() == size && m.cols() == size) || (m.rows() == size && m.cols() == 1));
596 }
597 }
598 }
599
600 Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, 1, 3, 3> FiberDirection::operator()(double px, double py, double pz, double x, double y, double z, double t, int el_id) const
601 {
602 // Per-element fiber file: bound by global el_id, returned as a size_ x 1
603 // column vector so downstream is_a_vector logic is unchanged.
605 {
606 if (el_id < 0 || el_id >= static_cast<int>(per_el_fibers_.size()))
607 log_and_throw_error(fmt::format(
608 "Fiber el_id {} out of range [0,{})", el_id, per_el_fibers_.size()));
609 Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, 1, 3, 3> res;
610 res.resize(size_, 1);
611 for (int i = 0; i < size_; ++i)
612 res(i, 0) = per_el_fibers_[el_id](i);
613 return res;
614 }
615
616 assert(dir_.size() == 1 || el_id < dir_.size());
617
618 const auto &tmp = dir_.size() == 1 ? dir_[0] : dir_[el_id];
619 Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, 1, 3, 3> res;
620 res.resize(tmp.rows(), tmp.cols());
621 for (int i = 0; i < tmp.rows(); ++i)
622 {
623 for (int j = 0; j < tmp.cols(); ++j)
624 {
625 res(i, j) = tmp(i, j)(x, y, z, t, el_id);
626
627 assert(!std::isnan(res(i, j)));
628 assert(!std::isinf(res(i, j)));
629 }
630 }
631 return res;
632 }
633
634 Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, 1, 6, 6> FiberDirection::stiffness_rotation_voigt(double px, double py, double pz, double x, double y, double z, double t, int el_id) const
635 {
636 // Rotate stiffness mtx in voigt notation according to:
637 // https://scicomp.stackexchange.com/questions/35600/4th-order-tensor-rotation-sources-to-refer
638
639 Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, 1, 3, 3> rot = (*this)(px, py, pz, x, y, z, t, el_id);
640 Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, 1, 6, 6> res;
641
642 int dim = rot.rows();
643
644 static const double sqrt2 = std::sqrt(2.0);
645
646 if (dim == 2)
647 {
648 res.resize(3, 3);
649 // Still need to compute for 2d
650 assert(false);
651 // res << rot(0, 0) * rot(0, 0), rot(0, 1) * rot(0, 1), 0,
652 // rot(1, 0) * rot(1, 0), rot(1, 1) * rot(1, 1), 0,
653 // 0, 0, 1;
654 }
655 else
656 {
657 assert(dim == 3);
658 res.resize(6, 6);
659 res << rot(0, 0) * rot(0, 0), rot(0, 1) * rot(0, 1), rot(0, 2) * rot(0, 2), sqrt2 * rot(0, 1) * rot(0, 2), sqrt2 * rot(0, 0) * rot(0, 2), sqrt2 * rot(0, 0) * rot(0, 1),
660 rot(1, 0) * rot(1, 0), rot(1, 1) * rot(1, 1), rot(1, 2) * rot(1, 2), sqrt2 * rot(1, 1) * rot(1, 2), sqrt2 * rot(1, 0) * rot(1, 2), sqrt2 * rot(1, 0) * rot(1, 1),
661 rot(2, 0) * rot(2, 0), rot(2, 1) * rot(2, 1), rot(2, 2) * rot(2, 2), sqrt2 * rot(2, 1) * rot(2, 2), sqrt2 * rot(2, 0) * rot(2, 2), sqrt2 * rot(2, 0) * rot(2, 1),
662 sqrt2 * rot(1, 0) * rot(2, 0), sqrt2 * rot(1, 1) * rot(2, 1), sqrt2 * rot(1, 2) * rot(2, 2), rot(1, 1) * rot(2, 2) + rot(1, 2) * rot(2, 1), rot(1, 0) * rot(2, 2) + rot(1, 2) * rot(2, 0), rot(1, 0) * rot(2, 1) + rot(1, 1) * rot(2, 0),
663 sqrt2 * rot(0, 0) * rot(2, 0), sqrt2 * rot(0, 1) * rot(2, 1), sqrt2 * rot(0, 2) * rot(2, 2), rot(0, 1) * rot(2, 2) + rot(0, 2) * rot(2, 1), rot(0, 0) * rot(2, 2) + rot(0, 2) * rot(2, 0), rot(0, 0) * rot(2, 1) + rot(0, 1) * rot(2, 0),
664 sqrt2 * rot(0, 0) * rot(1, 0), sqrt2 * rot(0, 1) * rot(1, 1), sqrt2 * rot(0, 2) * rot(1, 2), rot(0, 1) * rot(1, 2) + rot(0, 2) * rot(1, 1), rot(0, 0) * rot(1, 2) + rot(0, 2) * rot(1, 0), rot(0, 0) * rot(1, 1) + rot(0, 1) * rot(1, 0);
665 }
666
667 return res;
668 }
669
670 void FiberDirection::add_multimaterial(const int index, const json &dir, const std::string &unit, const std::string &root_path)
671 {
672 // Per-element fiber file:
673 // { "type": "per_element_file", "path": "...vtk", "field": "FIB_DIR1" }
674 // Read once at setup, bound by global el_id, normalized at load.
675 // NOTE: a JSON object with keys {type, path, field} reports size() == 3, so
676 // this MUST precede the size-based checks below to avoid being misread as a
677 // 3-component vector.
678 if (dir.is_object() && dir.value("type", std::string()) == "per_element_file")
679 {
680 const std::string field = dir.value("field", std::string("FIB_DIR1"));
681 const std::string p = utils::resolve_path(dir.at("path").get<std::string>(), root_path);
682
683 per_el_fibers_ = read_cell_vectors_legacy_vtk(p, field);
684 for (auto &v : per_el_fibers_)
685 {
686 const double n = v.norm();
687 if (n < 1e-12)
688 log_and_throw_error("Zero-length fiber vector in per-element file");
689 v /= n;
690 }
692 has_rotation_ = false; // a direction vector, not a rotation matrix
693 logger().info("FiberDirection: loaded {} per-element fibers ('{}') from {}",
694 per_el_fibers_.size(), field, p);
695 return; // dir_ left empty; operator() short-circuits
696 }
697
698 for (int i = dir_.size(); i <= index; ++i)
699 {
700 dir_.emplace_back();
701 }
702
703 if (dir.size() == 3 || dir.size() == 2)
704 {
705 const int size = dir.size();
706 const int other_size = dir[0].is_array() ? size : 1;
707
708 assert(size == size_);
709 dir_[index].resize(size, other_size);
710 for (int i = 0; i < size; ++i)
711 {
712 if (other_size == 1)
713 {
714 if (dir[i].is_array())
715 {
716 log_and_throw_error(fmt::format("Fiber must be a {} vector, row {} is {}", size, i, dir[i].dump()));
717 }
718 dir_[index](i, 0).init(dir[i], root_path);
719 dir_[index](i, 0).set_unit_type(unit);
720 continue;
721 }
722 if (dir[i].size() != size)
723 {
724 log_and_throw_error(fmt::format("Fiber must be {}x{}, row {} is {}", size, other_size, i, dir[i].dump()));
725 }
726 for (int j = 0; j < size; ++j)
727 {
728 dir_[index](i, j).init(dir[i][j], root_path);
729 dir_[index](i, j).set_unit_type(unit);
730 }
731 }
732 has_rotation_ = true;
733 }
734 else if (dir.size() == 9 || dir.size() == 4)
735 {
736 const int size = dir.size() == 9 ? 3 : 2;
737 assert(size == size_);
738 dir_[index].resize(size, size);
739 for (int i = 0; i < size; ++i)
740 {
741 for (int j = 0; j < size; ++j)
742 {
743 dir_[index](i, j).init(dir[i * size + j], root_path);
744 dir_[index](i, j).set_unit_type(unit);
745 }
746 }
747 has_rotation_ = true;
748 }
749 else if (dir.empty())
750 {
751 dir_[index].resize(size_, size_);
752 for (int i = 0; i < size_; ++i)
753 {
754 for (int j = 0; j < size_; ++j)
755 {
756 dir_[index](i, j).init(i == j ? 1.0 : 0.0);
757 dir_[index](i, j).set_unit_type(unit);
758 }
759 }
760 has_rotation_ = false;
761 }
762 else
763 {
764 log_and_throw_error("Fiber direction must be a 3x3 or 2x2 matrix");
765 }
766 }
767
768 // template instantiation
769 template double ElasticityTensor::compute_stress<3>(const std::array<double, 3> &strain, const int j) const;
770 template double ElasticityTensor::compute_stress<6>(const std::array<double, 6> &strain, const int j) const;
771
772} // namespace polyfem::assembler
std::vector< Eigen::Triplet< double > > entries
int y
int z
int x
std::vector< utils::ExpressionValue > rho_
virtual double operator()(double px, double py, double pz, double x, double y, double z, double t, int el_id) const
virtual void add_multimaterial(const int index, const json &params, const std::string &density_unit, const std::string &root_path)
double operator()(int i, int j) const
void set_from_young_poisson(const double young, const double poisson, const std::string &stress_unit, const std::string &root_path)
void rotate_stiffness(const Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, 0, 6, 6 > &rotation_mtx_voigt)
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, 0, 6, 6 > stiffness_tensor_
Definition MatParams.hpp:72
void set_transversely_isotropic(double Et, double Ea, double nu_t, double nu_a, double Ga, const std::string &stress_units, const std::string &root_path)
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, 0, 6, 6 > reference_stiffness_tensor_
Definition MatParams.hpp:73
void set_orthotropic(double Ex, double Ey, double Ez, double nuXY, double nuXZ, double nuYZ, double muYZ, double muZX, double muXY, const std::string &stress_unit, const std::string &root_path)
double compute_stress(const std::array< double, DIM > &strain, const int j) const
void set_from_entries(const std::vector< double > &entries, const std::string &stress_unit, const std::string &root_path)
void set_from_lambda_mu(const double lambda, const double mu, const std::string &stress_unit, const std::string &root_path)
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, 1, 6, 6 > stiffness_rotation_voigt(double px, double py, double pz, double x, double y, double z, double t, int el_id) const
std::vector< Eigen::Matrix< utils::ExpressionValue, Eigen::Dynamic, Eigen::Dynamic, 1, 3, 3 > > dir_
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, 1, 3, 3 > operator()(double px, double py, double pz, double x, double y, double z, double t, int el_id) const
void add_multimaterial(const int index, const json &params, const std::string &unit, const std::string &root_path)
std::vector< Eigen::Vector3d > per_el_fibers_
double operator()(const RowVectorNd &p, double t, int index) const
Definition MatParams.cpp:97
GenericMatParam(const std::string &param_name)
Definition MatParams.cpp:76
void add_multimaterial(const int index, const json &params, const std::string &unit_type, const std::string &root_path)
Definition MatParams.cpp:81
std::vector< utils::ExpressionValue > param_
Definition MatParams.hpp:23
std::vector< GenericMatParam > params_
Definition MatParams.hpp:40
GenericMatParams(const std::string &param_name)
void add_multimaterial(const int index, const json &params, const std::string &unit_type, const std::string &root_path)
std::vector< utils::ExpressionValue > mu_or_nu_
void lambda_mu(double px, double py, double pz, double x, double y, double z, double t, int el_id, double &lambda, double &mu) const
std::vector< utils::ExpressionValue > lambda_or_E_
void set_e_nu(const int index, const json &E, const json &nu, const std::string &stress_unit, const std::string &root_path)
void add_multimaterial(const int index, const json &params, const bool is_volume, const std::string &stress_unit, const std::string &root_path)
double rho(const RowVectorNd &p, double t, int el_id) const
double heat_capacity(const RowVectorNd &p, double t, int el_id) const
double operator()(double px, double py, double pz, double x, double y, double z, double t, int el_id) const override
void add_multimaterial(const int index, const json &params, const std::string &density_unit, const std::string &root_path) override
Used for test only.
constexpr const char * MATERIAL_ELEMENT_INDEX
Definition MatParams.hpp:9
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:41
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:44
nlohmann::json json
Definition Common.hpp:9
double convert_to_mu(const double E, const double nu)
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
Definition Types.hpp:13
double convert_to_lambda(const bool is_volume, const double E, const double nu)
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:73