LCOV - code coverage report
Current view: top level - models/phase_field_fracture - LinearIsotropicStrainEnergyDensity.cxx (source / functions) Coverage Total Hit
Test: coverage.info Lines: 97.9 % 94 92
Test Date: 2025-10-02 16:03:03 Functions: 100.0 % 6 6

            Line data    Source code
       1              : // Copyright 2024, UChicago Argonne, LLC
       2              : // All Rights Reserved
       3              : // Software Name: NEML2 -- the New Engineering material Model Library, version 2
       4              : // By: Argonne National Laboratory
       5              : // OPEN SOURCE LICENSE (MIT)
       6              : //
       7              : // Permission is hereby granted, free of charge, to any person obtaining a copy
       8              : // of this software and associated documentation files (the "Software"), to deal
       9              : // in the Software without restriction, including without limitation the rights
      10              : // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      11              : // copies of the Software, and to permit persons to whom the Software is
      12              : // furnished to do so, subject to the following conditions:
      13              : //
      14              : // The above copyright notice and this permission notice shall be included in
      15              : // all copies or substantial portions of the Software.
      16              : //
      17              : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      18              : // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      19              : // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      20              : // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      21              : // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      22              : // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      23              : // THE SOFTWARE.
      24              : 
      25              : #include "neml2/models/phase_field_fracture/LinearIsotropicStrainEnergyDensity.h"
      26              : #include "neml2/misc/errors.h"
      27              : #include "neml2/tensors/Vec.h"
      28              : #include "neml2/tensors/SR2.h"
      29              : #include "neml2/tensors/SSR4.h"
      30              : #include "neml2/tensors/Scalar.h"
      31              : #include "neml2/tensors/functions/macaulay.h"
      32              : #include "neml2/tensors/functions/heaviside.h"
      33              : #include "neml2/tensors/functions/linalg/eigh.h"
      34              : #include "neml2/tensors/functions/linalg/ieigh.h"
      35              : #include "neml2/tensors/functions/linalg/dsptrf.h"
      36              : #include "neml2/base/EnumSelection.h"
      37              : 
      38              : namespace neml2
      39              : {
      40              : register_NEML2_object(LinearIsotropicStrainEnergyDensity);
      41              : 
      42              : OptionSet
      43            2 : LinearIsotropicStrainEnergyDensity::expected_options()
      44              : {
      45            2 :   OptionSet options = ElasticityInterface<StrainEnergyDensity, 2>::expected_options();
      46            2 :   options.doc() =
      47            2 :       "Calculates elastic strain energy density based on linear elastic isotropic response";
      48            4 :   options.set<bool>("define_second_derivatives") = true;
      49              : 
      50            8 :   EnumSelection type_selection({"NONE", "SPECTRAL", "VOLDEV"}, "NONE");
      51            2 :   options.set<EnumSelection>("decomposition") = type_selection;
      52            4 :   options.set("decomposition").doc() =
      53            6 :       "Strain energy density decomposition types, options are: " + type_selection.candidates_str();
      54              : 
      55            4 :   return options;
      56            2 : }
      57              : 
      58           10 : LinearIsotropicStrainEnergyDensity::LinearIsotropicStrainEnergyDensity(const OptionSet & options)
      59              :   : ElasticityInterface<StrainEnergyDensity, 2>(options),
      60           10 :     _converter(_constant_types, _need_derivs),
      61           20 :     _decomposition(options.get<EnumSelection>("decomposition").as<DecompositionType>())
      62              : {
      63           10 : }
      64              : 
      65              : void
      66           30 : LinearIsotropicStrainEnergyDensity::set_value(bool out, bool dout_din, bool d2out_din2)
      67              : {
      68           30 :   switch (_decomposition)
      69              :   {
      70            3 :     case DecompositionType::NONE:
      71            3 :       no_decomposition(out, dout_din, d2out_din2);
      72            3 :       break;
      73           12 :     case DecompositionType::VOLDEV:
      74           12 :       voldev_decomposition(out, dout_din, d2out_din2);
      75           12 :       break;
      76           15 :     case DecompositionType::SPECTRAL:
      77           15 :       spectral_decomposition(out, dout_din, d2out_din2);
      78           15 :       break;
      79            0 :     default:
      80            0 :       throw NEMLException("LinearIsotropicStrainEnergyDensity: Unsupported decomposition type.");
      81              :   }
      82           30 : }
      83              : 
      84              : void
      85            3 : LinearIsotropicStrainEnergyDensity::no_decomposition(bool out, bool dout_din, bool d2out_din2)
      86              : {
      87            3 :   const auto [K_and_dK, G_and_dG] = _converter.convert(_constants);
      88            3 :   const auto & [K, dK] = K_and_dK;
      89            3 :   const auto & [G, dG] = G_and_dG;
      90            3 :   const auto etr = SR2(_strain).tr();
      91            3 :   const auto edev = SR2(_strain).dev();
      92            3 :   const auto I2 = SR2::identity(_strain.options());
      93            3 :   const auto I4 = SSR4::identity(_strain.options());
      94            3 :   const auto J = SSR4::identity_dev(_strain.options());
      95              : 
      96            3 :   if (out)
      97              :   {
      98            1 :     _psie_active = 0.5 * K * etr * etr + G * edev.inner(edev);
      99            1 :     _psie_inactive = Scalar::zeros_like(_psie_active);
     100              :   }
     101            3 :   if (dout_din)
     102              :   {
     103            1 :     _psie_active.d(_strain) = K * etr * I2 + 2 * G * edev;
     104              :   }
     105            3 :   if (d2out_din2)
     106              :   {
     107            1 :     _psie_active.d(_strain, _strain) = K * I4 + 2 * G * J;
     108              :   }
     109            3 : }
     110              : 
     111              : void
     112           12 : LinearIsotropicStrainEnergyDensity::voldev_decomposition(bool out, bool dout_din, bool d2out_din2)
     113              : {
     114           12 :   const auto [K_and_dK, G_and_dG] = _converter.convert(_constants);
     115           12 :   const auto & [K, dK] = K_and_dK;
     116           12 :   const auto & [G, dG] = G_and_dG;
     117           12 :   const auto etr = SR2(_strain).tr();
     118           12 :   const auto edev = SR2(_strain).dev();
     119           12 :   const auto I2 = SR2::identity(_strain.options());
     120           12 :   const auto I4 = SSR4::identity(_strain.options());
     121           12 :   const auto J = SSR4::identity_dev(_strain.options());
     122              : 
     123              :   // Decompose based on the trace of strain
     124           12 :   const auto etr_pos = macaulay(etr);
     125           12 :   const auto etr_neg = etr - etr_pos;
     126              : 
     127           12 :   if (out)
     128              :   {
     129            4 :     _psie_active = 0.5 * K * etr_pos * etr_pos + G * edev.inner(edev);
     130            4 :     _psie_inactive = 0.5 * K * etr_neg * etr_neg;
     131              :   }
     132           12 :   if (dout_din)
     133              :   {
     134            4 :     _psie_active.d(_strain) = K * etr_pos * I2 + 2 * G * edev;
     135            4 :     _psie_inactive.d(_strain) = K * etr_neg * I2;
     136              :   }
     137           12 :   if (d2out_din2)
     138              :   {
     139            4 :     _psie_active.d(_strain, _strain) = K * heaviside(etr) * I4 + 2 * G * J;
     140            4 :     _psie_inactive.d(_strain, _strain) = K * heaviside(-etr) * I4;
     141              :   }
     142           12 : }
     143              : 
     144              : void
     145           15 : LinearIsotropicStrainEnergyDensity::spectral_decomposition(bool out, bool dout_din, bool d2out_din2)
     146              : {
     147           15 :   const auto [lambda_and_dlambda, G_and_dG] = _converter.convert(_constants);
     148           15 :   const auto & [lambda, dlambda] = lambda_and_dlambda;
     149           15 :   const auto & [G, dG] = G_and_dG;
     150           15 :   const auto etr = SR2(_strain).tr();
     151           15 :   const auto edev = SR2(_strain).dev();
     152           15 :   const auto I2 = SR2::identity(_strain.options());
     153           15 :   const auto I4 = SSR4::identity(_strain.options());
     154              : 
     155              :   // Decompose based on the eigenvalues of strain
     156           15 :   const auto etr_pos = macaulay(etr);
     157           15 :   const auto etr_neg = etr - etr_pos;
     158           15 :   const auto [evals, evecs] = linalg::eigh(_strain);
     159           15 :   const auto evals_pos = macaulay(evals);
     160           15 :   const auto e_pos = linalg::ieigh(evals_pos, evecs);
     161           15 :   const auto e_neg = _strain - e_pos;
     162              : 
     163           15 :   if (out)
     164              :   {
     165            5 :     _psie_active = 0.5 * lambda * etr_pos * etr_pos + G * e_pos.inner(e_pos);
     166            5 :     _psie_inactive = 0.5 * lambda * etr_neg * etr_neg + G * e_neg.inner(e_neg);
     167              :   }
     168           15 :   if (dout_din)
     169              :   {
     170            5 :     _psie_active.d(_strain) = lambda * etr_pos * I2 + 2 * G * e_pos;
     171            5 :     _psie_inactive.d(_strain) = lambda * etr_neg * I2 + 2 * G * e_neg;
     172              :   }
     173           15 :   if (d2out_din2)
     174              :   {
     175            5 :     const auto P4_pos = linalg::dsptrf(evals, evecs, evals_pos, heaviside(evals));
     176            5 :     const auto P4_neg = SSR4::identity_sym(_strain.options()) - P4_pos;
     177            5 :     _psie_active.d(_strain, _strain) = lambda * heaviside(etr) * I4 + 2 * G * P4_pos;
     178            5 :     _psie_inactive.d(_strain, _strain) = lambda * heaviside(-etr) * I4 + 2 * G * P4_neg;
     179            5 :   }
     180           15 : }
     181              : 
     182              : } // namespace neml2
        

Generated by: LCOV version 2.0-1