1413 auto storakers_ptr =
1414 boost::dynamic_pointer_cast<HMHStorakers>(dataAtPts->physicsPtr);
1415 if (!storakers_ptr) {
1417 "Pointer to HMHStorakers is null");
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 =
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();
1439 auto get_ftensor2 = [](MatrixDouble &matrix,
const int row,
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);
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>();
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());
1464 auto no_h1 = [&]() {
1467 for (
int gg = 0; gg != nb_integration_pts; ++gg) {
1468 const double a = volume * t_w;
1471 const double jacobian_power =
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 "
1484 auto coordinate_stress = [parameters, gamma](
const double h) {
1487 auto coordinate_stress_derivative =
1488 [parameters](
const double h) {
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 =
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);
1508 (t_material_tangent(
i,
j,
k,
l) * t_L(
k,
l,
J));
1511 (t_L(
i,
j, L) * (t_diff(
i,
j,
k,
l) * t_L(
k,
l,
J)));
1515 (t_adjoint_dstretch(
i,
j) || t_adjoint_dstretch(
j,
i)) / 2.;
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);
1522 t_L(
i,
j, L) * (t_diff2_uP(
i,
j,
k,
l) * t_L(
k,
l,
J));
1524 ++t_adjoint_dstretch;
1530 if (minimEigenValue > 0) {
1532 t_hessian_eig_vecs(L,
J) =
1533 0.5 * (t_dP(L,
J) + t_dP(
J, L));
1535 CHKERR computeEigenValuesSymmetric(t_hessian_eig_vecs,
1536 t_hessian_eig_vals);
1538 const double eigenvalue_floor = bulk_modulus * minimEigenValue;
1539 bool project_hessian =
false;
1540 for (
int aa = 0; aa !=
size_symm; ++aa) {
1542 project_hessian || t_hessian_eig_vals(aa) < eigenvalue_floor;
1544 if (project_hessian) {
1545 auto min_eig_val = [eigenvalue_floor](
const double value) {
1547 (value + eigenvalue_floor +
1548 std::abs(value - eigenvalue_floor));
1551 t_hessian_eig_vals, t_hessian_eig_vecs, min_eig_val);
1552 t_dP(L,
J) = t_dP_min_eig(L,
J);
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);
1563 for (
int cc = 0; cc != col_nb_dofs /
size_symm; ++cc) {
1565 (t_row_base_fun * t_col_base_fun) * t_dP(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);
1576 for (; rr != row_nb_base_functions; ++rr) {
1585 auto not_implemented = [&]() {
1588 "Storakers full-order tangent is implemented only for no_h1");
1598 CHKERR not_implemented();
1602 "gradApproximator not handled");
1725 if (!dataAtPts || !strainPtr || !stressPtr || !storakersPtr) {
1727 "Invalid data passed to Storakers stretch-from-stress operator");
1731 const int nb_integration_pts = stressPtr->size1();
1733 if (nb_integration_pts != getGaussPts().size2()) {
1735 "Inconsistent number of Storakers integration points");
1738 if (externalPressurePtr &&
1739 externalPressurePtr->size() != nb_integration_pts) {
1741 "Inconsistent number of external-pressure integration points");
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);
1750 DL>::size(*dataAtPts->getDiffStretchTensorAtPts(),
1751 nb_integration_pts);
1752 MatrixSizeHelper<GetFTensor1FromMatType<
SPACE_DIM, -1,
DL>,
DL>::size(
1753 *dataAtPts->getEigenVals(), nb_integration_pts);
1755 DL>::size(*dataAtPts->getEigenVecs(), nb_integration_pts);
1756 dataAtPts->nbUniq.resize(nb_integration_pts,
false);
1758 DL>::size(dataAtPts->matD, nb_integration_pts);
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);
1767 DL>::size(dataAtPts->rotMatAtPts, nb_integration_pts);
1769 DL>::size(*dataAtPts->getAdjointPdstretchAtPts(),
1770 nb_integration_pts);
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();
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);
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);
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());
1803 const auto parameters =
1804 storakersPtr->getMaterialParameters(getFEEntityHandle());
1807 constexpr auto identity = [](
const double value) {
return value; };
1811 "Rotation selector not handled by Storakers");
1814 for (
int gg = 0; gg != nb_integration_pts; ++gg) {
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);
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) {
1828 std::ostringstream atom_reproducer;
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 "
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";
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);
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))) {
1865 "Non-finite Storakers strain eigenvalue");
1869 auto t_log_stretch =
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;
1876 getUniqNb<SPACE_DIM>(getVectorAdaptor(&t_eigen_vals(0),
SPACE_DIM),
1879 CHKERR sortEigenVals<SPACE_DIM>(
1880 getVectorAdaptor(&t_eigen_vals(0),
SPACE_DIM),
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);
1902 ++t_log_stretch_total;
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;
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,
1932 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
1933 "-storakers_atom_stretch_tol", &stretch_tolerance,
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,
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,
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) {
1961 "Invalid Storakers atom-test tolerance");
1963 if (atom_biot_stress_set &&
1964 atom_biot_stress_size != symmetric_tensor_size) {
1966 "-storakers_atom_biot_stress needs exactly six values in order "
1967 "s00,s01,s02,s11,s12,s22");
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)) {
1974 "Storakers reproduction parameters must be finite");
1976 for (
int rr = 0; rr != symmetric_tensor_size; ++rr) {
1977 if (!std::isfinite(atom_biot_stress_input[rr])) {
1979 "-storakers_atom_biot_stress values must be finite");
1981 atom_biot_stress[rr] = atom_biot_stress_input[rr];
1985 struct StretchCase {
1987 MaterialParameters parameters;
1988 std::array<double, SPACE_DIM> principalStretch;
1993 const MaterialParameters parameters{0.37, 2.4, 0.8};
1994 const MaterialParameters near_incompressible_parameters{0.5, 0.0080194,
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,
2004 {
"negative_gamma_eff", parameters, {1., 1., 1.},
false, -4.},
2005 {
"large_beta_dilated", near_incompressible_parameters, {4., 4., 4.},
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},
2012 const std::array<std::array<int, 2>, 6> mandel_directions{{
2021 auto set_solver_tolerances = [solver_tolerance](
auto &solver) {
2022 solver.absoluteTolerance = solver_tolerance;
2023 solver.relativeTolerance = solver_tolerance;
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);
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];
2041 << std::setprecision(std::numeric_limits<double>::max_digits10)
2042 <<
"Storakers reproduction input: eta=" << atom_eta
2043 <<
" mu=" << atom_mu <<
" beta=" << atom_beta
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] <<
"]";
2052 CHKERR response.stressToSpectralForm(t_stress, atom_q);
2053 const auto &principal_stress = response.getPrincipalStress();
2054 const auto &shifted_principal_stress =
2055 response.getShiftedPrincipalStress();
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) <<
"]";
2066 CHKERR response.solveScalarEquation(atom_initial_y);
2070 CHKERR response.spectralFormToStrech(t_recovered_stretch);
2071 CHKERR response.calculateBiotTangent(t_stiffness, t_compliance);
2073 const auto &final_state = response.getScalarEquationState();
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();
2086 <<
"Storakers reproduction recovered stretch:\n"
2087 << t_recovered_stretch;
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;
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.;
2102 if (stretch_case.rotate) {
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.;
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;
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];
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);
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);
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))) {
2160 "Non-finite Storakers stretch-recovery result");
2162 maximum_stretch_error = std::max(
2163 maximum_stretch_error,
2164 std::abs(t_recovered_stretch(ii, jj) - t_expected_stretch(ii, jj)));
2166 std::max(stretch_scale, std::abs(t_expected_stretch(ii, jj)));
2167 stress_norm_squared += t_stress(ii, jj) * t_stress(ii, jj);
2170 const double scaled_stretch_error = maximum_stretch_error / stretch_scale;
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) {
2178 "Storakers stress-to-stretch recovery failed");
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;
2187 for (
int column = 0; column !=
static_cast<int>(mandel_directions.size());
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;
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);
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,
2217 CHKERR minus_response.calculate(t_stress_minus, t_stretch_minus,
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);
2231 const double numerical =
2232 (t_stretch_plus(ii, jj) - t_stretch_minus(ii, jj)) /
2234 const double error = analytical - numerical;
2235 if (!std::isfinite(analytical) || !std::isfinite(numerical) ||
2236 !std::isfinite(error)) {
2238 "Non-finite Storakers compliance result");
2240 error_norm_squared += error * error;
2241 analytical_norm_squared += analytical * analytical;
2242 numerical_norm_squared += numerical * numerical;
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)) {
2251 "Non-finite Storakers compliance error");
2253 if (scaled_compliance_error > maximum_compliance_error) {
2254 maximum_compliance_error = scaled_compliance_error;
2255 worst_direction = column;
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) {
2266 "Storakers compliance finite-difference check failed");