LCOV - code coverage report
Current view: top level - src/Integrator - Flame.cpp (source / functions) Coverage Total Hit
Test: coverage_merged.info Lines: 77.2 % 391 302
Test Date: 2026-06-29 14:20:01 Functions: 66.7 % 30 20

            Line data    Source code
       1              : #include "Flame.H"
       2              : #include "IO/ParmParse.H"
       3              : #include "BC/Constant.H"
       4              : #include "Numeric/Stencil.H"
       5              : #include "IC/Laminate.H"
       6              : #include "IC/Constant.H"
       7              : #include "IC/PointList.H"
       8              : #include "IC/PSRead.H"
       9              : #include "Numeric/Function.H"
      10              : #include "IC/Expression.H"
      11              : #include "IC/BMP.H"
      12              : #include "IC/PNG.H"
      13              : #include "Base/Mechanics.H"
      14              : #include "Util/Util.H"
      15              : #include "Model/Propellant/Propellant.H"
      16              : #include "Model/Propellant/FullFeedback.H"
      17              : #include "Model/Propellant/Homogenize.H"
      18              : #include <cmath>
      19              : 
      20              : namespace Integrator
      21              : {
      22              : 
      23            3 : Flame::Flame() : 
      24            3 :     Base::Mechanics<model_type>() {}
      25              : 
      26            3 : Flame::Flame(IO::ParmParse& pp) : Flame()
      27              : {
      28            3 :     pp_queryclass(*this);
      29            3 : }
      30              : 
      31              : 
      32              : void
      33            3 : Flame::Forbids(IO::ParmParse& pp)
      34              : {
      35           12 :     pp.forbid("pressure.P","use chamber.pressure instead");
      36              : 
      37           12 :     pp.forbid("geometry.x_len","This is specified by geometry.prob_lo/hi");
      38           12 :     pp.forbid("geometry.y_len","This is specified by geometry.prob_lo/hi");
      39           12 :     pp.forbid("amr.ghost_cells", "This should not be adjustable ");
      40              : 
      41           12 :     pp.forbid("pf.gamma","use propellant.powerlaw.gamma"); 
      42              : 
      43           12 :     pp.forbid("pressure.r_ap",   "use propellant.powerlaw.r_ap");
      44           12 :     pp.forbid("pressure.r_htpb", "use propellant.powerlaw.r_htpb");
      45           12 :     pp.forbid("pressure.r_comb", "use propellant.powerlaw.r_comb");
      46           12 :     pp.forbid("pressure.n_ap",   "use propellant.powerlaw.n_ap");
      47           12 :     pp.forbid("pressure.n_htpb", "use propellant.powerlaw.n_htpb");
      48           12 :     pp.forbid("pressure.n_comb", "use propellant.powerlaw.n_comb");
      49              : 
      50           12 :     pp.forbid("thermal.bound",   "use thermal.Tref");
      51           12 :     pp.forbid("thermal.T_fluid",   "use thermal.Tfluid (or nothing)");
      52           12 :     pp.forbid("thermal.m_ap",   "use propellant.fullfeedback.m_ap");
      53           12 :     pp.forbid("thermal.m_htpb", "use propellant.fullfeedback.m_htpb");
      54           12 :     pp.forbid("thermal.E_ap",   "use propellant.fullfeedback.E_ap");
      55           12 :     pp.forbid("thermal.E_htpb", "use propellant.fullfeedback.E_htpb");
      56           12 :     pp.forbid("thermal.modeling_ap",   "Old debug variable. Should equal 1 "); 
      57           12 :     pp.forbid("thermal.modeling_htpb", "Old debug variable. Should equal 1"); 
      58              : 
      59           12 :     pp.forbid("pressure.a1", "use propellant.fullfeedback.a1 instead");
      60           12 :     pp.forbid("pressure.a2", "use propellant.fullfeedback.a2 instead");
      61           12 :     pp.forbid("pressure.a3", "use propellant.fullfeedback.a3 instead");
      62           12 :     pp.forbid("pressure.b1", "use propellant.fullfeedback.b1 instead");
      63           12 :     pp.forbid("pressure.b2", "use propellant.fullfeedback.b2 instead");
      64           12 :     pp.forbid("pressure.b3", "use propellant.fullfeedback.b3 instead");
      65           12 :     pp.forbid("pressure.c1", "use propellant.fullfeedback.c1 instead");
      66           12 :     pp.forbid("pressure.mob_ap", "no longer used"); 
      67           12 :     pp.forbid("pressure.dependency", "use propellant.fullfeedback.pressure_dependency"); 
      68           12 :     pp.forbid("pressure.h1", "use propellant.homogenize.h1 instead"); 
      69           12 :     pp.forbid("pressure.h2", "use propellant.homogenize.h2 instead"); 
      70           12 :     pp.forbid("thermal.mlocal_ap", "use propellant.homogenize.mlocal_ap");
      71           12 :     pp.forbid("thermal.mlocal_comb", "use propellant.homogenize.mlocal_comb");
      72           12 :     pp.forbid("thermal.mlocal_htpb", "this actually did **nothing** - it was overridden by a hard code using massfraction.");
      73              : 
      74           12 :     pp.forbid("thermal.disperssion1", "use propellant.homogenize.dispersion1");
      75           12 :     pp.forbid("thermal.disperssion2", "use propellant.homogenize.dispersion2");
      76           12 :     pp.forbid("thermal.disperssion3", "use propellant.homogenize.dispersion3"); 
      77              : 
      78           12 :     pp.forbid("thermal.rho_ap", "use propellant.fullfeedback/homogenize.rho_ap ");
      79           12 :     pp.forbid("thermal.rho_htpb","use propellant.fullfeedback/homogenize.rho_htpb ");
      80           12 :     pp.forbid("thermal.k_ap",   "use propellant.fullfeedback/homogenize.k_ap ");
      81           12 :     pp.forbid("thermal.k_htpb", "use propellant.fullfeedback/homogenize.k_htpb ");
      82           12 :     pp.forbid("thermal.cp_ap", "use propellant.fullfeedback/homogenize.cp_ap ");
      83            9 :     pp.forbid("thermal.cp_htpb","use propellant.fullfeedback/homogenize.cp_htpb "); 
      84            3 : }
      85              : 
      86              : 
      87              : // [parser]
      88              : void
      89            3 : Flame::Parse(Flame& value, IO::ParmParse& pp)
      90              : {
      91              :     BL_PROFILE("Integrator::Flame::Flame()");
      92              : 
      93            3 :     Forbids(pp);
      94              : 
      95              :     // Whether to include extra fields (such as mdot, etc) in the plot output
      96            6 :     pp.query_default("plot_field",value.plot_field,true); 
      97              :         
      98              :     //
      99              :     // PHASE FIELD VARIABLES
     100              :     //
     101              :         
     102              :     // Burn width thickness
     103           12 :     pp.query_default("pf.eps", value.pf.eps, "1.0_m", Unit::Length()); 
     104              :     // Interface energy param
     105           12 :     pp.query_default("pf.kappa", value.pf.kappa, "0.0_J/m^2", Unit::Energy() / Unit::Area()); 
     106              :     // Chemical potential multiplier
     107           12 :     pp.query_default("pf.lambda", value.pf.lambda, "0.0_J/m^2", Unit::Energy()/Unit::Area()); 
     108              :     // Unburned rest energy
     109           12 :     pp.query_default("pf.w1", value.pf.w1, "0.0",Unit::Less()); 
     110              :     // Barrier energy
     111           12 :     pp.query_default("pf.w12", value.pf.w12, "0.0", Unit::Less());  
     112              :     // Burned rest energy
     113           12 :     pp.query_default("pf.w0", value.pf.w0, "0.0",Unit::Less());    
     114              : 
     115              :     // Boundary conditions for phase field order params
     116            6 :     pp.select<BC::Constant>("pf.eta.bc", value.bc_eta, 1 ); 
     117            9 :     value.RegisterNewFab(value.eta_mf, value.bc_eta, 1, 2, "eta", true);
     118            9 :     value.RegisterNewFab(value.eta_old_mf, value.bc_eta, 1, 2, "eta_old", 0);
     119              : 
     120              :     // Inital value of eta that doesn't evolve and is used during refiment to set the updated values of eta with voids in the domain.
     121              :     // Used to fix a bug where duirn refinement, a void won't be updated correctly and would be a square, not a circle
     122            9 :     value.RegisterNewFab(value.eta_0_mf, value.bc_eta, 1, 2, "eta_0", 0);
     123              : 
     124              :     // value.RegisterNewFab(value.eta_mf_frozen, value.bc_eta, 1, 2, "eta_frozen", value.plot_field);
     125              : 
     126              :     // phase field initial condition
     127            6 :     pp.select<IC::Laminate,IC::Constant,IC::Expression,IC::BMP,IC::PNG, IC::PSRead>("pf.eta.ic",value.ic_eta,value.geom); 
     128              : 
     129              : 
     130              :     // Select reduced order model to capture heat feedback
     131              :     pp.select<  Model::Propellant::PowerLaw, 
     132              :                 Model::Propellant::FullFeedback,
     133              :                 Model::Propellant::Homogenize>
     134            6 :         ("propellant",value.propellant);
     135              : 
     136              : 
     137              :     // Whether to use the Thermal Transport Model
     138            6 :     pp_query_default("thermal.on", value.thermal.on, false); 
     139              : 
     140              :     // Reference temperature
     141              :     // Used to set all other reference temperatures by default.
     142           12 :     pp_query_default("thermal.Tref", value.thermal.Tref, "300.0_K",Unit::Temperature());
     143              : 
     144            3 :     if (value.thermal.on) {
     145              : 
     146              :         // Used to change heat flux units
     147            4 :         pp_query_default("thermal.hc", value.thermal.hc, "1.0", Unit::Power()/Unit::Area());
     148              : 
     149              :         // Effective fluid temperature, temp of the eta = 0 (fluid) region
     150            2 :         pp_query_default("thermal.Tfluid", value.thermal.Tfluid, value.thermal.Tref);
     151              : 
     152              :         // Cutoff value for regression, if T < Tcutoff eta won't evolve/regress
     153            4 :         pp.query_default("thermal.Tcutoff", value.thermal.Tcutoff, "0.0", Unit::Temperature());
     154              : 
     155              :         // Switch time of the improved regridding where eta and the temperature field are both used. It is recommended to make this time ~10x the timestep.
     156              :         // Before this the refinement is based on the gradient of eta which helps the laser IC start correctly. A regrid is forced when this time is reached.
     157            4 :         pp.query_default("thermal.end_initial_refine_time", value.thermal.end_initial_refine_time, "0.0", Unit::Time());
     158              : 
     159              :         // Inital refinement of the phi field based on phi gradient. After time > end_initial_refine_time stops refining at these phi values.
     160            2 :         pp.query_default("thermal.phi_refinement_criterion_inital", value.thermal.phi_refinement_criterion_inital, 1.0e100);
     161              : 
     162              :         //Temperature boundary condition
     163            2 :         pp.select_default<BC::Constant>("thermal.temp.bc", value.bc_temp, 1, Unit::Temperature());
     164              :             
     165            3 :         value.RegisterNewFab(value.temp_mf, value.bc_temp, 1, 3, "temp", true);
     166            3 :         value.RegisterNewFab(value.temp_old_mf, value.bc_temp, 1, 3, "temp_old", false);
     167            3 :         value.RegisterNewFab(value.temps_mf, value.bc_temp, 1, 0, "temps", false);
     168              : 
     169            3 :         value.RegisterNewFab(value.mdot_mf, value.bc_temp, 1, 0, "mdot", value.plot_field);
     170            3 :         value.RegisterNewFab(value.alpha_mf, value.bc_temp, 1, 0, "alpha", value.plot_field);
     171            3 :         value.RegisterNewFab(value.heatflux_mf, value.bc_temp, 1, 0, "heatflux", value.plot_field);
     172            3 :         value.RegisterNewFab(value.laser_mf, value.bc_temp, 1, 0, "laser", value.plot_field);
     173              : 
     174            2 :         value.RegisterIntegratedVariable(&value.chamber.volume, "volume");
     175            2 :         value.RegisterIntegratedVariable(&value.chamber.area, "area");
     176            2 :         value.RegisterIntegratedVariable(&value.chamber.massflux, "mass_flux");
     177              :         
     178            3 :         value.RegisterNewFab(value.thermal.has_exceeded_Tcutoff, value.bc_temp, 1, 2, "exceeded_Tcutoff", 0); // Used to determine where regression has started
     179              : 
     180              :         // laser initial condition
     181              :         pp.select_default<  IC::Constant,
     182              :                             IC::Expression  >
     183            2 :             ("laser.ic",value.ic_laser, value.geom, Unit::Power()/Unit::Area());
     184              : 
     185              :         // thermal initial condition
     186              :         pp.select_default<  IC::Constant,
     187              :                             IC::Expression,
     188              :                             IC::BMP,
     189              :                             IC::PNG  >
     190            3 :             ("temp.ic",value.thermal.ic_temp,value.geom, Unit::Temperature());
     191              :     }
     192              : 
     193              : 
     194              :     // Constant pressure value
     195           12 :     pp_query_default("chamber.pressure", value.chamber.pressure, "1.0_MPa", Unit::Pressure()); 
     196              : 
     197              :     // Whether to compute the pressure evolution
     198            6 :     pp_query_default("variable_pressure", value.variable_pressure, false);
     199              : 
     200              :     // Refinement criterion for eta field, if thermal is on, cells will only be tagged for refinement if T>0.9*TCutoff,
     201              :     // and the gradient of eta > m_refinement_criterion at each cell
     202           12 :     pp_query_default(   "amr.refinement_criterion", value.m_refinement_criterion, "0.001", 
     203              :                         Unit::Less());
     204              : 
     205              :     // Refinement criterion for temperature field    
     206           12 :     pp.query_default(   "amr.refinement_criterion_temp", value.t_refinement_criterion, "0.001_K",
     207              :                         Unit::Temperature());
     208              : 
     209              :     // Eta value to restrict the refinament for the temperature field 
     210           12 :     pp.query_default(   "amr.refinament_restriction", value.t_refinement_restriction, "0.1",
     211              :                         Unit::Less());
     212              : 
     213              :     // Refinement criterion for phi field [infinity]
     214            6 :     pp_query_default("amr.phi_refinement_criterion", value.phi_refinement_criterion, 1.0e100);
     215              : 
     216              :     // Minimum allowable threshold for $\eta$
     217            6 :     pp_query_default("small", value.small, 1.0e-8); 
     218              : 
     219              :     // Initial condition for $\phi$ field.
     220              :     pp.select_default<IC::Laminate,IC::Expression,IC::Constant,IC::BMP,IC::PNG, IC::PSRead>
     221            6 :         ("phi.ic",value.ic_phi,value.geom);
     222              : 
     223            9 :     value.RegisterNodalFab(value.phi_mf, 1, 2, "phi", true);
     224              : 
     225              :     // Whether to use Neo-hookean Elastic model
     226            6 :     pp_query_default("elastic.on", value.elastic.on, 0); 
     227              : 
     228              :     // Body force
     229            6 :     pp_query_default("elastic.traction", value.elastic.traction, 0.0); 
     230              : 
     231              :     // Phi refinement criteria 
     232            6 :     pp_query_default("elastic.phirefinement", value.elastic.phirefinement, 1); 
     233              : 
     234            6 :     pp.queryclass<Base::Mechanics<model_type>>("elastic",value);
     235              : 
     236            3 :     if (value.m_type != Type::Disable)
     237              :     {
     238              :         // Reference temperature for thermal expansion 
     239              :         // (temperature at which the material is strain-free)
     240            0 :         pp_query_default("Telastic", value.elastic.Telastic, value.thermal.Tref);
     241              :         // elastic model of AP
     242            0 :         pp.queryclass<Model::Solid::Finite::NeoHookeanPredeformed>("model_ap", value.elastic.model_ap);
     243              :         // elastic model of HTPB
     244            0 :         pp.queryclass<Model::Solid::Finite::NeoHookeanPredeformed>("model_htpb", value.elastic.model_htpb);
     245              : 
     246              :         // eta cutoff value to stop applying the traction force and/or chamber pressure to the RHS.
     247              :         // Below this vlaue the RHS is set to 0.0
     248            0 :         pp.query_default("etacutoff",value.elastic.etacutoff, "0", Unit::Less());
     249              : 
     250              :         // Boolean value, when not 0 the chamber.pressure value is applied at the diffuse interface where t>Tcutoff
     251            0 :         pp.query_default("apply_chamber_pressure",value.elastic.apply_chamber_pressure, "0", Unit::Less());
     252              : 
     253              :         // Use our current eta field as the psi field for the solver
     254            0 :         value.psi_on = false;
     255            0 :         value.solver.setPsi(value.eta_mf);
     256              :     }
     257              : 
     258              :     bool allow_unused;
     259              :     // Set this to true to allow unused inputs without error.
     260              :     // (Not recommended.)
     261            3 :     pp.query_default("allow_unused",allow_unused,false);
     262            3 :     if (!allow_unused && pp.AnyUnusedInputs())
     263              :     {
     264            0 :         Util::Warning(INFO,"The following inputs were specified but not used:");
     265            0 :         pp.AllUnusedInputs();
     266            0 :         Util::Exception(INFO,"Aborting. Specify 'allow_unused=True` to ignore this error.");
     267              :     }
     268            3 : }
     269              : 
     270           14 : void Flame::Initialize(int lev)
     271              : {
     272              :     BL_PROFILE("Integrator::Flame::Initialize");
     273           14 :     Base::Mechanics<model_type>::Initialize(lev);
     274              : 
     275           14 :     ic_eta->Initialize(lev, eta_mf);
     276           14 :     ic_eta->Initialize(lev, eta_old_mf);
     277           14 :     ic_phi->Initialize(lev, phi_mf);
     278              :     //ic_phicell->Initialize(lev, phicell_mf);
     279              : 
     280           14 :     if (elastic.on) {
     281            0 :         rhs_mf[lev]->setVal(Set::Vector::Zero());
     282              :     }
     283           14 :     if (thermal.on) {
     284            8 :         if (thermal.ic_temp)
     285              :         {
     286            8 :             thermal.ic_temp->Initialize(lev,temp_mf);
     287            8 :             thermal.ic_temp->Initialize(lev,temp_old_mf);
     288            8 :             thermal.ic_temp->Initialize(lev,temps_mf);
     289              :         }
     290              :         else
     291              :         {
     292            0 :             temp_mf[lev]->setVal(thermal.Tref);
     293            0 :             temp_old_mf[lev]->setVal(thermal.Tref);
     294            0 :             temps_mf[lev]->setVal(thermal.Tref);
     295              :         }
     296            8 :         alpha_mf[lev]->setVal(0.0);
     297            8 :         mdot_mf[lev]->setVal(0.0);
     298            8 :         heatflux_mf[lev]->setVal(0.0);
     299            8 :         ic_laser->Initialize(lev, laser_mf);
     300              :     }
     301           14 :     if (variable_pressure) chamber.pressure = 1.0;
     302           14 : }
     303              : 
     304            0 : void Flame::UpdateModel(int /*a_step*/, Set::Scalar /*a_time*/)
     305              : {
     306            0 :     if (m_type == Base::Mechanics<model_type>::Type::Disable) return;
     307              : 
     308            0 :     for (int lev = 0; lev <= finest_level; ++lev)
     309              :     {
     310            0 :         amrex::Box domain = this->geom[lev].Domain();
     311            0 :         domain.convert(amrex::IntVect::TheNodeVector());
     312            0 :         const Set::Scalar* DX = geom[lev].CellSize();
     313              : 
     314            0 :         phi_mf[lev]->FillBoundary();
     315            0 :         eta_mf[lev]->FillBoundary();
     316            0 :         temp_mf[lev]->FillBoundary();
     317              : 
     318            0 :         for (MFIter mfi(*model_mf[lev], false); mfi.isValid(); ++mfi)
     319              :         {
     320            0 :             amrex::Box smallbox = mfi.nodaltilebox();
     321            0 :             amrex::Box bx = mfi.grownnodaltilebox() & domain;
     322            0 :             Set::Patch<model_type>        model = model_mf.Patch(lev,mfi);
     323            0 :             Set::Patch<const Set::Scalar> phi   = phi_mf.Patch(lev,mfi);
     324            0 :             Set::Patch<const Set::Scalar> eta   = eta_mf.Patch(lev,mfi);
     325            0 :             Set::Patch<Set::Vector>       rhs   = rhs_mf.Patch(lev,mfi);
     326            0 :             Set::Scalar Tcutoff = thermal.Tcutoff;
     327              : 
     328            0 :             if (elastic.on)
     329              :             {
     330            0 :                 Set::Patch <const Set::Scalar> temp = temp_mf.Patch(lev,mfi);
     331            0 :                 amrex::ParallelFor(smallbox, [=] AMREX_GPU_DEVICE(int i, int j, int k)
     332              : 
     333              :                 {   
     334            0 :                     Set::Vector grad_eta = Numeric::CellGradientOnNode(eta, i, j, k, 0, DX);
     335              : 
     336            0 :                     if (temp(i,j,k) > Tcutoff && eta(i,j,k) > elastic.etacutoff && elastic.apply_chamber_pressure)
     337              :                         {
     338            0 :                             rhs(i, j, k) = (elastic.traction) * grad_eta - chamber.pressure*grad_eta;
     339              :                             // std::cout << "Applying chamber pressure" << std::endl;
     340              :                         }
     341            0 :                     else if (temp(i,j,k) > Tcutoff && eta(i,j,k) > elastic.etacutoff && !elastic.apply_chamber_pressure)
     342              :                         {
     343            0 :                             rhs(i, j, k) = (elastic.traction) * grad_eta;
     344              :                             // std::cout << "Applying traction" << std::endl;
     345              :                         }
     346              :                     else
     347            0 :                             rhs(i, j, k) = 0.0 * grad_eta;
     348              :                             // std::cout << "Applying neither" << std::endl;
     349              : 
     350            0 :                 });
     351            0 :                 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
     352              :                 {
     353            0 :                     Set::Scalar phi_avg = phi(i, j, k, 0);
     354            0 :                     Set::Scalar temp_avg = Numeric::Interpolate::CellToNodeAverage(temp, i, j, k, 0);
     355            0 :                     model_type model_ap = elastic.model_ap;
     356            0 :                     model_ap.F0 -= Set::Matrix::Identity();
     357            0 :                     model_ap.F0 *= (temp_avg - elastic.Telastic);
     358            0 :                     model_ap.F0 += Set::Matrix::Identity();
     359            0 :                     model_type model_htpb = elastic.model_htpb;
     360            0 :                     model_htpb.F0 -= Set::Matrix::Identity();
     361            0 :                     model_htpb.F0 *= (temp_avg - elastic.Telastic);
     362            0 :                     model_htpb.F0 += Set::Matrix::Identity();
     363              : 
     364            0 :                     model(i, j, k) = (model_ap * phi_avg + model_htpb * (1. - phi_avg));
     365            0 :                 });
     366              :             }
     367              :             else
     368              :             {
     369            0 :                 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
     370              :                 {
     371            0 :                     Set::Scalar phi_avg = Numeric::Interpolate::CellToNodeAverage(phi, i, j, k, 0);
     372              :                     //phi_avg = phi(i,j,k,0);
     373            0 :                     model_type model_ap = elastic.model_ap;
     374            0 :                     model_ap.F0 *= Set::Matrix::Zero();
     375            0 :                     model_type model_htpb = elastic.model_htpb;
     376            0 :                     model_htpb.F0 *= Set::Matrix::Zero();
     377            0 :                     model(i, j, k) = (model_ap * phi_avg + model_htpb * (1. - phi_avg));
     378            0 :                 });
     379              :             }
     380            0 :         }
     381            0 :         Util::RealFillBoundary(*model_mf[lev], geom[lev]);
     382              : 
     383              :     }
     384              : }
     385              : 
     386          818 : void Flame::TimeStepBegin(Set::Scalar a_time, int a_iter)
     387              : {
     388              :     BL_PROFILE("Integrator::Flame::TimeStepBegin");
     389          818 :     Base::Mechanics<model_type>::TimeStepBegin(a_time, a_iter);
     390          818 :     if (thermal.on) {
     391           90 :         for (int lev = 0; lev <= finest_level; ++lev)
     392           80 :             ic_laser->Initialize(lev, laser_mf, a_time);
     393              :     }
     394              : 
     395          818 :     if (a_time > thermal.end_initial_refine_time)
     396              :     {   
     397            9 :         if (!end_initial_refine) {
     398            9 :             for (int lev = 0; lev <= finest_level; ++lev)
     399            8 :                 Flame::Regrid(lev, a_time);
     400            1 :             end_initial_refine = 1;
     401              :         }
     402              : 
     403            9 :         prev_finest_ba = grids[finest_level];
     404            9 :         prev_finest_level = finest_level;
     405              :     }
     406          818 : }
     407              : 
     408          818 : void Flame::TimeStepComplete(Set::Scalar /*a_time*/, int /*a_iter*/)
     409              : {
     410              :     BL_PROFILE("Integrator::Flame::TimeStepComplete");
     411          818 :     if (variable_pressure) {
     412              :         //Set::Scalar x_len = geom[0].ProbDomain().length(0);
     413              :         //Set::Scalar y_len = geom[0].ProbDomain().length(1);
     414              :         // Set::Scalar domain_area = x_len * y_len;
     415            0 :         Util::Message(INFO, "Mass = ", chamber.massflux);
     416            0 :         Util::Message(INFO, "Pressure = ", chamber.pressure);
     417              :     }
     418          818 : }
     419              : 
     420         8206 : void Flame::Advance(int lev, Set::Scalar time, Set::Scalar dt)
     421              : {
     422              :     BL_PROFILE("Integrador::Flame::Advance");
     423         8206 :     Base::Mechanics<model_type>::Advance(lev, time, dt);
     424         8206 :     const Set::Scalar* DX = geom[lev].CellSize();
     425              : 
     426         8206 :     std::swap(eta_old_mf[lev], eta_mf[lev]);
     427              : 
     428              :     //
     429              :     // Chamber pressure update
     430              :     //
     431         8206 :     if (variable_pressure) {
     432            0 :         chamber.pressure = exp(0.00075 * chamber.massflux);
     433            0 :         if (chamber.pressure > 10.0) {
     434            0 :             chamber.pressure = 10.0;
     435              :         }
     436            0 :         else if (chamber.pressure <= 0.99) {
     437            0 :             chamber.pressure = 0.99;
     438              :         }
     439            0 :         elastic.traction = chamber.pressure;
     440              :     }
     441              : 
     442              : 
     443              :     //
     444              :     // Multi-well chemical potential
     445              :     //
     446              :     Numeric::Function::Polynomial<4> w( pf.w0,
     447              :                                         0.0,
     448         8206 :                                         -5.0 * pf.w1 + 16.0 * pf.w12 - 11.0 * pf.w0,
     449         8206 :                                         14.0 * pf.w1 - 32.0 * pf.w12 + 18.0 * pf.w0,
     450         8206 :                                         -8.0 * pf.w1 + 16.0 * pf.w12 - 8.0 * pf.w0);
     451         8206 :     Numeric::Function::Polynomial<3> dw = w.D();
     452              : 
     453         8206 :     propellant.set_pressure(chamber.pressure);
     454              : 
     455        30068 :     for (amrex::MFIter mfi(*eta_mf[lev], true); mfi.isValid(); ++mfi)
     456              :     {
     457        21862 :         const amrex::Box& bx = mfi.tilebox();
     458              :         // Phase fields
     459        21862 :         Set::Patch<Set::Scalar> etanew    = eta_mf.Patch(lev,mfi);
     460        21862 :         Set::Patch<const Set::Scalar> eta = eta_old_mf.Patch(lev,mfi);
     461        21862 :         Set::Patch<const Set::Scalar> phi = phi_mf.Patch(lev,mfi);
     462              :         // Heat transfer fields
     463        21862 :         Set::Patch<const Set::Scalar> temp = temp_mf.Patch(lev,mfi);
     464        21862 :         Set::Patch<Set::Scalar>       alpha = alpha_mf.Patch(lev,mfi);
     465        21862 :         Set::Patch<Set::Scalar>       laser = laser_mf.Patch(lev,mfi);
     466              :         // Diagnostic fields
     467        21862 :         Set::Patch<Set::Scalar> mdot     = mdot_mf.Patch(lev,mfi);
     468        21862 :         Set::Patch<Set::Scalar> heatflux = heatflux_mf.Patch(lev,mfi);
     469              : 
     470        21862 :         Set::Patch<Set::Scalar> exceeded_Tcutoff = thermal.has_exceeded_Tcutoff.Patch(lev, mfi);
     471        21862 :         Set::Scalar Tcutoff = thermal.Tcutoff;
     472              : 
     473              : 
     474        21862 :         amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
     475              :         {
     476              :             //
     477              :             // CALCULATE PHI-AVERAGED QUANTITIES
     478              :             //
     479     59108224 :             Set::Scalar phi_avg = Numeric::Interpolate::NodeToCellAverage(phi, i, j, k, 0);
     480     60856064 :             Set::Scalar T = thermal.on ? temp(i,j,k) : NAN;
     481              : 
     482     59108224 :             Set::Scalar K = propellant.get_K(phi_avg);
     483              : 
     484     59108224 :             Set::Scalar rho = propellant.get_rho(phi_avg);
     485              : 
     486     59108224 :             Set::Scalar cp = propellant.get_cp(phi_avg);
     487              : 
     488              :             //
     489              :             // CALCULATE MOBILITY
     490              :             // 
     491     59108224 :             Set::Scalar L = propellant.get_L(  phi_avg, T);
     492              : 
     493              :             // 
     494              :             // EVOLVE PHASE FIELD (ETA)
     495              :             // 
     496              : 
     497     59108224 :             Set::Scalar eta_lap = Numeric::Laplacian(eta, i, j, k, 0, DX);
     498    118216448 :             Set::Scalar df_deta = ((pf.lambda / pf.eps) * dw(eta(i, j, k)) - pf.eps * pf.kappa * eta_lap);
     499              :             
     500     59108224 :             if (df_deta < 0) {
     501              :                 // Prevent eta from increasing/healing. A bug was found where if the diffuse thickness was too large compared to a void
     502              :                 // (region of eta = 0), eta would heal/increase in a non-physcial way, this statement stops that behavior 
     503       196236 :                 df_deta = 0.0;
     504              :             }
     505     59108224 :             if (thermal.on && T < thermal.Tcutoff) {
     506              :                 // If the temperature is lower then the cutoff temperature don't evolve the eta field
     507            0 :                 df_deta = 0.0;
     508              :             }
     509    118216448 :             etanew(i, j, k) = eta(i, j, k) - L * dt * df_deta;
     510              :             
     511    118216448 :             if (etanew(i, j, k) <= small) etanew(i, j, k) = small;
     512              : 
     513     59108224 :             if (thermal.on)
     514              :             {
     515              :                 //
     516              :                 // Calculate thermal diffisivity and store for later gradient
     517              :                 //
     518              : 
     519      1747840 :                 alpha(i, j, k) = K / rho / cp; 
     520              : 
     521              :                 //
     522              :                 // CALCULATE MASS FLUX BASED ON EVOLVING ETA
     523              :                 //
     524              :             
     525      5243520 :                 mdot(i, j, k) = rho * fabs(eta(i, j, k) - etanew(i, j, k)) / dt; 
     526              : 
     527              :                 //
     528              :                 // CALCULATE HEAT FLUX BASED ON THE CALCULATED MASS FLUX
     529              :                 //
     530              : 
     531      3495680 :                 Set::Scalar q0 = propellant.get_qdot(mdot(i,j,k), phi_avg);
     532      3495680 :                 heatflux(i,j,k) = ( thermal.hc*q0 + laser(i,j,k) ) / K;
     533              : 
     534      3495680 :                 if (temp(i,j,k) > Tcutoff)
     535              :                 {
     536      3495680 :                     exceeded_Tcutoff(i,j,k) = 1;
     537              :                 }
     538              : 
     539              :             }
     540              : 
     541     59108224 :         });
     542              : 
     543         8206 :     } // MFi For loop 
     544              : 
     545              : 
     546              :     //
     547              :     // THERMAL TRANSPORT
     548              :     // 
     549         8206 :     if (thermal.on)
     550              :     {
     551         2550 :         std::swap(temp_old_mf[lev], temp_mf[lev]);
     552              : 
     553        13100 :         for (amrex::MFIter mfi(*eta_mf[lev], true); mfi.isValid(); ++mfi)
     554              :         {
     555        10550 :             const amrex::Box& bx = mfi.tilebox();
     556              : 
     557        10550 :             Set::Patch<Set::Scalar>       tempnew = temp_mf.Patch(lev,mfi);
     558        10550 :             Set::Patch<const Set::Scalar> temp = temp_old_mf.Patch(lev,mfi);
     559        10550 :             Set::Patch<const Set::Scalar> alpha = alpha_mf.Patch(lev,mfi);
     560              : 
     561        10550 :             Set::Patch<Set::Scalar>       temps = temps_mf.Patch(lev,mfi);
     562              : 
     563              : 
     564              :             // Phase field
     565        10550 :             Set::Patch<Set::Scalar>       etanew = (*eta_mf[lev]).array(mfi);
     566        10550 :             Set::Patch<const Set::Scalar> eta = (*eta_old_mf[lev]).array(mfi);
     567              :             // Diagnostic fields
     568        10550 :             Set::Patch<const Set::Scalar> heatflux = heatflux_mf.Patch(lev,mfi);
     569              : 
     570        10550 :             amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
     571              :             {
     572      1747840 :                 auto sten = Numeric::GetStencil(i, j, k, bx);
     573      1747840 :                 Set::Vector grad_eta = Numeric::Gradient(eta, i, j, k, 0, DX);
     574      1747840 :                 Set::Vector grad_temp = Numeric::Gradient(temp, i, j, k, 0, DX);
     575      1747840 :                 Set::Scalar lap_temp = Numeric::Laplacian(temp, i, j, k, 0, DX);
     576      1747840 :                 Set::Scalar grad_eta_mag = grad_eta.lpNorm<2>();
     577      1747840 :                 Set::Vector grad_alpha = Numeric::Gradient(alpha, i, j, k, 0, DX, sten);
     578      1747840 :                 Set::Scalar dTdt = 0.0;
     579      3495680 :                 dTdt += grad_eta.dot(grad_temp * alpha(i, j, k));
     580      3495680 :                 dTdt += grad_alpha.dot(eta(i, j, k) * grad_temp);
     581      3495680 :                 dTdt += eta(i, j, k) * alpha(i, j, k) * lap_temp;
     582      3495680 :                 dTdt += alpha(i, j, k) * heatflux(i, j, k) * grad_eta_mag;
     583              : 
     584      5243520 :                 Set::Scalar Tsolid = dTdt + temps(i, j, k) * (etanew(i, j, k) - eta(i, j, k)) / dt;
     585      3495680 :                 temps(i, j, k) = temps(i, j, k) + dt * Tsolid;
     586      6991360 :                 tempnew(i, j, k) = etanew(i, j, k) * temps(i, j, k) + (1.0 - etanew(i, j, k)) * thermal.Tfluid;
     587      1747840 :             });
     588         2550 :         }
     589              :     }
     590              :  
     591         8206 : } //Function
     592              : 
     593              : 
     594          190 : void Flame::TagCellsForRefinement(int lev, amrex::TagBoxArray& a_tags, Set::Scalar time, int ngrow)
     595              : {
     596              :     BL_PROFILE("Integrator::Flame::TagCellsForRefinement");
     597          190 :     Base::Mechanics<model_type>::TagCellsForRefinement(lev, a_tags, time, ngrow);
     598              : 
     599          190 :     const Set::Scalar* DX = geom[lev].CellSize();
     600          190 :     Set::Scalar dr = sqrt(AMREX_D_TERM(DX[0] * DX[0], +DX[1] * DX[1], +DX[2] * DX[2]));
     601              : 
     602              :     // Eta criterion for refinement
     603          602 :     for (amrex::MFIter mfi(*eta_mf[lev], true); mfi.isValid(); ++mfi)
     604              :     {
     605          412 :         const amrex::Box& bx = mfi.tilebox();
     606          412 :         amrex::Array4<char> const& tags = a_tags.array(mfi);
     607          412 :         Set::Patch<const Set::Scalar> eta = eta_mf.Patch(lev,mfi);
     608          412 :         Set::Patch<const Set::Scalar> temp = temp_mf.Patch(lev,mfi);
     609              : 
     610          412 :         if (thermal.on) {
     611          345 :         amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
     612              :         {
     613        50928 :             Set::Vector gradeta = Numeric::Gradient(eta, i, j, k, 0, DX);
     614        76356 :             if (gradeta.lpNorm<2>() * dr * 2 > m_refinement_criterion && eta(i, j, k) >= t_refinement_restriction && temp(i,j,k) > thermal.Tcutoff*0.9)
     615        25428 :                 tags(i, j, k) = amrex::TagBox::SET;
     616        50928 :         });
     617              : 
     618              :         } else {
     619           67 :         amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
     620              :         {
     621       468992 :             Set::Vector gradeta = Numeric::Gradient(eta, i, j, k, 0, DX);
     622       477748 :             if (gradeta.lpNorm<2>() * dr * 2 > m_refinement_criterion && eta(i, j, k) >= t_refinement_restriction)
     623        16508 :                 tags(i, j, k) = amrex::TagBox::SET;
     624       468992 :         });
     625              :         }
     626          190 :     }
     627              : 
     628              :     // Phi criterion for refinement 
     629          190 :     if (elastic.phirefinement) {
     630          602 :         for (amrex::MFIter mfi(*eta_mf[lev], true); mfi.isValid(); ++mfi)
     631              :         {
     632          412 :             const amrex::Box& bx = mfi.tilebox();
     633          412 :             amrex::Array4<char> const& tags = a_tags.array(mfi);
     634          412 :             Set::Patch<const Set::Scalar> phi = phi_mf.Patch(lev,mfi);
     635              : 
     636          412 :             amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
     637              :             {
     638       519920 :                 Set::Vector gradphi = Numeric::Gradient(phi, i, j, k, 0, DX);
     639       519920 :                 if (gradphi.lpNorm<2>() * dr >= phi_refinement_criterion)
     640            0 :                     tags(i, j, k) = amrex::TagBox::SET;
     641       519920 :             });
     642          190 :         }
     643              :     }
     644              : 
     645              : 
     646              :     // Thermal criterion for refinement 
     647          190 :     if (thermal.on) {
     648          507 :         for (amrex::MFIter mfi(*temp_mf[lev], true); mfi.isValid(); ++mfi)
     649              :         {
     650          345 :             const amrex::Box& bx = mfi.tilebox();
     651          345 :             amrex::Array4<char> const& tags = a_tags.array(mfi);
     652          345 :             Set::Patch<const Set::Scalar> temp = temp_mf.Patch(lev,mfi);
     653          345 :             Set::Patch<const Set::Scalar> eta  = eta_mf.Patch(lev,mfi);
     654          345 :             amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
     655              :             {
     656        50928 :                 Set::Vector tempgrad = Numeric::Gradient(temp, i, j, k, 0, DX);
     657        50928 :                 if (tempgrad.lpNorm<2>() * dr > t_refinement_criterion && eta(i, j, k) >= t_refinement_restriction)
     658            0 :                     tags(i, j, k) = amrex::TagBox::SET;
     659        50928 :             });
     660          162 :         }
     661              :     }
     662              : 
     663              :         // Refine at start
     664          602 :     for (amrex::MFIter mfi(*eta_mf[lev], true); mfi.isValid(); ++mfi)
     665              :     {
     666          412 :         const amrex::Box& bx = mfi.tilebox();
     667          412 :         amrex::Array4<char> const& tags = a_tags.array(mfi);
     668          412 :         Set::Patch<const Set::Scalar> eta = eta_mf.Patch(lev,mfi);
     669          412 :         Set::Patch<const Set::Scalar> phi = phi_mf.Patch(lev,mfi);
     670              : 
     671          412 :         amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
     672              :         {
     673       519920 :             Set::Vector gradeta = Numeric::Gradient(eta, i, j, k, 0, DX);
     674       519920 :             Set::Vector gradphi = Numeric::Gradient(phi, i, j, k, 0, DX);
     675       519920 :             if ((gradeta.lpNorm<2>() * dr * 2 > m_refinement_criterion || gradphi.lpNorm<2>() * dr >= thermal.phi_refinement_criterion_inital) && time < thermal.end_initial_refine_time)
     676            0 :                 tags(i, j, k) = amrex::TagBox::SET;
     677       519920 :         });
     678          190 :     }
     679          190 : }
     680              : 
     681           10 : void Flame::Regrid(int lev, Set::Scalar time)
     682              : {   
     683              :     BL_PROFILE("Integrator::Flame::Regrid");
     684              : 
     685           10 :     ic_phi->Initialize(lev, phi_mf, time);
     686           10 :     ic_eta->Initialize(lev, eta_0_mf, time);
     687              : 
     688           10 :     if (thermal.on) {
     689              :     /* 
     690              :     This regrid function works by using the "has_exceeded_Tcutoff" field. If the temperature in a cell is greater than Tcutoff,
     691              :     eta will change and when regridding won't use the initial eta field. If T < T_cutoff, when regriding happens it applies the inital 
     692              :     eta field condition. This gives at leat a 4x speed improvement in 2D when doing regression with voids. This is because orgionally
     693              :     there was a bug where when regridding, the orgional eta field wouldn't be applied, so there would be "squares" of voids instead of
     694              :     circles/spheres when using .xyzr files as the inital condition.
     695              :     */
     696           24 :     for (amrex::MFIter mfi(*eta_mf[lev], true); mfi.isValid(); ++mfi)
     697              :     {
     698           16 :         const amrex::Box &bx = mfi.tilebox();
     699           16 :         Set::Patch<Set::Scalar> eta    = eta_mf.Patch(lev, mfi);
     700           16 :         Set::Patch<const Set::Scalar> temp = temp_mf.Patch(lev, mfi);
     701           16 :         Set::Patch<const Set::Scalar> eta_0 = eta_0_mf.Patch(lev, mfi);
     702           16 :         Set::Patch<Set::Scalar> exceeded_Tcutoff = thermal.has_exceeded_Tcutoff.Patch(lev, mfi);
     703              : 
     704           16 :         Set::Scalar Tcutoff = thermal.Tcutoff;
     705              : 
     706           16 :         amrex::BoxList boxes_to_update;
     707           16 :         if (lev == finest_level && prev_finest_level == finest_level)
     708            0 :             boxes_to_update = amrex::complementIn(bx, prev_finest_ba).boxList();
     709              :         else
     710           16 :             boxes_to_update.push_back(bx);
     711              : 
     712           32 :         for (const amrex::Box &box : boxes_to_update)
     713           16 :             amrex::ParallelFor(box, [=] AMREX_GPU_DEVICE(int i, int j, int k)
     714              :             {
     715              : 
     716         4128 :                 if (!exceeded_Tcutoff(i,j,k) && temp(i,j,k) < Tcutoff)
     717              :                 {
     718            0 :                         eta(i, j, k) = eta_0(i, j, k);
     719              :                 }
     720         2064 :             });
     721           24 :     }
     722              : 
     723            8 :     if (lev == finest_level)
     724              :     {
     725            1 :         prev_finest_ba    = grids[finest_level];
     726            1 :         prev_finest_level = finest_level;
     727              :     }
     728              :     }
     729           10 : }
     730              : 
     731          220 : void Flame::Integrate(int amrlev, Set::Scalar time, int /*step*/,
     732              :     const amrex::MFIter& mfi, const amrex::Box& box)
     733              : {
     734              :     BL_PROFILE("Flame::Integrate");
     735              :     
     736          220 :     Base::Mechanics<model_type>::Integrate(amrlev,time,timestep,mfi,box);
     737              : 
     738          220 :     const Set::Scalar* DX = geom[amrlev].CellSize();
     739          220 :     Set::Scalar dv = AMREX_D_TERM(DX[0], *DX[1], *DX[2]);
     740          220 :     Set::Patch<const Set::Scalar> eta  = eta_mf.Patch(amrlev,mfi);
     741          220 :     Set::Patch<const Set::Scalar> mdot = mdot_mf.Patch(amrlev,mfi);
     742          220 :     if (variable_pressure) {
     743            0 :         amrex::ParallelFor(box, [=] AMREX_GPU_DEVICE(int i, int j, int k)
     744              :         {
     745            0 :             chamber.volume += eta(i, j, k, 0) * dv;
     746            0 :             Set::Vector grad = Numeric::Gradient(eta, i, j, k, 0, DX);
     747            0 :             Set::Scalar normgrad = grad.lpNorm<2>();
     748            0 :             Set::Scalar da = normgrad * dv;
     749            0 :             chamber.area += da;
     750              : 
     751            0 :             Set::Vector mgrad = Numeric::Gradient(mdot, i, j, k, 0, DX);
     752            0 :             Set::Scalar mnormgrad = mgrad.lpNorm<2>();
     753            0 :             Set::Scalar dm = mnormgrad * dv;
     754            0 :             chamber.massflux += dm;
     755              : 
     756            0 :         });
     757              :     }
     758              :     else {
     759          220 :         amrex::ParallelFor(box, [=] AMREX_GPU_DEVICE(int i, int j, int k)
     760              :         {
     761        15560 :             chamber.volume += eta(i, j, k, 0) * dv;
     762        15560 :             Set::Vector grad = Numeric::Gradient(eta, i, j, k, 0, DX);
     763        15560 :             Set::Scalar normgrad = grad.lpNorm<2>();
     764        15560 :             Set::Scalar da = normgrad * dv;
     765        15560 :             chamber.area += da;
     766        15560 :         });
     767              :     }
     768              :     // time dependent pressure data from experimenta -> p = 0.0954521220950523 * exp(15.289993148880678 * t)
     769          220 : }
     770              : } // namespace Integrator
     771              : 
        

Generated by: LCOV version 2.0-1