v0.16.0
Loading...
Searching...
No Matches
HMHStorakers.cpp
Go to the documentation of this file.
1/**
2 * @file HMHStorakers.cpp
3 * @brief Storakers material model
4 *
5 * The model can either approximate the full natural stretch H = log(U), or
6 * recover U locally from the Biot stress in the no-stretch formulation.
7 */
8
9#include <Lie.hpp>
10#include <algorithm>
11#include <array>
12#include <cmath>
13#include <cstdio>
14#include <iomanip>
15#include <limits>
16#include <regex>
17#include <sstream>
18#include <utility>
19
20namespace EshelbianPlasticity {
21
24 double eta;
25 double mu;
26 double beta;
27 };
28
32 double lameLambda;
33 };
34
35 HMHStorakers(MoFEM::Interface &m_field, const double eta, const double mu,
36 const double beta)
37 : mField(m_field), defaultParameters{eta, mu, beta} {
38 CHK_THROW_MESSAGE(getOptions(), "get options failed");
40 "extract block data failed");
41
44 }
47 "Storakers requires natural logarithmic stretch");
48 }
49 }
50
52 const double beta) {
53 return {
54 2 * mu * (beta + 1. / 3.),
55 beta / (1 + 2 * beta),
56 2 * beta * mu,
57 };
58 }
59
60 static inline double getMu1(const MaterialParameters &parameters) {
61 return (1 - parameters.eta) * parameters.mu;
62 }
63
64 static inline double getMu2(const MaterialParameters &parameters) {
65 return parameters.eta * parameters.mu;
66 }
67
68 template <typename T>
69 static inline double getLogJacobian(T &principal_hencky_stretches) {
71 constexpr auto t_one = FTensor::One<>();
72 return principal_hencky_stretches(i) * t_one(i);
73 }
74
75 template <typename T>
76 static inline double getJacobianPower(
77 const MaterialParameters &parameters,
78 T &principal_hencky_stretches) {
79 return std::exp(-parameters.beta *
80 getLogJacobian(principal_hencky_stretches));
81 }
82
83 static inline double getGamma(const MaterialParameters &parameters,
84 const double jacobian_power) {
85 const double mu1 = getMu1(parameters);
86 const double mu2 = getMu2(parameters);
87 return 2 * mu1 * jacobian_power +
88 mu2 * jacobian_power * jacobian_power;
89 }
90
91 static inline double getGammaY(const MaterialParameters &parameters,
92 const double jacobian_power) {
93 const double mu1 = getMu1(parameters);
94 const double mu2 = getMu2(parameters);
95 return 2 * mu1 * jacobian_power +
96 2 * mu2 * jacobian_power * jacobian_power;
97 }
98
99 static inline double getPrincipalCoordinateStress(
100 const MaterialParameters &parameters, const double gamma,
101 const double hencky_stretch) {
102 const double stretch = std::exp(hencky_stretch);
103 return 2 * getMu1(parameters) * stretch +
104 getMu2(parameters) * stretch * stretch - gamma;
105 }
106
108 const MaterialParameters &parameters, const double hencky_stretch) {
109 const double stretch = std::exp(hencky_stretch);
110 return 2 * getMu1(parameters) * stretch +
111 2 * getMu2(parameters) * stretch * stretch;
112 }
113
115 for (const auto &block : blockData) {
116 if (block.blockEnts.find(ent) != block.blockEnts.end()) {
117 return {block.eta, block.mu, block.beta};
118 }
119 }
120
121 if (!blockData.empty()) {
123 "Storakers material block not found for entity. If "
124 "blocks are used, assign all material entities");
125 }
126
127 return defaultParameters;
128 }
129
130 struct OpJacobian : public EshelbianPlasticity::OpJacobian {
131 using EshelbianPlasticity::OpJacobian::OpJacobian;
132
133 MoFEMErrorCode evaluateRhs(EntData &) override { return 0; }
134 MoFEMErrorCode evaluateLhs(EntData &) override { return 0; }
135 };
136
138 returnOpJacobian(const bool eval_rhs, const bool eval_lhs,
139 boost::shared_ptr<DataAtIntegrationPts> data_ptr,
140 boost::shared_ptr<PhysicalEquations> physics_ptr) override {
141 return new OpJacobian(eval_rhs, eval_lhs, data_ptr, physics_ptr);
142 }
143
144 MoFEMErrorCode getOptions() {
146
147 PetscOptionsBegin(PETSC_COMM_WORLD, "storakers_", "Storakers material",
148 "none");
149 CHKERR PetscOptionsScalar("-eta", "Blend fraction eta", "",
151 PETSC_NULLPTR);
152 CHKERR PetscOptionsScalar("-mu", "Infinitesimal shear modulus mu", "",
154 PETSC_NULLPTR);
155 CHKERR PetscOptionsScalar("-beta", "Compressibility parameter beta", "",
157 PETSC_NULLPTR);
158 alphaGradU = 0;
159 CHKERR PetscOptionsScalar("-viscosity_alpha_grad_u", "viscosity", "",
160 alphaGradU, &alphaGradU, PETSC_NULLPTR);
161 PetscOptionsEnd();
162
163 CHKERR validateParameters(defaultParameters, "default options");
164
165 const auto derived =
167 MOFEM_LOG("EP", Sev::inform)
168 << "MatBlock Storakers (default): "
169 << "eta = " << defaultParameters.eta
170 << ", mu = " << defaultParameters.mu
171 << ", beta = " << defaultParameters.beta
172 << ", K = " << derived.bulkModulus << ", nu = " << derived.poissonRatio
173 << ", lambda_L = " << derived.lameLambda
174 << ", grad alpha u = " << alphaGradU;
175 MOFEM_LOG("EP", Sev::inform)
176 << "Storakers conversions: mu_1 = (1 - eta)*mu; mu_2 = eta*mu; "
177 "lambda_L = 2*beta*mu; K = 2*mu*(beta + 1/3); "
178 "nu = beta/(1 + 2*beta); beta = nu/(1 - 2*nu)";
179
181 }
182
183 MoFEMErrorCode extractBlockData(const Sev sev) {
184 return extractBlockData(
185 mField.getInterface<MeshsetsManager>()->getCubitMeshsetPtr(
186 std::regex((boost::format("%s(.*)") % "MAT_STORAKES").str())),
187 sev);
188 }
189
190 MoFEMErrorCode
191 extractBlockData(std::vector<const CubitMeshSets *> meshset_vec_ptr,
192 const Sev sev) {
194
195 for (const auto meshset : meshset_vec_ptr) {
196 MOFEM_LOG("EP", sev) << *meshset;
197
198 std::vector<double> attributes;
199 CHKERR meshset->getAttributes(attributes);
200
201 const auto json_parameters =
202 mField.getInterface<JsonConfigManager>()->getParamsFromBlockset(
203 "MAT_STORAKES", meshset->getMeshsetId());
204
205 MaterialParameters parameters{};
206 if (!json_parameters.empty()) {
207 if (json_parameters.size() != 3 ||
208 json_parameters.find("eta") == json_parameters.end() ||
209 json_parameters.find("mu") == json_parameters.end() ||
210 json_parameters.find("beta") == json_parameters.end()) {
211 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
212 "MAT_STORAKES JSON block must have exactly three "
213 "attributes: eta, mu, beta");
214 }
215 parameters = {json_parameters.at("eta"), json_parameters.at("mu"),
216 json_parameters.at("beta")};
217 } else {
218 if (attributes.size() != 3) {
219 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
220 "MAT_STORAKES block must have exactly three attributes: "
221 "eta, mu, beta");
222 }
223 parameters = {attributes[0], attributes[1], attributes[2]};
224 }
225 CHKERR validateParameters(parameters, "MAT_STORAKES block");
226
227 Range entities;
228 CHKERR mField.get_moab().get_entities_by_handle(meshset->meshset,
229 entities, true);
230 blockData.push_back(
231 {parameters.eta, parameters.mu, parameters.beta, entities});
232
233 const auto derived = getDerivedParameters(parameters.mu, parameters.beta);
234 MOFEM_LOG("EP", sev) << "MatBlock Storakers: eta = " << parameters.eta
235 << ", mu = " << parameters.mu
236 << ", beta = " << parameters.beta
237 << ", K = " << derived.bulkModulus
238 << ", nu = " << derived.poissonRatio
239 << ", lambda_L = " << derived.lameLambda
240 << ", nb. entities = " << entities.size();
241 }
242
244 }
245
246 /**
247 * @brief Assemble the full-order equation conjugate to H = log(U)
248 *
249 * The material part of the coordinate stress is
250 * \f[
251 * \boldsymbol{\tau}_H = \mathbf U\mathbf T
252 * = 2\mu_1\mathbf U + \mu_2\mathbf U^2 - \gamma\mathbf I.
253 * \f]
254 */
256 OpSpatialPhysical(const std::string &field_name,
257 boost::shared_ptr<DataAtIntegrationPts> data_ptr,
258 const double alpha_u);
259
260 MoFEMErrorCode integrate(EntData &data) override;
261
262 private:
263 const double alphaU;
264 };
265
268 boost::shared_ptr<DataAtIntegrationPts> data_ptr,
269 const double alpha_u) override {
270 return new OpSpatialPhysical(field_name, data_ptr, alpha_u);
271 }
272
273 /**
274 * @brief Assemble the consistent full-order H--H tangent
275 *
276 * \f[
277 * \mathbb C_H = D_H(2\mu_1e^H + \mu_2e^{2H})
278 * + \beta\gamma_Y\mathbf I\otimes\mathbf I.
279 * \f]
280 */
282 OpSpatialPhysical_du_du(std::string row_field, std::string col_field,
283 boost::shared_ptr<DataAtIntegrationPts> data_ptr,
284 const double alpha_u);
285
286 MoFEMErrorCode getOptions();
287 MoFEMErrorCode integrate(EntData &row_data, EntData &col_data) override;
288
289 private:
290 const double alphaU;
291 double minimEigenValue = 0;
292 };
293
295 std::string row_field, std::string col_field,
296 boost::shared_ptr<DataAtIntegrationPts> data_ptr,
297 const double alpha_u) override {
298 return new OpSpatialPhysical_du_du(
299 std::move(row_field), std::move(col_field), std::move(data_ptr),
300 alpha_u);
301 }
302
303 /**
304 * @brief Recover the right stretch from a symmetric Biot stress.
305 *
306 * This class inverts the Storakers constitutive equation
307 *
308 * \f[
309 * \mathbf{T} = 2\mu_1\mathbf{I} + \mu_2\mathbf{U}
310 * - (\gamma+q)\mathbf{U}^{-1}, \qquad
311 * \mu_1 = (1-\eta)\mu, \quad \mu_2 = \eta\mu,
312 * \f]
313 *
314 * by diagonalising
315 * \f$\mathbf{T}=\mathbf{Q}\operatorname{diag}(t_i)\mathbf{Q}^T\f$.
316 * For a trial \f$y=\log z\f$, where \f$z=J^{-\beta}\f$, it evaluates
317 *
318 * \f[
319 * \gamma = 2\mu_1 z + \mu_2 z^2, \qquad
320 * \gamma_{\mathrm{eff}}=\gamma+q, \qquad
321 * u_i(y) =
322 * \frac{b_i+\sqrt{b_i^2+4\mu_2\gamma_{\mathrm{eff}}}}
323 * {2\mu_2}, \qquad
324 * b_i=t_i-2\mu_1.
325 * \f]
326 * The positive-tangent \f$+\f$ root is used. If
327 * \f$\gamma_{\mathrm{eff}}<0\f$, it is admissible only while
328 * \f$b_i>0\f$ and
329 * \f$b_i^2+4\mu_2\gamma_{\mathrm{eff}}>0\f$ for every principal value.
330 *
331 * The only nonlinear unknown is obtained with safeguarded Newton--Brent
332 * iterations from
333 *
334 * \f[
335 * \phi(y)=\frac{y}{\beta}+\sum_i\log u_i(y)=0.
336 * \f]
337 *
338 * Finally,
339 * \f$\mathbf{U}=\mathbf{Q}\operatorname{diag}(u_i)\mathbf{Q}^T\f$ and
340 * \f$J=\exp(-y/\beta)\f$. The spelling "Strech" is retained to match the
341 * existing material interface.
342 */
345 double y = 0;
346 double z = 1;
347 double gamma = 0;
348 double gammaY = 0;
349 double q = 0;
350 double gammaEffective = 0;
353 -std::numeric_limits<double>::infinity();
354 double phi = 0;
355 double dPhi = 0;
359 };
360
361 explicit StressToStrech(const MaterialParameters &parameters)
362 : materialParameters(parameters),
363 mu1((1 - parameters.eta) * parameters.mu),
364 mu2(parameters.eta * parameters.mu) {
365 CHK_THROW_MESSAGE(validateParameters(parameters, "StressToStrech"),
366 "invalid Storakers parameters");
367 }
368
369 /**
370 * @brief Diagonalise sym(stress) and calculate b_i = t_i - 2 mu_1
371 */
372 template <typename T> MoFEMErrorCode stressToSpectralForm(T &t_stress) {
373 return stressToSpectralForm(t_stress, 0.);
374 }
375
376 template <typename T>
377 MoFEMErrorCode stressToSpectralForm(T &t_stress, const double q) {
379
380 if (!std::isfinite(q)) {
381 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
382 "Storakers external pressure q must be finite");
383 }
385
387 tEigenVectors(i, j) =
388 0.5 * (t_stress(i, j) + t_stress(j, i));
389
390 for (int ii = 0; ii != SPACE_DIM; ++ii) {
391 for (int jj = 0; jj != SPACE_DIM; ++jj) {
392 if (!std::isfinite(tEigenVectors(ii, jj))) {
393 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
394 "Storakers Biot stress contains a non-finite value");
395 }
396 }
397 }
398
399 CHKERR computeEigenValuesSymmetric(tEigenVectors, tPrincipalStress);
400 nbUniqueEigenvalues = getUniqNb<SPACE_DIM>(tPrincipalStress);
402 CHKERR sortEigenVals<SPACE_DIM>(tPrincipalStress, tEigenVectors);
403 }
404 tB(i) = tPrincipalStress(i) - 2 * mu1;
405
406 hasSpectralForm = true;
407 hasScalarSolution = false;
409 }
410
411 /**
412 * @brief Evaluate u_i(y), phi(y), and the exact Newton derivative
413 */
414 MoFEMErrorCode
416 ScalarEquationState &state) const {
418
419 if (!hasSpectralForm) {
420 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
421 "Call stressToSpectralForm before evaluating the scalar "
422 "equation");
423 }
424 if (!std::isfinite(y)) {
425 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
426 "Non-finite Storakers scalar iterate");
427 }
428
429 state = {};
430 state.y = y;
432 constexpr auto t_one = FTensor::One<>();
433 const double log_minimum =
434 std::log(std::numeric_limits<double>::min());
435 const double log_maximum =
436 std::log(std::numeric_limits<double>::max());
437 const double negative_infinity =
438 -std::numeric_limits<double>::infinity();
439 const double log_linear_gamma =
440 mu1 > 0 ? std::log(2 * mu1) + y : negative_infinity;
441 const double log_quadratic_gamma = std::log(mu2) + 2 * y;
442 const double log_gamma =
443 logSumExp(log_linear_gamma, log_quadratic_gamma);
444 const double log_gamma_y = logSumExp(
445 log_linear_gamma, std::log(2 * mu2) + 2 * y);
446
447 int gamma_effective_sign = 1;
448 double log_abs_gamma_effective = log_gamma;
449 if (externalPressure > 0) {
450 log_abs_gamma_effective =
451 logSumExp(log_gamma, std::log(externalPressure));
452 } else if (externalPressure < 0) {
453 const double log_minus_q = std::log(-externalPressure);
454 if (log_gamma > log_minus_q) {
455 log_abs_gamma_effective =
456 log_gamma +
457 std::log(-std::expm1(log_minus_q - log_gamma));
458 } else if (log_gamma < log_minus_q) {
459 gamma_effective_sign = -1;
460 log_abs_gamma_effective =
461 log_minus_q +
462 std::log(-std::expm1(log_gamma - log_minus_q));
463 } else {
464 gamma_effective_sign = 0;
465 log_abs_gamma_effective = negative_infinity;
466 }
467 }
468
469 if (y > log_maximum || log_gamma > log_maximum ||
470 log_abs_gamma_effective > log_maximum ||
471 log_gamma_y > log_maximum) {
472 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
473 "Storakers scalar iterate is outside the floating-point "
474 "range");
475 }
476
477 state.z = y >= log_minimum ? std::exp(y) : 0;
478 state.gamma = log_gamma >= log_minimum ? std::exp(log_gamma) : 0;
479 state.gammaY =
480 log_gamma_y >= log_minimum ? std::exp(log_gamma_y) : 0;
481 state.q = externalPressure;
482 state.gammaEffectiveSign = gamma_effective_sign;
483 state.logAbsGammaEffective = log_abs_gamma_effective;
484 if (gamma_effective_sign &&
485 log_abs_gamma_effective >= log_minimum) {
486 state.gammaEffective =
487 gamma_effective_sign * std::exp(log_abs_gamma_effective);
488 }
489
490 const double log_two_mu2 = std::log(2.) + std::log(mu2);
491 double positive_gamma_term = 0;
492 if (gamma_effective_sign > 0) {
493 const double log_gamma_term =
494 std::log(2.) +
495 0.5 * (std::log(mu2) + log_abs_gamma_effective);
496 if (log_gamma_term > log_maximum) {
497 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
498 "Storakers principal-stretch discriminant is outside the "
499 "floating-point range");
500 }
501 positive_gamma_term =
502 log_gamma_term >= log_minimum ? std::exp(log_gamma_term) : 0;
503 }
504
505 double log_derivative_sum = negative_infinity;
506 for (int ii = 0; ii != SPACE_DIM; ++ii) {
507 double log_s = negative_infinity;
508 if (gamma_effective_sign > 0) {
509 state.tS(ii) = std::hypot(tB(ii), positive_gamma_term);
510 log_s = std::log(state.tS(ii));
511 } else if (gamma_effective_sign == 0) {
512 if (tB(ii) <= 0) {
513 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
514 "No positive stable Storakers stretch at gamma_eff=0");
515 }
516 state.tS(ii) = tB(ii);
517 log_s = std::log(tB(ii));
518 } else {
519 if (tB(ii) <= 0) {
520 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
521 "Negative Storakers gamma_eff requires positive shifted "
522 "principal stresses");
523 }
524 const double log_b = std::log(tB(ii));
525 const double log_negative_term =
526 std::log(4.) + std::log(mu2) + log_abs_gamma_effective;
527 const double log_b_squared = 2 * log_b;
528 if (!(log_negative_term < log_b_squared)) {
529 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
530 "Storakers state is outside the stable principal-stretch "
531 "branch");
532 }
533 log_s =
534 log_b +
535 0.5 * std::log(-std::expm1(log_negative_term -
536 log_b_squared));
537 state.tS(ii) = log_s >= log_minimum ? std::exp(log_s) : 0;
538 }
539 if (!std::isfinite(state.tS(ii)) || state.tS(ii) <= 0) {
540 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
541 "Invalid Storakers principal-stretch discriminant");
542 }
543
544 if (tB(ii) >= 0) {
545 const double log_b =
546 tB(ii) > 0 ? std::log(tB(ii)) : negative_infinity;
547 state.tLogU(ii) =
548 logSumExp(log_b, log_s) - log_two_mu2;
549 } else {
550 const double log_denominator =
551 logSumExp(log_s, std::log(-tB(ii)));
552 state.tLogU(ii) =
553 std::log(2.) + log_abs_gamma_effective - log_denominator;
554 }
555
556 if (!std::isfinite(state.tLogU(ii)) ||
557 state.tLogU(ii) < log_minimum ||
558 state.tLogU(ii) > log_maximum) {
559 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
560 "Invalid Storakers principal stretch");
561 }
562 state.tU(ii) = std::exp(state.tLogU(ii));
563
564 const double log_derivative_term =
565 -state.tLogU(ii) - log_s;
566 log_derivative_sum =
567 logSumExp(log_derivative_sum, log_derivative_term);
568 }
569
570 state.phi =
571 y / materialParameters.beta + state.tLogU(i) * t_one(i);
572 const double log_volumetric_derivative =
573 log_gamma_y + log_derivative_sum;
574 if (log_volumetric_derivative > log_maximum) {
575 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
576 "Invalid Storakers scalar derivative term");
577 }
578 const double volumetric_derivative =
579 log_volumetric_derivative >= log_minimum
580 ? std::exp(log_volumetric_derivative)
581 : 0;
582 state.dPhi = 1 / materialParameters.beta + volumetric_derivative;
583 if (!std::isfinite(state.phi) || !std::isfinite(state.dPhi) ||
584 state.dPhi <= 0) {
585 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
586 "Invalid Storakers scalar residual or derivative");
587 }
588
590 }
591
592 /**
593 * @brief Solve phi(y)=0 with safeguarded Newton--Brent iterations
594 *
595 * The last converged value is used as the initial guess, which allows one
596 * instance to be reused over a sequence of nearby quadrature-point states.
597 */
598 MoFEMErrorCode solveScalarEquation() {
600 const auto [safe_lower_y, safe_upper_y] = getSafeYBounds();
601 if (!(safe_lower_y < safe_upper_y)) {
602 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
603 "No admissible Storakers scalar interval for q=%.16g",
605 }
607 std::clamp(scalarState.y, safe_lower_y, safe_upper_y));
609 }
610
611 MoFEMErrorCode solveScalarEquation(const double initial_y) {
613
614 if (!hasSpectralForm) {
615 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
616 "Call stressToSpectralForm before solveScalarEquation");
617 }
618
619 const auto [safe_lower_y, safe_upper_y] = getSafeYBounds();
620 if (!(safe_lower_y < safe_upper_y)) {
621 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
622 "No admissible Storakers scalar interval for q=%.16g",
624 }
625 if (!std::isfinite(initial_y) || initial_y < safe_lower_y ||
626 initial_y > safe_upper_y) {
627 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
628 "Initial Storakers scalar iterate %.16g is outside the safe "
629 "interval [%.16g, %.16g]",
630 initial_y, safe_lower_y, safe_upper_y);
631 }
632
635 if (std::abs(initial.phi) <= absoluteTolerance) {
637 hasScalarSolution = true;
639 }
640
641 double lower_y = initial_y;
642 double upper_y = initial_y;
645 double bracket_step = 1;
646 bool bracketed = false;
647 double bracket_endpoint_y = initial_y;
648 constexpr double bracket_growth_factor = 1.5;
649
650 for (int iteration = 0; iteration < maxBracketIterations; ++iteration) {
651 const bool search_lower = initial.phi > 0;
652 const double available_distance =
653 search_lower ? bracket_endpoint_y - safe_lower_y
654 : safe_upper_y - bracket_endpoint_y;
655 if (available_distance <= 0) {
656 break;
657 }
658
659 const double increment = std::min(bracket_step, available_distance);
660 const double candidate_y =
661 bracket_endpoint_y + (search_lower ? -increment : increment);
662 if (candidate_y == bracket_endpoint_y) {
663 break;
664 }
665
666 ScalarEquationState candidate;
667 CHKERR calculatePrincipalStretches(candidate_y, candidate);
668 if (initial.phi > 0) {
669 lower_y = candidate_y;
670 lower = candidate;
671 upper_y = initial_y;
672 upper = initial;
673 bracketed = lower.phi <= 0;
674 } else {
675 lower_y = initial_y;
676 lower = initial;
677 upper_y = candidate_y;
678 upper = candidate;
679 bracketed = upper.phi >= 0;
680 }
681 if (bracketed) {
682 break;
683 }
684 bracket_endpoint_y = candidate_y;
685 bracket_step *= bracket_growth_factor;
686 }
687
688 if (!bracketed) {
689 SETERRQ(PETSC_COMM_SELF, MOFEM_OPERATION_UNSUCCESSFUL,
690 "Could not bracket the Storakers scalar root inside the safe "
691 "interval [%.16g, %.16g]",
692 safe_lower_y, safe_upper_y);
693 }
694
695 double current_y =
696 std::abs(lower.phi) < std::abs(upper.phi) ? lower_y : upper_y;
697 ScalarEquationState current =
698 std::abs(lower.phi) < std::abs(upper.phi) ? lower : upper;
699 double history_y = std::numeric_limits<double>::quiet_NaN();
700 double history_phi = std::numeric_limits<double>::quiet_NaN();
701
702 for (int iteration = 0; iteration < maxIterations; ++iteration) {
703 if (hasConverged(current, lower_y, upper_y)) {
704 scalarState = current;
705 hasScalarSolution = true;
707 }
708
709 const double width = upper_y - lower_y;
710 const double guard = 0.05 * width;
711 auto is_safely_bracketed = [&](const double candidate) {
712 return std::isfinite(candidate) && candidate > lower_y + guard &&
713 candidate < upper_y - guard;
714 };
715
716 // First use the exact derivative from Section 3.2.
717 double candidate_y = current_y - current.phi / current.dPhi;
718 bool accepted = is_safely_bracketed(candidate_y) &&
719 std::abs(candidate_y - current_y) <= 0.5 * width;
720
721 // Brent inverse quadratic interpolation, when three distinct residual
722 // values are available.
723 if (!accepted && std::isfinite(history_phi) && lower.phi != upper.phi &&
724 lower.phi != history_phi && upper.phi != history_phi) {
725 candidate_y =
726 lower_y * upper.phi * history_phi /
727 ((lower.phi - upper.phi) * (lower.phi - history_phi)) +
728 upper_y * lower.phi * history_phi /
729 ((upper.phi - lower.phi) * (upper.phi - history_phi)) +
730 history_y * lower.phi * upper.phi /
731 ((history_phi - lower.phi) * (history_phi - upper.phi));
732 accepted = is_safely_bracketed(candidate_y);
733 }
734
735 // Brent secant interpolation, followed by the bisection safeguard.
736 if (!accepted && lower.phi != upper.phi) {
737 candidate_y = upper_y - upper.phi * (upper_y - lower_y) /
738 (upper.phi - lower.phi);
739 accepted = is_safely_bracketed(candidate_y);
740 }
741 if (!accepted) {
742 candidate_y = 0.5 * (lower_y + upper_y);
743 }
744
745 ScalarEquationState candidate;
746 CHKERR calculatePrincipalStretches(candidate_y, candidate);
747 history_y = current_y;
748 history_phi = current.phi;
749
750 if (candidate.phi <= 0) {
751 lower_y = candidate_y;
752 lower = candidate;
753 } else {
754 upper_y = candidate_y;
755 upper = candidate;
756 }
757
758 current_y = candidate_y;
759 current = candidate;
760 }
761
762 SETERRQ(PETSC_COMM_SELF, MOFEM_OPERATION_UNSUCCESSFUL,
763 "Storakers Newton--Brent solver did not converge in %d "
764 "iterations",
767 }
768
769 /**
770 * @brief Reconstruct U = Q diag(u_i) Q^T in the working basis
771 */
772 template <typename T>
773 MoFEMErrorCode spectralFormToStrech(T &t_stretch) const {
775
776 if (!hasScalarSolution) {
777 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
778 "Call solveScalarEquation before spectralFormToStrech");
779 }
780
782 auto identity = [](const double u) { return u; };
783 auto t_reconstructed_stretch =
785 t_stretch(i, j) = t_reconstructed_stretch(i, j);
786
788 }
789
790 /**
791 * @brief Calculate C = dT/dU in the working/reference coordinates
792 *
793 * EigenMatrix::getDiffMat evaluates the derivative at fixed
794 * gamma_eff=gamma+q and rotates it from the principal basis with the stored
795 * eigenvectors. The rank-one term adds the variation of J, z and material
796 * gamma; prescribed q has zero derivative.
797 */
798 template <typename T>
799 MoFEMErrorCode calculateStiffness(T &t_stiffness) const {
801
802 if (!hasScalarSolution) {
803 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
804 "Call solveScalarEquation before calculateStiffness");
805 }
806
808 const double gamma = scalarState.gammaEffective;
809 auto principal_stress = [this, gamma](const double u) {
810 return 2 * mu1 + mu2 * u - gamma / u;
811 };
812 auto principal_stiffness = [this, gamma](const double u) {
813 return mu2 + (gamma / u) / u;
814 };
815 auto t_frozen_gamma_stiffness = EigenMatrix::getDiffMat(
816 scalarState.tU, tEigenVectors, principal_stress,
817 principal_stiffness, nbUniqueEigenvalues);
818
819 auto inverse_stretch = [](const double u) { return 1 / u; };
820 auto t_inverse_stretch = EigenMatrix::getMat(
821 scalarState.tU, tEigenVectors, inverse_stretch);
822 const double alpha = materialParameters.beta * scalarState.gammaY;
823
824 t_stiffness(i, j, k, l) =
825 t_frozen_gamma_stiffness(i, j, k, l) +
826 alpha * t_inverse_stretch(i, j) * t_inverse_stretch(k, l);
827
829 }
830
831 /**
832 * @brief Calculate S = dU/dT in the working/reference coordinates
833 *
834 * getDiffMat applied to the inverse principal stress law gives L^{-1}
835 * already rotated from the principal basis. The second term is the exact
836 * Sherman--Morrison correction.
837 */
838 template <typename T>
839 MoFEMErrorCode calculateCompliance(T &t_compliance) const {
841
842 if (!hasScalarSolution) {
843 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
844 "Call solveScalarEquation before calculateCompliance");
845 }
846
848 const int gamma_sign = scalarState.gammaEffectiveSign;
849 const double log_abs_gamma = scalarState.logAbsGammaEffective;
850 auto inverse_state = [this, gamma_sign,
851 log_abs_gamma](const double stress) {
852 const double b = stress - 2 * mu1;
853 double log_s;
854 if (gamma_sign > 0) {
855 const double log_gamma_term =
856 std::log(2.) +
857 0.5 * (std::log(mu2) + log_abs_gamma);
858 const double gamma_term = std::exp(log_gamma_term);
859 const double s = std::hypot(b, gamma_term);
860 log_s = std::log(s);
861 if (b < 0) {
862 const double log_denominator =
863 logSumExp(log_s, std::log(-b));
864 const double log_u =
865 std::log(2.) + log_abs_gamma - log_denominator;
866 return std::pair{std::exp(log_u), s};
867 }
868 } else if (gamma_sign == 0) {
869 log_s = std::log(b);
870 } else {
871 const double log_b = std::log(b);
872 const double log_ratio = std::log(4.) + std::log(mu2) +
873 log_abs_gamma - 2 * log_b;
874 log_s = log_b + 0.5 * std::log(-std::expm1(log_ratio));
875 }
876
877 const double log_b = std::log(b);
878 const double log_u =
879 logSumExp(log_b, log_s) - std::log(2.) - std::log(mu2);
880 return std::pair{std::exp(log_u), std::exp(log_s)};
881 };
882 auto inverse_stress_law = [inverse_state](const double stress) {
883 return inverse_state(stress).first;
884 };
885 auto inverse_stress_law_derivative =
886 [inverse_state](const double stress) {
887 const auto [u, s] = inverse_state(stress);
888 return u / s;
889 };
890 auto t_inverse_l = EigenMatrix::getDiffMat(
891 tPrincipalStress, tEigenVectors, inverse_stress_law,
892 inverse_stress_law_derivative, nbUniqueEigenvalues);
893
894 auto inverse_discriminant = [](const double s) { return 1 / s; };
896 inverse_discriminant);
897
898 const double alpha = materialParameters.beta * scalarState.gammaY;
899 FTensor::Tensor1<double, SPACE_DIM> t_inverse_stretch_dot_y_terms;
900 for (int aa = 0; aa != SPACE_DIM; ++aa) {
901 t_inverse_stretch_dot_y_terms(aa) =
902 1 / (scalarState.tU(aa) * scalarState.tS(aa));
903 }
904 constexpr auto t_one = FTensor::One<>();
905 const double inverse_stretch_dot_y =
906 t_inverse_stretch_dot_y_terms(i) * t_one(i);
907 const double denominator = 1 + alpha * inverse_stretch_dot_y;
908 if (!std::isfinite(denominator) || denominator <= 0) {
909 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
910 "Invalid Storakers compliance denominator");
911 }
912 const double correction = alpha / denominator;
913
914 t_compliance(i, j, k, l) =
915 t_inverse_l(i, j, k, l) - correction * t_y(i, j) * t_y(k, l);
916
918 }
919
920 /**
921 * @brief Calculate stiffness and compliance in reference coordinates
922 *
923 * The output arguments can be owning Ddg tensors or MoFEM Ddg views over
924 * matD and matInvD storage.
925 */
926 template <typename TStiffness, typename TCompliance>
927 MoFEMErrorCode calculateBiotTangent(TStiffness &t_stiffness,
928 TCompliance &t_compliance) const {
930 CHKERR calculateStiffness(t_stiffness);
931 CHKERR calculateCompliance(t_compliance);
933 }
934
935 template <typename TStress, typename TStretch>
936 MoFEMErrorCode calculate(TStress &t_stress, TStretch &t_stretch) {
937 return calculate(t_stress, t_stretch, 0.);
938 }
939
940 template <typename TStress, typename TStretch>
941 MoFEMErrorCode calculate(TStress &t_stress, TStretch &t_stretch,
942 const double q) {
944 CHKERR stressToSpectralForm(t_stress, q);
946 CHKERR spectralFormToStrech(t_stretch);
948 }
949
950 /**
951 * @brief Calculate U(T), dT/dU, and dU/dT in reference coordinates
952 */
953 template <typename TStress, typename TStretch, typename TStiffness,
954 typename TCompliance>
955 MoFEMErrorCode calculate(TStress &t_stress, TStretch &t_stretch,
956 TStiffness &t_stiffness,
957 TCompliance &t_compliance) {
958 return calculate(t_stress, t_stretch, t_stiffness, t_compliance, 0.);
959 }
960
961 template <typename TStress, typename TStretch, typename TStiffness,
962 typename TCompliance>
963 MoFEMErrorCode calculate(TStress &t_stress, TStretch &t_stretch,
964 TStiffness &t_stiffness,
965 TCompliance &t_compliance, const double q) {
967 CHKERR calculate(t_stress, t_stretch, q);
968 CHKERR calculateBiotTangent(t_stiffness, t_compliance);
970 }
971
973 return scalarState;
974 }
975
976 double getJacobian() const {
977 return std::exp(-scalarState.y / materialParameters.beta);
978 }
979
983
986 return tB;
987 }
988
991 return tEigenVectors;
992 }
993
994 int maxIterations = 100;
996 double absoluteTolerance = 1e-12;
997 double relativeTolerance = 1e-12;
998
999 private:
1000 static double logSumExp(const double first, const double second) {
1001 if (first == -std::numeric_limits<double>::infinity()) {
1002 return second;
1003 }
1004 if (second == -std::numeric_limits<double>::infinity()) {
1005 return first;
1006 }
1007 const double maximum = std::max(first, second);
1008 const double minimum = std::min(first, second);
1009 return maximum + std::log1p(std::exp(minimum - maximum));
1010 }
1011
1012 std::pair<double, double> getSafeYBounds() const {
1013 const double safety_margin = std::log(16.);
1014 const double log_maximum =
1015 std::log(std::numeric_limits<double>::max()) - safety_margin;
1016
1017 // gamma, gammaY, and z may safely underflow to zero because the scalar
1018 // residual is evaluated from their logarithms. The lower limit instead
1019 // keeps J = exp(-y/beta) representable.
1020 double lower_y = -materialParameters.beta * log_maximum;
1021 if (externalPressure < 0) {
1022 const double negative_infinity =
1023 -std::numeric_limits<double>::infinity();
1024 const double log_minus_q = std::log(-externalPressure);
1025 bool all_shifted_stresses_positive = true;
1026 double minimum_shifted_stress = std::numeric_limits<double>::max();
1027 for (int ii = 0; ii != SPACE_DIM; ++ii) {
1028 if (tB(ii) <= 0) {
1029 all_shifted_stresses_positive = false;
1030 break;
1031 }
1032 minimum_shifted_stress =
1033 std::min(minimum_shifted_stress, tB(ii));
1034 }
1035
1036 double log_required_gamma = log_minus_q;
1037 if (all_shifted_stresses_positive) {
1038 const double log_spinodal_magnitude =
1039 2 * std::log(minimum_shifted_stress) -
1040 std::log(4.) - std::log(mu2);
1041 if (log_spinodal_magnitude > log_minus_q) {
1042 log_required_gamma = negative_infinity;
1043 } else if (log_spinodal_magnitude == log_minus_q) {
1044 log_required_gamma =
1045 log_minus_q +
1046 std::log(64 * std::numeric_limits<double>::epsilon());
1047 } else {
1048 log_required_gamma =
1049 log_minus_q +
1050 std::log(-std::expm1(log_spinodal_magnitude - log_minus_q));
1051 }
1052 }
1053
1054 if (std::isfinite(log_required_gamma)) {
1055 const double log_mu1 =
1056 mu1 > 0 ? std::log(mu1) : negative_infinity;
1057 const double log_sqrt_discriminant =
1058 0.5 * logSumExp(2 * log_mu1,
1059 std::log(mu2) + log_required_gamma);
1060 const double log_denominator =
1061 logSumExp(log_mu1, log_sqrt_discriminant);
1062 const double boundary_y =
1063 log_required_gamma - log_denominator;
1064 const double boundary_margin =
1065 64 * std::numeric_limits<double>::epsilon() *
1066 std::max(1., std::abs(boundary_y));
1067 lower_y = std::max(lower_y, boundary_y + boundary_margin);
1068 }
1069 }
1070
1071 double upper_y = log_maximum;
1072 const int active_terms = mu1 > 0 ? 2 : 1;
1073 const double log_term_maximum =
1074 log_maximum - std::log(static_cast<double>(active_terms));
1075
1076 // Bound gammaY, whose quadratic coefficient is the largest coefficient
1077 // used by gamma and gammaY.
1078 if (mu1 > 0) {
1079 upper_y = std::min(
1080 upper_y, log_term_maximum - std::log(2 * mu1));
1081 }
1082 upper_y = std::min(
1083 upper_y,
1084 0.5 * (log_term_maximum - std::log(2 * mu2)));
1085
1086 // Also reserve enough range for 2*sqrt(mu2*gamma), which enters the
1087 // principal-stretch discriminant.
1088 const double log_gamma_effective_limit = std::min(
1089 log_maximum,
1090 2 * (log_maximum - std::log(2.)) - std::log(mu2));
1091 double log_gamma_limit = log_gamma_effective_limit;
1092 if (externalPressure > 0) {
1093 const double log_q = std::log(externalPressure);
1094 if (log_q >= log_gamma_effective_limit) {
1095 return {1., 0.};
1096 }
1097 log_gamma_limit =
1098 log_gamma_effective_limit +
1099 std::log(-std::expm1(log_q - log_gamma_effective_limit));
1100 }
1101 const double log_gamma_term_limit =
1102 log_gamma_limit - std::log(static_cast<double>(active_terms));
1103 if (mu1 > 0) {
1104 upper_y = std::min(
1105 upper_y, log_gamma_term_limit - std::log(2 * mu1));
1106 }
1107 upper_y = std::min(
1108 upper_y,
1109 0.5 * (log_gamma_term_limit - std::log(mu2)));
1110
1111 return {lower_y, upper_y};
1112 }
1113
1114 bool hasConverged(const ScalarEquationState &state, const double lower_y,
1115 const double upper_y) const {
1116 const double y_scale = std::max(1., std::abs(state.y));
1117 const double y_tolerance =
1119 return std::abs(state.phi) <= absoluteTolerance ||
1120 std::abs(upper_y - lower_y) <= y_tolerance;
1121 }
1122
1124 double mu1;
1125 double mu2;
1132 bool hasSpectralForm = false;
1133 bool hasScalarSolution = false;
1134 };
1135
1136 /** @brief Assemble the isotropic external-strain contribution */
1139 const std::string &field_name,
1140 boost::shared_ptr<DataAtIntegrationPts> data_ptr,
1141 boost::shared_ptr<ExternalStrainVec> external_strain_vec_ptr,
1142 std::map<std::string, boost::shared_ptr<ScalingMethod>> smv);
1143
1144 MoFEMErrorCode integrate(EntData &data) override;
1145
1146 private:
1147 boost::shared_ptr<ExternalStrainVec> externalStrainVecPtr;
1148 std::map<std::string, boost::shared_ptr<ScalingMethod>> scalingMethodsMap;
1149 };
1150
1152 const std::string &field_name,
1153 boost::shared_ptr<DataAtIntegrationPts> data_ptr,
1154 boost::shared_ptr<ExternalStrainVec> external_strain_vec_ptr,
1155 std::map<std::string, boost::shared_ptr<ScalingMethod>> smv) override {
1157 field_name, data_ptr, external_strain_vec_ptr, std::move(smv));
1158 }
1159
1161 VectorPtr external_pressure_ptr,
1162 boost::shared_ptr<ExternalStrainVec> external_strain_vec_ptr,
1163 std::map<std::string, boost::shared_ptr<ScalingMethod>> smv) override {
1164 return new OpCalculateExternalPressure(
1165 std::move(external_pressure_ptr),
1166 std::move(external_strain_vec_ptr), std::move(smv));
1167 }
1168
1169 /**
1170 * @brief Evaluate Storakers stretch and its tangent from the supplied stress
1171 */
1174 boost::shared_ptr<DataAtIntegrationPts> data_ptr,
1175 boost::shared_ptr<MatrixDouble> strain_ptr,
1176 boost::shared_ptr<MatrixDouble> stress_ptr,
1177 boost::shared_ptr<HMHStorakers> storakers_ptr,
1178 VectorPtr external_pressure_ptr);
1179
1180 MoFEMErrorCode doWork(int side, EntityType type, EntData &data) override;
1181
1182 private:
1183 boost::shared_ptr<DataAtIntegrationPts> dataAtPts;
1184 boost::shared_ptr<MatrixDouble> strainPtr;
1185 boost::shared_ptr<MatrixDouble> stressPtr;
1186 boost::shared_ptr<HMHStorakers> storakersPtr;
1188 };
1189
1191 boost::shared_ptr<DataAtIntegrationPts> data_ptr,
1192 boost::shared_ptr<PhysicalEquations> physics_ptr,
1193 boost::shared_ptr<MatrixDouble> strain_ptr) override {
1195 std::move(data_ptr), std::move(physics_ptr), std::move(strain_ptr),
1196 nullptr);
1197 }
1198
1200 boost::shared_ptr<DataAtIntegrationPts> data_ptr,
1201 boost::shared_ptr<PhysicalEquations> physics_ptr,
1202 boost::shared_ptr<MatrixDouble> strain_ptr,
1203 VectorPtr external_pressure_ptr) override {
1204 auto storakers_ptr = boost::dynamic_pointer_cast<HMHStorakers>(physics_ptr);
1205 if (!storakers_ptr) {
1207 "Pointer to HMHStorakers is null");
1208 }
1209
1211 data_ptr,
1212 strain_ptr ? strain_ptr : data_ptr->getLogStretchTensorAtPts(),
1213 data_ptr->getApproxPAtPts(), storakers_ptr,
1214 std::move(external_pressure_ptr));
1215 }
1216
1217private:
1218 static MoFEMErrorCode validateParameters(const MaterialParameters &parameters,
1219 const char *source) {
1221
1222 if (!std::isfinite(parameters.eta) || parameters.eta <= 0 ||
1223 parameters.eta > 1) {
1224 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1225 "Storakers eta must satisfy 0 < eta <= 1 in %s", source);
1226 }
1227 if (!std::isfinite(parameters.mu) || parameters.mu <= 0) {
1228 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1229 "Storakers mu must be positive in %s", source);
1230 }
1231 if (!std::isfinite(parameters.beta) || parameters.beta <= 0) {
1232 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1233 "Storakers beta must be positive in %s", source);
1234 }
1235
1237 }
1238
1239 struct BlockData {
1240 double eta;
1241 double mu;
1242 double beta;
1244 };
1245
1248 double alphaGradU = 0;
1249 std::vector<BlockData> blockData;
1250};
1251
1253 const std::string &field_name,
1254 boost::shared_ptr<DataAtIntegrationPts> data_ptr, const double alpha_u)
1255 : OpAssembleVolume(field_name, std::move(data_ptr), OPROW),
1256 alphaU(alpha_u) {}
1257
1260
1261 auto storakers_ptr =
1262 boost::dynamic_pointer_cast<HMHStorakers>(dataAtPts->physicsPtr);
1263 if (!storakers_ptr) {
1264 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1265 "Pointer to HMHStorakers is null");
1266 }
1267
1268 const auto parameters =
1269 storakers_ptr->getMaterialParameters(getFEEntityHandle());
1270 const double alpha_u = alphaU;
1271 const double alpha_grad_u = storakers_ptr->alphaGradU;
1272
1275
1276 const auto t_L = FTensor::SymmLTensor<double, SPACE_DIM>();
1277 const int nb_dofs = data.getIndices().size();
1278 const int nb_integration_pts = data.getN().size1();
1279 const int nb_base_functions = data.getN().size2();
1280 const double volume = getVolume();
1281
1282 auto t_w = getFTensor0IntegrationWeight();
1283 auto t_adjoint = dataAtPts->getFTensorAdjointPdU(nb_integration_pts);
1284 auto t_dot_log_u =
1285 dataAtPts->getFTensorLogStretchDot(nb_integration_pts);
1286 auto t_grad_dot_log_u =
1287 dataAtPts->getFTensorGradLogStretchDot(nb_integration_pts);
1288 auto t_eigen_vals = dataAtPts->getFTensorEigenVals(nb_integration_pts);
1289 auto t_eigen_vecs = dataAtPts->getFTensorEigenVecs(nb_integration_pts);
1290
1291 auto t_row_base_fun = data.getFTensor0N();
1292 auto t_row_grad_fun = data.getFTensor1DiffN<SPACE_DIM>();
1293
1294 auto no_h1 = [&]() {
1296
1297 for (int gg = 0; gg != nb_integration_pts; ++gg) {
1298 const double a = volume * t_w;
1299 ++t_w;
1300
1301 const double jacobian_power =
1302 getJacobianPower(parameters, t_eigen_vals);
1303 const double gamma = getGamma(parameters, jacobian_power);
1304 if (!std::isfinite(jacobian_power) || jacobian_power < 0 ||
1305 !std::isfinite(gamma)) {
1306 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FP,
1307 "Non-finite Storakers volumetric response in spatial "
1308 "residual");
1309 }
1310
1311 auto coordinate_stress = [parameters, gamma](const double h) {
1312 return getPrincipalCoordinateStress(parameters, gamma, h);
1313 };
1314 auto t_material_stress = EigenMatrix::getMat(
1315 t_eigen_vals, t_eigen_vecs, coordinate_stress);
1316
1318 t_material(L) = t_L(i, j, L) * t_material_stress(i, j);
1319
1320 ++t_eigen_vals;
1321 ++t_eigen_vecs;
1322
1324 t_viscous(L) =
1325 alpha_u * (t_L(i, j, L) * t_dot_log_u(i, j));
1326
1328 t_residual(L) = t_adjoint(L) - t_material(L) - t_viscous(L);
1329 t_residual(L) *= a;
1330
1332 t_grad_residual(L, i) = alpha_grad_u * t_grad_dot_log_u(L, i);
1333 t_grad_residual(L, i) *= a;
1334
1335 ++t_adjoint;
1336 ++t_dot_log_u;
1337 ++t_grad_dot_log_u;
1338
1339 auto t_nf = getFTensor1FromPtr<size_symm>(&*nF.data().begin());
1340 int bb = 0;
1341 for (; bb != nb_dofs / size_symm; ++bb) {
1342 t_nf(L) -= t_row_base_fun * t_residual(L);
1343 t_nf(L) += t_row_grad_fun(i) * t_grad_residual(L, i);
1344 ++t_nf;
1345 ++t_row_base_fun;
1346 ++t_row_grad_fun;
1347 }
1348 for (; bb != nb_base_functions; ++bb) {
1349 ++t_row_base_fun;
1350 ++t_row_grad_fun;
1351 }
1352 }
1353
1355 };
1356
1357 auto not_implemented = [&]() {
1359 SETERRQ(PETSC_COMM_SELF, MOFEM_NOT_IMPLEMENTED,
1360 "Storakers full-order operator is implemented only for no_h1");
1362 };
1363
1366 CHKERR no_h1();
1367 break;
1368 case LARGE_ROT:
1369 case MODERATE_ROT:
1370 CHKERR not_implemented();
1371 break;
1372 default:
1373 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1374 "gradApproximator not handled");
1375 }
1376
1378}
1379
1381 std::string row_field, std::string col_field,
1382 boost::shared_ptr<DataAtIntegrationPts> data_ptr, const double alpha_u)
1383 : OpAssembleVolumePositiveDefine(std::move(row_field),
1384 std::move(col_field),
1385 std::move(data_ptr), OPROWCOL, false),
1386 alphaU(alpha_u) {
1387 CHK_THROW_MESSAGE(getOptions(), "get options failed");
1388 sYmm = false;
1389}
1390
1393
1394 PetscOptionsBegin(PETSC_COMM_WORLD, "storakers_", "Storakers material",
1395 "none");
1396 CHKERR PetscOptionsScalar("-min_eigen_value", "Minimum eigenvalue", "",
1397 minimEigenValue, &minimEigenValue, PETSC_NULLPTR);
1398 PetscOptionsEnd();
1399 if (!std::isfinite(minimEigenValue) || minimEigenValue < 0) {
1400 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1401 "Storakers min_eigen_value must be finite and non-negative");
1402 }
1403 MOFEM_LOG("EP", Sev::inform)
1404 << "Storakers min_eigen_value = " << minimEigenValue;
1405
1407}
1408
1410 EntData &row_data, EntData &col_data) {
1412
1413 auto storakers_ptr =
1414 boost::dynamic_pointer_cast<HMHStorakers>(dataAtPts->physicsPtr);
1415 if (!storakers_ptr) {
1416 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1417 "Pointer to HMHStorakers is null");
1418 }
1419
1420 const auto parameters =
1421 storakers_ptr->getMaterialParameters(getFEEntityHandle());
1422 const double alpha_u = alphaU;
1423 const double alpha_grad_u = storakers_ptr->alphaGradU;
1424 const double bulk_modulus =
1425 getDerivedParameters(parameters.mu, parameters.beta).bulkModulus;
1426
1429
1430 constexpr auto t_kd_sym = FTensor::Kronecker_Delta_symmetric<int>();
1431 const auto t_L = FTensor::SymmLTensor<double, SPACE_DIM>();
1432 const auto t_diff = FTensor::DiffTensor<double>();
1433
1434 const int nb_integration_pts = row_data.getN().size1();
1435 const int row_nb_dofs = row_data.getIndices().size();
1436 const int col_nb_dofs = col_data.getIndices().size();
1437 const int row_nb_base_functions = row_data.getN().size2();
1438
1439 auto get_ftensor2 = [](MatrixDouble &matrix, const int row,
1440 const int col) {
1441 std::array<double *, size_symm * size_symm> pointers;
1442 for (int rr = 0; rr != size_symm; ++rr) {
1443 for (int cc = 0; cc != size_symm; ++cc) {
1444 pointers[size_symm * rr + cc] = &matrix(row + rr, col + cc);
1445 }
1446 }
1448 size_symm>(pointers);
1449 };
1450
1451 const double volume = getVolume();
1452 const double ts_a = getTSa();
1453 auto t_w = getFTensor0IntegrationWeight();
1454 auto t_row_base_fun = row_data.getFTensor0N();
1455 auto t_row_grad_fun = row_data.getFTensor1DiffN<SPACE_DIM>();
1456
1457 auto t_adjoint_dstretch =
1458 dataAtPts->getFTensorAdjointPdstretch(nb_integration_pts);
1459 auto t_eigen_vals = dataAtPts->getFTensorEigenVals(nb_integration_pts);
1460 auto t_eigen_vecs = dataAtPts->getFTensorEigenVecs(nb_integration_pts);
1462 dataAtPts->nbUniq.data().data());
1463
1464 auto no_h1 = [&]() {
1466
1467 for (int gg = 0; gg != nb_integration_pts; ++gg) {
1468 const double a = volume * t_w;
1469 ++t_w;
1470
1471 const double jacobian_power =
1472 getJacobianPower(parameters, t_eigen_vals);
1473 const double gamma = getGamma(parameters, jacobian_power);
1474 const double gamma_y = getGammaY(parameters, jacobian_power);
1475 const double volumetric_tangent = parameters.beta * gamma_y;
1476 if (!std::isfinite(jacobian_power) || jacobian_power < 0 ||
1477 !std::isfinite(gamma) || !std::isfinite(gamma_y) ||
1478 !std::isfinite(volumetric_tangent)) {
1479 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FP,
1480 "Non-finite Storakers volumetric response in spatial "
1481 "tangent");
1482 }
1483
1484 auto coordinate_stress = [parameters, gamma](const double h) {
1485 return getPrincipalCoordinateStress(parameters, gamma, h);
1486 };
1487 auto coordinate_stress_derivative =
1488 [parameters](const double h) {
1490 parameters, h);
1491 };
1492 auto t_frozen_gamma_tangent = EigenMatrix::getDiffMat(
1493 t_eigen_vals, t_eigen_vecs, coordinate_stress,
1494 coordinate_stress_derivative, t_nb_uniq);
1495 auto identity = [](const double) { return 1.; };
1496 auto t_log_jacobian_gradient =
1497 EigenMatrix::getMat(t_eigen_vals, t_eigen_vecs, identity);
1498
1500 t_material_tangent(i, j, k, l) =
1501 t_frozen_gamma_tangent(i, j, k, l) +
1502 volumetric_tangent * t_log_jacobian_gradient(i, j) *
1503 t_log_jacobian_gradient(k, l);
1504
1506 t_dP(L, J) =
1507 t_L(i, j, L) *
1508 (t_material_tangent(i, j, k, l) * t_L(k, l, J));
1509 t_dP(L, J) +=
1510 (alpha_u * ts_a) *
1511 (t_L(i, j, L) * (t_diff(i, j, k, l) * t_L(k, l, J)));
1512
1514 t_deltaP(i, j) =
1515 (t_adjoint_dstretch(i, j) || t_adjoint_dstretch(j, i)) / 2.;
1516 auto t_diff2_uP = EigenMatrix::getDiffDiffMat(
1517 t_eigen_vals, t_eigen_vecs,
1518 static_cast<double (*)(double)>(std::exp),
1519 static_cast<double (*)(double)>(std::exp),
1520 static_cast<double (*)(double)>(std::exp), t_deltaP, t_nb_uniq);
1521 t_dP(L, J) -=
1522 t_L(i, j, L) * (t_diff2_uP(i, j, k, l) * t_L(k, l, J));
1523
1524 ++t_adjoint_dstretch;
1525 ++t_eigen_vals;
1526 ++t_eigen_vecs;
1527 ++t_nb_uniq;
1528
1529 t_dP(L, J) *= a;
1530 if (minimEigenValue > 0) {
1532 t_hessian_eig_vecs(L, J) =
1533 0.5 * (t_dP(L, J) + t_dP(J, L));
1534 FTensor::Tensor1<double, size_symm> t_hessian_eig_vals;
1535 CHKERR computeEigenValuesSymmetric(t_hessian_eig_vecs,
1536 t_hessian_eig_vals);
1537
1538 const double eigenvalue_floor = bulk_modulus * minimEigenValue;
1539 bool project_hessian = false;
1540 for (int aa = 0; aa != size_symm; ++aa) {
1541 project_hessian =
1542 project_hessian || t_hessian_eig_vals(aa) < eigenvalue_floor;
1543 }
1544 if (project_hessian) {
1545 auto min_eig_val = [eigenvalue_floor](const double value) {
1546 return 0.5 *
1547 (value + eigenvalue_floor +
1548 std::abs(value - eigenvalue_floor));
1549 };
1550 auto t_dP_min_eig = EigenMatrix::getMat(
1551 t_hessian_eig_vals, t_hessian_eig_vecs, min_eig_val);
1552 t_dP(L, J) = t_dP_min_eig(L, J);
1553 }
1554 }
1555
1556 int rr = 0;
1557 for (; rr != row_nb_dofs / size_symm; ++rr) {
1558 auto t_col_base_fun = col_data.getFTensor0N(gg, 0);
1559 auto t_col_grad_fun =
1560 col_data.getFTensor1DiffN<SPACE_DIM>(gg, 0);
1561 auto t_m = get_ftensor2(K, size_symm * rr, 0);
1562
1563 for (int cc = 0; cc != col_nb_dofs / size_symm; ++cc) {
1564 t_m(L, J) +=
1565 (t_row_base_fun * t_col_base_fun) * t_dP(L, J);
1566 t_m(L, J) +=
1567 (a * alpha_grad_u * ts_a) *
1568 (t_row_grad_fun(i) * t_col_grad_fun(i)) * t_kd_sym(L, J);
1569 ++t_m;
1570 ++t_col_base_fun;
1571 ++t_col_grad_fun;
1572 }
1573 ++t_row_base_fun;
1574 ++t_row_grad_fun;
1575 }
1576 for (; rr != row_nb_base_functions; ++rr) {
1577 ++t_row_base_fun;
1578 ++t_row_grad_fun;
1579 }
1580 }
1581
1583 };
1584
1585 auto not_implemented = [&]() {
1587 SETERRQ(PETSC_COMM_SELF, MOFEM_NOT_IMPLEMENTED,
1588 "Storakers full-order tangent is implemented only for no_h1");
1590 };
1591
1594 CHKERR no_h1();
1595 break;
1596 case LARGE_ROT:
1597 case MODERATE_ROT:
1598 CHKERR not_implemented();
1599 break;
1600 default:
1601 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1602 "gradApproximator not handled");
1603 }
1604
1606}
1607
1609 const std::string &field_name,
1610 boost::shared_ptr<DataAtIntegrationPts> data_ptr,
1611 boost::shared_ptr<ExternalStrainVec> external_strain_vec_ptr,
1612 std::map<std::string, boost::shared_ptr<ScalingMethod>> smv)
1613 : OpAssembleVolume(field_name, data_ptr, OPROW),
1614 externalStrainVecPtr(std::move(external_strain_vec_ptr)),
1615 scalingMethodsMap(std::move(smv)) {}
1616
1617MoFEMErrorCode
1620
1621 auto storakers_ptr =
1622 boost::dynamic_pointer_cast<HMHStorakers>(dataAtPts->physicsPtr);
1623 if (!storakers_ptr) {
1624 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1625 "Pointer to HMHStorakers is null");
1626 }
1627 if (!externalStrainVecPtr || externalStrainVecPtr->empty()) {
1629 }
1630
1631 double time = OpAssembleVolume::getFEMethod()->ts_t;
1634 }
1636
1637 for (const auto &external_strain_block : *externalStrainVecPtr) {
1638 if (std::regex_match(external_strain_block.blockName,
1639 std::regex("(.*)ANALYTICAL_EXTERNALSTRAIN(.*)"))) {
1640 SETERRQ(PETSC_COMM_SELF, MOFEM_NOT_IMPLEMENTED,
1641 "Analytical external strain not implemented for Storakers "
1642 "material");
1643 }
1644 if (external_strain_block.ents.find(fe_ent) ==
1645 external_strain_block.ents.end()) {
1646 continue;
1647 }
1648
1649 double scale = 1;
1650 const auto scaling_method =
1651 scalingMethodsMap.find(external_strain_block.blockName);
1652 if (scaling_method != scalingMethodsMap.end()) {
1653 if (!scaling_method->second) {
1654 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1655 "Null scaling method for %s",
1656 external_strain_block.blockName.c_str());
1657 }
1658 scale *= scaling_method->second->getScale(time);
1659 } else {
1660 MOFEM_LOG("EP", Sev::warning)
1661 << "No scaling method found for "
1662 << external_strain_block.blockName;
1663 }
1664
1665 const double q = 3 * external_strain_block.bulkModulusK * scale *
1666 external_strain_block.val;
1667 if (!std::isfinite(q)) {
1668 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FP,
1669 "Non-finite Storakers external pressure for %s",
1670 external_strain_block.blockName.c_str());
1671 }
1672
1675 constexpr auto t_kd = FTensor::Kronecker_Delta<int>();
1676 const auto t_L = FTensor::SymmLTensor<double, SPACE_DIM>();
1677
1678 const int nb_dofs = data.getIndices().size();
1679 const int nb_integration_pts = data.getN().size1();
1680 const int nb_base_functions = data.getN().size2();
1681 const double volume = getVolume();
1682 auto t_w = getFTensor0IntegrationWeight();
1683 auto t_row_base_fun = data.getFTensor0N();
1684
1685 for (int gg = 0; gg != nb_integration_pts; ++gg) {
1687 t_residual(L) = volume * t_w * q *
1688 (t_L(i, j, L) * t_kd(i, j));
1689 ++t_w;
1690
1691 auto t_nf = getFTensor1FromPtr<size_symm>(&*nF.data().begin());
1692 int bb = 0;
1693 for (; bb != nb_dofs / size_symm; ++bb) {
1694 t_nf(L) -= t_row_base_fun * t_residual(L);
1695 ++t_nf;
1696 ++t_row_base_fun;
1697 }
1698 for (; bb != nb_base_functions; ++bb) {
1699 ++t_row_base_fun;
1700 }
1701 }
1702 }
1703
1705}
1706
1708 boost::shared_ptr<DataAtIntegrationPts> data_ptr,
1709 boost::shared_ptr<MatrixDouble> strain_ptr,
1710 boost::shared_ptr<MatrixDouble> stress_ptr,
1711 boost::shared_ptr<HMHStorakers> storakers_ptr,
1712 VectorPtr external_pressure_ptr)
1713 : VolUserDataOperator(H1, OPLAST), dataAtPts(std::move(data_ptr)),
1714 strainPtr(std::move(strain_ptr)), stressPtr(std::move(stress_ptr)),
1715 storakersPtr(std::move(storakers_ptr)),
1716 externalPressurePtr(std::move(external_pressure_ptr)) {
1717 std::fill(&doEntities[MBVERTEX], &doEntities[MBMAXTYPE], false);
1718 doEntities[MBVERTEX] = true;
1719}
1720
1721MoFEMErrorCode
1724
1725 if (!dataAtPts || !strainPtr || !stressPtr || !storakersPtr) {
1726 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1727 "Invalid data passed to Storakers stretch-from-stress operator");
1728 }
1730
1731 const int nb_integration_pts = stressPtr->size1();
1732#ifndef NDEBUG
1733 if (nb_integration_pts != getGaussPts().size2()) {
1734 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1735 "Inconsistent number of Storakers integration points");
1736 }
1737#endif
1738 if (externalPressurePtr &&
1739 externalPressurePtr->size() != nb_integration_pts) {
1740 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1741 "Inconsistent number of external-pressure integration points");
1742 }
1743
1744 MatrixSizeHelper<GetFTensor2SymmetricFromMatType<SPACE_DIM, -1, DL>,
1745 DL>::size(*strainPtr, nb_integration_pts);
1746 MatrixSizeHelper<GetFTensor2SymmetricFromMatType<SPACE_DIM, -1, DL>,
1747 DL>::size(*dataAtPts->getStretchTensorAtPts(),
1748 nb_integration_pts);
1749 MatrixSizeHelper<GetFTensor4DdgFromMatType<SPACE_DIM, SPACE_DIM, -1, DL>,
1750 DL>::size(*dataAtPts->getDiffStretchTensorAtPts(),
1751 nb_integration_pts);
1752 MatrixSizeHelper<GetFTensor1FromMatType<SPACE_DIM, -1, DL>, DL>::size(
1753 *dataAtPts->getEigenVals(), nb_integration_pts);
1754 MatrixSizeHelper<GetFTensor2FromMatType<SPACE_DIM, SPACE_DIM, -1, DL>,
1755 DL>::size(*dataAtPts->getEigenVecs(), nb_integration_pts);
1756 dataAtPts->nbUniq.resize(nb_integration_pts, false);
1757 MatrixSizeHelper<GetFTensor4DdgFromMatType<SPACE_DIM, SPACE_DIM, -1, DL>,
1758 DL>::size(dataAtPts->matD, nb_integration_pts);
1759 MatrixSizeHelper<GetFTensor4DdgFromMatType<SPACE_DIM, SPACE_DIM, -1, DL>,
1760 DL>::size(dataAtPts->matInvD, nb_integration_pts);
1761 MatrixSizeHelper<GetFTensor2SymmetricFromMatType<SPACE_DIM, -1, DL>,
1762 DL>::size(dataAtPts->logStretch2H1AtPts, nb_integration_pts);
1763 MatrixSizeHelper<GetFTensor2SymmetricFromMatType<SPACE_DIM, -1, DL>,
1764 DL>::size(dataAtPts->logStretchTotalTensorAtPts,
1765 nb_integration_pts);
1766 MatrixSizeHelper<GetFTensor2FromMatType<SPACE_DIM, SPACE_DIM, -1, DL>,
1767 DL>::size(dataAtPts->rotMatAtPts, nb_integration_pts);
1768 MatrixSizeHelper<GetFTensor2FromMatType<SPACE_DIM, SPACE_DIM, -1, DL>,
1769 DL>::size(*dataAtPts->getAdjointPdstretchAtPts(),
1770 nb_integration_pts);
1771
1772 strainPtr->clear();
1773 dataAtPts->getStretchTensorAtPts()->clear();
1774 dataAtPts->getDiffStretchTensorAtPts()->clear();
1775 dataAtPts->getEigenVals()->clear();
1776 dataAtPts->getEigenVecs()->clear();
1777 dataAtPts->nbUniq.clear();
1778 dataAtPts->matD.clear();
1779 dataAtPts->matInvD.clear();
1780 dataAtPts->logStretch2H1AtPts.clear();
1781 dataAtPts->logStretchTotalTensorAtPts.clear();
1782
1783 auto t_strain = getFTensor2SymmetricFromMat<SPACE_DIM, -1, DL>(*strainPtr);
1784 auto t_biot_stretch = dataAtPts->getFTensorStretch(nb_integration_pts);
1785 auto t_diff_stretch = dataAtPts->getFTensorDiffStretch(nb_integration_pts);
1786 auto t_stress = getFTensor2FromMat<SPACE_DIM, SPACE_DIM, -1, DL>(*stressPtr);
1787 auto t_omega = dataAtPts->getFTensorRotAxis(nb_integration_pts);
1788 auto t_R = dataAtPts->getFTensorRotMat(nb_integration_pts);
1789 auto t_biot_stress =
1790 dataAtPts->getFTensorAdjointPdstretch(nb_integration_pts);
1791 auto t_eigen_vals = dataAtPts->getFTensorEigenVals(nb_integration_pts);
1792 auto t_eigen_vecs = dataAtPts->getFTensorEigenVecs(nb_integration_pts);
1793 auto t_mat_d =
1794 getFTensor4DdgFromMat<SPACE_DIM, SPACE_DIM, -1, DL>(dataAtPts->matD);
1795 auto t_mat_inv_d =
1796 getFTensor4DdgFromMat<SPACE_DIM, SPACE_DIM, -1, DL>(dataAtPts->matInvD);
1797 auto t_log_u2_h1 = dataAtPts->getFTensorLogStretch2H1(nb_integration_pts);
1798 auto t_log_stretch_total =
1799 dataAtPts->getFTensorLogStretchTotal(nb_integration_pts);
1801 dataAtPts->nbUniq.data().data());
1802
1803 const auto parameters =
1804 storakersPtr->getMaterialParameters(getFEEntityHandle());
1805 StressToStrech stress_to_stretch(parameters);
1806 constexpr auto t_diff_sym = FTensor::DiffSymmetrize<double>();
1807 constexpr auto identity = [](const double value) { return value; };
1808
1810 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1811 "Rotation selector not handled by Storakers");
1812 }
1813
1814 for (int gg = 0; gg != nb_integration_pts; ++gg) {
1815 t_R(i, j) = LieGroups::SO3::exp(t_omega, t_omega.l2())(i, j);
1816
1818 t_rotated_stress(i, j) = t_R(k, i) * t_stress(k, j);
1819 t_biot_stress(i, j) = t_diff_sym(i, j, k, l) * t_rotated_stress(k, l);
1820
1821 const double initial_y = stress_to_stretch.getScalarEquationState().y;
1822 const double q = externalPressurePtr ? (*externalPressurePtr)[gg] : 0.;
1823 const auto calculate_error = stress_to_stretch.calculate(
1824 t_biot_stress, t_biot_stretch, t_mat_d, t_mat_inv_d, q);
1825 if (calculate_error) {
1826 // Keep the production diagnostic to one copy-paste line. The atom prints
1827 // the material and spectral state, plus the scalar state on convergence.
1828 std::ostringstream atom_reproducer;
1829 atom_reproducer
1830 << std::scientific
1831 << std::setprecision(std::numeric_limits<double>::max_digits10)
1832 << "./storakers_stress_to_stretch_atom"
1833 << " -storakers_atom_eta " << parameters.eta
1834 << " -storakers_atom_mu " << parameters.mu
1835 << " -storakers_atom_beta " << parameters.beta
1836 << " -storakers_atom_q " << q
1837 << " -storakers_atom_initial_y " << initial_y
1838 << " -storakers_atom_solver_tol "
1839 << stress_to_stretch.absoluteTolerance
1840 << " -storakers_atom_biot_stress " << t_biot_stress(0, 0) << ","
1841 << t_biot_stress(0, 1) << "," << t_biot_stress(0, 2) << ","
1842 << t_biot_stress(1, 1) << "," << t_biot_stress(1, 2) << ","
1843 << t_biot_stress(2, 2) << " -log_no_color";
1844
1845 PetscMPIInt rank = -1;
1846 (void)MPI_Comm_rank(PETSC_COMM_WORLD, &rank);
1847 std::ostringstream atom_reproducer_log;
1848 atom_reproducer_log << "[" << rank
1849 << "] <warning> atom_reproducer_command="
1850 << atom_reproducer.str() << '\n';
1851 const auto atom_reproducer_log_string = atom_reproducer_log.str();
1852 std::fwrite(atom_reproducer_log_string.data(), 1,
1853 atom_reproducer_log_string.size(), stderr);
1854 std::fflush(stderr);
1855 }
1856 CHKERR calculate_error;
1857
1858 const auto &state = stress_to_stretch.getScalarEquationState();
1859 const auto &eigenvectors = stress_to_stretch.getEigenVectors();
1860 t_eigen_vals(i) = state.tLogU(i);
1861 t_eigen_vecs(i, j) = eigenvectors(i, j);
1862 for (int aa = 0; aa != SPACE_DIM; ++aa) {
1863 if (!std::isfinite(t_eigen_vals(aa))) {
1864 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
1865 "Non-finite Storakers strain eigenvalue");
1866 }
1867 }
1868
1869 auto t_log_stretch =
1870 EigenMatrix::getMat(t_eigen_vals, t_eigen_vecs, identity);
1871 t_strain(i, j) = t_log_stretch(i, j);
1872 t_log_stretch_total(i, j) = t_strain(i, j);
1873 t_log_u2_h1(i, j) = 0;
1874
1875 t_nb_uniq =
1876 getUniqNb<SPACE_DIM>(getVectorAdaptor(&t_eigen_vals(0), SPACE_DIM),
1878 if (t_nb_uniq < SPACE_DIM) {
1879 CHKERR sortEigenVals<SPACE_DIM>(
1880 getVectorAdaptor(&t_eigen_vals(0), SPACE_DIM),
1881 getMatrixAdaptor(&t_eigen_vecs(0, 0), SPACE_DIM, SPACE_DIM),
1883 }
1884 auto t_diff_stretch_mat = EigenMatrix::getDiffMat(
1885 t_eigen_vals, t_eigen_vecs,
1886 static_cast<double (*)(double)>(std::exp),
1887 static_cast<double (*)(double)>(std::exp), t_nb_uniq);
1888 t_diff_stretch(i, j, k, l) = t_diff_stretch_mat(i, j, k, l);
1889
1890 ++t_strain;
1891 ++t_biot_stretch;
1892 ++t_diff_stretch;
1893 ++t_stress;
1894 ++t_omega;
1895 ++t_R;
1896 ++t_biot_stress;
1897 ++t_eigen_vals;
1898 ++t_eigen_vecs;
1899 ++t_mat_d;
1900 ++t_mat_inv_d;
1901 ++t_log_u2_h1;
1902 ++t_log_stretch_total;
1903 ++t_nb_uniq;
1904 }
1905
1907}
1908
1911
1912 using MaterialParameters = HMHStorakers::MaterialParameters;
1913 using StressToStrech = HMHStorakers::StressToStrech;
1914
1915 PetscReal finite_difference_step = 1e-6;
1916 PetscReal stretch_tolerance = 1e-10;
1917 PetscReal compliance_tolerance = 1e-5;
1918 PetscReal solver_tolerance = 1e-14;
1919 PetscReal atom_eta = 0.37;
1920 PetscReal atom_mu = 2.4;
1921 PetscReal atom_beta = 0.8;
1922 PetscReal atom_q = 0;
1923 PetscReal atom_initial_y = 0;
1924 constexpr PetscInt symmetric_tensor_size = SPACE_DIM * (SPACE_DIM + 1) / 2;
1925 std::array<PetscReal, symmetric_tensor_size + 1> atom_biot_stress_input{};
1926 std::array<PetscReal, symmetric_tensor_size> atom_biot_stress{};
1927 PetscInt atom_biot_stress_size = atom_biot_stress_input.size();
1928 PetscBool atom_biot_stress_set = PETSC_FALSE;
1929 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
1930 "-storakers_atom_fd_step", &finite_difference_step,
1931 PETSC_NULLPTR);
1932 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
1933 "-storakers_atom_stretch_tol", &stretch_tolerance,
1934 PETSC_NULLPTR);
1935 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
1936 "-storakers_atom_compliance_tol",
1937 &compliance_tolerance, PETSC_NULLPTR);
1938 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
1939 "-storakers_atom_solver_tol", &solver_tolerance,
1940 PETSC_NULLPTR);
1941 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
1942 "-storakers_atom_eta", &atom_eta, PETSC_NULLPTR);
1943 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
1944 "-storakers_atom_mu", &atom_mu, PETSC_NULLPTR);
1945 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
1946 "-storakers_atom_beta", &atom_beta, PETSC_NULLPTR);
1947 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
1948 "-storakers_atom_q", &atom_q, PETSC_NULLPTR);
1949 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
1950 "-storakers_atom_initial_y", &atom_initial_y,
1951 PETSC_NULLPTR);
1952 CHKERR PetscOptionsGetRealArray(
1953 PETSC_NULLPTR, PETSC_NULLPTR, "-storakers_atom_biot_stress",
1954 atom_biot_stress_input.data(), &atom_biot_stress_size,
1955 &atom_biot_stress_set);
1956 if (!std::isfinite(finite_difference_step) || finite_difference_step <= 0 ||
1957 !std::isfinite(stretch_tolerance) || stretch_tolerance <= 0 ||
1958 !std::isfinite(compliance_tolerance) || compliance_tolerance <= 0 ||
1959 !std::isfinite(solver_tolerance) || solver_tolerance <= 0) {
1960 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
1961 "Invalid Storakers atom-test tolerance");
1962 }
1963 if (atom_biot_stress_set &&
1964 atom_biot_stress_size != symmetric_tensor_size) {
1965 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
1966 "-storakers_atom_biot_stress needs exactly six values in order "
1967 "s00,s01,s02,s11,s12,s22");
1968 }
1969 if (atom_biot_stress_set) {
1970 if (!std::isfinite(atom_eta) || !std::isfinite(atom_mu) ||
1971 !std::isfinite(atom_beta) || !std::isfinite(atom_q) ||
1972 !std::isfinite(atom_initial_y)) {
1973 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
1974 "Storakers reproduction parameters must be finite");
1975 }
1976 for (int rr = 0; rr != symmetric_tensor_size; ++rr) {
1977 if (!std::isfinite(atom_biot_stress_input[rr])) {
1978 SETERRQ(PETSC_COMM_SELF, MOFEM_INVALID_DATA,
1979 "-storakers_atom_biot_stress values must be finite");
1980 }
1981 atom_biot_stress[rr] = atom_biot_stress_input[rr];
1982 }
1983 }
1984
1985 struct StretchCase {
1986 const char *name;
1987 MaterialParameters parameters;
1988 std::array<double, SPACE_DIM> principalStretch;
1989 bool rotate;
1990 double q;
1991 };
1992
1993 const MaterialParameters parameters{0.37, 2.4, 0.8};
1994 const MaterialParameters near_incompressible_parameters{0.5, 0.0080194,
1995 248.};
1996 // eta=1 is the power-law Neo-Hookean specialization used by
1997 // HMHNeohookean: mu=2*c10 and beta=p/2.
1998 const MaterialParameters power_neohookean_parameters{1., 3.4, 11. / 12.};
1999 const std::array<StretchCase, 7> stretch_cases{{
2000 {"reference", parameters, {1., 1., 1.}, false, 0.},
2001 {"rotated_distinct_q", parameters, {0.71, 1.08, 1.43}, true, 0.65},
2002 {"rotated_repeated_negative_q", parameters, {0.82, 0.82, 1.36}, true,
2003 -0.4},
2004 {"negative_gamma_eff", parameters, {1., 1., 1.}, false, -4.},
2005 {"large_beta_dilated", near_incompressible_parameters, {4., 4., 4.},
2006 false, 0.},
2007 {"power_neohookean_distinct", power_neohookean_parameters,
2008 {0.62, 1.12, 1.51}, true, 0.},
2009 {"power_neohookean_repeated_q", power_neohookean_parameters,
2010 {0.55, 0.55, 0.9}, true, 0.75},
2011 }};
2012 const std::array<std::array<int, 2>, 6> mandel_directions{{
2013 {0, 0},
2014 {1, 1},
2015 {2, 2},
2016 {1, 2},
2017 {0, 2},
2018 {0, 1},
2019 }};
2020
2021 auto set_solver_tolerances = [solver_tolerance](auto &solver) {
2022 solver.absoluteTolerance = solver_tolerance;
2023 solver.relativeTolerance = solver_tolerance;
2024 };
2025
2026 if (atom_biot_stress_set) {
2027 const MaterialParameters atom_parameters{atom_eta, atom_mu, atom_beta};
2028 StressToStrech response(atom_parameters);
2029 set_solver_tolerances(response);
2030
2032 t_stress(0, 0) = atom_biot_stress[0];
2033 t_stress(0, 1) = atom_biot_stress[1];
2034 t_stress(0, 2) = atom_biot_stress[2];
2035 t_stress(1, 1) = atom_biot_stress[3];
2036 t_stress(1, 2) = atom_biot_stress[4];
2037 t_stress(2, 2) = atom_biot_stress[5];
2038
2039 MOFEM_LOG("EP", Sev::inform)
2040 << std::scientific
2041 << std::setprecision(std::numeric_limits<double>::max_digits10)
2042 << "Storakers reproduction input: eta=" << atom_eta
2043 << " mu=" << atom_mu << " beta=" << atom_beta
2044 << " q=" << atom_q
2045 << " initial_y=" << atom_initial_y
2046 << " solver_tol=" << solver_tolerance
2047 << " biot_stress=[" << atom_biot_stress[0] << ","
2048 << atom_biot_stress[1] << "," << atom_biot_stress[2] << ","
2049 << atom_biot_stress[3] << "," << atom_biot_stress[4] << ","
2050 << atom_biot_stress[5] << "]";
2051
2052 CHKERR response.stressToSpectralForm(t_stress, atom_q);
2053 const auto &principal_stress = response.getPrincipalStress();
2054 const auto &shifted_principal_stress =
2055 response.getShiftedPrincipalStress();
2056 MOFEM_LOG("EP", Sev::inform)
2057 << std::scientific
2058 << std::setprecision(std::numeric_limits<double>::max_digits10)
2059 << "Storakers reproduction spectrum: principal_stress=["
2060 << principal_stress(0) << "," << principal_stress(1) << ","
2061 << principal_stress(2) << "] shifted_principal_stress=["
2062 << shifted_principal_stress(0) << ","
2063 << shifted_principal_stress(1) << ","
2064 << shifted_principal_stress(2) << "]";
2065
2066 CHKERR response.solveScalarEquation(atom_initial_y);
2070 CHKERR response.spectralFormToStrech(t_recovered_stretch);
2071 CHKERR response.calculateBiotTangent(t_stiffness, t_compliance);
2072
2073 const auto &final_state = response.getScalarEquationState();
2074 MOFEM_LOG("EP", Sev::inform)
2075 << std::scientific
2076 << std::setprecision(std::numeric_limits<double>::max_digits10)
2077 << "Storakers reproduction converged scalar state: y=" << final_state.y
2078 << " z=" << final_state.z << " gamma=" << final_state.gamma
2079 << " q=" << final_state.q
2080 << " gamma_eff=" << final_state.gammaEffective
2081 << " gamma_y=" << final_state.gammaY << " phi=" << final_state.phi
2082 << " d_phi=" << final_state.dPhi << " u=[" << final_state.tU(0)
2083 << "," << final_state.tU(1) << "," << final_state.tU(2) << "]"
2084 << " jacobian=" << response.getJacobian();
2085 MOFEM_LOG("EP", Sev::inform)
2086 << "Storakers reproduction recovered stretch:\n"
2087 << t_recovered_stretch;
2089 }
2090
2091 for (const auto &stretch_case : stretch_cases) {
2092 const auto &p = stretch_case.parameters;
2093 const double mu1 = (1 - p.eta) * p.mu;
2094 const double mu2 = p.eta * p.mu;
2095
2097 for (int ii = 0; ii != SPACE_DIM; ++ii) {
2098 for (int jj = 0; jj != SPACE_DIM; ++jj) {
2099 t_eigenvectors(ii, jj) = ii == jj ? 1. : 0.;
2100 }
2101 }
2102 if (stretch_case.rotate) {
2103 // Rows are orthonormal eigenvectors in the MoFEM convention.
2104 t_eigenvectors(0, 0) = 2. / 3.;
2105 t_eigenvectors(0, 1) = -2. / 3.;
2106 t_eigenvectors(0, 2) = 1. / 3.;
2107 t_eigenvectors(1, 0) = 2. / 3.;
2108 t_eigenvectors(1, 1) = 1. / 3.;
2109 t_eigenvectors(1, 2) = -2. / 3.;
2110 t_eigenvectors(2, 0) = 1. / 3.;
2111 t_eigenvectors(2, 1) = 2. / 3.;
2112 t_eigenvectors(2, 2) = 2. / 3.;
2113 }
2114
2115 const double jacobian = stretch_case.principalStretch[0] *
2116 stretch_case.principalStretch[1] *
2117 stretch_case.principalStretch[2];
2118 const double z = std::pow(jacobian, -p.beta);
2119 const double gamma = 2 * mu1 * z + mu2 * z * z;
2120
2124 for (int ii = 0; ii != SPACE_DIM; ++ii) {
2125 for (int jj = ii; jj != SPACE_DIM; ++jj) {
2126 t_expected_stretch(ii, jj) = 0;
2127 t_inverse_stretch(ii, jj) = 0;
2128 for (int aa = 0; aa != SPACE_DIM; ++aa) {
2129 const double projector =
2130 t_eigenvectors(aa, ii) * t_eigenvectors(aa, jj);
2131 t_expected_stretch(ii, jj) +=
2132 projector * stretch_case.principalStretch[aa];
2133 t_inverse_stretch(ii, jj) +=
2134 projector / stretch_case.principalStretch[aa];
2135 }
2136 t_stress(ii, jj) = (ii == jj ? 2 * mu1 : 0.) +
2137 mu2 * t_expected_stretch(ii, jj) -
2138 (gamma + stretch_case.q) *
2139 t_inverse_stretch(ii, jj);
2140 }
2141 }
2142
2143 StressToStrech base_response(p);
2144 set_solver_tolerances(base_response);
2148 CHKERR base_response.calculate(t_stress, t_recovered_stretch, t_stiffness,
2149 t_compliance, stretch_case.q);
2150
2151 double maximum_stretch_error = 0;
2152 double stretch_scale = 1;
2153 double stress_norm_squared = 0;
2154 for (int ii = 0; ii != SPACE_DIM; ++ii) {
2155 for (int jj = 0; jj != SPACE_DIM; ++jj) {
2156 if (!std::isfinite(t_recovered_stretch(ii, jj)) ||
2157 !std::isfinite(t_expected_stretch(ii, jj)) ||
2158 !std::isfinite(t_stress(ii, jj))) {
2159 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
2160 "Non-finite Storakers stretch-recovery result");
2161 }
2162 maximum_stretch_error = std::max(
2163 maximum_stretch_error,
2164 std::abs(t_recovered_stretch(ii, jj) - t_expected_stretch(ii, jj)));
2165 stretch_scale =
2166 std::max(stretch_scale, std::abs(t_expected_stretch(ii, jj)));
2167 stress_norm_squared += t_stress(ii, jj) * t_stress(ii, jj);
2168 }
2169 }
2170 const double scaled_stretch_error = maximum_stretch_error / stretch_scale;
2171 MOFEM_LOG("EP", Sev::inform)
2172 << "Storakers stretch recovery: case=" << stretch_case.name
2173 << " q=" << stretch_case.q
2174 << " scaled_error=" << scaled_stretch_error;
2175 if (!std::isfinite(scaled_stretch_error) ||
2176 scaled_stretch_error > stretch_tolerance) {
2177 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
2178 "Storakers stress-to-stretch recovery failed");
2179 }
2180
2181 const double perturbation =
2182 finite_difference_step *
2183 std::max({1., p.mu, std::sqrt(stress_norm_squared)});
2184 double maximum_compliance_error = 0;
2185 int worst_direction = -1;
2186
2187 for (int column = 0; column != static_cast<int>(mandel_directions.size());
2188 ++column) {
2189 const int kk = mandel_directions[column][0];
2190 const int ll = mandel_directions[column][1];
2194 for (int ii = 0; ii != SPACE_DIM; ++ii) {
2195 for (int jj = ii; jj != SPACE_DIM; ++jj) {
2196 t_direction(ii, jj) = 0;
2197 }
2198 }
2199 t_direction(kk, ll) = kk == ll ? 1. : 1. / std::sqrt(2.);
2200 for (int ii = 0; ii != SPACE_DIM; ++ii) {
2201 for (int jj = ii; jj != SPACE_DIM; ++jj) {
2202 t_stress_plus(ii, jj) =
2203 t_stress(ii, jj) + perturbation * t_direction(ii, jj);
2204 t_stress_minus(ii, jj) =
2205 t_stress(ii, jj) - perturbation * t_direction(ii, jj);
2206 }
2207 }
2208
2209 StressToStrech plus_response(p);
2210 StressToStrech minus_response(p);
2211 set_solver_tolerances(plus_response);
2212 set_solver_tolerances(minus_response);
2215 CHKERR plus_response.calculate(t_stress_plus, t_stretch_plus,
2216 stretch_case.q);
2217 CHKERR minus_response.calculate(t_stress_minus, t_stretch_minus,
2218 stretch_case.q);
2219
2220 double error_norm_squared = 0;
2221 double analytical_norm_squared = 0;
2222 double numerical_norm_squared = 0;
2223 for (int ii = 0; ii != SPACE_DIM; ++ii) {
2224 for (int jj = 0; jj != SPACE_DIM; ++jj) {
2225 double analytical = 0;
2226 for (int mm = 0; mm != SPACE_DIM; ++mm) {
2227 for (int nn = 0; nn != SPACE_DIM; ++nn) {
2228 analytical += t_compliance(ii, jj, mm, nn) * t_direction(mm, nn);
2229 }
2230 }
2231 const double numerical =
2232 (t_stretch_plus(ii, jj) - t_stretch_minus(ii, jj)) /
2233 (2 * perturbation);
2234 const double error = analytical - numerical;
2235 if (!std::isfinite(analytical) || !std::isfinite(numerical) ||
2236 !std::isfinite(error)) {
2237 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
2238 "Non-finite Storakers compliance result");
2239 }
2240 error_norm_squared += error * error;
2241 analytical_norm_squared += analytical * analytical;
2242 numerical_norm_squared += numerical * numerical;
2243 }
2244 }
2245 const double scaled_compliance_error =
2246 std::sqrt(error_norm_squared) /
2247 std::max({1., std::sqrt(analytical_norm_squared),
2248 std::sqrt(numerical_norm_squared)});
2249 if (!std::isfinite(scaled_compliance_error)) {
2250 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
2251 "Non-finite Storakers compliance error");
2252 }
2253 if (scaled_compliance_error > maximum_compliance_error) {
2254 maximum_compliance_error = scaled_compliance_error;
2255 worst_direction = column;
2256 }
2257 }
2258
2259 MOFEM_LOG("EP", Sev::inform)
2260 << "Storakers compliance finite difference: case=" << stretch_case.name
2261 << " max_scaled_error=" << maximum_compliance_error
2262 << " direction=" << worst_direction;
2263 if (!std::isfinite(maximum_compliance_error) ||
2264 maximum_compliance_error > compliance_tolerance) {
2265 SETERRQ(PETSC_COMM_SELF, MOFEM_ATOM_TEST_INVALID,
2266 "Storakers compliance finite-difference check failed");
2267 }
2268 }
2269
2271}
2272
2273} // namespace EshelbianPlasticity
std::string type
Lie algebra implementation.
#define FTENSOR_INDEXES(DIM,...)
#define FTENSOR_INDEX(DIM, I)
void initial(double P1[], double P2[], double P3[], double c[], const int N)
Definition acoustic.cpp:9
constexpr double a
constexpr int SPACE_DIM
Fourth-order symmetrization tensor.
Fourth-order differential tensor symmetric in both index pairs.
Kronecker Delta class symmetric.
Kronecker Delta class.
Stateless vector whose components are all equal to one.
Definition One.hpp:22
Mapping from symmetric tensor indices to packed storage index.
#define CHK_THROW_MESSAGE(err, msg)
Check and throw MoFEM exception.
#define MoFEMFunctionReturnHot(a)
Last executable line of each PETSc function used for error handling. Replaces return()
@ H1
continuous field
Definition definitions.h:85
#define MoFEMFunctionBegin
First executable line of each MoFEM function, used for error handling. Final line of MoFEM functions ...
@ MOFEM_OPERATION_UNSUCCESSFUL
Definition definitions.h:34
@ MOFEM_ATOM_TEST_INVALID
Definition definitions.h:40
@ MOFEM_DATA_INCONSISTENCY
Definition definitions.h:31
@ MOFEM_INVALID_DATA
Definition definitions.h:36
@ MOFEM_NOT_IMPLEMENTED
Definition definitions.h:32
#define MoFEMFunctionReturn(a)
Last executable line of each PETSc function used for error handling. Replaces return()
#define CHKERR
Inline error check.
constexpr auto t_kd
double eta
#define MOFEM_LOG(channel, severity)
Log.
FTensor::Index< 'i', SPACE_DIM > i
FTensor::Index< 'J', DIM1 > J
Definition level_set.cpp:30
FTensor::Index< 'l', 3 > l
FTensor::Index< 'j', 3 > j
FTensor::Index< 'k', 3 > k
auto getMat(A &&t_val, B &&t_vec, Fun< double > f)
Get the Mat object.
auto getDiffMat(A &&t_val, B &&t_vec, Fun< double > f, Fun< double > d_f, const int nb)
Get the Diff Mat object.
auto getDiffDiffMat(A &&t_val, B &&t_vec, Fun< double > f, Fun< double > d_f, Fun< double > dd_f, C &&t_S, const int nb)
Get the Diff Diff Mat object.
EntitiesFieldData::EntData EntData
ForcesAndSourcesCore::UserDataOperator UserDataOperator
MoFEMErrorCode testHMHStorakersStressToStretch()
static constexpr auto size_symm
boost::shared_ptr< VectorDouble > VectorPtr
double h
constexpr auto field_name
static enum StretchSelector stretchSelector
static enum StretchHandling stretchHandling
static enum RotSelector rotSelector
static enum RotSelector gradApproximator
static PetscBool physicalTimeFlg
static double currentPhysicalTime
static bool isNoStretch()
Evaluate Storakers stretch and its tangent from the supplied stress.
OpCalculateStretchFromStress(boost::shared_ptr< DataAtIntegrationPts > data_ptr, boost::shared_ptr< MatrixDouble > strain_ptr, boost::shared_ptr< MatrixDouble > stress_ptr, boost::shared_ptr< HMHStorakers > storakers_ptr, VectorPtr external_pressure_ptr)
MoFEMErrorCode doWork(int side, EntityType type, EntData &data) override
MoFEMErrorCode evaluateRhs(EntData &) override
MoFEMErrorCode evaluateLhs(EntData &) override
Assemble the isotropic external-strain contribution.
OpSpatialPhysicalExternalStrain(const std::string &field_name, boost::shared_ptr< DataAtIntegrationPts > data_ptr, boost::shared_ptr< ExternalStrainVec > external_strain_vec_ptr, std::map< std::string, boost::shared_ptr< ScalingMethod > > smv)
std::map< std::string, boost::shared_ptr< ScalingMethod > > scalingMethodsMap
Assemble the consistent full-order H–H tangent.
MoFEMErrorCode integrate(EntData &row_data, EntData &col_data) override
OpSpatialPhysical_du_du(std::string row_field, std::string col_field, boost::shared_ptr< DataAtIntegrationPts > data_ptr, const double alpha_u)
Assemble the full-order equation conjugate to H = log(U)
MoFEMErrorCode integrate(EntData &data) override
OpSpatialPhysical(const std::string &field_name, boost::shared_ptr< DataAtIntegrationPts > data_ptr, const double alpha_u)
Recover the right stretch from a symmetric Biot stress.
static double logSumExp(const double first, const double second)
const ScalarEquationState & getScalarEquationState() const
const FTensor::Tensor1< double, SPACE_DIM > & getShiftedPrincipalStress() const
MoFEMErrorCode stressToSpectralForm(T &t_stress)
Diagonalise sym(stress) and calculate b_i = t_i - 2 mu_1.
MoFEMErrorCode calculatePrincipalStretches(const double y, ScalarEquationState &state) const
Evaluate u_i(y), phi(y), and the exact Newton derivative.
bool hasConverged(const ScalarEquationState &state, const double lower_y, const double upper_y) const
const FTensor::Tensor1< double, SPACE_DIM > & getPrincipalStress() const
FTensor::Tensor2< double, SPACE_DIM, SPACE_DIM > tEigenVectors
MoFEMErrorCode calculate(TStress &t_stress, TStretch &t_stretch, TStiffness &t_stiffness, TCompliance &t_compliance)
Calculate U(T), dT/dU, and dU/dT in reference coordinates.
MoFEMErrorCode calculateCompliance(T &t_compliance) const
Calculate S = dU/dT in the working/reference coordinates.
MoFEMErrorCode calculate(TStress &t_stress, TStretch &t_stretch, const double q)
MoFEMErrorCode calculate(TStress &t_stress, TStretch &t_stretch)
MoFEMErrorCode calculateStiffness(T &t_stiffness) const
Calculate C = dT/dU in the working/reference coordinates.
MoFEMErrorCode solveScalarEquation()
Solve phi(y)=0 with safeguarded Newton–Brent iterations.
MoFEMErrorCode solveScalarEquation(const double initial_y)
std::pair< double, double > getSafeYBounds() const
MoFEMErrorCode calculateBiotTangent(TStiffness &t_stiffness, TCompliance &t_compliance) const
Calculate stiffness and compliance in reference coordinates.
MoFEMErrorCode calculate(TStress &t_stress, TStretch &t_stretch, TStiffness &t_stiffness, TCompliance &t_compliance, const double q)
FTensor::Tensor1< double, SPACE_DIM > tB
const FTensor::Tensor2< double, SPACE_DIM, SPACE_DIM > & getEigenVectors() const
StressToStrech(const MaterialParameters &parameters)
MoFEMErrorCode spectralFormToStrech(T &t_stretch) const
Reconstruct U = Q diag(u_i) Q^T in the working basis.
FTensor::Tensor1< double, SPACE_DIM > tPrincipalStress
MoFEMErrorCode stressToSpectralForm(T &t_stress, const double q)
UserDataOperator * returnOpJacobian(const bool eval_rhs, const bool eval_lhs, boost::shared_ptr< DataAtIntegrationPts > data_ptr, boost::shared_ptr< PhysicalEquations > physics_ptr) override
VolUserDataOperator * returnOpCalculateExternalPressure(VectorPtr external_pressure_ptr, boost::shared_ptr< ExternalStrainVec > external_strain_vec_ptr, std::map< std::string, boost::shared_ptr< ScalingMethod > > smv) override
std::vector< BlockData > blockData
static double getLogJacobian(T &principal_hencky_stretches)
static DerivedParameters getDerivedParameters(const double mu, const double beta)
VolUserDataOperator * returnOpCalculateStretchFromStress(boost::shared_ptr< DataAtIntegrationPts > data_ptr, boost::shared_ptr< PhysicalEquations > physics_ptr, boost::shared_ptr< MatrixDouble > strain_ptr, VectorPtr external_pressure_ptr) override
MoFEMErrorCode extractBlockData(std::vector< const CubitMeshSets * > meshset_vec_ptr, const Sev sev)
static double getPrincipalCoordinateStressDerivativeAtFixedGamma(const MaterialParameters &parameters, const double hencky_stretch)
VolUserDataOperator * returnOpCalculateStretchFromStress(boost::shared_ptr< DataAtIntegrationPts > data_ptr, boost::shared_ptr< PhysicalEquations > physics_ptr, boost::shared_ptr< MatrixDouble > strain_ptr) override
static double getJacobianPower(const MaterialParameters &parameters, T &principal_hencky_stretches)
static double getGamma(const MaterialParameters &parameters, const double jacobian_power)
static double getMu1(const MaterialParameters &parameters)
static MoFEMErrorCode validateParameters(const MaterialParameters &parameters, const char *source)
HMHStorakers(MoFEM::Interface &m_field, const double eta, const double mu, const double beta)
VolUserDataOperator * returnOpSpatialPhysical(const std::string &field_name, boost::shared_ptr< DataAtIntegrationPts > data_ptr, const double alpha_u) override
VolUserDataOperator * returnOpSpatialPhysicalExternalStrain(const std::string &field_name, boost::shared_ptr< DataAtIntegrationPts > data_ptr, boost::shared_ptr< ExternalStrainVec > external_strain_vec_ptr, std::map< std::string, boost::shared_ptr< ScalingMethod > > smv) override
VolUserDataOperator * returnOpSpatialPhysical_du_du(std::string row_field, std::string col_field, boost::shared_ptr< DataAtIntegrationPts > data_ptr, const double alpha_u) override
static double getGammaY(const MaterialParameters &parameters, const double jacobian_power)
static double getPrincipalCoordinateStress(const MaterialParameters &parameters, const double gamma, const double hencky_stretch)
MoFEMErrorCode extractBlockData(const Sev sev)
static double getMu2(const MaterialParameters &parameters)
MaterialParameters getMaterialParameters(const EntityHandle ent) const
static auto exp(A &&t_w_vee, B &&theta)
Definition Lie.hpp:69
virtual moab::Interface & get_moab()=0
bool sYmm
If true assume that matrix is symmetric structure.
Deprecated interface functions.
Data on single entity (This is passed as argument to DataOperator::doWork)
EntityHandle getFEEntityHandle() const
Return finite element entity handle.
const FEMethod * getFEMethod() const
Return raw pointer to Finite Element Method object.
PetscReal ts_t
Current time value.
MoFEMErrorCode getInterface(IFACE *&iface) const
Get interface reference to pointer of interface.
Calculate q = 3 K_ext epsilon_ext at integration points.
double width
Width of Gaussian distribution.
double scale
Definition plastic.cpp:124