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

Generated by: LCOV version 2.0-1