1780 "Neo-Hookean stretch-from-stress SNES is not set up");
1785 CHKERR computeEigenValuesSymmetric(tBiotStress, tBiotStressEigenVals,
1786 tBiotStressEigenVecs);
1789 t_target_biot_stress_eigenvalues(
i) = tBiotStressEigenVals(
i);
1792 const double tr_biot_stress =
1793 tBiotStressEigenVals(
i) * t_one(
i) +
SPACE_DIM * externalPressure;
1794 auto set_linear_initial_guess = [&]() {
1796 (tBiotStressEigenVals(
i) + externalPressure - tr_biot_stress / 3.) /
1798 tr_biot_stress / (9 * K);
1800 set_linear_initial_guess();
1801 PetscBool test_zero_initial_guess = PETSC_FALSE;
1802 CHKERR PetscOptionsHasName(PETSC_NULLPTR, PETSC_NULLPTR,
1803 "-nh_stretch_test_zero_initial_guess",
1804 &test_zero_initial_guess);
1805 if (test_zero_initial_guess) {
1808 struct LocalSolveState {
1809 PetscErrorCode solveError = 0;
1810 PetscErrorCode acceptedStateError = 0;
1811 PetscErrorCode reasonQueryError = 0;
1812 PetscErrorCode residualError = 0;
1813 PetscErrorCode hessianError = 0;
1814 SNESConvergedReason reason = SNES_CONVERGED_ITERATING;
1815 double residualNorm = std::numeric_limits<double>::infinity();
1816 bool finiteIterate =
false;
1817 bool boundedIterate =
false;
1818 bool stableHessian =
false;
1819 bool acceptable =
false;
1822 auto solve_from_current_guess = [&]() {
1823 LocalSolveState state;
1824 double initial_residual_norm = std::numeric_limits<double>::infinity();
1825 if (!evaluateRhs()) {
1826 initial_residual_norm = tReVec.l2();
1829 double *chi_array = PETSC_NULLPTR;
1830 state.acceptedStateError = VecGetArray(Chi, &chi_array);
1831 if (!state.acceptedStateError) {
1832 for (
int aa = 0; aa !=
SPACE_DIM; ++aa) {
1833 chi_array[aa] = tStretchVec(aa);
1835 state.acceptedStateError = VecRestoreArray(Chi, &chi_array);
1837 if (!state.acceptedStateError) {
1838 state.acceptedStateError = VecAssemblyBegin(Chi);
1840 if (!state.acceptedStateError) {
1841 state.acceptedStateError = VecAssemblyEnd(Chi);
1843 if (state.acceptedStateError) {
1847 state.solveError = SNESSolve(sNes, PETSC_NULLPTR, Chi);
1851 const double *accepted_chi_array = PETSC_NULLPTR;
1852 state.acceptedStateError = VecGetArrayRead(Chi, &accepted_chi_array);
1853 if (!state.acceptedStateError) {
1854 for (
int aa = 0; aa !=
SPACE_DIM; ++aa) {
1855 tStretchVec(aa) = accepted_chi_array[aa];
1857 state.acceptedStateError = VecRestoreArrayRead(Chi, &accepted_chi_array);
1859 state.reasonQueryError = SNESGetConvergedReason(sNes, &state.reason);
1860 if (!state.acceptedStateError) {
1861 state.residualError = evaluateRhs();
1862 state.hessianError = evaluateLhs();
1865 state.finiteIterate = !state.acceptedStateError;
1866 double maximum_absolute_natural_hencky = 0.;
1867 for (
int aa = 0; aa !=
SPACE_DIM && state.finiteIterate; ++aa) {
1868 state.finiteIterate = std::isfinite(tStretchVec(aa));
1869 maximum_absolute_natural_hencky =
1870 std::max(maximum_absolute_natural_hencky, std::abs(tStretchVec(aa)));
1873 state.boundedIterate =
1874 state.finiteIterate &&
1875 maximum_absolute_natural_hencky <= catastrophic_hencky_limit;
1877 if (!state.residualError) {
1878 state.residualNorm = tReVec.l2();
1881 const double stability_tolerance =
1882 std::sqrt(std::numeric_limits<double>::epsilon()) *
1883 std::max({1., std::abs(K), complementaryHessianSpectralRadius});
1884 state.stableHessian =
1885 !state.hessianError &&
1886 std::isfinite(minimumComplementaryHessianEigenvalue) &&
1887 minimumComplementaryHessianEigenvalue > stability_tolerance;
1889 PetscReal absolute_tolerance =
tol;
1890 PetscReal relative_tolerance =
tol;
1891 PetscReal step_tolerance = PETSC_DEFAULT;
1892 PetscInt maximum_iterations = max_iter;
1893 PetscInt maximum_function_evaluations = PETSC_DEFAULT;
1894 const PetscErrorCode tolerance_error = SNESGetTolerances(
1895 sNes, &absolute_tolerance, &relative_tolerance, &step_tolerance,
1896 &maximum_iterations, &maximum_function_evaluations);
1897 const double acceptable_residual =
1900 : 10. * std::max(
static_cast<double>(absolute_tolerance),
1901 static_cast<double>(relative_tolerance) *
1902 initial_residual_norm);
1903 const bool converged =
1904 !state.reasonQueryError && state.reason > SNES_CONVERGED_ITERATING;
1905 const bool mildly_nonconverged = allowNonConverged &&
1906 !state.reasonQueryError &&
1907 state.reason == SNES_DIVERGED_MAX_IT &&
1908 state.residualNorm <= acceptable_residual;
1909 const bool admissible_error =
1910 !state.solveError || state.solveError == PETSC_ERR_NOT_CONVERGED;
1912 admissible_error && !state.acceptedStateError && !state.residualError &&
1913 !state.hessianError && state.finiteIterate && state.boundedIterate &&
1914 state.stableHessian && (converged || mildly_nonconverged);
1918 struct ContinuationState {
1919 LocalSolveState finalState;
1920 double acceptedFraction = 0.;
1921 double rejectedFraction = 0.;
1925 auto run_stress_continuation = [&]() {
1926 ContinuationState continuation;
1928 double fraction_step = 0.25;
1929 constexpr double minimum_fraction_step = 1.e-6;
1930 constexpr int maximum_continuation_attempts = 200;
1932 for (
int attempt = 0; attempt != maximum_continuation_attempts &&
1933 continuation.acceptedFraction < 1.;
1935 const double trial_fraction =
1936 std::min(1., continuation.acceptedFraction + fraction_step);
1937 tBiotStressEigenVals(
i) =
1938 trial_fraction * t_target_biot_stress_eigenvalues(
i);
1939 tStretchVec(
i) = t_accepted_hencky(
i);
1941 continuation.finalState = solve_from_current_guess();
1942 if (continuation.finalState.acceptable) {
1943 continuation.acceptedFraction = trial_fraction;
1944 t_accepted_hencky(
i) = tStretchVec(
i);
1946 std::min(1. - continuation.acceptedFraction, 1.5 * fraction_step);
1948 continuation.rejectedFraction = trial_fraction;
1949 continuation.tRejectedHencky(
i) = tStretchVec(
i);
1950 fraction_step *= 0.5;
1951 tStretchVec(
i) = t_accepted_hencky(
i);
1954 if (continuation.acceptedFraction < 1. &&
1955 fraction_step < minimum_fraction_step) {
1960 tBiotStressEigenVals(
i) = t_target_biot_stress_eigenvalues(
i);
1961 return continuation;
1964 auto direct_state = solve_from_current_guess();
1965 auto final_state = direct_state;
1966 bool used_continuation =
false;
1968 if (!direct_state.acceptable) {
1969 used_continuation =
true;
1970 const auto original_continuation = run_stress_continuation();
1971 final_state = original_continuation.finalState;
1973 if (original_continuation.acceptedFraction < 1.) {
1974 const double failure_minimum_hessian_eigenvalue =
1975 minimumComplementaryHessianEigenvalue;
1976 (void)logSnesFailure(final_state.solveError,
1977 final_state.acceptedStateError,
1978 final_state.reasonQueryError);
1979 const double reached_fraction = original_continuation.acceptedFraction;
1981 <<
"Neo-Hookean stress continuation stopped at accepted fraction "
1982 << reached_fraction <<
" after rejecting fraction "
1983 << original_continuation.rejectedFraction
1984 <<
"; residual = " << final_state.residualNorm
1985 <<
"; minimum complementary Hessian eigenvalue = "
1986 << failure_minimum_hessian_eigenvalue
1987 <<
"; rejected principal Hencky = ["
1988 << original_continuation.tRejectedHencky(0) <<
","
1989 << original_continuation.tRejectedHencky(1) <<
","
1990 << original_continuation.tRejectedHencky(2) <<
"]";
1992 <<
"Abaqus Neo-Hookean stretch-from-stress solve failed to reach a "
1993 "globally unique constitutive target; this indicates a numerical "
1994 "range or nonlinear-solver failure; aborting all MPI ranks";
2002 "Abaqus Neo-Hookean stretch-from-stress numerical solve failed");
2006 if (used_continuation) {
2008 <<
"Neo-Hookean stretch-from-stress recovered by adaptive stress "
2010 }
else if (final_state.reason == SNES_DIVERGED_MAX_IT) {
2012 <<
"Neo-Hookean stretch-from-stress accepted a bounded, stable "
2013 "maximum-iteration state with residual "
2014 << final_state.residualNorm;
2017 for (
int aa = 0; aa !=
SPACE_DIM; ++aa) {
2019 isEq(tBiotStressEigenVals(aa), tBiotStressEigenVals(aa - 1))) {
2022 double group_sum = 0;
2024 for (
int bb = aa; bb !=
SPACE_DIM; ++bb) {
2025 if (isEq(tBiotStressEigenVals(aa), tBiotStressEigenVals(bb))) {
2026 group_sum += tStretchVec(bb);
2030 const double group_average = group_sum / group_size;
2031 for (
int bb = aa; bb !=
SPACE_DIM; ++bb) {
2032 if (isEq(tBiotStressEigenVals(aa), tBiotStressEigenVals(bb))) {
2033 tStretchVec(bb) = group_average;
2038 tHenckyStreachEigenVals(
i) = tStretchVec(
i);
2039 auto identity = [](
const double v) {
return v; };
2041 tHenckyStreachEigenVals, tBiotStressEigenVecs, identity)(
i,
j);
2042 CHKERR calculateBiotStretch();
2050 using StretchFromStress =
2053 PetscReal atom_c10 = 1.7;
2054 PetscReal atom_K = 8.5;
2055 PetscReal atom_q = 0.;
2056 PetscReal atom_tol = 1e-13;
2057 PetscInt atom_max_iter = 100;
2058 std::array<PetscReal, size_symm + 1> atom_biot_stress_input{};
2059 std::array<PetscReal, size_symm> atom_biot_stress{};
2060 PetscInt atom_biot_stress_size = atom_biot_stress_input.size();
2061 PetscBool atom_biot_stress_set = PETSC_FALSE;
2062 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
2063 "-nh_stretch_atom_c10", &atom_c10, PETSC_NULLPTR);
2064 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
"-nh_stretch_atom_k",
2065 &atom_K, PETSC_NULLPTR);
2066 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
"-nh_stretch_atom_q",
2067 &atom_q, PETSC_NULLPTR);
2068 CHKERR PetscOptionsGetInt(PETSC_NULLPTR, PETSC_NULLPTR,
2069 "-nh_stretch_atom_max_it", &atom_max_iter,
2071 CHKERR PetscOptionsGetReal(PETSC_NULLPTR, PETSC_NULLPTR,
2072 "-nh_stretch_atom_tol", &atom_tol, PETSC_NULLPTR);
2073 CHKERR PetscOptionsGetRealArray(
2074 PETSC_NULLPTR, PETSC_NULLPTR,
"-nh_stretch_atom_biot_stress",
2075 atom_biot_stress_input.data(), &atom_biot_stress_size,
2076 &atom_biot_stress_set);
2077 if (!std::isfinite(atom_c10) || atom_c10 <= 0. || !std::isfinite(atom_K) ||
2078 !std::isfinite(atom_q) || !std::isfinite(atom_tol) || atom_tol <= 0. ||
2079 atom_max_iter < 0 ||
2080 atom_max_iter >
static_cast<PetscInt
>(std::numeric_limits<int>::max())) {
2082 "Invalid Neo-Hookean atom material or SNES parameters");
2085 "Neo-Hookean atom test");
2086 if (atom_biot_stress_set && atom_biot_stress_size !=
size_symm) {
2088 "-nh_stretch_atom_biot_stress needs exactly six values in "
2089 "order s00,s01,s02,s11,s12,s22");
2091 if (atom_biot_stress_set) {
2092 for (
int rr = 0; rr !=
size_symm; ++rr) {
2093 const auto value = atom_biot_stress_input[rr];
2094 if (!std::isfinite(value)) {
2096 "-nh_stretch_atom_biot_stress values must be finite");
2098 atom_biot_stress[rr] = value;
2104 for (
const double coordinate_stretch :
2106 const double stretch = std::exp(coordinate_stretch);
2107 double recovered_coordinate_stretch = 0.;
2109 stretch, recovered_coordinate_stretch);
2110 const double inverse_error =
2111 std::abs(recovered_coordinate_stretch - coordinate_stretch);
2112 const double inverse_tolerance =
2113 1e-12 * std::max(1., std::abs(coordinate_stretch));
2114 if (!std::isfinite(inverse_error) || inverse_error > inverse_tolerance) {
2116 "HMHNeohookean stretch-coordinate inverse check failed");
2124 auto set_symm_tensor = [&](
auto &t_symm, VectorDouble &values) {
2125 auto t_values = getFTensor1FromPtr<size_symm>(&*values.data().begin());
2126 t_symm(
i,
j) = t_L(
i,
j, L) * t_values(L);
2129 auto get_symm_tensor = [&](
auto &t_symm, VectorDouble &values) {
2131 values[0] = t_symm(0, 0);
2132 values[1] = t_symm(0, 1);
2133 values[2] = t_symm(0, 2);
2134 values[3] = t_symm(1, 1);
2135 values[4] = t_symm(1, 2);
2136 values[5] = t_symm(2, 2);
2139 auto set_material = [&](StretchFromStress &stretch_from_stress) {
2140 stretch_from_stress.c10 = atom_c10;
2141 stretch_from_stress.K = atom_K;
2142 stretch_from_stress.externalPressure = atom_q;
2143 stretch_from_stress.max_iter = atom_max_iter;
2144 stretch_from_stress.tol = atom_tol;
2147 auto check_snes_solve =
2148 [&](StretchFromStress &stretch_from_stress,
const char *case_name,
2149 const int col,
const double perturbation,
const bool log_values) {
2152 SNESConvergedReason reason;
2153 const char *reason_string =
"unknown";
2154 PetscInt iterations;
2156 CHKERR SNESGetConvergedReason(stretch_from_stress.sNes, &reason);
2157 CHKERR SNESGetConvergedReasonString(stretch_from_stress.sNes,
2159 CHKERR SNESGetIterationNumber(stretch_from_stress.sNes, &iterations);
2160 CHKERR SNESGetFunctionNorm(stretch_from_stress.sNes, &fnorm);
2162 const bool exceeded_built_in_iteration_limit =
2163 !atom_biot_stress_set && iterations > 25;
2164 const bool inaccurate_built_in_solution =
2165 !atom_biot_stress_set && fnorm > 1e-10;
2166 if (reason <= 0 || !std::isfinite(fnorm) ||
2167 inaccurate_built_in_solution || exceeded_built_in_iteration_limit) {
2169 <<
"HMHNeohookean principal SNES failed: case=" << case_name
2170 <<
" col=" << col <<
" perturbation=" << perturbation
2171 <<
" iterations=" << iterations <<
" fnorm=" << fnorm
2172 <<
" reason=" <<
static_cast<int>(reason) <<
" (" << reason_string
2175 "HMHNeohookean principal stretch SNES did not converge");
2180 <<
"HMHNeohookean principal SNES solved: case=" << case_name
2181 <<
" iterations=" << iterations <<
" fnorm=" << fnorm
2182 <<
" reason=" <<
static_cast<int>(reason) <<
" (" << reason_string
2183 <<
")\nbiot_stress=" << stretch_from_stress.tBiotStress
2184 <<
"\nhencky_stretch=" << stretch_from_stress.tHenckyStreach
2185 <<
"\nbiot_stretch=" << stretch_from_stress.tBiotStreach;
2191 auto solve_for_hencky = [&](VectorDouble &stress_vec,
2192 VectorDouble &hencky_vec,
const char *case_name,
2193 const int col,
const double perturbation) {
2196 StretchFromStress stretch_from_stress;
2197 set_material(stretch_from_stress);
2198 CHKERR stretch_from_stress.setUPSnes();
2199 set_symm_tensor(stretch_from_stress.tBiotStress, stress_vec);
2200 CHKERR stretch_from_stress.calculateStretch();
2201 CHKERR check_snes_solve(stretch_from_stress, case_name, col, perturbation,
2203 get_symm_tensor(stretch_from_stress.tHenckyStreach, hencky_vec);
2208 struct StretchCase {
2210 std::array<double, size_symm> stress;
2211 std::array<double, size_symm> expected_hencky;
2212 bool check_expected;
2213 bool check_zero_tangent;
2214 double tangent_tolerance;
2217 const std::array<StretchCase, 8> built_in_stretch_cases{{
2219 {0.18, -0.035, 0.024, -0.095, 0.041, 0.13},
2224 {
"zero", {0., 0., 0., 0., 0., 0.}, {},
true,
true, 5e-5},
2226 {3.6322073565896487, 0., 0., 3.6322073565896487, 0., 3.6322073565896487},
2227 {0.1, 0., 0., 0.1, 0., 0.1},
2231 {
"rotated_repeated",
2232 {0.09259999671274205, -0.73713612813466134, -0.36856806406733089,
2233 0.092599996712742161, 0.368568064067331, -0.4602520993882544},
2234 {0.02666666666666665, -0.1066666666666666, -0.05333333333333333,
2235 0.02666666666666666, 0.05333333333333333, -0.05333333333333334},
2239 {
"rotated_near_repeated",
2240 {0.092600176677642032, -0.73713594355526935, -0.36856807329630148,
2241 0.092600112074853747, 0.36856820250187705, -0.46025239471529633},
2242 {0.02666669266666667, -0.10666664, -0.05333333466666666,
2243 0.02666668333333332, 0.05333335333333333, -0.05333337600000001},
2247 {
"rotated_distinct",
2248 {0.8407464902540408, 0.82227899319182596, 0.28074304918139947,
2249 0.75776693280844076, -0.1147839342902007, 0.66914993941471534},
2250 {0.044, 0.14, 0.048, 0.03, -0.02, 0.016},
2254 {
"tensile_distortional",
2255 {3.037800922108802, 0., 0., 1.1493777171719499, 0., 1.7405883229709418},
2256 {0.3, 0., 0., -0.12, 0., 0.02},
2260 {
"compressive_distortional",
2261 {-3.6858407112404157, 0., 0., 0.067487441652110958, 0.,
2262 -1.1528550088055771},
2263 {-0.3, 0., 0., 0.12, 0., -0.02},
2269 std::vector<StretchCase> stretch_cases;
2270 if (atom_biot_stress_set) {
2271 StretchCase command_line_case{
"command_line", {}, {},
false,
false, 5e-5};
2272 for (
int rr = 0; rr !=
size_symm; ++rr) {
2273 command_line_case.stress[rr] = atom_biot_stress[rr];
2275 stretch_cases.push_back(command_line_case);
2277 stretch_cases.assign(built_in_stretch_cases.begin(),
2278 built_in_stretch_cases.end());
2280 const bool use_reference_configuration =
2281 atom_c10 == 1.7 && atom_K == 8.5 && atom_q == 0.;
2283 auto make_vector = [](
const std::array<double, size_symm> &values) {
2284 VectorDouble vector;
2286 for (
int rr = 0; rr !=
size_symm; ++rr) {
2287 vector[rr] = values[rr];
2292 auto commutator_error = [](
auto &t_a,
auto &t_b) {
2293 double commutator_norm_squared = 0.;
2294 double a_norm_squared = 0.;
2295 double b_norm_squared = 0.;
2296 for (
int rr = 0; rr !=
SPACE_DIM; ++rr) {
2297 for (
int cc = 0; cc !=
SPACE_DIM; ++cc) {
2298 double commutator = 0.;
2299 for (
int kk = 0; kk !=
SPACE_DIM; ++kk) {
2300 commutator += t_a(rr, kk) * t_b(kk, cc) - t_b(rr, kk) * t_a(kk, cc);
2302 commutator_norm_squared += commutator * commutator;
2303 a_norm_squared += t_a(rr, cc) * t_a(rr, cc);
2304 b_norm_squared += t_b(rr, cc) * t_b(rr, cc);
2307 return std::sqrt(commutator_norm_squared) /
2308 std::max(1., std::sqrt(a_norm_squared * b_norm_squared));
2311 constexpr double eps = 1e-6;
2312 for (
const auto &stretch_case : stretch_cases) {
2313 auto stress_vec = make_vector(stretch_case.stress);
2315 StretchFromStress base_stretch_from_stress;
2316 set_material(base_stretch_from_stress);
2317 CHKERR base_stretch_from_stress.setUPSnes();
2318 set_symm_tensor(base_stretch_from_stress.tBiotStress, stress_vec);
2319 CHKERR base_stretch_from_stress.calculateStretch();
2320 CHKERR check_snes_solve(base_stretch_from_stress, stretch_case.name, -1, 0.,
2322 CHKERR base_stretch_from_stress.calculateDHenckyStreachDBiotStress();
2324 VectorDouble base_hencky_vec;
2325 get_symm_tensor(base_stretch_from_stress.tHenckyStreach, base_hencky_vec);
2327 for (
int rr = 0; rr !=
SPACE_DIM; ++rr) {
2328 for (
int cc = rr; cc !=
SPACE_DIM; ++cc) {
2329 if (!std::isfinite(base_stretch_from_stress.tBiotStress(rr, cc)) ||
2330 !std::isfinite(base_stretch_from_stress.tHenckyStreach(rr, cc)) ||
2331 !std::isfinite(base_stretch_from_stress.tBiotStreach(rr, cc))) {
2333 "HMHNeohookean spectral reconstruction is not finite");
2338 if (atom_q == 0. && stretch_case.check_expected &&
2339 (use_reference_configuration || stretch_case.check_zero_tangent)) {
2340 double max_expected_error = 0.;
2341 for (
int rr = 0; rr !=
size_symm; ++rr) {
2342 const double expected_error =
2343 std::abs(base_hencky_vec[rr] - stretch_case.expected_hencky[rr]);
2344 if (!std::isfinite(expected_error)) {
2346 "HMHNeohookean spectral reconstruction is not finite");
2348 max_expected_error = std::max(max_expected_error, expected_error);
2351 <<
"HMHNeohookean spectral reconstruction check: case="
2352 << stretch_case.name <<
" max_abs=" << max_expected_error;
2353 if (max_expected_error > 1e-9) {
2355 "HMHNeohookean spectral reconstruction check failed");
2359 const double stress_hencky_commutator =
2360 commutator_error(base_stretch_from_stress.tBiotStress,
2361 base_stretch_from_stress.tHenckyStreach);
2362 const double stress_biot_commutator =
2363 commutator_error(base_stretch_from_stress.tBiotStress,
2364 base_stretch_from_stress.tBiotStreach);
2365 const double hencky_biot_commutator =
2366 commutator_error(base_stretch_from_stress.tHenckyStreach,
2367 base_stretch_from_stress.tBiotStreach);
2368 const double max_commutator =
2369 std::max(stress_hencky_commutator,
2370 std::max(stress_biot_commutator, hencky_biot_commutator));
2371 if (!std::isfinite(max_commutator) || max_commutator > 1e-10) {
2373 "HMHNeohookean coaxial tensor check failed");
2379 t_eigen_vecs(
i,
j) = base_stretch_from_stress.tHenckyStreach(
i,
j);
2380 CHKERR computeEigenValuesSymmetric(t_eigen_vecs, t_hencky_eigen_vals);
2381 auto t_expected_biot_stretch =
2383 static_cast<double (*)(
double)
>(std::exp));
2384 for (
int rr = 0; rr !=
SPACE_DIM; ++rr) {
2385 for (
int cc = rr; cc !=
SPACE_DIM; ++cc) {
2386 const double biot_stretch_error =
2387 std::abs(base_stretch_from_stress.tBiotStreach(rr, cc) -
2388 t_expected_biot_stretch(rr, cc));
2389 if (!std::isfinite(biot_stretch_error) || biot_stretch_error > 1e-10) {
2391 "HMHNeohookean Biot stretch does not match the selected "
2396 t_eigen_vecs(
i,
j) = base_stretch_from_stress.tBiotStreach(
i,
j);
2397 CHKERR computeEigenValuesSymmetric(t_eigen_vecs, t_biot_eigen_vals);
2398 for (
int aa = 0; aa !=
SPACE_DIM; ++aa) {
2399 if (!std::isfinite(t_biot_eigen_vals(aa)) ||
2400 t_biot_eigen_vals(aa) <= 0.) {
2402 "HMHNeohookean Biot stretch is not positive definite");
2406 MatrixDouble fd_gradient;
2408 for (
int col = 0; col !=
size_symm; ++col) {
2409 auto stress_plus_vec = stress_vec;
2410 auto stress_minus_vec = stress_vec;
2411 stress_plus_vec[col] +=
eps;
2412 stress_minus_vec[col] -=
eps;
2414 VectorDouble plus_hencky_vec;
2415 VectorDouble minus_hencky_vec;
2416 CHKERR solve_for_hencky(stress_plus_vec, plus_hencky_vec,
2417 stretch_case.name, col,
eps);
2418 CHKERR solve_for_hencky(stress_minus_vec, minus_hencky_vec,
2419 stretch_case.name, col, -
eps);
2421 for (
int row = 0; row !=
size_symm; ++row) {
2422 fd_gradient(row, col) =
2423 (plus_hencky_vec[row] - minus_hencky_vec[row]) / (2. *
eps);
2427 double max_abs_error = 0.;
2428 double max_scaled_error = 0.;
2429 double max_analytical = 0.;
2430 double max_numerical = 0.;
2433 for (
int row = 0; row !=
size_symm; ++row) {
2434 for (
int col = 0; col !=
size_symm; ++col) {
2435 const double analytical =
2436 base_stretch_from_stress.tDHenckyStreachDBiotStressMat(row, col);
2437 const double numerical = fd_gradient(row, col);
2438 if (!std::isfinite(analytical) || !std::isfinite(numerical)) {
2440 "HMHNeohookean full tangent is not finite");
2442 const double abs_error = std::abs(analytical - numerical);
2443 const double scale =
2444 std::max(1., std::max(std::abs(analytical), std::abs(numerical)));
2445 const double scaled_error = abs_error /
scale;
2447 if (scaled_error > max_scaled_error) {
2448 max_scaled_error = scaled_error;
2449 max_abs_error = abs_error;
2450 max_analytical = analytical;
2451 max_numerical = numerical;
2459 <<
"HMHNeohookean full tangent finite difference check: case="
2460 << stretch_case.name <<
" max_abs=" << max_abs_error
2461 <<
" max_scaled=" << max_scaled_error
2462 <<
" analytical=" << max_analytical <<
" numerical=" << max_numerical
2463 <<
" at (" << max_row <<
"," << max_col <<
")";
2465 if (max_scaled_error > stretch_case.tangent_tolerance) {
2467 "HMHNeohookean full tangent finite difference check failed");
2470 if (atom_q == 0. && stretch_case.check_zero_tangent) {
2471 const double a = 1. / (4. * atom_c10);
2472 const double q = 1. / (9. * atom_K) -
a / 3.;
2473 MatrixDouble expected_tangent;
2475 expected_tangent.clear();
2476 for (
const int row : {0, 3, 5}) {
2477 for (
const int col : {0, 3, 5}) {
2478 expected_tangent(row, col) = q;
2480 expected_tangent(row, row) +=
a;
2482 for (
const int shear : {1, 2, 4}) {
2483 expected_tangent(shear, shear) =
a;
2486 double max_zero_tangent_error = 0.;
2487 for (
int row = 0; row !=
size_symm; ++row) {
2488 for (
int col = 0; col !=
size_symm; ++col) {
2489 max_zero_tangent_error = std::max(
2490 max_zero_tangent_error,
2491 std::abs(base_stretch_from_stress.tDHenckyStreachDBiotStressMat(
2493 expected_tangent(row, col)));
2496 if (!std::isfinite(max_zero_tangent_error) ||
2497 max_zero_tangent_error > 1e-10) {
2499 "HMHNeohookean zero-stress analytical tangent check failed");