LCOV - code coverage report
Current view: top level - src/Integrator - Fracture.H (source / functions) Coverage Total Hit
Test: coverage_merged.info Lines: 0.0 % 358 0
Test Date: 2025-08-05 17:56:56 Functions: 0.0 % 18 0

            Line data    Source code
       1              : //
       2              : // This class implements second and fourth order phase field brittle fracture with near singular solver.
       3              : // This class inherits from the base class :code:`Base/Mechanics.H`.
       4              : // See `this link <https://doi.org/10.1007/s00466-023-02325-8>`_
       5              : // for more information on this implementation.
       6              : //
       7              : // The energy functional for a second order model is given by
       8              : // .. math::
       9              : //
      10              : //      \mathcal{L}= \int_\Omega \left[\left(g(c) + \eta \right)W_0^+ + W_0^-\right] dV + \int_\Omega G_c \left[ \frac{w(c)}{4\xi} + \xi |\nabla c|^2\right] dV
      11              : //
      12              : // where :math:`g(c)` and :math:`w(c)` are interpolation functions specified by the user.
      13              : // The fracture energy :math:`G_c` and crack length scale :math:`xi` are also read from input file.
      14              : // The tension compression asymmetry is accounted by splitting the strain energy :math:`W_0` as
      15              : // .. math::
      16              : //
      17              : // W_0^\pm = \frac{1}{2} \lambda (\operatorname{tr}\bm{\varepsilon}_\pm)^2 + \mu \operatorname{tr}\left(\bm{\varepsilon}_\pm^2\right), \quad \bm{\varepsilon_\pm} = \sum_{i=1}^d \left(\varepsilon_i \right)_\pm \hat{\bm{v}}_i\otimes\hat{\bm{v}}_i
      18              : //
      19              : // where :math:`\lambda` and :math:`\mu` are material models for linear elastic isotropic material, :math:`\bm{\varepsilon}_\pm` are the positive and negative components of the strain tensor :math:`\bm{\varepsilon}` computed through eigenvalue decomposition.
      20              : // The fourth order model adds a laplacian term to the free energy functional.
      21              : // The code performs a staggered solve where it solves the elastic problem implicitly and crack problem explicitly.
      22              : //
      23              : // Class methods:
      24              : //
      25              : // #. :code:`Fracture()`:
      26              : //    Basic constructor. Does nothing, and leaves all values initiated as NAN.
      27              : // #. :code:`Fracture(IO::ParmParse &pp)`:
      28              : //    Calls the parser.
      29              : // #. :code:`static void Parse(Fracture &value, IO::ParmParse &pp)`
      30              : //    Parses input file, ICs, BCs, and sets up multifabs appropriately
      31              : // #. :code:`void Initialize(int lev) override`
      32              : //    Calls IC and sets up the initial crack geometry.
      33              : // #. :code:`virtual void UpdateModel(int a_step) override`
      34              : //    Performs degradation by updating the :math:`\psi` field using :math:`g(c)`.
      35              : // #. :code:`void TimeStepBegin(Set::Scalar a_time, int a_step) override`
      36              : //    Solves the elastic problem, performs eigen value decomposition of strain, and computes the positive part of the strain energy.
      37              : // #. :code:`void Advance(int a_lev, amrex::Real a_time, amrex::Real a_dt)`
      38              : //    Advances the crack field by computing the variational derivative of energy functional with :math:`c`.
      39              : // #. :code:`void TagCellsForRefinement(int lev, amrex::TagBoxArray &a_tags, Set::Scalar a_time, int a_ngrow) override`
      40              : //    Refines the grid based on the crack field.
      41              : // #. :code:`void Integrate(int amrlev, Set::Scalar time, int step, const amrex::MFIter &mfi, const amrex::Box &a_box) override`
      42              : //    Performs spatial integration of the driving force to check for convergence of crack problem
      43              : // #. :code:`void TimeStepComplete(Set::Scalar /*time*/, int /* iter*/)`
      44              : //    Checks whether the solver should work on crack problem or elastic problem.
      45              : //
      46              : #ifndef INTEGRATOR_FRACTURE_H
      47              : #define INTEGRATOR_FRACTURE_H
      48              : 
      49              : #include "Integrator/Base/Mechanics.H"
      50              : 
      51              : #include "Model/Interface/Crack/PFCZM.H"
      52              : #include "Model/Solid/Linear/Isotropic.H"
      53              : 
      54              : #include "BC/Constant.H"
      55              : #include "IC/BMP.H"
      56              : #include "IC/Ellipse.H"
      57              : // #include "IC/Ellipsoid.H"
      58              : #include "IC/Expression.H"
      59              : #include "IC/IC.H"
      60              : #include "IC/Laminate.H"
      61              : #include "IC/Notch.H"
      62              : #include "IC/PNG.H"
      63              : #include "IC/PerturbedInterface.H"
      64              : 
      65              : #include "Numeric/Stencil.H"
      66              : 
      67              : #include <cmath>
      68              : #include <eigen3/Eigen/Dense>
      69              : 
      70              : namespace Integrator
      71              : {
      72              : using brittle_model = Model::Solid::Linear::Isotropic;
      73              : using pfczm_crack_type = Model::Interface::Crack::PFCZM;
      74              : 
      75              : class Fracture : virtual public Base::Mechanics<brittle_model>
      76              : {
      77              : public:
      78              :     static constexpr const char *name = "fracture";
      79              :     Fracture() : Base::Mechanics<brittle_model>() {}
      80              : 
      81            0 :     Fracture(IO::ParmParse &pp) : Base::Mechanics<brittle_model>()
      82              :     {
      83            0 :         Parse(*this, pp);
      84            0 :     }
      85              : 
      86              :     static void
      87            0 :     Parse(Fracture &value, IO::ParmParse &pp)
      88              :     {
      89            0 :         Base::Mechanics<brittle_model>::Parse(value, pp);
      90              : 
      91              :         // Material field related parsing
      92            0 :         pp.query("material.refinement_threshold", value.material.m_eta_ref_threshold);
      93              : 
      94              :         // Driving force threshold
      95            0 :         pp.query_default("driving_force_refinement_threshold", value.crack.driving_force_refinement_threshold, 1E100);
      96              : 
      97              :         // Let's figure out if we are doing multi-material simulations.
      98            0 :         std::string mat_ic_type;
      99            0 :         if (pp.contains("material.ic.type"))
     100              :         {
     101              :             // IC determining material distribution
     102            0 :             pp.select<IC::Laminate, IC::Ellipse, IC::PerturbedInterface, IC::BMP, IC::PNG, IC::Expression>("material.ic", value.material.ic, value.geom);
     103            0 :             value.material.is_ic = true;
     104              :         }
     105              :         else
     106              :         {
     107            0 :             value.material.is_ic = false;
     108              :         }
     109              : 
     110              :         // Let's query material properties for different materials in the system
     111            0 :         pp.queryclass_enumerate<brittle_model>("material.model", value.material.models);
     112            0 :         value.material.num_mat = value.material.models.size();
     113            0 :         Util::Assert(INFO, TEST(value.material.models.size() > 0));
     114              : 
     115              :         // This is the fab that stores the material field.
     116              :         // For a simple, single material simulation, this will be a uniform field with value 1 everywhere
     117            0 :         value.RegisterNodalFab(value.material.eta_mf, value.material.num_mat, 2, "eta", true);
     118              : 
     119              :         // Crack related parsing
     120            0 :         pp.query_default("crack.refinement_threshold", value.crack.refinement_threshold, 0.0001); // mesh refinement criteria
     121            0 :         pp.query_default("crack.df.beta", value.crack.beta, 0.0);                                 // constant multiplier for fourth orther model with bilaplacian
     122            0 :         pp.query_default("crack.df.tol_rel", value.crack.tol_rel, 1E-3);                          // relative tolerance for convergence of driving force
     123            0 :         pp.query_default("crack.df.tol_abs", value.crack.tol_abs, 1E-3);                          // absolute tolerance for convergence of driving force
     124            0 :         pp.query_default("crack.df.max_iter", value.crack.max_iter, 500.);                        // Maximum number of solver iterations
     125            0 :         pp.query_default("crack.df.mult_Gc", value.crack.mult_Gc, 1.0);                           // constant multiplier for controlling fracture energy of interface
     126            0 :         pp.query_default("crack.df.mult_lap", value.crack.mult_lap, 1.0);                         // constant multiplier for second order model with laplacian
     127            0 :         pp.query_default("crack.df.el_mult", value.crack.el_mult, 1.0);                           // unit conversion multiplier between elastic problem and crack problem
     128              : 
     129              :         // Let's figure out if there is an initial crack or void
     130            0 :         pp.select_enumerate<IC::Constant,IC::Notch, IC::Ellipse, IC::Expression>("crack.ic", value.crack.ic, value.geom);
     131              : 
     132            0 :         value.RegisterNodalFab(value.crack.c_mf, 1, 2, "crack", true);
     133            0 :         value.RegisterNodalFab(value.crack.c_old_mf, 1, 2, "crack_old", true);
     134            0 :         value.RegisterNodalFab(value.crack.driving_force_mf, 5, 2, "driving_force", true);
     135            0 :         value.RegisterNodalFab(value.crack.energy_pristine_mf, 3, 2, "energy_pristine", true);
     136            0 :         value.RegisterNodalFab(value.crack.history_var_mf, 3, 2, "history_variable", true);
     137            0 :         value.RegisterIntegratedVariable(&(value.crack.driving_force_norm), "driving_force_norm");
     138            0 :         value.RegisterIntegratedVariable(&(value.crack.crack_l2_err), "crack_l2_err");
     139            0 :         value.RegisterIntegratedVariable(&(value.crack.crack_norm), "crack_norm");
     140              : 
     141              :         // By default the psi variable will be on for fracture simulations
     142            0 :         value.psi_on = true;
     143            0 :         value.bc_psi = new BC::Constant(1, pp, "crack.bc"); // boundary conditions for crack
     144            0 :         value.RegisterNewFab(value.psi_mf, value.bc_psi, 1, 2, "psi", true);
     145              : 
     146              :         // This is needed for the specific crack model we are implementing
     147            0 :         pp.queryclass_enumerate<pfczm_crack_type>("crack.model",value.crack.cracktype);
     148            0 :         Util::Assert(INFO, TEST(value.material.models.size() > 0));
     149            0 :     }
     150              : 
     151              :     void
     152            0 :     Initialize(int lev) override
     153              :     {
     154            0 :         Base::Mechanics<brittle_model>::Initialize(lev);
     155              : 
     156            0 :         crack.c_mf[lev]->setVal(1.0);
     157            0 :         crack.c_old_mf[lev]->setVal(1.0);
     158              : 
     159              :         // For now, we are setting psi field to 1. This prevents nans.
     160              :         // We will modify the psi field later.
     161            0 :         psi_mf[lev]->setVal(1.0);
     162              : 
     163            0 :         if (crack.is_ic)
     164              :         {
     165            0 :             for (unsigned int i = 0; i < crack.ic.size(); i++)
     166              :             {
     167              :                 // This initializes the crack field.
     168            0 :                 crack.ic[i]->Add(lev, crack.c_mf);
     169            0 :                 crack.ic[i]->Add(lev, crack.c_old_mf);
     170              :             }
     171              :         }
     172              : 
     173              :         // This initializes the material field
     174            0 :         material.eta_mf[lev]->setVal(1.0);
     175            0 :         if (material.is_ic)
     176            0 :             material.ic->Initialize(lev, material.eta_mf);
     177              :         else
     178            0 :             material.eta_mf[lev]->setVal(1.0);
     179              : 
     180              :         // set history variable to zero
     181            0 :         crack.energy_pristine_mf[lev]->setVal(0.0);
     182            0 :         crack.history_var_mf[lev]->setVal(0.0);
     183            0 :     }
     184              : 
     185              :     virtual void
     186            0 :     UpdateModel(int a_step, Set::Scalar /*a_time*/) override
     187              :     {
     188            0 :         if (m_type == Base::Mechanics<brittle_model>::Type::Disable)
     189            0 :             return;
     190              : 
     191              :         // This sets the model field for the very first time step
     192              :         // We should not be resetting model field everytime.
     193            0 :         if (a_step == 0)
     194              :         {
     195            0 :             for (int lev = 0; lev <= finest_level; ++lev)
     196              :             {
     197            0 :                 material.eta_mf[lev]->FillBoundary();
     198              : 
     199            0 :                 for (MFIter mfi(*model_mf[lev], false); mfi.isValid(); ++mfi)
     200              :                 {
     201            0 :                     amrex::Box bx = mfi.grownnodaltilebox();
     202            0 :                     amrex::Array4<brittle_model> const &model = model_mf[lev]->array(mfi);
     203            0 :                     amrex::Array4<const Set::Scalar> const &eta = material.eta_mf[lev]->array(mfi);
     204            0 :                     amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
     205            0 :                         model(i, j, k) = brittle_model::Zero();
     206            0 :                         for (int n = 0; n < material.num_mat; n++)
     207            0 :                             model(i, j, k) += eta(i, j, k, n) * material.models[n];
     208            0 :                     });
     209            0 :                 }
     210            0 :                 Util::RealFillBoundary(*model_mf[lev], geom[lev]);
     211              :             }
     212              :         }
     213              : 
     214              :         // This is where we perform "degradation"
     215              :         // Essentially we will set the psi field based on the c^2 value.
     216            0 :         for (int lev = 0; lev <= finest_level; ++lev)
     217              :         {
     218            0 :             crack.c_mf[lev]->FillBoundary();
     219            0 :             for (MFIter mfi(*psi_mf[lev], false); mfi.isValid(); ++mfi)
     220              :             {
     221            0 :                 amrex::Box bx = mfi.growntilebox();
     222            0 :                 amrex::Array4<Set::Scalar> const &psi = psi_mf[lev]->array(mfi);
     223            0 :                 amrex::Array4<const Set::Scalar> const &c = crack.c_mf[lev]->array(mfi);
     224            0 :                 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
     225              :                     // psi(i,j,k,0) = crack.cracktype[0].g_phi(c(i,j,k,0));
     226            0 :                     psi(i, j, k, 0) = crack.cracktype[0].g_phi(Numeric::Interpolate::NodeToCellAverage(c, i, j, k, 0));
     227            0 :                 });
     228            0 :             }
     229            0 :             psi_mf[lev]->FillBoundary();
     230              :         }
     231              :     }
     232              : 
     233              :     void
     234            0 :     TimeStepBegin(Set::Scalar /*a_time*/, int a_step) override
     235              :     {
     236              :         // Deciding whether to do an elastic solve or not
     237            0 :         Util::Message(INFO, crack.driving_force_norm, " ", crack.driving_force_reference, " ", crack.driving_force_reference_prev, " ", crack.tol_rel);
     238            0 :         if ((crack.driving_force_norm / crack.driving_force_reference < crack.tol_rel) || crack.crack_prop_iter > crack.max_iter)
     239              :         {
     240            0 :             crack.crack_prop_iter = 0;
     241            0 :             elastic_do_solve_now = true;
     242              :         }
     243            0 :         if ((crack.driving_force_norm < crack.tol_abs) || crack.crack_prop_iter > crack.max_iter)
     244              :         {
     245            0 :             elastic_do_solve_now = true;
     246            0 :             crack.crack_prop_iter = 0;
     247              :         }
     248            0 :         if (increase_load_step)
     249              :         {
     250            0 :             Util::Message(INFO, "Load step = ", loadstep + 1);
     251            0 :             elastic_do_solve_now = true;
     252            0 :             crack.crack_prop_iter = 0;
     253            0 :             increase_load_step = false;
     254            0 :             set_new_reference = true;
     255              :             // if (loadstep == 0) set_initial_df_once = true;
     256            0 :             loadstep++;
     257              :         }
     258              : 
     259              :         // This means that the crack field has not converged.
     260            0 :         if (!elastic_do_solve_now)
     261              :         {
     262            0 :             crack.crack_prop_iter++;
     263            0 :             return;
     264              :         }
     265              : 
     266              :         // Doing an elastic solve
     267            0 :         Base::Mechanics<brittle_model>::TimeStepBegin((double)loadstep, a_step);
     268              : 
     269              :         // Computing pristine energy based on eigen value decomposition
     270            0 :         for (int lev = 0; lev <= disp_mf.finest_level; lev++)
     271              :         {
     272            0 :             amrex::Box domain = geom[lev].Domain();
     273            0 :             domain.convert(amrex::IntVect::TheNodeVector());
     274            0 :             Set::Vector DX(geom[lev].CellSize());
     275              : 
     276            0 :             for (MFIter mfi(*disp_mf[lev], false); mfi.isValid(); ++mfi)
     277              :             {
     278            0 :                 amrex::Box bx = mfi.grownnodaltilebox(); // & domain;
     279            0 :                 amrex::Array4<Set::Scalar> const &eta = material.eta_mf[lev]->array(mfi);
     280            0 :                 amrex::Array4<Set::Scalar> const &c = crack.c_mf[lev]->array(mfi);
     281            0 :                 amrex::Array4<Set::Matrix> const &stress = stress_mf[lev]->array(mfi);
     282            0 :                 amrex::Array4<Set::Matrix> const &strain = strain_mf[lev]->array(mfi);
     283            0 :                 amrex::Array4<Set::Scalar> const &energy = crack.energy_pristine_mf[lev]->array(mfi);
     284            0 :                 amrex::Array4<Set::Scalar> const &history_var = crack.history_var_mf[lev]->array(mfi);
     285              : 
     286            0 :                 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
     287              :                     //==========================================================
     288              :                     // The formulation below uses decomposition of stress
     289              :                     // Set::Matrix sig = stress(i,j,k);
     290              :                     // Eigen::SelfAdjointEigenSolver<Set::Matrix> eigensolver(sig);
     291              :                     // Set::Vector eValues = eigensolver.eigenvalues();
     292              :                     // Set::Matrix eVectors = eigensolver.eigenvectors();
     293              : 
     294              :                     // Set::Matrix sig_p = Set::Matrix::Zero();
     295              :                     // Set::Matrix sig_n = Set::Matrix::Zero();
     296              : 
     297              :                     // for (int n = 0; n < AMREX_SPACEDIM; n++)
     298              :                     // {
     299              :                     //     if (eValues(n) > 0.0) sig_p += eValues(n)*(eVectors.col(n)*eVectors.col(n).transpose());
     300              :                     //     else sig_n += eValues(n)*(eVectors.col(n)*eVectors.col(n).transpose());
     301              :                     // }
     302              : 
     303              :                     // Set::Matrix sig_n_dev = sig_n - (1.0/3.0)*sig_n.trace()*Set::Matrix::Identity();
     304              : 
     305              :                     // for (int n = 0; n < material.num_mat; n++)
     306              :                     // {
     307              :                     //     // computing energy based on tensile stress
     308              :                     //     energy(i,j,k,0) += eta(i,j,k,n)*material.models[n].W2(sig_p);
     309              : 
     310              :                     //     // compute energy from deviatoric portion
     311              :                     //     energy(i,j,k,1) += eta(i,j,k,n) * material.models[n].W2(sig_n_dev);
     312              :                     // }
     313              : 
     314              :                     // // Only update energy if it is increasing. (H^+ according to Miehe)
     315              :                     // if (energy(i,j,k,0) < energy_old(i,j,k,0)) energy(i,j,k,0) = energy_old(i,j,k,0);
     316              :                     // if (energy(i,j,k,1) < energy_old(i,j,k,1)) energy(i,j,k,1) = energy_old(i,j,k,1);
     317              : 
     318              :                     //==========================================================
     319              :                     // The formulation below uses Ambati's decomposition of strain
     320              :                     // Perform eigenvalue decomposition of strain
     321            0 :                     Set::Matrix eps = strain(i, j, k);
     322            0 :                     Set::Matrix sig = stress(i, j, k);
     323            0 :                     energy(i, j, k, 0) = 0.0;
     324            0 :                     energy(i, j, k, 1) = 0.0;
     325            0 :                     energy(i, j, k, 2) = 0.0;
     326              : 
     327            0 :                     if (!crack.cracktype[0].mixed_mode())
     328              :                     {
     329              :                         // perform eigenvalue decomposition of strain tensor.
     330            0 :                         Eigen::SelfAdjointEigenSolver<Set::Matrix> eigensolver(eps);
     331            0 :                         Set::Vector eValues = eigensolver.eigenvalues();
     332            0 :                         Set::Matrix eVectors = eigensolver.eigenvectors();
     333              : 
     334              :                         // Reconstruct positive and negative counterparts of strain
     335            0 :                         Set::Matrix eps_p = Set::Matrix::Zero();
     336            0 :                         Set::Matrix eps_n = Set::Matrix::Zero();
     337              : 
     338            0 :                         for (int n = 0; n < AMREX_SPACEDIM; n++)
     339              :                         {
     340            0 :                             if (eValues(n) > 0.0)
     341            0 :                                 eps_p += eValues(n) * (eVectors.col(n) * eVectors.col(n).transpose());
     342              :                             else
     343            0 :                                 eps_n += eValues(n) * (eVectors.col(n) * eVectors.col(n).transpose());
     344              :                         }
     345              : 
     346            0 :                         for (int n = 0; n < material.num_mat; n++)
     347            0 :                             energy(i, j, k, 0) += eta(i, j, k, n) * material.models[n].W(eps_p);
     348              : 
     349            0 :                         if (energy(i, j, k, 0) > history_var(i, j, k, 0))
     350            0 :                             history_var(i, j, k, 0) = energy(i, j, k, 0);
     351              :                     }
     352              :                     else
     353              :                     {
     354              :                         // Here we implement the 2023 model by Wang et al (DOI: 10.1016/j.apm.2022.12.006)
     355            0 :                         Set::Vector crack_grad = Numeric::Gradient(c, i, j, k, 0, DX.data());
     356            0 :                         if (crack_grad.lpNorm<2>() > 1.e-4)
     357              :                         {
     358              : 
     359              :                             // perform eigenvalue decomposition of stress tensor.
     360            0 :                             Eigen::SelfAdjointEigenSolver<Set::Matrix> eigensolver(sig);
     361            0 :                             Set::Vector eValues = eigensolver.eigenvalues();
     362            0 :                             Set::Matrix eVectors = eigensolver.eigenvectors();
     363              : 
     364              :                             // Let's order the evalues
     365            0 :                             Set::Scalar sig1 = 0., sig2 = 0.;
     366            0 :                             Set::Vector vec1 = Set::Vector::Zero(), vec2 = Set::Vector::Zero();
     367            0 :                             if (eValues(0) > eValues(1))
     368              :                             {
     369            0 :                                 sig1 = eValues(0);
     370            0 :                                 sig2 = eValues(1);
     371            0 :                                 vec1 = eVectors.col(0);
     372            0 :                                 vec2 = eVectors.col(1);
     373              :                             }
     374              :                             else
     375              :                             {
     376            0 :                                 sig1 = eValues(1);
     377            0 :                                 sig2 = eValues(0);
     378            0 :                                 vec1 = eVectors.col(1);
     379            0 :                                 vec2 = eVectors.col(0);
     380              :                             }
     381              : 
     382            0 :                             Set::Scalar irwing_length = crack.cracktype[0].l_w();
     383            0 :                             Set::Scalar c_alpha = crack.cracktype[0].c_alpha();
     384              : 
     385            0 :                             Set::Scalar mohr_center = 0.5 * (sig1 + sig2);
     386            0 :                             Set::Scalar mohr_radius = 0.5 * std::abs(sig1 - sig2);
     387              : 
     388            0 :                             Set::Scalar f_theta = 0.0;
     389              : 
     390            0 :                             if (crack.cracktype[0].failure_surface() == Model::Interface::Crack::PFCZM::FSType::WANG2023)
     391              :                             {
     392              :                                 // Storing useful quantities for later.
     393            0 :                                 Set::Scalar sig_t = crack.cracktype[0].sig_t();
     394            0 :                                 Set::Scalar tau_f = crack.cracktype[0].tau_s();
     395            0 :                                 Set::Scalar chi = crack.cracktype[0].chi();
     396            0 :                                 Set::Scalar beta_bar = crack.cracktype[0].beta_bar();
     397              : 
     398            0 :                                 Set::Scalar beta = 1.0;
     399              :                             
     400              :                                 // Now let's compute a candidate theta
     401              :                                 // Ideally, theta = 0 is always a valid solution and corresponds to sig_nn = sig1, tau_nm = 0
     402              :                                 // Set::Scalar theta = 0.0; // removed because: set but not used
     403            0 :                                 beta = sig1 < 0 ? beta_bar : 1.0;
     404            0 :                                 f_theta = (beta * (crack.el_mult * crack.el_mult * sig1 * sig1 / (sig_t * sig_t))) - 1;
     405              :                                 
     406              :                                 // Now we check for other feasible solutions. First we check for beta = 1.
     407            0 :                                 Set::Scalar sig_nn1 = 0., tau_nm1 = 0.;
     408            0 :                                 Set::Scalar theta_candidate1 = 0.0, f_candidate1 = 0.;
     409            0 :                                 Set::Scalar temp = std::abs((mohr_center / mohr_radius) * (chi * chi / (1.0 - (chi * chi))));
     410            0 :                                 if (temp >= 0.0 && temp <= 1.0)
     411              :                                 {
     412            0 :                                     theta_candidate1 = 0.5 * std::acos(temp);
     413              : 
     414              :                                     // Now check if this actually leas to a positive sig_nn value
     415            0 :                                     sig_nn1 = mohr_center + (mohr_radius * std::cos(2.0 * theta_candidate1));
     416            0 :                                     tau_nm1 = mohr_radius * std::sin(2.0 * theta_candidate1);
     417              : 
     418            0 :                                     if (sig_nn1 >= 0.0)
     419              :                                     {
     420            0 :                                         f_candidate1 = (crack.el_mult * crack.el_mult * sig_nn1 * sig_nn1 / (sig_t * sig_t)) + (crack.el_mult * crack.el_mult * tau_nm1 * tau_nm1 / (tau_f * tau_f)) - 1.0;
     421              : 
     422              :                                         // We only use this solution if the value of the failure surface is larger.
     423            0 :                                         if (f_candidate1 > f_theta)
     424              :                                         {
     425            0 :                                             f_theta = f_candidate1;
     426            0 :                                             beta = 1.0;
     427              :                                         }
     428              :                                     }
     429              :                                 }
     430              : 
     431              :                                 // Now we check for the third feasible solution for beta = beta_bar.
     432            0 :                                 Set::Scalar sig_nn2 = 0., tau_nm2 = 0.;
     433            0 :                                 Set::Scalar theta_candidate2 = 0.0, f_candidate2 = 0.;
     434            0 :                                 temp = std::abs((mohr_center / mohr_radius) * (beta_bar * chi * chi / (1.0 - (beta_bar * chi * chi))));
     435            0 :                                 if (temp >= 0.0 && temp <= 1.0)
     436              :                                 {
     437            0 :                                     theta_candidate2 = 0.5 * std::acos(temp);
     438              : 
     439              :                                     // Now check if this actually leas to a negative sig_nn value
     440            0 :                                     sig_nn2 = mohr_center + (mohr_radius * std::cos(2.0 * theta_candidate2));
     441            0 :                                     tau_nm2 = mohr_radius * std::sin(2.0 * theta_candidate2);
     442              : 
     443            0 :                                     if (sig_nn2 <= 0.0)
     444              :                                     {
     445            0 :                                         f_candidate2 = (beta_bar * crack.el_mult * crack.el_mult * sig_nn2 * sig_nn2 / (sig_t * sig_t)) + (crack.el_mult * crack.el_mult * tau_nm2 * tau_nm2 / (tau_f * tau_f)) - 1.0;
     446              : 
     447              :                                         // We only use this solution if the value of the failure surface is larger.
     448            0 :                                         if (f_candidate2 > f_theta)
     449              :                                         {
     450            0 :                                             f_theta = f_candidate2;
     451            0 :                                             beta = beta_bar;
     452              :                                         }
     453              :                                     }
     454              :                                 }
     455              :                             }
     456            0 :                             else if (crack.cracktype[0].failure_surface() == Model::Interface::Crack::PFCZM::FSType::MC)
     457              :                             {
     458            0 :                                 Set::Scalar cohesion = crack.cracktype[0].cohesion();
     459            0 :                                 Set::Scalar friction = crack.cracktype[0].friction();
     460              :                                 // In this case, the angle theta is straightforward.
     461              :                                 // theta = (pi / 4) - (\phi / 2)
     462            0 :                                 Set::Scalar sig_nn = 0., tau_nm = 0.;
     463            0 :                                 Set::Scalar theta = std::atan(1) - std::atan(friction);
     464              : 
     465            0 :                                 sig_nn = crack.el_mult * (mohr_center + (mohr_radius * std::cos(2.0 * theta)));
     466            0 :                                 tau_nm = crack.el_mult * (mohr_radius * std::sin(2.0 * theta));
     467              : 
     468            0 :                                 f_theta = (tau_nm + (friction * sig_nn)) * (tau_nm + (friction * sig_nn)) / (cohesion * cohesion);
     469            0 :                                 f_theta = f_theta - 1.0;
     470              : 
     471              :                                 // Util::Message(INFO, "sig1 = ", sig1, ", sig2 = ", sig2, ", f_theta = ", f_theta);
     472              :                             }
     473              : 
     474            0 :                             energy(i,j,k,0) = (f_theta > 0) ? (c_alpha / (2.0 * irwing_length)) * ((f_theta)) : 0.0;
     475            0 :                             energy(i,j,k,1) = 0.0;
     476              : 
     477            0 :                             if (energy(i, j, k, 0) + energy(i, j, k, 1) > history_var(i, j, k, 0) + history_var(i, j, k, 1))
     478              :                             {
     479            0 :                                 history_var(i, j, k, 0) = energy(i, j, k, 0);
     480            0 :                                 history_var(i, j, k, 1) = energy(i, j, k, 1);
     481              :                             }
     482              :                         }
     483              :                     }
     484              :                     //=====================================================================
     485            0 :                 });
     486            0 :             }
     487            0 :             Util::RealFillBoundary(*crack.energy_pristine_mf[lev], geom[lev]);
     488            0 :             Util::RealFillBoundary(*crack.history_var_mf[lev], geom[lev]);
     489              :         }
     490              : 
     491            0 :         integrate_variables_before_advance = false;
     492            0 :         integrate_variables_after_advance = true;
     493              :     }
     494              : 
     495              :     void
     496            0 :     Advance(int a_lev, amrex::Real a_time, amrex::Real a_dt) override
     497              :     {
     498              :         // advance for crack field
     499              :         // Util::RealFillBoundary(*crack.c_old_mf[a_lev],geom[a_lev]);
     500            0 :         crack.c_mf[a_lev]->FillBoundary();
     501              :         // return;
     502            0 :         std::swap(crack.c_old_mf[a_lev], crack.c_mf[a_lev]);
     503              : 
     504            0 :         const Set::Scalar *DX = geom[a_lev].CellSize();
     505            0 :         amrex::Box domain(geom[a_lev].Domain());
     506            0 :         domain.convert(amrex::IntVect::TheNodeVector());
     507            0 :         const amrex::Dim3 lo = amrex::lbound(domain), hi = amrex::ubound(domain);
     508              : 
     509              :         // Trying out the predictor corrector approach.
     510              :         //======================================================================
     511              :         // Predictor step
     512              :         //======================================================================
     513            0 :         for (amrex::MFIter mfi(*crack.c_mf[a_lev], true); mfi.isValid(); ++mfi)
     514              :         {
     515            0 :             amrex::Box bx = mfi.tilebox();
     516              :             // bx.grow(1);
     517              :             // bx = bx & domain;
     518              : 
     519            0 :             amrex::Array4<const Set::Scalar> const &c_old = crack.c_old_mf[a_lev]->array(mfi);
     520            0 :             amrex::Array4<const Set::Scalar> const &energy = crack.history_var_mf[a_lev]->array(mfi);
     521            0 :             amrex::Array4<const Set::Scalar> const &eta = material.eta_mf[a_lev]->array(mfi);
     522              : 
     523            0 :             amrex::Array4<Set::Scalar> const &c = crack.c_mf[a_lev]->array(mfi);
     524            0 :             amrex::Array4<Set::Scalar> const &df = crack.driving_force_mf[a_lev]->array(mfi);
     525              : 
     526            0 :             amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
     527              : #if AMREX_SPACEDIM != 2
     528              :                 Util::Abort(INFO, "This does not work for 1D or 3D yet.");
     529              : #endif
     530              : 
     531            0 :                 if (i == lo.x && j == lo.y)
     532            0 :                     c(i, j, k, 0) = c(i + 1, j + 1, k, 0);
     533            0 :                 else if (i == lo.x && j == hi.y)
     534            0 :                     c(i, j, k, 0) = c(i + 1, j - 1, k, 0);
     535            0 :                 else if (i == hi.x && j == lo.y)
     536            0 :                     c(i, j, k, 0) = c(i - 1, j + 1, k, 0);
     537            0 :                 else if (i == hi.x && j == hi.y)
     538            0 :                     c(i, j, k, 0) = c(i - 1, j - 1, k, 0);
     539            0 :                 else if (i == lo.x)
     540            0 :                     c(i, j, k) = c(i + 1, j, k, 0);
     541            0 :                 else if (j == lo.y)
     542            0 :                     c(i, j, k) = c(i, j + 1, k, 0);
     543            0 :                 else if (i == hi.x)
     544            0 :                     c(i, j, k) = c(i - 1, j, k, 0);
     545            0 :                 else if (j == hi.y)
     546            0 :                     c(i, j, k) = c(i, j - 1, k, 0);
     547              : 
     548              :                 // Next, we are doing crack evolution
     549              :                 else
     550              :                 {
     551            0 :                     Set::Scalar rhs = 0.0;
     552            0 :                     Set::Scalar bilap = 0.0;
     553            0 :                     if (crack.beta > 0.0)
     554              :                     {
     555            0 :                         bilap = Numeric::Stencil<Set::Scalar, 4, 0, 0>::D(c_old, i, j, k, 0, DX) + Numeric::Stencil<Set::Scalar, 2, 2, 0>::D(c_old, i, j, k, 0, DX) * 2.0 + Numeric::Stencil<Set::Scalar, 0, 4, 0>::D(c_old, i, j, k, 0, DX);
     556              :                     }
     557              : 
     558              :                     // if (std::isnan(bilap))  Util::Message(INFO, "Bilaplacian is nan at (",i,", ",j,")");
     559              : 
     560            0 :                     Set::Scalar laplacian = Numeric::Laplacian(c_old, i, j, k, 0, DX);
     561            0 :                     if (std::isnan(laplacian))
     562            0 :                         Util::Message(INFO, "Laplacian is nan at (", i, ", ", j, ")");
     563              : 
     564            0 :                     Set::Scalar Gc = 0.0;
     565            0 :                     Set::Scalar Zeta = 0.0;
     566            0 :                     Set::Scalar Threshold = 0.0;
     567            0 :                     Set::Scalar Mobility = 0.0;
     568            0 :                     Set::Scalar _temp_product = 1.0, _temp_product2 = 1.0;
     569              : 
     570            0 :                     for (int m = 0; m < material.num_mat; m++)
     571              :                     {
     572            0 :                         Gc += eta(i, j, k, m) * crack.cracktype[m].Gc(c_old(i, j, k, 0));
     573            0 :                         Zeta += eta(i, j, k, m) * crack.cracktype[m].Zeta(c_old(i, j, k, 0));
     574            0 :                         Threshold += eta(i, j, k, m) * crack.cracktype[m].DrivingForceThreshold(c_old(i, j, k, 0));
     575            0 :                         Mobility += eta(i, j, k, m) * crack.cracktype[m].Mobility(c_old(i, j, k, 0));
     576            0 :                         _temp_product *= eta(i, j, k, m);
     577            0 :                         _temp_product2 *= 0.5;
     578              :                     }
     579            0 :                     if (material.num_mat > 1)
     580            0 :                         Gc *= (1.0 - _temp_product * (1. - crack.mult_Gc) / _temp_product2);
     581              : 
     582              :                     // =====================================================================
     583              :                     // Set::Scalar en_cell = energy(i, j, k, 0); // set but not used
     584            0 :                     if (!crack.cracktype[0].mixed_mode())
     585              :                     {
     586            0 :                         df(i, j, k, 0) = crack.cracktype[0].Dg_phi(c_old(i, j, k)) * (energy(i, j, k, 0) + energy(i, j, k, 1)) * crack.el_mult / Gc;
     587              :                     }
     588              :                     else
     589              :                     {
     590            0 :                         df(i, j, k, 0) = crack.cracktype[0].Dg_phi(c_old(i, j, k)) * (energy(i, j, k, 0) + energy(i, j, k, 1)); // * crack.el_mult;
     591              :                     }
     592            0 :                     rhs += df(i, j, k, 0);
     593              : 
     594            0 :                     df(i, j, k, 1) = crack.cracktype[0].Dw_phi(c_old(i, j, k, 0), 0.0) / (Zeta);
     595            0 :                     rhs += df(i, j, k, 1);
     596              : 
     597            0 :                     df(i, j, k, 2) = 2.0 * Zeta * laplacian * crack.mult_lap;
     598            0 :                     if (std::isnan(df(i, j, k, 2)))
     599            0 :                         Util::Message(INFO, "DF(2) is nan at (", i, ",", j, ")");
     600            0 :                     rhs -= df(i, j, k, 2);
     601              : 
     602            0 :                     df(i, j, k, 3) = crack.beta * (0.5 * Zeta * Zeta * Zeta) * bilap;
     603            0 :                     if (std::isnan(df(i, j, k, 3)))
     604            0 :                         Util::Message(INFO, "DF(3) is nan at (", i, ",", j, ")");
     605            0 :                     rhs += df(i, j, k, 3);
     606              : 
     607            0 :                     df(i, j, k, 4) = std::max(0., rhs - Threshold);
     608            0 :                     c(i, j, k, 0) = c_old(i, j, k, 0) - a_dt * df(i, j, k, 4) * Mobility; //*(4.*c_old(i,j,k,0) - 4.*c_old(i,j,k,0)*c_old(i,j,k,0));
     609              : 
     610            0 :                     if (c(i, j, k, 0) < 0.0)
     611            0 :                         c(i, j, k, 0) = 0.0;
     612            0 :                     if (c(i, j, k, 0) > 1.0)
     613            0 :                         c(i, j, k, 0) = 1.0;
     614              :                 }
     615            0 :             });
     616            0 :         }
     617            0 :         crack.c_mf[a_lev]->FillBoundary();
     618            0 :         crack.driving_force_mf[a_lev]->FillBoundary();
     619              : 
     620            0 :         Base::Mechanics<brittle_model>::Advance(a_lev, a_time, a_dt);
     621            0 :     }
     622              : 
     623              :     void
     624            0 :     TagCellsForRefinement(int lev, amrex::TagBoxArray &a_tags, Set::Scalar a_time, int a_ngrow) override
     625              :     {
     626            0 :         Base::Mechanics<brittle_model>::TagCellsForRefinement(lev, a_tags, a_time, a_ngrow);
     627              : 
     628            0 :         Set::Vector DX(geom[lev].CellSize());
     629            0 :         Set::Scalar DXnorm = DX.lpNorm<2>();
     630              : 
     631            0 :         for (amrex::MFIter mfi(*crack.c_mf[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
     632              :         {
     633            0 :             amrex::Box bx = mfi.tilebox();
     634            0 :             bx.convert(amrex::IntVect::TheCellVector());
     635            0 :             amrex::Array4<char> const &tags = a_tags.array(mfi);
     636            0 :             amrex::Array4<Set::Scalar> const &c = crack.c_mf[lev]->array(mfi);
     637            0 :             amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
     638            0 :                 Set::Vector grad = Numeric::NodeGradientOnCell(c, i, j, k, DX.data());
     639            0 :                 if (grad.lpNorm<2>() * DXnorm > crack.refinement_threshold)
     640            0 :                     tags(i, j, k) = amrex::TagBox::SET;
     641            0 :             });
     642            0 :         }
     643              : 
     644            0 :         for (amrex::MFIter mfi(*crack.driving_force_mf[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
     645              :         {
     646            0 :             amrex::Box bx = mfi.tilebox();
     647            0 :             bx.convert(amrex::IntVect::TheCellVector());
     648            0 :             amrex::Array4<char> const &tags = a_tags.array(mfi);
     649            0 :             amrex::Array4<Set::Scalar> const &df = crack.driving_force_mf[lev]->array(mfi);
     650            0 :             amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
     651            0 :                 Set::Vector grad = Numeric::Gradient(df, i, j, k, 0, DX.data());
     652            0 :                 if (grad.lpNorm<2>() * DXnorm > crack.driving_force_refinement_threshold)
     653            0 :                     tags(i, j, k) = amrex::TagBox::SET;
     654            0 :             });
     655            0 :         }
     656              : 
     657            0 :         for (amrex::MFIter mfi(*psi_mf[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
     658              :         {
     659            0 :             amrex::Box bx = mfi.tilebox();
     660            0 :             bx.convert(amrex::IntVect::TheCellVector());
     661            0 :             amrex::Array4<char> const &tags = a_tags.array(mfi);
     662            0 :             amrex::Array4<Set::Scalar> const &psi = psi_mf[lev]->array(mfi);
     663            0 :             amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
     664            0 :                 Set::Vector grad = Numeric::Gradient(psi, i, j, k, 0, DX.data());
     665            0 :                 if (grad.lpNorm<2>() * DXnorm > crack.refinement_threshold)
     666            0 :                     tags(i, j, k) = amrex::TagBox::SET;
     667            0 :             });
     668            0 :         }
     669              : 
     670            0 :         if (material.num_mat > 1)
     671              :         {
     672            0 :             for (amrex::MFIter mfi(*material.eta_mf[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
     673              :             {
     674            0 :                 amrex::Box bx = mfi.tilebox();
     675            0 :                 bx.convert(amrex::IntVect::TheCellVector());
     676            0 :                 amrex::Array4<char> const &tags = a_tags.array(mfi);
     677            0 :                 amrex::Array4<Set::Scalar> const &eta = material.eta_mf[lev]->array(mfi);
     678            0 :                 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
     679            0 :                     Set::Matrix grad = Numeric::Gradient(eta, i, j, k, DX.data());
     680            0 :                     if (grad.lpNorm<2>() * DXnorm > material.m_eta_ref_threshold)
     681            0 :                         tags(i, j, k) = amrex::TagBox::SET;
     682            0 :                 });
     683            0 :             }
     684              :         }
     685            0 :     }
     686              : 
     687              :     void
     688            0 :     Integrate(int amrlev, Set::Scalar time, int step, const amrex::MFIter &mfi, const amrex::Box &a_box) override
     689              :     {
     690            0 :         Set::Vector DX(geom[amrlev].CellSize());
     691            0 :         const Set::Scalar DV = AMREX_D_TERM(DX[0], *DX[1], *DX[2]);
     692            0 :         amrex::Array4<const Set::Scalar> const &df = crack.driving_force_mf[amrlev]->array(mfi);
     693              :         // amrex::Array4<const Set::Scalar> const &c = crack.c_mf[amrlev]->array(mfi); // not used
     694              :         // amrex::Array4<const Set::Scalar> const &c_old = crack.c_old_mf[amrlev]->array(mfi); // not used
     695            0 :         amrex::ParallelFor(a_box, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
     696            0 :             crack.driving_force_norm += df(i, j, k, 4) * DV;
     697            0 :         });
     698              : 
     699            0 :         Base::Mechanics<brittle_model>::Integrate(amrlev, time, step, mfi, a_box);
     700            0 :     }
     701              : 
     702              :     void
     703            0 :     TimeStepComplete(Set::Scalar /*time*/, int /* iter*/) override
     704              :     {
     705            0 :         if (elastic_do_solve_now)
     706            0 :             crack.driving_force_reference = crack.driving_force_norm;
     707              : 
     708            0 :         if (set_initial_df_once)
     709              :         {
     710            0 :             Util::Message(INFO, "First setting the reference value");
     711            0 :             crack.driving_force_reference_prev = crack.driving_force_reference;
     712            0 :             set_initial_df_once = false;
     713              :         }
     714              : 
     715            0 :         if (crack.driving_force_reference / crack.driving_force_reference_prev < 0.1)
     716              :         {
     717            0 :             increase_load_step = true;
     718              :             // crack.driving_force_reference_prev = crack.driving_force_reference;
     719              :         }
     720            0 :         if (set_new_reference)
     721              :         {
     722            0 :             crack.driving_force_reference_prev = crack.driving_force_reference;
     723            0 :             set_new_reference = false;
     724              :         }
     725              : 
     726            0 :         elastic_do_solve_now = false;
     727            0 :     }
     728              : 
     729              :     // Member variables
     730              : protected:
     731              :     struct
     732              :     {
     733              :         Set::Field<Set::Scalar> c_mf;
     734              :         Set::Field<Set::Scalar> c_old_mf;
     735              :         Set::Field<Set::Scalar> energy_pristine_mf;
     736              :         Set::Field<Set::Scalar> history_var_mf;
     737              :         Set::Field<Set::Scalar> driving_force_mf;
     738              :         std::vector<pfczm_crack_type> cracktype;
     739              : 
     740              :         std::vector<std::string> ic_type;
     741              :         std::vector<IC::IC<Set::Scalar> *> ic;
     742              :         bool is_ic = true;
     743              : 
     744              :         Set::Scalar scaleModulusMax = 0.02;
     745              :         Set::Scalar refinement_threshold = NAN;
     746              :         Set::Scalar mult_lap = NAN;
     747              :         Set::Scalar mult_Gc = NAN;
     748              :         Set::Scalar el_mult = NAN;
     749              : 
     750              :         Set::Scalar driving_force_reference = 1.0;
     751              :         Set::Scalar driving_force_reference_prev = 1.0;
     752              :         Set::Scalar driving_force_norm = 0.0;
     753              :         Set::Scalar driving_force_refinement_threshold = NAN;
     754              :         Set::Scalar tol_rel = NAN;
     755              :         Set::Scalar tol_abs = NAN;
     756              :         Set::Scalar max_iter = NAN;
     757              : 
     758              :         Set::Scalar crack_l2_err = 0.0;
     759              :         Set::Scalar crack_norm = 0.0;
     760              :         Set::Scalar crack_prop_iter = 0;
     761              : 
     762              :         Set::Scalar beta = NAN;
     763              :     } crack;
     764              :     struct
     765              :     {
     766              :         Set::Field<Set::Scalar> eta_mf;
     767              :         Set::Scalar m_eta_ref_threshold = 0.01;
     768              :         std::vector<brittle_model> models;
     769              :         IC::IC<Set::Scalar> *ic;
     770              :         bool is_ic = false;
     771              :         int num_mat = 1;
     772              :     } material;
     773              : 
     774              :     bool elastic_do_solve_now = true;
     775              :     bool increase_load_step = true;
     776              :     bool set_initial_df_once = false;
     777              :     bool set_new_reference = true;
     778              :     int loadstep = 0;
     779              : 
     780              :     BC::BC<Set::Scalar> *bc_psi = nullptr;
     781              : 
     782              :     using Base::Mechanics<brittle_model>::m_type;
     783              :     using Base::Mechanics<brittle_model>::finest_level;
     784              :     using Base::Mechanics<brittle_model>::geom;
     785              :     using Base::Mechanics<brittle_model>::model_mf;
     786              :     using Base::Mechanics<brittle_model>::psi_mf;
     787              :     using Base::Mechanics<brittle_model>::psi_on;
     788              : };
     789              : }
     790              : 
     791              : #endif
        

Generated by: LCOV version 2.0-1