LCOV - code coverage report
Current view: top level - src/Integrator - Mechanics.H (source / functions) Hit Total Coverage
Test: coverage_merged.info Lines: 91 134 67.9 %
Date: 2024-11-18 05:28:54 Functions: 42 133 31.6 %

          Line data    Source code
       1             : //
       2             : // This is a general purpose integrator that focuses on solving 
       3             : // elasticity/mechanics equations in the absence of other multiphysics
       4             : // simulations.
       5             : // It is enabled by :code:`alamo.program=mechanics` if used on its own, in
       6             : // which case there is no prefix.
       7             : // If it is being used by another integrator, see that integrator to determine
       8             : // the value of :code:`[prefix]` (often equal to :code:`elastic`).
       9             : //
      10             : // This integrator inherits from :ref:`Integrator::Base::Mechanics`; see 
      11             : // documentation for that integrator for additional parameters.
      12             : //
      13             : // :bdg-primary-line:`Model setup`
      14             : // There are two basic tools for setting up a mechanics problem in this
      15             : // integrator.
      16             : //
      17             : // 1. The :code:`eta` field: this is used to mix models of different types.
      18             : //    Use :code:`nmodels` to specify how many material models to use, and then
      19             : //    specify each model as :code:`model1`, :code:`model2`, etc.
      20             : //    The type of moel is specified using the :code:`alamo.program.mechanics.model`
      21             : //    input.
      22             : // 
      23             : //    Once you have done this, you must determine the spatial distribution of each
      24             : //    model. This is done by choosing an IC for the eta field with :code:`ic.type`.
      25             : //    The :code:`IC::Expression` is the most general and recommended.
      26             : //    The models are then mixed linearly, i.e.
      27             : //
      28             : //    .. math::
      29             : //
      30             : //       W_{\textrm{eff}} = \sum_{i=1}^N W_i\,\eta_i(\mathbf{x})
      31             : // 
      32             : //    See the :ref:`Eshelby` test for an example of model mixing.
      33             : // 
      34             : // 2. The :code:`psi` field: this is used specifically for cases where a "void" region
      35             : //    is desired. Its usage is similar to the :code:`eta` case, and is conceptually
      36             : //    similar in that it scales the model field to near-zero in order to mimic the
      37             : //    (lack of) mechanical behavior in an empty region.
      38             : //    It is important to use :code:`psi` here, for reasons that are discussed in detail
      39             : //    in
      40             : //    `this paper <https://doi.org/10.1007/s00466-023-02325-8>`_.
      41             : //    The initialization of :code:`psi` is similar to that for :code:`eta`.
      42             : //    
      43             : //    See the :ref:`PlateHole` and :ref:`RubberPlateHole` for canonical exmaples.
      44             : //    The :ref:`Integrator::Fracture` and :ref:`Integrator::TopOp` integrators are examples
      45             : //    of integrators that leverage the psi property.
      46             : //
      47             : // :bdg-primary-line:`Body forces` 
      48             : // currently have limited support due to the relatively low number of 
      49             : // times they are needed. See the :ref:`Integrator::Base::Mechanics` documentation for 
      50             : // detail. 
      51             : // See the :ref:`TrigTest` test for examples of current body force implementation.
      52             : //
      53             : // :bdg-primary-line:`Boundary conditions` 
      54             : // are implemented using the :ref:`BC::Operator::Elastic` classes.
      55             : // See the documentation on these classes for more detail.
      56             : // See any of the mechanics-based tests for examples of boundary condition application.
      57             : //
      58             : 
      59             : #ifndef INTEGRATOR_MECHANICS_H
      60             : #define INTEGRATOR_MECHANICS_H
      61             : #include <iostream>
      62             : #include <fstream>
      63             : #include <iomanip>
      64             : #include <numeric>
      65             : 
      66             : #include "AMReX.H"
      67             : #include "AMReX_ParallelDescriptor.H"
      68             : #include "AMReX_ParmParse.H"
      69             : 
      70             : #include "IO/ParmParse.H"
      71             : #include "Integrator/Base/Mechanics.H"
      72             : 
      73             : 
      74             : #include "IC/IC.H"
      75             : #include "BC/BC.H"
      76             : #include "BC/Operator/Elastic/Constant.H"
      77             : #include "BC/Operator/Elastic/TensionTest.H"
      78             : #include "BC/Operator/Elastic/Expression.H"
      79             : 
      80             : #include "IC/Ellipse.H"
      81             : #include "IC/Voronoi.H"
      82             : #include "IC/Constant.H"
      83             : #include "IC/Expression.H"
      84             : #include "IC/BMP.H"
      85             : #include "IC/PNG.H"
      86             : #include "IC/PSRead.H"
      87             : #include "IC/Constant.H"
      88             : #include "BC/Constant.H"
      89             : #include "Numeric/Stencil.H"
      90             : 
      91             : #include "Model/Solid/Solid.H"
      92             : #include "Solver/Nonlocal/Linear.H"
      93             : #include "Solver/Nonlocal/Newton.H"
      94             : 
      95             : #include "Operator/Operator.H"
      96             : 
      97             : 
      98             : namespace Integrator
      99             : {
     100             : template<class MODEL>
     101             : class Mechanics : virtual public Base::Mechanics<MODEL>
     102             : {
     103             : public:
     104             : 
     105           0 :     Mechanics() : Base::Mechanics<MODEL>() { }
     106          18 :     Mechanics(IO::ParmParse& pp) : Base::Mechanics<MODEL>()
     107             :     {
     108          18 :         Parse(*this, pp);
     109          18 :     }
     110             : 
     111             :     // Mechanics inputs. See also :ref:`Integrator::Base::Mechanics`
     112          18 :     static void Parse(Mechanics& value, IO::ParmParse& pp)
     113             :     {
     114          18 :         pp.queryclass<Base::Mechanics<MODEL>>(value);
     115             : 
     116             :         int nmodels;
     117          18 :         pp_query_default("nmodels", nmodels, 1); // Number of elastic model varieties
     118          39 :         for (int i = 0; i < nmodels; i++)
     119             :         {
     120          21 :             bool init = false;
     121          42 :             MODEL tmp_model;
     122          21 :             if (pp.contains("model"))
     123             :             {
     124           4 :                 pp_queryclass("model", tmp_model);
     125           4 :                 init = true;
     126             :             }
     127          42 :             std::string name = "model" + std::to_string(i + 1);
     128          21 :             if (pp.contains(name.c_str()))
     129             :             {
     130          17 :                 pp_queryclass(std::string(name.data()), tmp_model);
     131          17 :                 init = true;
     132             :             }
     133             : 
     134          21 :             if (!init) Util::Exception(INFO,"No way to initialize ",pp.full(name));
     135             : 
     136          21 :             value.models.push_back(tmp_model);
     137             :         }
     138          36 :         Util::Assert(INFO, TEST(value.models.size() > 0));
     139          18 :         value.RegisterNodalFab(value.eta_mf, value.models.size(), 2, "eta", true);
     140             :         // Refinement threshold for eta field
     141          18 :         pp_query_default("eta_ref_threshold", value.m_eta_ref_threshold, 0.01);
     142             :         // Refinement threshold for strain gradient
     143          18 :         pp_query_default("ref_threshold", value.m_elastic_ref_threshold, 0.01);
     144             :         // Explicity impose neumann condition on model at domain boundaries (2d only)
     145          18 :         pp_query_default("model_neumann_boundary",value.model_neumann_boundary,false);
     146             : 
     147             :         // Read in IC for eta
     148          18 :         if (nmodels > 1 && pp.contains("ic.type"))
     149             :         {
     150           3 :             pp.select<IC::Constant,IC::Ellipse,IC::Voronoi,IC::BMP,IC::PNG,IC::Expression,IC::PSRead>("ic",value.ic_eta,value.geom);
     151             : 
     152           3 :             value.eta_reset_on_regrid = true;
     153             :             // Whether to re-initialize eta when re-gridding occurs.
     154             :             // Default is false unless eta ic is set, then default is.
     155             :             // true.
     156           3 :             pp_query_default("eta.reset_on_regrid", value.eta_reset_on_regrid, true);
     157             :         }
     158             : 
     159             :         // Read in IC for psi
     160          18 :         if (pp.contains("psi.ic.type"))
     161             :         {
     162           3 :             pp.select<IC::Ellipse,IC::Constant,IC::Expression,IC::PSRead,IC::PNG>("psi.ic",value.ic_psi,value.geom);
     163             : 
     164           3 :             value.bc_psi = new BC::Nothing();
     165           3 :             value.RegisterNewFab(value.psi_mf, value.bc_psi, 1, 2, "psi", value.plot_psi);
     166           3 :             value.psi_on = true;
     167             : 
     168           3 :             value.psi_reset_on_regrid = true;
     169             :             // Whether to re-initialize psi when re-gridding occurs.
     170             :             // Default is false unless a psi ic is set, then default is
     171             :             // true.
     172           3 :             pp_query_default("psi.reset_on_regrid", value.psi_reset_on_regrid, true);
     173             :         }
     174             : 
     175             :         // Read in IC for psi
     176          18 :         if (pp.contains("trac_normal.ic.type"))
     177             :         {
     178           0 :             pp.select<IC::Ellipse,IC::Constant,IC::Expression,IC::PSRead>("trac_normal.ic",value.ic_trac_normal,value.geom);
     179             : 
     180           0 :             if (value.ic_trac_normal)
     181             :             {
     182           0 :                 value.bc_trac_normal = new BC::Nothing();
     183           0 :                 value.RegisterNewFab(value.trac_normal_mf, value.bc_trac_normal, 1, 2, "trac_normal", true);
     184             :             }
     185             :         }
     186          18 :         Util::Message(INFO);
     187             : 
     188          18 :     }
     189             : 
     190          73 :     void Initialize(int lev) override
     191             :     {
     192          73 :         Base::Mechanics<MODEL>::Initialize(lev);
     193          73 :         eta_mf[lev]->setVal(0.0);
     194          73 :         if (models.size() > 1 && ic_eta) ic_eta->Initialize(lev, eta_mf);
     195          45 :         else eta_mf[lev]->setVal(1.0);
     196             : 
     197          73 :         if (psi_on) ic_psi->Initialize(lev, psi_mf);
     198          73 :         if (ic_trac_normal) ic_trac_normal->Initialize(lev, trac_normal_mf);
     199          73 :     }
     200             : 
     201         424 :     virtual void UpdateModel(int a_step, Set::Scalar time) override
     202             :     {
     203         424 :         if (m_type == Base::Mechanics<MODEL>::Type::Disable) return;
     204             : 
     205         424 :         if (ic_trac_normal)
     206             :         {
     207           0 :             for (int lev = 0; lev <= finest_level; ++lev)
     208             :             {
     209           0 :                 ic_trac_normal->Initialize(lev, trac_normal_mf, time);
     210           0 :                 psi_mf[lev]->FillBoundary();
     211           0 :                 amrex::Box domain = this->geom[lev].Domain();
     212           0 :                 domain.convert(amrex::IntVect::TheNodeVector());
     213           0 :                 Set::Vector DX(geom[lev].CellSize());
     214             : 
     215           0 :                 for (MFIter mfi(*model_mf[lev], false); mfi.isValid(); ++mfi)
     216             :                 {
     217           0 :                     amrex::Box bx = mfi.grownnodaltilebox();
     218           0 :                     bx = bx & domain;
     219             : 
     220           0 :                     amrex::Array4<Set::Vector> const& rhs = rhs_mf[lev]->array(mfi);
     221           0 :                     amrex::Array4<const Set::Scalar> const& psi = psi_mf[lev]->array(mfi);
     222           0 :                     amrex::Array4<const Set::Scalar> const& trac_normal = trac_normal_mf[lev]->array(mfi);
     223             : 
     224           0 :                     amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
     225           0 :                         Set::Vector grad = Numeric::CellGradientOnNode(psi, i, j, k, 0, DX.data());
     226           0 :                         rhs(i,j,k) = trac_normal(i,j,k) * grad;
     227             :                     });
     228             :                 }
     229           0 :                 Util::RealFillBoundary(*rhs_mf[lev], geom[lev]);
     230             :             }
     231             :         }
     232             : 
     233         424 :         if (a_step > 0) return;
     234             : 
     235          64 :         for (int lev = 0; lev <= finest_level; ++lev)
     236             :         {
     237          46 :             eta_mf[lev]->FillBoundary();
     238             : 
     239          46 :             amrex::Box domain = this->geom[lev].Domain();
     240          46 :             domain.convert(amrex::IntVect::TheNodeVector());
     241             : 
     242          46 :             Set::Vector DX(geom[lev].CellSize());
     243             : 
     244         225 :             for (MFIter mfi(*model_mf[lev], false); mfi.isValid(); ++mfi)
     245             :             {
     246         179 :                 amrex::Box bx = mfi.grownnodaltilebox();
     247         179 :                 bx = bx & domain;
     248             : 
     249         179 :                 amrex::Array4<MODEL> const& model = model_mf[lev]->array(mfi);
     250         179 :                 amrex::Array4<const Set::Scalar> const& eta = eta_mf[lev]->array(mfi);
     251             : 
     252      105574 :                 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
     253      210790 :                     model(i, j, k) = MODEL::Zero();
     254      253150 :                     for (unsigned int n = 0; n < models.size(); n++)
     255      726235 :                         model(i, j, k) += eta(i, j, k, n) * models[n];
     256             :                 });
     257             :             }
     258             : 
     259             : 
     260          46 :             if (model_neumann_boundary)
     261             :             {
     262           0 :                 Util::AssertException(INFO,TEST(AMREX_SPACEDIM==2),"neumann boundary works in 2d only");
     263           0 :                 for (MFIter mfi(*model_mf[lev], false); mfi.isValid(); ++mfi)
     264             :                 {
     265           0 :                     amrex::Box bx = mfi.grownnodaltilebox() & domain;
     266           0 :                     amrex::Array4<MODEL> const& model = this->model_mf[lev]->array(mfi);
     267           0 :                     const Dim3 lo = amrex::lbound(domain), hi = amrex::ubound(domain);
     268             : 
     269           0 :                     amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
     270             :                     {
     271           0 :                         if      (i==lo.x && j==lo.y)
     272           0 :                             model(i,j,k) = 0.5*(model(i+1,j,k)+model(i,j+1,k));
     273           0 :                         else if (i==lo.x && j==hi.y)
     274           0 :                             model(i,j,k) = 0.5*(model(i+1,j,k)+model(i,j-1,k));
     275           0 :                         else if (i==hi.x && j==lo.y)
     276           0 :                             model(i,j,k) = 0.5*(model(i-1,j,k)+model(i,j+1,k));
     277           0 :                         else if (i==hi.x && j==hi.y)
     278           0 :                             model(i,j,k) = 0.5*(model(i-1,j,k)+model(i,j-1,k));
     279             : 
     280           0 :                         else if (i==lo.x)
     281           0 :                             model(i,j,k) = model(i+1,j,k);
     282           0 :                         else if (i==hi.x)
     283           0 :                             model(i,j,k) = model(i-1,j,k);
     284           0 :                         else if (j==lo.y)
     285           0 :                             model(i,j,k) = model(i,j+1,k);
     286           0 :                         else if (j==hi.y)
     287           0 :                             model(i,j,k) = model(i,j-1,k);
     288             :                     });
     289             :                 }
     290             :             }
     291             : 
     292          46 :             Util::RealFillBoundary(*model_mf[lev], geom[lev]);
     293             :         }
     294             :     }
     295             : 
     296         211 :     void TagCellsForRefinement(int lev, amrex::TagBoxArray& a_tags, Set::Scalar a_time, int a_ngrow) override
     297             :     {
     298         211 :         if (m_type == Base::Mechanics<MODEL>::Type::Disable) return;
     299         211 :         Base::Mechanics<MODEL>::TagCellsForRefinement(lev, a_tags, a_time, a_ngrow);
     300             : 
     301         211 :         Set::Vector DX(geom[lev].CellSize());
     302         211 :         Set::Scalar DXnorm = DX.lpNorm<2>();
     303        1192 :         for (amrex::MFIter mfi(*eta_mf[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
     304             :         {
     305         981 :             amrex::Box bx = mfi.nodaltilebox();
     306         981 :             amrex::Array4<char> const& tags = a_tags.array(mfi);
     307         981 :             amrex::Array4<Set::Scalar> const& eta = eta_mf[lev]->array(mfi);
     308      402254 :             amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
     309             :             {
     310      401273 :                 auto sten = Numeric::GetStencil(i, j, k, bx);
     311     1087610 :                 for (int n = 0; n < eta_mf[lev]->nComp(); n++)
     312             :                 {
     313      686337 :                     Set::Vector grad = Numeric::Gradient(eta, i, j, k, n, DX.data(), sten);
     314      686337 :                     if (grad.lpNorm<2>() * DXnorm > m_eta_ref_threshold)
     315      225368 :                         tags(i, j, k) = amrex::TagBox::SET;
     316             :                 }
     317             :             });
     318         981 :             if (psi_on)
     319             :             {
     320         296 :                 amrex::Array4<Set::Scalar> const& psi = psi_mf[lev]->array(mfi);
     321      114712 :                 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
     322             :                 {
     323      114416 :                     auto sten = Numeric::GetStencil(i, j, k, bx);
     324             :                     {
     325      114416 :                         Set::Vector gradpsi = Numeric::Gradient(psi, i, j, k, 0, DX.data(), sten);
     326      114416 :                         if (gradpsi.lpNorm<2>() * DXnorm > m_eta_ref_threshold)
     327       42996 :                             tags(i, j, k) = amrex::TagBox::SET;
     328             :                     }
     329             :                 });
     330             :             }
     331             :         }
     332             :     }
     333             : 
     334           4 :     void Regrid(int lev, Set::Scalar time) override
     335             :     {
     336           4 :         if (eta_reset_on_regrid && models.size() > 1 && ic_eta) ic_eta->Initialize(lev, eta_mf, time);
     337           4 :         if (psi_reset_on_regrid) ic_psi->Initialize(lev, psi_mf, time);
     338           4 :     }
     339             : 
     340             : protected:
     341             :     Set::Field<Set::Scalar> eta_mf;
     342             :     Set::Scalar m_eta_ref_threshold = NAN;
     343             :     std::vector<MODEL> models;
     344             :     IC::IC* ic_eta = nullptr;
     345             :     IC::IC* ic_psi = nullptr;
     346             :     IC::IC* ic_trac_normal = nullptr;
     347             :     BC::BC<Set::Scalar>* bc_psi = nullptr;
     348             :     BC::BC<Set::Scalar>* bc_trac_normal = nullptr;
     349             :     bool psi_reset_on_regrid = false;
     350             :     bool eta_reset_on_regrid = false;
     351             : 
     352             :     bool model_neumann_boundary = false;
     353             : 
     354             :     Set::Field<Set::Scalar> trac_normal_mf;
     355             : 
     356             :     using Base::Mechanics<MODEL>::m_type;
     357             :     using Base::Mechanics<MODEL>::finest_level;
     358             :     using Base::Mechanics<MODEL>::geom;
     359             :     using Base::Mechanics<MODEL>::model_mf;
     360             :     using Base::Mechanics<MODEL>::psi_mf;
     361             :     using Base::Mechanics<MODEL>::psi_on;
     362             :     using Base::Mechanics<MODEL>::rhs_mf;
     363             : };
     364             : 
     365             : 
     366             : 
     367             : 
     368             : 
     369             : 
     370             : 
     371             : 
     372             : 
     373             : 
     374             : } // namespace Integrator
     375             : #endif

Generated by: LCOV version 1.14