From 5ef2a4650105adcf1352ae0a24576207ed3d5900 Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Mon, 20 Jun 2022 12:05:42 -0600 Subject: [PATCH 01/27] initial changes to allow eosparm --- Source/PeleLM.H | 4 ++++ Source/PeleLM.cpp | 4 ++++ Source/PeleLMSetup.cpp | 2 ++ 3 files changed, 10 insertions(+) diff --git a/Source/PeleLM.H b/Source/PeleLM.H index 041928c05..383f2c44f 100644 --- a/Source/PeleLM.H +++ b/Source/PeleLM.H @@ -1045,6 +1045,10 @@ class PeleLM : public amrex::AmrCore { static pele::physics::transport::TransportParams< pele::physics::PhysicsType::transport_type> trans_parms; + // EOS pointer + static pele::physics::eos::EosParams< + pele::physics::PhysicsType::eos_type> eos_parms; + // Reactor pointer std::string m_chem_integrator; std::unique_ptr m_reactor; diff --git a/Source/PeleLM.cpp b/Source/PeleLM.cpp index 0577aab54..dd8a9aa83 100644 --- a/Source/PeleLM.cpp +++ b/Source/PeleLM.cpp @@ -5,6 +5,9 @@ using namespace amrex; pele::physics::transport::TransportParams< pele::physics::PhysicsType::transport_type> PeleLM::trans_parms; +pele::physics::eos::EosParams< + pele::physics::PhysicsType::eos_type> + PeleLM::eos_parms; PeleLM::PeleLM() = default; @@ -15,6 +18,7 @@ PeleLM::~PeleLM() } if (!m_incompressible) { + eos_parms.deallocate(); trans_parms.deallocate(); m_reactor->close(); } diff --git a/Source/PeleLMSetup.cpp b/Source/PeleLMSetup.cpp index 373b95915..d748086e6 100644 --- a/Source/PeleLMSetup.cpp +++ b/Source/PeleLMSetup.cpp @@ -76,6 +76,8 @@ void PeleLM::Setup() { // Initialize EOS and others if (!m_incompressible) { + amrex::Print() << " Initialization of EOS (CPP)... \n"; + eos_parms.allocate(); amrex::Print() << " Initialization of Transport ... \n"; trans_parms.allocate(); if (m_do_react) { From f04b9dde7530357c63abe8f2b67ae9fed6107517 Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Mon, 20 Jun 2022 15:24:03 -0600 Subject: [PATCH 02/27] more updates to use eosparm for (almost) all eos calls --- Source/PeleLMAdvection.cpp | 14 ++++---- Source/PeleLMDeriveFunc.cpp | 13 ++++--- Source/PeleLMDiffusion.cpp | 28 +++++++++------ Source/PeleLMEos.cpp | 25 ++++++++------ Source/PeleLMPlot.cpp | 3 +- Source/PeleLMReactions.cpp | 17 ++++++---- Source/PeleLMSetup.cpp | 2 ++ Source/PeleLMTransportProp.cpp | 7 ++-- Source/PeleLM_K.H | 62 ++++++++++++++++++++-------------- 9 files changed, 103 insertions(+), 68 deletions(-) diff --git a/Source/PeleLMAdvection.cpp b/Source/PeleLMAdvection.cpp index 0d611a75a..5468aa202 100644 --- a/Source/PeleLMAdvection.cpp +++ b/Source/PeleLMAdvection.cpp @@ -241,6 +241,7 @@ void PeleLM::getScalarAdvForce(std::unique_ptr &advData, // Get t^{n} data pointer auto ldata_p = getLevelDataPtr(lev,AmrOldTime); auto ldataR_p = getLevelDataReactPtr(lev); + auto const* leosparm = eos_parms.device_eos_parm(); #ifdef AMREX_USE_OMP #pragma omp parallel if (Gpu::notInLaunchRegion()) @@ -258,12 +259,12 @@ void PeleLM::getScalarAdvForce(std::unique_ptr &advData, auto const& extRhoH = m_extSource[lev]->const_array(mfi,RHOH); auto const& fY = advData->Forcing[lev].array(mfi,0); auto const& fT = advData->Forcing[lev].array(mfi,NUM_SPECIES); - amrex::ParallelFor(bx, [rho, rhoY, T, dn, ddn, r, fY, fT, extRhoY, extRhoH, dp0dt=m_dp0dt, + amrex::ParallelFor(bx, [rho, rhoY, T, dn, ddn, r, fY, fT, extRhoY, extRhoH, leosparm, dp0dt=m_dp0dt, is_closed_ch=m_closed_chamber, do_react=m_do_react] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { buildAdvectionForcing( i, j, k, rho, rhoY, T, dn, ddn, r, extRhoY, extRhoH, - dp0dt, is_closed_ch, do_react, fY, fT ); + dp0dt, is_closed_ch, do_react, fY, fT, leosparm ); }); } } @@ -530,6 +531,7 @@ void PeleLM::computeScalarAdvTerms(std::unique_ptr &advData) for (MFIter mfi(ldata_p->state,TilingIfNotGPU()); mfi.isValid(); ++mfi) { Box const& bx = mfi.tilebox(); + auto const* leosparm = eos_parms.device_eos_parm(); #ifdef AMREX_USE_EB auto const& flagfab = ebfact.getMultiEBCellFlagFab()[mfi]; @@ -550,22 +552,22 @@ void PeleLM::computeScalarAdvTerms(std::unique_ptr &advData) }); } else if (flagfab.getType(ebx) != FabType::regular ) { // EB containing boxes const auto& afrac = areafrac[idim]->array(mfi); - amrex::ParallelFor(ebx, [rho, rhoY, T, rhoHm, afrac] + amrex::ParallelFor(ebx, [rho, rhoY, T, rhoHm, afrac, leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { if (afrac(i,j,k) <= 0.0) { // Covered faces rhoHm(i,j,k) = 0.0; } else { - getRHmixGivenTY( i, j, k, rho, rhoY, T, rhoHm ); + getRHmixGivenTY( i, j, k, rho, rhoY, T, rhoHm, leosparm ); } }); } else // Regular boxes #endif { - amrex::ParallelFor(ebx, [rho, rhoY, T, rhoHm] + amrex::ParallelFor(ebx, [rho, rhoY, T, rhoHm, leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { - getRHmixGivenTY( i, j, k, rho, rhoY, T, rhoHm ); + getRHmixGivenTY( i, j, k, rho, rhoY, T, rhoHm, leosparm ); }); } } diff --git a/Source/PeleLMDeriveFunc.cpp b/Source/PeleLMDeriveFunc.cpp index c829af4ef..02d0e6993 100644 --- a/Source/PeleLMDeriveFunc.cpp +++ b/Source/PeleLMDeriveFunc.cpp @@ -70,6 +70,7 @@ void pelelm_dermolefrac (PeleLM* a_pelelm, const Box& bx, FArrayBox& derfab, int AMREX_ASSERT(!a_pelelm->m_incompressible); auto const in_dat = statefab.array(); auto der = derfab.array(dcomp); + auto const* leosparm = a_pelelm->eos_parms.device_eos_parm(); amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { @@ -79,7 +80,7 @@ void pelelm_dermolefrac (PeleLM* a_pelelm, const Box& bx, FArrayBox& derfab, int for (int n = 0; n < NUM_SPECIES; n++) { Yt[n] = in_dat(i,j,k,FIRSTSPEC+n) * rhoinv; } - auto eos = pele::physics::PhysicsType::eos(); + auto eos = pele::physics::PhysicsType::eos(leosparm); eos.Y2X(Yt,Xt); for (int n = 0; n < NUM_SPECIES; n++) { der(i,j,k,n) = Xt[n]; @@ -328,10 +329,11 @@ void pelelm_derdiffc (PeleLM* a_pelelm, const Box& bx, FArrayBox& derfab, int dc auto lambda = dummies.array(0); auto mu = dummies.array(1); auto const* ltransparm = a_pelelm->trans_parms.device_trans_parm(); + auto const* leosparm = a_pelelm->eos_parms.device_eos_parm(); amrex::ParallelFor(bx, - [rhoY,T,rhoD,lambda,mu,ltransparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept + [rhoY,T,rhoD,lambda,mu,ltransparm, leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { - getTransportCoeff(i, j, k, rhoY, T, rhoD, lambda, mu, ltransparm); + getTransportCoeff(i, j, k, rhoY, T, rhoD, lambda, mu, ltransparm, leosparm); }); } @@ -354,9 +356,10 @@ void pelelm_derlambda (PeleLM* a_pelelm, const Box& bx, FArrayBox& derfab, int d auto lambda = derfab.array(dcomp); auto mu = dummies.array(0); auto const* ltransparm = a_pelelm->trans_parms.device_trans_parm(); + auto const* leosparm = a_pelelm->eos_parms.device_eos_parm(); amrex::ParallelFor(bx, - [rhoY,T,rhoD,lambda,mu,ltransparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept + [rhoY,T,rhoD,lambda,mu,ltransparm,leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { - getTransportCoeff(i, j, k, rhoY, T, rhoD, lambda, mu, ltransparm); + getTransportCoeff(i, j, k, rhoY, T, rhoD, lambda, mu, ltransparm, leosparm); }); } diff --git a/Source/PeleLMDiffusion.cpp b/Source/PeleLMDiffusion.cpp index fbbacd356..52d807489 100644 --- a/Source/PeleLMDiffusion.cpp +++ b/Source/PeleLMDiffusion.cpp @@ -250,6 +250,7 @@ void PeleLM::addWbarTerm(const Vector > &a_spflu for (int lev = 0; lev <= finest_level; ++lev) { Wbar[lev].define(grids[lev],dmap[lev],1,nGrow,MFInfo(),Factory(lev)); + auto const* leosparm = eos_parms.device_eos_parm(); #ifdef AMREX_USE_OMP #pragma omp parallel if (Gpu::notInLaunchRegion()) @@ -260,10 +261,10 @@ void PeleLM::addWbarTerm(const Vector > &a_spflu auto const& rho_arr = a_rho[lev]->const_array(mfi); auto const& rhoY_arr = a_spec[lev]->const_array(mfi); auto const& Wbar_arr = Wbar[lev].array(mfi); - amrex::ParallelFor(gbx, [rho_arr, rhoY_arr, Wbar_arr] + amrex::ParallelFor(gbx, [rho_arr, rhoY_arr, Wbar_arr, leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { - getMwmixGivenRY(i, j, k, rho_arr, rhoY_arr, Wbar_arr); + getMwmixGivenRY(i, j, k, rho_arr, rhoY_arr, Wbar_arr, leosparm); }); } } @@ -299,6 +300,7 @@ void PeleLM::addWbarTerm(const Vector > &a_spflu Array beta_ec = getDiffusivity(lev, 0, NUM_SPECIES, doZeroVisc, bcRecSpec, *a_beta[lev]); const Box& domain = geom[lev].Domain(); + auto const* leosparm = eos_parms.device_eos_parm(); bool use_harmonic_avg = m_harm_avg_cen2edge ? true : false; #ifdef AMREX_USE_OMP @@ -340,10 +342,10 @@ void PeleLM::addWbarTerm(const Vector > &a_spflu // Wbar flux is : - \rho Y_m / \overline{W} * D_m * \nabla \overline{W} // with beta_m = \rho * D_m below - amrex::ParallelFor(ebx, [need_wbar_fluxes, gradWbar_ar, beta_ar, rhoY, spFlux_ar, spwbarFlux_ar] + amrex::ParallelFor(ebx, [need_wbar_fluxes, gradWbar_ar, beta_ar, rhoY, spFlux_ar, spwbarFlux_ar, leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { - auto eos = pele::physics::PhysicsType::eos(); + auto eos = pele::physics::PhysicsType::eos(leosparm); // Get Wbar from rhoYs amrex::Real rho = 0.0; for (int n = 0; n < NUM_SPECIES; n++) { @@ -487,6 +489,8 @@ void PeleLM::computeSpeciesEnthalpyFlux(const Vector &a_rhs, auto ldataOld_p = getLevelDataPtr(lev,AmrOldTime); auto ldataNew_p = getLevelDataPtr(lev,AmrNewTime); + auto const* leosparm = eos_parms.device_eos_parm(); #ifdef AMREX_USE_OMP #pragma omp parallel if (Gpu::notInLaunchRegion()) @@ -819,7 +824,7 @@ void PeleLM::deltaTIter_prepare(const Vector &a_rhs, + force(i,j,k) + fourier(i,j,k) + diffDiff(i,j,k)); // Get \rho * Cp_{mix} - getCpmixGivenRYT( i, j, k, rho, rhoY, T, rhocp ); + getCpmixGivenRYT( i, j, k, rho, rhoY, T, rhocp, leosparm ); rhocp(i,j,k) *= rho(i,j,k); // Save T @@ -884,6 +889,7 @@ void PeleLM::deltaTIter_update(int a_dtiter, for (int lev = 0; lev <= finest_level; ++lev) { auto ldata_p = getLevelDataPtr(lev,AmrNewTime); + auto const* leosparm = eos_parms.device_eos_parm(); #ifdef AMREX_USE_OMP #pragma omp parallel if (Gpu::notInLaunchRegion()) @@ -896,10 +902,10 @@ void PeleLM::deltaTIter_update(int a_dtiter, auto const& T = ldata_p->state.const_array(mfi,TEMP); auto const& rhoHm = ldata_p->state.array(mfi,RHOH); - amrex::ParallelFor(bx, [rho, rhoY, T, rhoHm] + amrex::ParallelFor(bx, [rho, rhoY, T, rhoHm, leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { - getRHmixGivenTY( i, j, k, rho, rhoY, T, rhoHm ); + getRHmixGivenTY( i, j, k, rho, rhoY, T, rhoHm, leosparm); }); } } diff --git a/Source/PeleLMEos.cpp b/Source/PeleLMEos.cpp index 3c52d4ae4..9319cc30d 100644 --- a/Source/PeleLMEos.cpp +++ b/Source/PeleLMEos.cpp @@ -20,6 +20,7 @@ void PeleLM::setThermoPress(int lev, const TimeStamp &a_time) { AMREX_ASSERT(a_time == AmrOldTime || a_time == AmrNewTime); auto ldata_p = getLevelDataPtr(lev,a_time); + auto const* leosparm = eos_parms.device_eos_parm(); #ifdef AMREX_USE_OMP #pragma omp parallel if (Gpu::notInLaunchRegion()) @@ -33,10 +34,10 @@ void PeleLM::setThermoPress(int lev, const TimeStamp &a_time) { auto const& T = ldata_p->state.const_array(mfi,TEMP); auto const& P = ldata_p->state.array(mfi,RHORT); - amrex::ParallelFor(bx, [rho, rhoY, T, P] + amrex::ParallelFor(bx, [rho, rhoY, T, P, leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { - getPGivenRTY( i, j, k, rho, rhoY, T, P ); + getPGivenRTY( i, j, k, rho, rhoY, T, P, leosparm); }); } } @@ -91,6 +92,8 @@ void PeleLM::calcDivU(int is_init, const auto& ebfact = EBFactory(lev); #endif + auto const* leosparm = eos_parms.device_eos_parm(); + #ifdef AMREX_USE_OMP #pragma omp parallel if (Gpu::notInLaunchRegion()) #endif @@ -126,22 +129,22 @@ void PeleLM::calcDivU(int is_init, divu(i,j,k) = 0.0; }); } else if (flagfab.getType(bx) != FabType::regular ) { // EB containing boxes - amrex::ParallelFor(bx, [ rhoY, T, SpecD, Fourier, DiffDiff, r, extRhoY, extRhoH, divu, use_react, flag] + amrex::ParallelFor(bx, [ rhoY, T, SpecD, Fourier, DiffDiff, r, extRhoY, extRhoH, divu, use_react, flag, leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { if ( flag(i,j,k).isCovered() ) { divu(i,j,k) = 0.0; } else { - compute_divu( i, j, k, rhoY, T, SpecD, Fourier, DiffDiff, r, extRhoY, extRhoH, divu, use_react ); + compute_divu( i, j, k, rhoY, T, SpecD, Fourier, DiffDiff, r, extRhoY, extRhoH, divu, use_react, leosparm ); } }); } else #endif { - amrex::ParallelFor(bx, [ rhoY, T, SpecD, Fourier, DiffDiff, r, extRhoY, extRhoH, divu, use_react] + amrex::ParallelFor(bx, [ rhoY, T, SpecD, Fourier, DiffDiff, r, extRhoY, extRhoH, divu, use_react, leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { - compute_divu( i, j, k, rhoY, T, SpecD, Fourier, DiffDiff, r, extRhoY, extRhoH, divu, use_react ); + compute_divu( i, j, k, rhoY, T, SpecD, Fourier, DiffDiff, r, extRhoY, extRhoH, divu, use_react, leosparm ); }); } } @@ -188,6 +191,7 @@ void PeleLM::setTemperature(int lev, const TimeStamp &a_time) { AMREX_ASSERT(a_time == AmrOldTime || a_time == AmrNewTime); auto ldata_p = getLevelDataPtr(lev,a_time); + auto const* leosparm = eos_parms.device_eos_parm(); #ifdef AMREX_USE_OMP #pragma omp parallel if (Gpu::notInLaunchRegion()) @@ -201,10 +205,10 @@ void PeleLM::setTemperature(int lev, const TimeStamp &a_time) { auto const& rhoh = ldata_p->state.const_array(mfi,RHOH); auto const& T = ldata_p->state.array(mfi,TEMP); - amrex::ParallelFor(bx, [rho, rhoY, rhoh, T] + amrex::ParallelFor(bx, [rho, rhoY, rhoh, T, leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { - getTfromHY( i, j, k, rho, rhoY, rhoh, T); + getTfromHY( i, j, k, rho, rhoY, rhoh, T, leosparm); }); } } @@ -268,6 +272,7 @@ PeleLM::adjustPandDivU(std::unique_ptr &advData) auto ldataOld_p = getLevelDataPtr(lev,AmrOldTime); auto ldataNew_p = getLevelDataPtr(lev,AmrNewTime); + auto const* leosparm = eos_parms.device_eos_parm(); ThetaHalft[lev].reset(new MultiFab(grids[lev], dmap[lev], 1, 0, MFInfo(), *m_factory[lev])); @@ -285,8 +290,8 @@ PeleLM::adjustPandDivU(std::unique_ptr &advData) amrex::ParallelFor(bx, [=,pOld=m_pOld,pNew=m_pNew] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { - Real gammaInv_o = getGammaInv(i,j,k,rhoYo,T_o); - Real gammaInv_n = getGammaInv(i,j,k,rhoYn,T_n); + Real gammaInv_o = getGammaInv(i,j,k,rhoYo,T_o,leosparm); + Real gammaInv_n = getGammaInv(i,j,k,rhoYn,T_n,leosparm); theta(i,j,k) = 0.5 * (gammaInv_o/pOld + gammaInv_n/pNew); }); } diff --git a/Source/PeleLMPlot.cpp b/Source/PeleLMPlot.cpp index 21cd471da..bc25e1489 100644 --- a/Source/PeleLMPlot.cpp +++ b/Source/PeleLMPlot.cpp @@ -636,6 +636,7 @@ void PeleLM::initLevelDataFromPlt(int a_lev, ldata_p->gp.setVal(0.0); ProbParm const* lprobparm = prob_parm_d; + auto const* leosparm = eos_parms.device_eos_parm(); // Enforce rho and rhoH consistent with temperature and mixture // TODO the above handles species mapping (to some extent), but nothing enforce @@ -653,7 +654,7 @@ void PeleLM::initLevelDataFromPlt(int a_lev, amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { - auto eos = pele::physics::PhysicsType::eos(); + auto eos = pele::physics::PhysicsType::eos(leosparm); Real massfrac[NUM_SPECIES] = {0.0}; Real sumYs = 0.0; for (int n = 0; n < NUM_SPECIES; n++){ diff --git a/Source/PeleLMReactions.cpp b/Source/PeleLMReactions.cpp index ca533318d..c2f6be823 100644 --- a/Source/PeleLMReactions.cpp +++ b/Source/PeleLMReactions.cpp @@ -217,6 +217,7 @@ void PeleLM::advanceChemistry(int lev, #endif //VisMF::Write(chemAvgDownIR,"avgDownIRNewBA_Level"+std::to_string(lev)+"_step"+std::to_string(m_nstep)); + auto const* leosparm = eos_parms.device_eos_parm(); #ifdef AMREX_USE_OMP #pragma omp parallel if (Gpu::notInLaunchRegion()) #endif @@ -283,7 +284,7 @@ void PeleLM::advanceChemistry(int lev, } else { // Use forcing and averaged down IR to advance species/rhoH/temp Real dt_incr = a_dt; - linearChemForcing(bx, rhoY_o, extF_rhoY, temp_o, rhoH_o, extF_rhoH, fcl, avgIR, dt_incr); + linearChemForcing(bx, rhoY_o, extF_rhoY, temp_o, rhoH_o, extF_rhoH, fcl, avgIR, dt_incr, leosparm); } // Convert CGS -> MKS @@ -389,6 +390,7 @@ void PeleLM::computeInstantaneousReactionRate(int lev, { BL_PROFILE("PeleLM::computeInstantaneousReactionRate()"); auto ldata_p = getLevelDataPtr(lev,a_time); + auto const* leosparm = eos_parms.device_eos_parm(); #ifdef AMREX_USE_EB auto const& ebfact = EBFactory(lev); @@ -415,7 +417,7 @@ void PeleLM::computeInstantaneousReactionRate(int lev, rhoYdot(i,j,k,n) = 0.0; }); } else if (flagfab.getType(bx) != FabType::regular ) { // EB containing boxes - amrex::ParallelFor(bx, [rhoY, rhoH, T, rhoYdot, flag] + amrex::ParallelFor(bx, [rhoY, rhoH, T, rhoYdot, flag, leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { if ( flag(i,j,k).isCovered() ) { @@ -423,16 +425,16 @@ void PeleLM::computeInstantaneousReactionRate(int lev, rhoYdot(i,j,k,n) = 0.0; } } else { - reactionRateRhoY( i, j, k, rhoY, rhoH, T, rhoYdot ); + reactionRateRhoY( i, j, k, rhoY, rhoH, T, rhoYdot, leosparm ); } }); } else #endif { - amrex::ParallelFor(bx, [rhoY, rhoH, T, rhoYdot] + amrex::ParallelFor(bx, [rhoY, rhoH, T, rhoYdot, leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { - reactionRateRhoY( i, j, k, rhoY, rhoH, T, rhoYdot ); + reactionRateRhoY( i, j, k, rhoY, rhoH, T, rhoYdot, leosparm ); }); } } @@ -481,6 +483,7 @@ void PeleLM::getHeatRelease(int a_lev, { auto ldataNew_p = getLevelDataPtr(a_lev,AmrNewTime); auto ldataR_p = getLevelDataReactPtr(a_lev); + auto const* leosparm = eos_parms.device_eos_parm(); #ifdef AMREX_USE_OMP #pragma omp parallel if (Gpu::notInLaunchRegion()) @@ -496,10 +499,10 @@ void PeleLM::getHeatRelease(int a_lev, auto const& T = ldataNew_p->state.const_array(mfi,TEMP); auto const& Hi = EnthFab.array(); auto const& HRR = a_HR->array(mfi); - amrex::ParallelFor(bx, [T, Hi, HRR, react] + amrex::ParallelFor(bx, [T, Hi, HRR, react, leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { - getHGivenT( i, j, k, T, Hi ); + getHGivenT( i, j, k, T, Hi, leosparm); HRR(i,j,k) = 0.0; for (int n = 0; n < NUM_SPECIES; n++) { HRR(i,j,k) -= Hi(i,j,k,n) * react(i,j,k,n); diff --git a/Source/PeleLMSetup.cpp b/Source/PeleLMSetup.cpp index d748086e6..a6eddb4e3 100644 --- a/Source/PeleLMSetup.cpp +++ b/Source/PeleLMSetup.cpp @@ -89,6 +89,8 @@ void PeleLM::Setup() { pp.query("chem_integrator",m_chem_integrator); m_reactor = pele::physics::reactions::ReactorBase::create(m_chem_integrator); m_reactor->init(reactor_type, ncells_chem); + // Give the eosparm to the reactor + m_reactor->set_eos_parm(eos_parms.device_eos_parm()); // For ReactorNull, we need to also skip instantaneous RR used in divU if (m_chem_integrator == "ReactorNull") { m_skipInstantRR = 1; diff --git a/Source/PeleLMTransportProp.cpp b/Source/PeleLMTransportProp.cpp index 798476685..5edc22ba2 100644 --- a/Source/PeleLMTransportProp.cpp +++ b/Source/PeleLMTransportProp.cpp @@ -50,6 +50,7 @@ void PeleLM::calcDiffusivity(const TimeStamp &a_time) { // Transport data pointer auto const* ltransparm = trans_parms.device_trans_parm(); + auto const* leosparm = eos_parms.device_eos_parm(); #ifdef AMREX_USE_OMP #pragma omp parallel if (Gpu::notInLaunchRegion()) @@ -65,14 +66,14 @@ void PeleLM::calcDiffusivity(const TimeStamp &a_time) { // TODO: unity Lewis - amrex::ParallelFor(gbx, [rhoY, T, rhoD, lambda, mu, ltransparm] + amrex::ParallelFor(gbx, [rhoY, T, rhoD, lambda, mu, ltransparm, leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { - getTransportCoeff( i, j, k, rhoY, T, rhoD, lambda, mu, ltransparm); + getTransportCoeff( i, j, k, rhoY, T, rhoD, lambda, mu, ltransparm, leosparm); }); #ifdef PELE_USE_EFIELD auto const& Ks = ldata_p->mob_cc.array(mfi,0); - auto eos = pele::physics::PhysicsType::eos(); + auto eos = pele::physics::PhysicsType::eos(leosparm); Real mwt[NUM_SPECIES] = {0.0}; eos.molecular_weight(mwt); amrex::ParallelFor(gbx, [rhoY, rhoD, T, Ks, mwt, zk=zk] diff --git a/Source/PeleLM_K.H b/Source/PeleLM_K.H index f5c040612..b8c4000b9 100644 --- a/Source/PeleLM_K.H +++ b/Source/PeleLM_K.H @@ -14,11 +14,12 @@ getTransportCoeff(int i, int j, int k, amrex::Array4< amrex::Real> const& lambda, amrex::Array4< amrex::Real> const& mu, pele::physics::transport::TransParm const* trans_parm) noexcept + pele::physics::PhysicsType::transport_type> const* trans_parm, + const pele::physics::eos::EosParm* eosparm) noexcept { using namespace amrex::literals; - auto eos = pele::physics::PhysicsType::eos(); + auto eos = pele::physics::PhysicsType::eos(eosparm); amrex::Real mwtinv[NUM_SPECIES] = {0.0}; eos.inv_molecular_weight(mwtinv); @@ -108,11 +109,12 @@ getPGivenRTY(int i, int j, int k, amrex::Array4 const& rho, amrex::Array4 const& rhoY, amrex::Array4 const& T, - amrex::Array4< amrex::Real> const& P) noexcept + amrex::Array4< amrex::Real> const& P, + const pele::physics::eos::EosParm * eosparm) noexcept { using namespace amrex::literals; - auto eos = pele::physics::PhysicsType::eos(); + auto eos = pele::physics::PhysicsType::eos(eosparm); amrex::Real rhoinv = 1.0_rt / rho(i,j,k); amrex::Real rho_cgs = rho(i,j,k) * 0.001_rt; @@ -138,11 +140,12 @@ compute_divu(int i, int j, int k, amrex::Array4 const& extRhoY, amrex::Array4 const& extRhoH, amrex::Array4< amrex::Real> const& divu, - int do_react) noexcept + int do_react, + const pele::physics::eos::EosParm* eosparm) noexcept { using namespace amrex::literals; - auto eos = pele::physics::PhysicsType::eos(); + auto eos = pele::physics::PhysicsType::eos(eosparm); amrex::Real mwtinv[NUM_SPECIES] = {0.0}; eos.inv_molecular_weight(mwtinv); @@ -314,11 +317,12 @@ void getMwmixGivenRY(int i, int j, int k, amrex::Array4 const& rho, amrex::Array4 const& rhoY, - amrex::Array4< amrex::Real> const& Mwmix) noexcept + amrex::Array4< amrex::Real> const& Mwmix, + const pele::physics::eos::EosParm* eosparm) noexcept { using namespace amrex::literals; - auto eos = pele::physics::PhysicsType::eos(); + auto eos = pele::physics::PhysicsType::eos(eosparm); amrex::Real rhoinv = 1.0_rt / rho(i,j,k); amrex::Real y[NUM_SPECIES] = {0.0_rt}; for (int n = 0; n < NUM_SPECIES; n++) { @@ -460,11 +464,12 @@ AMREX_FORCE_INLINE void getHGivenT(int i, int j, int k, amrex::Array4 const& T, - amrex::Array4< amrex::Real> const& Hi) noexcept + amrex::Array4< amrex::Real> const& Hi, + const pele::physics::eos::EosParm * eosparm) noexcept { using namespace amrex::literals; - auto eos = pele::physics::PhysicsType::eos(); + auto eos = pele::physics::PhysicsType::eos(eosparm); amrex::Real hi_spec[NUM_SPECIES] = {0.0_rt}; eos.T2Hi(T(i,j,k), hi_spec); for (int n = 0; n < NUM_SPECIES; n++) { @@ -525,11 +530,12 @@ getRHmixGivenTY(int i, int j, int k, amrex::Array4 const& rho, amrex::Array4 const& rhoY, amrex::Array4 const& T, - amrex::Array4< amrex::Real> const& Hmix) noexcept + amrex::Array4< amrex::Real> const& Hmix, + const pele::physics::eos::EosParm * eosparm) noexcept { using namespace amrex::literals; - auto eos = pele::physics::PhysicsType::eos(); + auto eos = pele::physics::PhysicsType::eos(eosparm); amrex::Real rhoinv = 1.0_rt / rho(i,j,k); amrex::Real y[NUM_SPECIES] = {0.0_rt}; for (int n = 0; n < NUM_SPECIES; n++) { @@ -547,11 +553,12 @@ getTfromHY(int i, int j, int k, amrex::Array4 const& rho, amrex::Array4 const& rhoY, amrex::Array4 const& rhoH, - amrex::Array4< amrex::Real> const& T) noexcept + amrex::Array4< amrex::Real> const& T, + const pele::physics::eos::EosParm * eosparm) noexcept { using namespace amrex::literals; - auto eos = pele::physics::PhysicsType::eos(); + auto eos = pele::physics::PhysicsType::eos(eosparm); amrex::Real rhoinv = 1.0_rt / rho(i,j,k); amrex::Real y[NUM_SPECIES] = {0.0}; for (int n = 0; n < NUM_SPECIES; n++) { @@ -570,11 +577,12 @@ getCpmixGivenRYT(int i, int j, int k, amrex::Array4 const& rho, amrex::Array4 const& rhoY, amrex::Array4 const& T, - amrex::Array4< amrex::Real> const& cpmix) noexcept + amrex::Array4< amrex::Real> const& cpmix, + const pele::physics::eos::EosParm * eosparm) noexcept { using namespace amrex::literals; - auto eos = pele::physics::PhysicsType::eos(); + auto eos = pele::physics::PhysicsType::eos(eosparm); amrex::Real rhoinv = 1.0_rt / rho(i,j,k); amrex::Real y[NUM_SPECIES] = {0.0}; for (int n = 0; n < NUM_SPECIES; n++) { @@ -601,11 +609,12 @@ buildAdvectionForcing(int i, int j, int k, int const& closed_chamber, int do_react, amrex::Array4< amrex::Real> const& forceY, - amrex::Array4< amrex::Real> const& forceT) noexcept + amrex::Array4< amrex::Real> const& forceT, + const pele::physics::eos::EosParm * eosparm) noexcept { using namespace amrex::literals; - auto eos = pele::physics::PhysicsType::eos(); + auto eos = pele::physics::PhysicsType::eos(eosparm); // Get species enthalpy amrex::Real hi_spec[NUM_SPECIES] = {0.0}; eos.T2Hi(T(i,j,k), hi_spec); @@ -667,11 +676,12 @@ reactionRateRhoY(int i, int j, int k, amrex::Array4 const& rhoY, amrex::Array4 const& rhoH, amrex::Array4 const& T, - amrex::Array4< amrex::Real> const& rhoYdot) noexcept + amrex::Array4< amrex::Real> const& rhoYdot, + const pele::physics::eos::EosParm * eosparm) noexcept { using namespace amrex::literals; - auto eos = pele::physics::PhysicsType::eos(); + auto eos = pele::physics::PhysicsType::eos(eosparm); // Get rho & Ys from rhoYs. amrex::Real rho = 0.0_rt; @@ -724,7 +734,8 @@ linearChemForcing(const amrex::Box& bx, amrex::Array4 const& rhoH_src_in, amrex::Array4 const& FC_in, amrex::Array4 const& avgIR, - amrex::Real &dt_react) + amrex::Real &dt_react, + const pele::physics::eos::EosParm * eosparm) { ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept @@ -754,7 +765,7 @@ linearChemForcing(const amrex::Box& bx, // Get curr estimate of T amrex::Real T_loc = T_in(i,j,k,0); - auto eos = pele::physics::PhysicsType::eos(); + auto eos = pele::physics::PhysicsType::eos(eosparm); eos.HY2T(H_loc,Y_loc,T_loc); T_in(i,j,k,0) = T_loc; @@ -790,12 +801,13 @@ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getGammaInv(int i, int j, int k, - amrex::Array4 const& rhoY, - amrex::Array4 const& T) noexcept + amrex::Array4 const& rhoY, + amrex::Array4 const& T, + const pele::physics::eos::EosParm * eosparm) noexcept { using namespace amrex::literals; - auto eos = pele::physics::PhysicsType::eos(); + auto eos = pele::physics::PhysicsType::eos(eosparm); // Get rho & Y from rhoY amrex::Real rho = 0.0_rt; for (int n = 0; n < NUM_SPECIES; n++) { From fcb7c2c85064195f3d3943f1ba4e6951b6bf6a02 Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Mon, 20 Jun 2022 15:34:25 -0600 Subject: [PATCH 03/27] dont hardcode reference to species ID, instead normalize by rescaling --- Source/PeleLMPlot.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/PeleLMPlot.cpp b/Source/PeleLMPlot.cpp index bc25e1489..67e15f66d 100644 --- a/Source/PeleLMPlot.cpp +++ b/Source/PeleLMPlot.cpp @@ -657,13 +657,14 @@ void PeleLM::initLevelDataFromPlt(int a_lev, auto eos = pele::physics::PhysicsType::eos(leosparm); Real massfrac[NUM_SPECIES] = {0.0}; Real sumYs = 0.0; + // TODO : is this the best way to Init when species don't add to 1? for (int n = 0; n < NUM_SPECIES; n++){ massfrac[n] = rhoY_arr(i,j,k,n); - if (n != N2_ID) { - sumYs += massfrac[n]; - } + sumYs += massfrac[n]; + } + for (int n = 0; n < NUM_SPECIES; n++){ + massfrac[n] /= sumYs; } - massfrac[N2_ID] = 1.0 - sumYs; // Get density Real P_cgs = lprobparm->P_mean * 10.0; From 83fd3be96185f5b61f97dfb18767fdffd0e62ecf Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Mon, 20 Jun 2022 15:34:53 -0600 Subject: [PATCH 04/27] Manifold EOS and Transport defines in makefile --- Utils/Make.PeleLMeX | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Utils/Make.PeleLMeX b/Utils/Make.PeleLMeX index 25e1f7117..5abccf08f 100644 --- a/Utils/Make.PeleLMeX +++ b/Utils/Make.PeleLMeX @@ -60,6 +60,9 @@ endif ifeq ($(Eos_Model),$(filter $(Eos_Model),Soave-Redlich-Kwong)) DEFINES += -DUSE_SRK_EOS endif +ifeq ($(Eos_Model),$(filter $(Eos_Model),Manifold)) + DEFINES += -DUSE_MANIFOLD_EOS +endif # Transport model switches ifeq ($(Transport_Model), Simple) @@ -75,6 +78,9 @@ endif ifeq ($(Transport_Model), Sutherland) DEFINES += -DUSE_SUTHERLAND_TRANSPORT endif +ifeq ($(Transport_Model), Manifold) + DEFINES += -DUSE_MANIFOLD_TRANSPORT +endif ifeq ($(PELE_USE_KLU), TRUE) DEFINES += -DPELE_USE_KLU From 3419b443ee9f6b4597f4f1ae2aa562e9284f53ff Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Mon, 20 Jun 2022 17:00:53 -0600 Subject: [PATCH 05/27] Add templated divu and transport functions for Manifold EOS --- Source/PeleLMDeriveFunc.cpp | 14 ++--- Source/PeleLMEos.cpp | 14 ++--- Source/PeleLMTransportProp.cpp | 8 +-- Source/PeleLM_K.H | 109 +++++++++++++++++++++++++++++++-- 4 files changed, 121 insertions(+), 24 deletions(-) diff --git a/Source/PeleLMDeriveFunc.cpp b/Source/PeleLMDeriveFunc.cpp index 02d0e6993..f5c8e0657 100644 --- a/Source/PeleLMDeriveFunc.cpp +++ b/Source/PeleLMDeriveFunc.cpp @@ -248,7 +248,7 @@ void pelelm_derprogvar (PeleLM* a_pelelm, const Box& bx, FArrayBox& derfab, int { AMREX_ASSERT(derfab.box().contains(bx)); AMREX_ASSERT(statefab.box().contains(bx)); - AMREX_ASSERT(ncomp == 1); + AMREX_ASSERT(ncomp == 1); if (a_pelelm->m_C0 < 0.0) amrex::Abort("Progress variable not initialized"); @@ -263,22 +263,22 @@ void pelelm_derprogvar (PeleLM* a_pelelm, const Box& bx, FArrayBox& derfab, int amrex::GpuArray Cweights; for (int n=0; nm_Cweights[n]; - } + } amrex::ParallelFor(bx, [=,revert=a_pelelm->m_Crevert] AMREX_GPU_DEVICE (int i, int j, int k) noexcept - { + { amrex::Real rho_inv = 1.0_rt / density(i,j,k); prog_var(i,j,k) = 0.0_rt; for (int n = 0; n(i, j, k, rhoY, T, rhoD, lambda, mu, ltransparm, leosparm); }); } @@ -360,6 +360,6 @@ void pelelm_derlambda (PeleLM* a_pelelm, const Box& bx, FArrayBox& derfab, int d amrex::ParallelFor(bx, [rhoY,T,rhoD,lambda,mu,ltransparm,leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { - getTransportCoeff(i, j, k, rhoY, T, rhoD, lambda, mu, ltransparm, leosparm); + getTransportCoeff(i, j, k, rhoY, T, rhoD, lambda, mu, ltransparm, leosparm); }); } diff --git a/Source/PeleLMEos.cpp b/Source/PeleLMEos.cpp index 9319cc30d..398f45b2f 100644 --- a/Source/PeleLMEos.cpp +++ b/Source/PeleLMEos.cpp @@ -128,14 +128,14 @@ void PeleLM::calcDivU(int is_init, { divu(i,j,k) = 0.0; }); - } else if (flagfab.getType(bx) != FabType::regular ) { // EB containing boxes + } else if (flagfab.getType(bx) != FabType::regular ) { // EB containing boxes amrex::ParallelFor(bx, [ rhoY, T, SpecD, Fourier, DiffDiff, r, extRhoY, extRhoH, divu, use_react, flag, leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { if ( flag(i,j,k).isCovered() ) { divu(i,j,k) = 0.0; } else { - compute_divu( i, j, k, rhoY, T, SpecD, Fourier, DiffDiff, r, extRhoY, extRhoH, divu, use_react, leosparm ); + compute_divu( i, j, k, rhoY, T, SpecD, Fourier, DiffDiff, r, extRhoY, extRhoH, divu, use_react, leosparm ); } }); } else @@ -144,7 +144,7 @@ void PeleLM::calcDivU(int is_init, amrex::ParallelFor(bx, [ rhoY, T, SpecD, Fourier, DiffDiff, r, extRhoY, extRhoH, divu, use_react, leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { - compute_divu( i, j, k, rhoY, T, SpecD, Fourier, DiffDiff, r, extRhoY, extRhoH, divu, use_react, leosparm ); + compute_divu( i, j, k, rhoY, T, SpecD, Fourier, DiffDiff, r, extRhoY, extRhoH, divu, use_react, leosparm ); }); } } @@ -290,8 +290,8 @@ PeleLM::adjustPandDivU(std::unique_ptr &advData) amrex::ParallelFor(bx, [=,pOld=m_pOld,pNew=m_pNew] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { - Real gammaInv_o = getGammaInv(i,j,k,rhoYo,T_o,leosparm); - Real gammaInv_n = getGammaInv(i,j,k,rhoYn,T_n,leosparm); + Real gammaInv_o = getGammaInv(i,j,k,rhoYo,T_o,leosparm); + Real gammaInv_n = getGammaInv(i,j,k,rhoYn,T_n,leosparm); theta(i,j,k) = 0.5 * (gammaInv_o/pOld + gammaInv_n/pNew); }); } @@ -317,10 +317,10 @@ PeleLM::adjustPandDivU(std::unique_ptr &advData) // mac_divu is now delta_S advData->mac_divu[lev].plus(-Sbar,0,1); } - + // Compute 1/Volume * int(U_inflow)dA across all boundary faces amrex::Real umacFluxBalance = AMREX_D_TERM( m_domainUmacFlux[0] + m_domainUmacFlux[1], - + m_domainUmacFlux[2] + m_domainUmacFlux[3], + + m_domainUmacFlux[2] + m_domainUmacFlux[3], + m_domainUmacFlux[4] + m_domainUmacFlux[5]); Real divu_vol = umacFluxBalance/m_uncoveredVol; diff --git a/Source/PeleLMTransportProp.cpp b/Source/PeleLMTransportProp.cpp index 5edc22ba2..fbcca60a3 100644 --- a/Source/PeleLMTransportProp.cpp +++ b/Source/PeleLMTransportProp.cpp @@ -69,7 +69,7 @@ void PeleLM::calcDiffusivity(const TimeStamp &a_time) { amrex::ParallelFor(gbx, [rhoY, T, rhoD, lambda, mu, ltransparm, leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { - getTransportCoeff( i, j, k, rhoY, T, rhoD, lambda, mu, ltransparm, leosparm); + getTransportCoeff( i, j, k, rhoY, T, rhoD, lambda, mu, ltransparm, leosparm); }); #ifdef PELE_USE_EFIELD auto const& Ks = ldata_p->mob_cc.array(mfi,0); @@ -123,7 +123,7 @@ PeleLM::getDiffusivity(int lev, int beta_comp, int ncomp, int doZeroVisc, for (MFIter mfi(beta_cc,TilingIfNotGPU()); mfi.isValid();++mfi) { for (int idim = 0; idim < AMREX_SPACEDIM; idim++) - { + { const Box ebx = mfi.nodaltilebox(idim); const Box& edomain = amrex::surroundingNodes(domain,idim); const auto& diff_c = beta_cc.const_array(mfi,beta_comp); @@ -141,11 +141,11 @@ PeleLM::getDiffusivity(int lev, int beta_comp, int ncomp, int doZeroVisc, } } #endif - + // Enable zeroing diffusivity on faces to produce walls if (doZeroVisc) { const auto geomdata = geom[lev].data(); - for (int idim = 0; idim < AMREX_SPACEDIM; idim++) { + for (int idim = 0; idim < AMREX_SPACEDIM; idim++) { const Box& edomain = amrex::surroundingNodes(domain,idim); #ifdef AMREX_USE_OMP #pragma omp parallel if (Gpu::notInLaunchRegion()) diff --git a/Source/PeleLM_K.H b/Source/PeleLM_K.H index b8c4000b9..053dc1bb9 100644 --- a/Source/PeleLM_K.H +++ b/Source/PeleLM_K.H @@ -4,6 +4,7 @@ #include #include +template AMREX_GPU_DEVICE AMREX_FORCE_INLINE void @@ -60,6 +61,55 @@ getTransportCoeff(int i, int j, int k, mu(i,j,k) = mu_cgs * 1.0e-1_rt; } +template <> +AMREX_GPU_DEVICE +AMREX_FORCE_INLINE +void +getTransportCoeff(int i, int j, int k, + amrex::Array4 const& rhoY, + amrex::Array4 const& T, + amrex::Array4< amrex::Real> const& rhoDi, + amrex::Array4< amrex::Real> const& lambda, + amrex::Array4< amrex::Real> const& mu, + pele::physics::transport::TransParm const* trans_parm, + const pele::physics::eos::EosParm* eosparm) noexcept +{ + using namespace amrex::literals; + + // Get rho & Y from rhoY + amrex::Real rho, rhoinv; + amrex::Real y[NUM_SPECIES] = {0.0}; + amrex::Real massdens[NUM_SPECIES]; + for (int n = 0; n < NUM_SPECIES; n++) { + massdens[n] = rhoY(i,j,k,n); + } + auto eos = pele::physics::PhysicsType::eos(eosparm); + eos.RY2RRinvY(massdens, rho, rhoinv, y); + + rho *= 1.0e-3_rt; // MKS -> CGS conversion + amrex::Real rhoDi_cgs[NUM_SPECIES] = {0.0}; + amrex::Real lambda_cgs = 0.0_rt; + amrex::Real mu_cgs = 0.0_rt; + amrex::Real dummy_xi = 0.0_rt; + amrex::Real Tloc = T(i,j,k); + + bool get_xi = false; + bool get_mu = true; + bool get_lam = false; + bool get_Ddiag = true; + auto trans = pele::physics::PhysicsType::transport(); + trans.transport(get_xi, get_mu, get_lam, get_Ddiag, Tloc, + rho, y, rhoDi_cgs, mu_cgs, dummy_xi, lambda_cgs, trans_parm); + + // Do CGS -> MKS conversions + for (int n = 0; n < NUM_SPECIES; n++) { + rhoDi(i,j,k,n) = rhoDi_cgs[n] * 1.0e-1_rt; + } + lambda(i,j,k) = 0.0; // lambda_cgs * 1.0e-5_rt; No need to carry lambda for manifold + mu(i,j,k) = mu_cgs * 1.0e-1_rt; +} + AMREX_GPU_DEVICE AMREX_FORCE_INLINE void @@ -127,6 +177,7 @@ getPGivenRTY(int i, int j, int k, P(i,j,k) = P(i,j,k) * 0.1_rt; // CGS -> MKS conversion } +template AMREX_GPU_DEVICE AMREX_FORCE_INLINE void @@ -176,10 +227,56 @@ compute_divu(int i, int j, int k, for (int n = 0; n < NUM_SPECIES; n++) { amrex::Real specTerm = specDiff(i,j,k,n) + extRhoY(i,j,k,n); if (do_react) specTerm += rhoYdot(i,j,k,n); - divu(i,j,k) += specTerm * ( mwtinv[n] * Wbar * rhoinv - hi[n] * denominv ); + divu(i,j,k) += specTerm * ( mwtinv[n] * Wbar * rhoinv - hi[n] * denominv ); } } +template <> +AMREX_GPU_HOST_DEVICE +AMREX_FORCE_INLINE +void +compute_divu(int i, int j, int k, + amrex::Array4 const& rhoY, + amrex::Array4 const& T, + amrex::Array4 const& specDiff, + amrex::Array4 const& /*tempDiff*/, + amrex::Array4 const& /*specEnthDiff*/, + amrex::Array4 const& rhoYdot, + amrex::Array4 const& /*extRhoY*/, + amrex::Array4 const& /*extRhoH*/, + amrex::Array4< amrex::Real> const& divu, + int do_react, + const pele::physics::eos::EosParm* eosparm) noexcept +{ + using namespace amrex::literals; + + // Get rho & Ys from rhoYs. + amrex::Real rho, rhoinv; + amrex::Real y[NUM_SPECIES] = {0.0_rt}; + amrex::Real massdens[NUM_SPECIES] = {0.0_rt}; + for (int n = 0; n < NUM_SPECIES; n++) { + massdens[n] = rhoY(i,j,k,n); + } + + auto eos = pele::physics::PhysicsType::eos(eosparm); + eos.RY2RRinvY(massdens, rho, rhoinv, y); + amrex::Real derivs[NUM_SPECIES-1] = {0.0}; + eos.Y2dRdY(y, derivs); + + divu(i,j,k) = 0.0; + + for (int n = 0; n < NUM_SPECIES-1; n++) { + derivs[n] *= 1.0e3; + divu(i,j,k) += derivs[n] * specDiff(i,j,k,n); + if (do_react) divu(i,j,k) += derivs[n] * (rhoYdot(i,j,k,n)); + // TODO: deal with extRhoY (below is not correct because derivs are dRho/dY, not dRho/dRhoY) + // divu(i,j,k) += derivs[n] * (extRhoY(i,j,k,n)); + } + // First -rhoinv: from divu = -1/rho Drho/Dt + // Second rhoinv: convert from rho*Dxi/Dt (which RHS terms are evaluated for) to Dxi/Dt + divu(i,j,k) *= -rhoinv*rhoinv; +} + AMREX_GPU_DEVICE AMREX_FORCE_INLINE void @@ -475,7 +572,7 @@ getHGivenT(int i, int j, int k, for (int n = 0; n < NUM_SPECIES; n++) { Hi(i,j,k,n) = hi_spec[n] * 0.0001_rt; // CGS -> MKS conversion } - + } AMREX_GPU_DEVICE @@ -627,7 +724,7 @@ buildAdvectionForcing(int i, int j, int k, for (int n = 0; n < NUM_SPECIES; n++) { y[n] = rhoY(i,j,k,n) * rhoinv; // get y forceY(i,j,k,n) = dn(i,j,k,n); // forceY = Dnk - if (do_react) forceY(i,j,k,n) += r(i,j,k,n); // + Rk + if (do_react) forceY(i,j,k,n) += r(i,j,k,n); // + Rk forceT(i,j,k) -= forceY(i,j,k,n) * hi_spec[n] * 0.0001_rt; // forceT -= Sum{ hk. (Rk + Dnk) } with CGS -> MKS conversion of hk forceY(i,j,k,n) += extRhoY(i,j,k,n); } @@ -698,7 +795,7 @@ reactionRateRhoY(int i, int j, int k, amrex::Real Tloc = T(i,j,k); amrex::Real H = rhoH(i,j,k) * rhoinv * 1.0e4_rt; // Include MKS -> CGS conversion eos.HY2T(H, y, Tloc); - + // Get wdot. rho = rho * 0.001_rt; // rho MKS -> CGS amrex::Real wdot[NUM_SPECIES] = {0.0_rt}; @@ -813,7 +910,7 @@ getGammaInv(int i, int j, int k, for (int n = 0; n < NUM_SPECIES; n++) { rho += rhoY(i,j,k,n); } - amrex::Real rhoinv = 1.0_rt / rho; + amrex::Real rhoinv = 1.0_rt / rho; amrex::Real y[NUM_SPECIES] = {0.0}; for (int n = 0; n < NUM_SPECIES; n++) { y[n] = rhoY(i,j,k,n) * rhoinv; @@ -823,7 +920,7 @@ getGammaInv(int i, int j, int k, eos.TY2Cp(T(i,j,k), y, cpmix); amrex::Real cvmix = 0.0_rt; eos.TY2Cv(T(i,j,k), y, cvmix); - + amrex::Real gammainv = cvmix / cpmix; return gammainv; From 9c0bf3ef090d4b1f052909d9e972e186087e8a1e Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Mon, 20 Jun 2022 17:47:40 -0600 Subject: [PATCH 06/27] eosparm for initdata and bcnormal --- Exec/RegTests/FlameSheet/pelelm_prob.H | 10 +-- Source/PeleLMBC.cpp | 76 +++++++++++++---------- Source/PeleLMBCfill.H | 84 ++++++++++++++++---------- Source/PeleLMInit.cpp | 7 ++- 4 files changed, 105 insertions(+), 72 deletions(-) diff --git a/Exec/RegTests/FlameSheet/pelelm_prob.H b/Exec/RegTests/FlameSheet/pelelm_prob.H index 6bf71d29d..063a51b83 100644 --- a/Exec/RegTests/FlameSheet/pelelm_prob.H +++ b/Exec/RegTests/FlameSheet/pelelm_prob.H @@ -19,7 +19,8 @@ void pelelm_initdata(int i, int j, int k, amrex::Array4 const& /*aux*/, amrex::GeometryData const& geomdata, ProbParm const& prob_parm, - pele::physics::PMF::PmfData::DataContainer const * pmf_data) + pele::physics::PMF::PmfData::DataContainer const * pmf_data, + const pele::physics::eos::EosParm* eosparm) { const amrex::Real* prob_lo = geomdata.ProbLo(); const amrex::Real* prob_hi = geomdata.ProbHi(); @@ -41,11 +42,11 @@ void pelelm_initdata(int i, int j, int k, amrex::Real massfrac[NUM_SPECIES] = {0.0}; amrex::Real pert = 0.0; - if (prob_parm.pertmag > 0.0) + if (prob_parm.pertmag > 0.0) { #if ( AMREX_SPACEDIM == 2 ) - pert = prob_parm.pertmag * + pert = prob_parm.pertmag * (1.0 * std::sin(2 * Pi * 4 * x / Lx) + 1.023 * std::sin(2 * Pi * 2 * (x - 0.004598) / Lx) + 0.945 * std::sin(2 * Pi * 3 * (x - 0.00712435) / Lx) + @@ -122,7 +123,8 @@ bcnormal( const amrex::Real /*time*/, amrex::GeometryData const& geomdata, ProbParm const& prob_parm, - pele::physics::PMF::PmfData::DataContainer const *pmf_data) + pele::physics::PMF::PmfData::DataContainer const *pmf_data, + const pele::physics::eos::EosParm* eosparm) { const amrex::Real* prob_lo = geomdata.ProbLo(); amrex::GpuArray pmf_vals = {0.0}; diff --git a/Source/PeleLMBC.cpp b/Source/PeleLMBC.cpp index 1034337d0..ba7454988 100644 --- a/Source/PeleLMBC.cpp +++ b/Source/PeleLMBC.cpp @@ -85,14 +85,14 @@ InterpBase* PeleLM::getInterpolator() { // // Get EB-aware interpolater when needed // -#ifdef AMREX_USE_EB +#ifdef AMREX_USE_EB return (EBFactory(0).isAllRegular()) ? &mf_cell_cons_interp : &eb_mf_cell_cons_interp; #else return &mf_cell_cons_interp; #endif } - + void PeleLM::setBoundaryConditions() { // Initialize the BCRecs @@ -116,7 +116,7 @@ void PeleLM::setBoundaryConditions() { m_bcrec_state[VELX+idim].setHi(idim2,tang_vel_bc[hi_bc[idim2]]); } } - } + } // General forces: use int_dir in interior and foextrap otherwise for (int i = 0; i < sizeForceBC; i++) { @@ -328,6 +328,7 @@ void PeleLM::fillpatch_state(int lev, int nGhost) { ProbParm const* lprobparm = prob_parm_d; pele::physics::PMF::PmfData::DataContainer const* lpmfdata = pmf_data.getDeviceData(); + auto const* leosparm = eos_parms.device_eos_parm(); int nCompState = ( m_incompressible ) ? AMREX_SPACEDIM : NVAR; @@ -335,7 +336,7 @@ void PeleLM::fillpatch_state(int lev, if (lev == 0) { PhysBCFunct> bndry_func(geom[lev], fetchBCRecArray(0,nCompState), - PeleLMCCFillExtDirState{lprobparm, lpmfdata, + PeleLMCCFillExtDirState{lprobparm, lpmfdata, leosparm, m_nAux, turb_inflow.is_initialized()}); FillPatchSingleLevel(a_state, IntVect(nGhost), a_time, {&(m_leveldata_old[lev]->state),&(m_leveldata_new[lev]->state)}, @@ -346,10 +347,10 @@ void PeleLM::fillpatch_state(int lev, auto* mapper = getInterpolator(); PhysBCFunct> crse_bndry_func(geom[lev-1], fetchBCRecArray(0,nCompState), - PeleLMCCFillExtDirState{lprobparm, lpmfdata, + PeleLMCCFillExtDirState{lprobparm, lpmfdata, leosparm, m_nAux, turb_inflow.is_initialized()}); PhysBCFunct> fine_bndry_func(geom[lev], fetchBCRecArray(0,nCompState), - PeleLMCCFillExtDirState{lprobparm, lpmfdata, + PeleLMCCFillExtDirState{lprobparm, lpmfdata, leosparm, m_nAux, turb_inflow.is_initialized()}); FillPatchTwoLevels(a_state, IntVect(nGhost), a_time, {&(m_leveldata_old[lev-1]->state),&(m_leveldata_new[lev-1]->state)}, @@ -372,11 +373,12 @@ void PeleLM::fillpatch_density(int lev, int nGhost) { ProbParm const* lprobparm = prob_parm_d; pele::physics::PMF::PmfData::DataContainer const* lpmfdata = pmf_data.getDeviceData(); + auto const* leosparm = eos_parms.device_eos_parm(); if (lev == 0) { // Density PhysBCFunct> bndry_func_rho(geom[lev], fetchBCRecArray(DENSITY,1), - PeleLMCCFillExtDirDens{lprobparm, lpmfdata, m_nAux}); + PeleLMCCFillExtDirDens{lprobparm, lpmfdata, leosparm, m_nAux}); FillPatchSingleLevel(a_density, IntVect(nGhost), a_time, {&(m_leveldata_old[lev]->state),&(m_leveldata_new[lev]->state)}, {m_t_old[lev], m_t_new[lev]},DENSITY,rho_comp,1,geom[lev], bndry_func_rho, 0); @@ -387,10 +389,10 @@ void PeleLM::fillpatch_density(int lev, auto* mapper = getInterpolator(); // Density - PhysBCFunct> crse_bndry_func_rho(geom[lev-1], fetchBCRecArray(DENSITY,1), - PeleLMCCFillExtDirDens{lprobparm, lpmfdata, m_nAux}); + PhysBCFunct> crse_bndry_func_rho(geom[lev-1], fetchBCRecArray(DENSITY,1), + PeleLMCCFillExtDirDens{lprobparm, lpmfdata, leosparm, m_nAux}); PhysBCFunct> fine_bndry_func_rho(geom[lev], fetchBCRecArray(DENSITY,1), - PeleLMCCFillExtDirDens{lprobparm, lpmfdata, m_nAux}); + PeleLMCCFillExtDirDens{lprobparm, lpmfdata, leosparm, m_nAux}); FillPatchTwoLevels(a_density, IntVect(nGhost), a_time, {&(m_leveldata_old[lev-1]->state),&(m_leveldata_new[lev-1]->state)}, {m_t_old[lev-1], m_t_new[lev-1]}, @@ -410,11 +412,12 @@ void PeleLM::fillpatch_species(int lev, int nGhost) { ProbParm const* lprobparm = prob_parm_d; pele::physics::PMF::PmfData::DataContainer const* lpmfdata = pmf_data.getDeviceData(); + auto const* leosparm = eos_parms.device_eos_parm(); if (lev == 0) { // Species PhysBCFunct> bndry_func(geom[lev], fetchBCRecArray(FIRSTSPEC,NUM_SPECIES), - PeleLMCCFillExtDirSpec{lprobparm, lpmfdata, m_nAux}); + PeleLMCCFillExtDirSpec{lprobparm, lpmfdata, leosparm, m_nAux}); FillPatchSingleLevel(a_species, IntVect(nGhost), a_time, {&(m_leveldata_old[lev]->state),&(m_leveldata_new[lev]->state)}, {m_t_old[lev], m_t_new[lev]},FIRSTSPEC,rhoY_comp,NUM_SPECIES,geom[lev], bndry_func, 0); @@ -425,9 +428,9 @@ void PeleLM::fillpatch_species(int lev, // Species PhysBCFunct> crse_bndry_func(geom[lev-1], fetchBCRecArray(FIRSTSPEC,NUM_SPECIES), - PeleLMCCFillExtDirSpec{lprobparm, lpmfdata, m_nAux}); + PeleLMCCFillExtDirSpec{lprobparm, lpmfdata, leosparm, m_nAux}); PhysBCFunct> fine_bndry_func(geom[lev], fetchBCRecArray(FIRSTSPEC,NUM_SPECIES), - PeleLMCCFillExtDirSpec{lprobparm, lpmfdata, m_nAux}); + PeleLMCCFillExtDirSpec{lprobparm, lpmfdata, leosparm, m_nAux}); FillPatchTwoLevels(a_species, IntVect(nGhost), a_time, {&(m_leveldata_old[lev-1]->state),&(m_leveldata_new[lev-1]->state)}, {m_t_old[lev-1], m_t_new[lev-1]}, @@ -447,9 +450,10 @@ void PeleLM::fillpatch_temp(int lev, int nGhost) { ProbParm const* lprobparm = prob_parm_d; pele::physics::PMF::PmfData::DataContainer const* lpmfdata = pmf_data.getDeviceData(); + auto const* leosparm = eos_parms.device_eos_parm(); if (lev == 0) { PhysBCFunct> bndry_func(geom[lev], fetchBCRecArray(TEMP,1), - PeleLMCCFillExtDirTemp{lprobparm, lpmfdata, m_nAux}); + PeleLMCCFillExtDirTemp{lprobparm, lpmfdata, leosparm, m_nAux}); FillPatchSingleLevel(a_temp, IntVect(nGhost), a_time, {&(m_leveldata_old[lev]->state),&(m_leveldata_new[lev]->state)}, {m_t_old[lev], m_t_new[lev]},TEMP,temp_comp,1,geom[lev], bndry_func, 0); @@ -459,9 +463,9 @@ void PeleLM::fillpatch_temp(int lev, auto* mapper = getInterpolator(); PhysBCFunct> crse_bndry_func(geom[lev-1], fetchBCRecArray(TEMP,1), - PeleLMCCFillExtDirTemp{lprobparm, lpmfdata, m_nAux}); + PeleLMCCFillExtDirTemp{lprobparm, lpmfdata, leosparm, m_nAux}); PhysBCFunct> fine_bndry_func(geom[lev], fetchBCRecArray(TEMP,1), - PeleLMCCFillExtDirTemp{lprobparm, lpmfdata, m_nAux}); + PeleLMCCFillExtDirTemp{lprobparm, lpmfdata, leosparm, m_nAux}); FillPatchTwoLevels(a_temp, IntVect(nGhost), a_time, {&(m_leveldata_old[lev-1]->state),&(m_leveldata_new[lev-1]->state)}, {m_t_old[lev-1], m_t_new[lev-1]}, @@ -544,11 +548,12 @@ void PeleLM::fillpatch_nE(int lev, int nGhost) { ProbParm const* lprobparm = prob_parm_d; pele::physics::PMF::PmfData::DataContainer const* lpmfdata = pmf_data.getDeviceData(); + auto const* leosparm = eos_parms.device_eos_parm(); if (lev == 0) { // nE PhysBCFunct> bndry_func(geom[lev], fetchBCRecArray(NE,1), - PeleLMCCFillExtDirnE{lprobparm, lpmfdata, m_nAux}); + PeleLMCCFillExtDirnE{lprobparm, lpmfdata, leosparm, m_nAux}); FillPatchSingleLevel(a_nE, IntVect(nGhost), a_time, {&(m_leveldata_old[lev]->nE),&(m_leveldata_new[lev]->nE)}, {m_t_old[lev], m_t_new[lev]},0,0,1,geom[lev], bndry_func, 0); @@ -559,10 +564,10 @@ void PeleLM::fillpatch_nE(int lev, auto* mapper = getInterpolator(); // nE - PhysBCFunct> crse_bndry_func(geom[lev-1], fetchBCRecArray(NE,1), - PeleLMCCFillExtDirnE{lprobparm, lpmfdata, m_nAux}); + PhysBCFunct> crse_bndry_func(geom[lev-1], fetchBCRecArray(NE,1), + PeleLMCCFillExtDirnE{lprobparm, lpmfdata, leosparm, m_nAux}); PhysBCFunct> fine_bndry_func(geom[lev], fetchBCRecArray(NE,1), - PeleLMCCFillExtDirnE{lprobparm, lpmfdata, m_nAux}); + PeleLMCCFillExtDirnE{lprobparm, lpmfdata, leosparm, m_nAux}); FillPatchTwoLevels(a_nE, IntVect(nGhost), a_time, {&(m_leveldata_old[lev-1]->nE),&(m_leveldata_new[lev-1]->nE)}, {m_t_old[lev-1], m_t_new[lev-1]}, @@ -582,11 +587,12 @@ void PeleLM::fillpatch_phiV(int lev, int nGhost) { ProbParm const* lprobparm = prob_parm_d; pele::physics::PMF::PmfData::DataContainer const* lpmfdata = pmf_data.getDeviceData(); + auto const* leosparm = eos_parms.device_eos_parm(); if (lev == 0) { // Density PhysBCFunct> bndry_func(geom[lev], fetchBCRecArray(PHIV,1), - PeleLMCCFillExtDirPhiV{lprobparm, lpmfdata, m_nAux}); + PeleLMCCFillExtDirPhiV{lprobparm, lpmfdata, leosparm, m_nAux}); FillPatchSingleLevel(a_phiV, IntVect(nGhost), a_time, {&(m_leveldata_old[lev]->phiV),&(m_leveldata_new[lev]->phiV)}, {m_t_old[lev], m_t_new[lev]},0,0,1,geom[lev], bndry_func, 0); @@ -597,10 +603,10 @@ void PeleLM::fillpatch_phiV(int lev, auto* mapper = getInterpolator(); // Density - PhysBCFunct> crse_bndry_func(geom[lev-1], fetchBCRecArray(PHIV,1), - PeleLMCCFillExtDirPhiV{lprobparm, lpmfdata, m_nAux}); + PhysBCFunct> crse_bndry_func(geom[lev-1], fetchBCRecArray(PHIV,1), + PeleLMCCFillExtDirPhiV{lprobparm, lpmfdata, leosparm, m_nAux}); PhysBCFunct> fine_bndry_func(geom[lev], fetchBCRecArray(PHIV,1), - PeleLMCCFillExtDirPhiV{lprobparm, lpmfdata, m_nAux}); + PeleLMCCFillExtDirPhiV{lprobparm, lpmfdata, leosparm, m_nAux}); FillPatchTwoLevels(a_phiV, IntVect(nGhost), a_time, {&(m_leveldata_old[lev-1]->phiV),&(m_leveldata_new[lev-1]->phiV)}, {m_t_old[lev-1], m_t_new[lev-1]}, @@ -683,6 +689,7 @@ void PeleLM::fillcoarsepatch_state(int lev, AMREX_ASSERT(lev>0); ProbParm const* lprobparm = prob_parm_d; pele::physics::PMF::PmfData::DataContainer const* lpmfdata = pmf_data.getDeviceData(); + auto const* leosparm = eos_parms.device_eos_parm(); int nCompState = ( m_incompressible ) ? AMREX_SPACEDIM : NVAR; @@ -692,10 +699,10 @@ void PeleLM::fillcoarsepatch_state(int lev, auto* mapper = getInterpolator(); PhysBCFunct> crse_bndry_func(geom[lev-1], fetchBCRecArray(0,nCompState), - PeleLMCCFillExtDirState{lprobparm, lpmfdata, + PeleLMCCFillExtDirState{lprobparm, lpmfdata, leosparm, m_nAux, turb_inflow.is_initialized()}); PhysBCFunct> fine_bndry_func(geom[lev], fetchBCRecArray(0,nCompState), - PeleLMCCFillExtDirState{lprobparm, lpmfdata, + PeleLMCCFillExtDirState{lprobparm, lpmfdata, leosparm, m_nAux, turb_inflow.is_initialized()}); InterpFromCoarseLevel(a_state, IntVect(nGhost), a_time, m_leveldata_new[lev-1]->state, 0, 0, nCompState, @@ -775,14 +782,15 @@ void PeleLM::fillcoarsepatch_nE(int lev, int nGhost) { ProbParm const* lprobparm = prob_parm_d; pele::physics::PMF::PmfData::DataContainer const* lpmfdata = pmf_data.getDeviceData(); + auto const* leosparm = eos_parms.device_eos_parm(); // Interpolator auto* mapper = getInterpolator(); PhysBCFunct> crse_bndry_func(geom[lev-1], fetchBCRecArray(NE,1), - PeleLMCCFillExtDirnE{lprobparm, lpmfdata, m_nAux}); + PeleLMCCFillExtDirnE{lprobparm, lpmfdata, leosparm, m_nAux}); PhysBCFunct> fine_bndry_func(geom[lev], fetchBCRecArray(NE,1), - PeleLMCCFillExtDirnE{lprobparm, lpmfdata, m_nAux}); + PeleLMCCFillExtDirnE{lprobparm, lpmfdata, leosparm, m_nAux}); InterpFromCoarseLevel(a_nE, IntVect(nGhost), a_time, m_leveldata_new[lev-1]->nE, 0, 0, 1, geom[lev-1], geom[lev], @@ -797,14 +805,15 @@ void PeleLM::fillcoarsepatch_phiV(int lev, int nGhost) { ProbParm const* lprobparm = prob_parm_d; pele::physics::PMF::PmfData::DataContainer const* lpmfdata = pmf_data.getDeviceData(); + auto const* leosparm = eos_parms.device_eos_parm(); // Interpolator auto* mapper = getInterpolator(); PhysBCFunct> crse_bndry_func(geom[lev-1], fetchBCRecArray(PHIV,1), - PeleLMCCFillExtDirPhiV{lprobparm, lpmfdata, m_nAux}); + PeleLMCCFillExtDirPhiV{lprobparm, lpmfdata, leosparm, m_nAux}); PhysBCFunct> fine_bndry_func(geom[lev], fetchBCRecArray(PHIV,1), - PeleLMCCFillExtDirPhiV{lprobparm, lpmfdata, m_nAux}); + PeleLMCCFillExtDirPhiV{lprobparm, lpmfdata, leosparm, m_nAux}); InterpFromCoarseLevel(a_phiV, IntVect(nGhost), a_time, m_leveldata_new[lev-1]->phiV, 0, 0, 1, geom[lev-1], geom[lev], @@ -819,7 +828,7 @@ void PeleLM::setInflowBoundaryVel(MultiFab &a_vel, int lev, TimeStamp a_time) { BL_PROFILE_VAR("PeleLM::setInflowBoundaryVel()", setInflowBoundaryVel); - + Real time = getTime(lev, a_time); // Create a dummy BCRec from Velocity BCRec keeping only Inflow and set the other to bogus @@ -838,14 +847,15 @@ void PeleLM::setInflowBoundaryVel(MultiFab &a_vel, dummyVelBCRec[idim].setHi(idim2,BCType::bogus); } } - } + } fillTurbInflow(a_vel, 0, lev, time); ProbParm const* lprobparm = prob_parm_d; pele::physics::PMF::PmfData::DataContainer const* lpmfdata = pmf_data.getDeviceData(); + auto const* leosparm = eos_parms.device_eos_parm(); PhysBCFunct> bndry_func(geom[lev], dummyVelBCRec, - PeleLMCCFillExtDirState{lprobparm, lpmfdata, + PeleLMCCFillExtDirState{lprobparm, lpmfdata, leosparm, m_nAux, turb_inflow.is_initialized()}); bndry_func(a_vel, 0, AMREX_SPACEDIM, a_vel.nGrowVect(), time, 0); diff --git a/Source/PeleLMBCfill.H b/Source/PeleLMBCfill.H index 3af828c2d..8e6083419 100644 --- a/Source/PeleLMBCfill.H +++ b/Source/PeleLMBCfill.H @@ -11,14 +11,16 @@ struct PeleLMCCFillExtDirState ProbParm const* lprobparm; pele::physics::PMF::PmfData::DataContainer const* lpmfdata; + pele::physics::eos::EosParm const* leosparm; const int m_nAux; int m_do_turbInflow = 0; AMREX_GPU_HOST constexpr PeleLMCCFillExtDirState(ProbParm const* a_prob_parm, pele::physics::PMF::PmfData::DataContainer const* a_pmf_data, + pele::physics::eos::EosParm const* a_eos_parm, int a_nAux, int do_turbInflow) - : lprobparm(a_prob_parm), lpmfdata(a_pmf_data), + : lprobparm(a_prob_parm), lpmfdata(a_pmf_data), leosparm(a_eos_parm), m_nAux(a_nAux), m_do_turbInflow(do_turbInflow) {} AMREX_GPU_DEVICE @@ -65,7 +67,7 @@ struct PeleLMCCFillExtDirState } } // bcnormal() is defined in pelelm_prob.H in problem directory in /Exec - bcnormal(x, m_nAux, s_ext, idir, 1, time, geom, *lprobparm, lpmfdata); + bcnormal(x, m_nAux, s_ext, idir, 1, time, geom, *lprobparm, lpmfdata, leosparm); state(iv,n) = s_ext[n]; } } @@ -90,7 +92,7 @@ struct PeleLMCCFillExtDirState } } // bcnormal() is defined in pelelm_prob.H in problem directory in /Exec - bcnormal(x, m_nAux, s_ext, idir, -1, time, geom, *lprobparm, lpmfdata); + bcnormal(x, m_nAux, s_ext, idir, -1, time, geom, *lprobparm, lpmfdata, leosparm); state(iv,n) = s_ext[n]; } } @@ -103,12 +105,15 @@ struct PeleLMCCFillExtDirSpec ProbParm const* lprobparm; pele::physics::PMF::PmfData::DataContainer const* lpmfdata; + pele::physics::eos::EosParm const* leosparm; const int m_nAux; AMREX_GPU_HOST constexpr PeleLMCCFillExtDirSpec(ProbParm const* a_prob_parm, - pele::physics::PMF::PmfData::DataContainer const* a_pmf_data, int a_nAux) - : lprobparm(a_prob_parm), lpmfdata(a_pmf_data), m_nAux(a_nAux) {} + pele::physics::PMF::PmfData::DataContainer const* a_pmf_data, + pele::physics::eos::EosParm const* a_eos_parm, + int a_nAux) + : lprobparm(a_prob_parm), lpmfdata(a_pmf_data), leosparm(a_eos_parm), m_nAux(a_nAux) {} AMREX_GPU_DEVICE void operator()( @@ -145,7 +150,7 @@ struct PeleLMCCFillExtDirSpec if ((bc[idir] == amrex::BCType::ext_dir) and (iv[idir] < domlo[idir])) { // bcnormal() is defined in pelelm_prob.H in problem directory in /Exec - bcnormal(x, m_nAux, s_ext, idir, 1, time, geom, *lprobparm, lpmfdata); + bcnormal(x, m_nAux, s_ext, idir, 1, time, geom, *lprobparm, lpmfdata, leosparm); rhoY(iv,dcomp+n) = s_ext[FIRSTSPEC+n]; @@ -153,7 +158,7 @@ struct PeleLMCCFillExtDirSpec } else if ((bc[idir+AMREX_SPACEDIM] == amrex::BCType::ext_dir) and (iv[idir] > domhi[idir])) { // bcnormal() is defined in pelelm_prob.H in problem directory in /Exec - bcnormal(x, m_nAux, s_ext, idir, -1, time, geom, *lprobparm, lpmfdata); + bcnormal(x, m_nAux, s_ext, idir, -1, time, geom, *lprobparm, lpmfdata, leosparm); rhoY(iv,dcomp+n) = s_ext[FIRSTSPEC+n]; } @@ -167,12 +172,15 @@ struct PeleLMCCFillExtDirDens ProbParm const* lprobparm; pele::physics::PMF::PmfData::DataContainer const* lpmfdata; + pele::physics::eos::EosParm const* leosparm; const int m_nAux; AMREX_GPU_HOST constexpr PeleLMCCFillExtDirDens(ProbParm const* a_prob_parm, - pele::physics::PMF::PmfData::DataContainer const* a_pmf_data, int a_nAux) - : lprobparm(a_prob_parm), lpmfdata(a_pmf_data), m_nAux(a_nAux) {} + pele::physics::PMF::PmfData::DataContainer const* a_pmf_data, + pele::physics::eos::EosParm const* a_eos_parm, + int a_nAux) + : lprobparm(a_prob_parm), lpmfdata(a_pmf_data), leosparm(a_eos_parm), m_nAux(a_nAux) {} AMREX_GPU_DEVICE void operator()( @@ -207,7 +215,7 @@ struct PeleLMCCFillExtDirDens if ((bc[idir] == amrex::BCType::ext_dir) and (iv[idir] < domlo[idir])) { // bcnormal() is defined in pelelm_prob.H in problem directory in /Exec - bcnormal(x, m_nAux, s_ext, idir, 1, time, geom, *lprobparm, lpmfdata); + bcnormal(x, m_nAux, s_ext, idir, 1, time, geom, *lprobparm, lpmfdata, leosparm); state(iv,dcomp) = s_ext[DENSITY]; @@ -215,7 +223,7 @@ struct PeleLMCCFillExtDirDens } else if ((bc[idir+AMREX_SPACEDIM] == amrex::BCType::ext_dir) and (iv[idir] > domhi[idir])) { // bcnormal() is defined in pelelm_prob.H in problem directory in /Exec - bcnormal(x, m_nAux, s_ext, idir, -1, time, geom, *lprobparm, lpmfdata); + bcnormal(x, m_nAux, s_ext, idir, -1, time, geom, *lprobparm, lpmfdata, leosparm); state(iv,dcomp) = s_ext[DENSITY]; @@ -229,12 +237,15 @@ struct PeleLMCCFillExtDirRhoH ProbParm const* lprobparm; pele::physics::PMF::PmfData::DataContainer const* lpmfdata; + pele::physics::eos::EosParm const* leosparm; const int m_nAux; AMREX_GPU_HOST constexpr PeleLMCCFillExtDirRhoH(ProbParm const* a_prob_parm, - pele::physics::PMF::PmfData::DataContainer const* a_pmf_data, int a_nAux) - : lprobparm(a_prob_parm), lpmfdata(a_pmf_data), m_nAux(a_nAux) {} + pele::physics::PMF::PmfData::DataContainer const* a_pmf_data, + pele::physics::eos::EosParm const* a_eos_parm, + int a_nAux) + : lprobparm(a_prob_parm), lpmfdata(a_pmf_data), leosparm(a_eos_parm), m_nAux(a_nAux) {} AMREX_GPU_DEVICE void operator()( @@ -269,7 +280,7 @@ struct PeleLMCCFillExtDirRhoH if ((bc[idir] == amrex::BCType::ext_dir) and (iv[idir] < domlo[idir])) { // bcnormal() is defined in pelelm_prob.H in problem directory in /Exec - bcnormal(x, m_nAux, s_ext, idir, 1, time, geom, *lprobparm, lpmfdata); + bcnormal(x, m_nAux, s_ext, idir, 1, time, geom, *lprobparm, lpmfdata, leosparm); rhoH(iv) = s_ext[RHOH]; @@ -277,7 +288,7 @@ struct PeleLMCCFillExtDirRhoH } else if ((bc[idir+AMREX_SPACEDIM] == amrex::BCType::ext_dir) and (iv[idir] > domhi[idir])) { // bcnormal() is defined in pelelm_prob.H in problem directory in /Exec - bcnormal(x, m_nAux, s_ext, idir, -1, time, geom, *lprobparm, lpmfdata); + bcnormal(x, m_nAux, s_ext, idir, -1, time, geom, *lprobparm, lpmfdata, leosparm); rhoH(iv) = s_ext[RHOH]; @@ -291,12 +302,15 @@ struct PeleLMCCFillExtDirTemp ProbParm const* lprobparm; pele::physics::PMF::PmfData::DataContainer const* lpmfdata; + pele::physics::eos::EosParm const* leosparm; const int m_nAux; AMREX_GPU_HOST constexpr PeleLMCCFillExtDirTemp(ProbParm const* a_prob_parm, - pele::physics::PMF::PmfData::DataContainer const* a_pmf_data, int a_nAux) - : lprobparm(a_prob_parm), lpmfdata(a_pmf_data), m_nAux(a_nAux) {} + pele::physics::PMF::PmfData::DataContainer const* a_pmf_data, + pele::physics::eos::EosParm const* a_eos_parm, + int a_nAux) + : lprobparm(a_prob_parm), lpmfdata(a_pmf_data), leosparm(a_eos_parm), m_nAux(a_nAux) {} AMREX_GPU_DEVICE void operator()( @@ -331,7 +345,7 @@ struct PeleLMCCFillExtDirTemp if ((bc[idir] == amrex::BCType::ext_dir) and (iv[idir] < domlo[idir])) { // bcnormal() is defined in pelelm_prob.H in problem directory in /Exec - bcnormal(x, m_nAux, s_ext, idir, 1, time, geom, *lprobparm, lpmfdata); + bcnormal(x, m_nAux, s_ext, idir, 1, time, geom, *lprobparm, lpmfdata, leosparm); state(iv,dcomp) = s_ext[TEMP]; @@ -339,7 +353,7 @@ struct PeleLMCCFillExtDirTemp } else if ((bc[idir+AMREX_SPACEDIM] == amrex::BCType::ext_dir) and (iv[idir] > domhi[idir])) { // bcnormal() is defined in pelelm_prob.H in problem directory in /Exec - bcnormal(x, m_nAux, s_ext, idir, -1, time, geom, *lprobparm, lpmfdata); + bcnormal(x, m_nAux, s_ext, idir, -1, time, geom, *lprobparm, lpmfdata, leosparm); state(iv,dcomp) = s_ext[TEMP]; @@ -354,12 +368,15 @@ struct PeleLMCCFillExtDirnE ProbParm const* lprobparm; pele::physics::PMF::PmfData::DataContainer const* lpmfdata; + pele::physics::eos::EosParm const* leosparm; const int m_nAux; AMREX_GPU_HOST constexpr PeleLMCCFillExtDirnE(ProbParm const* a_prob_parm, - pele::physics::PMF::PmfData::DataContainer const* a_pmf_data, int a_nAux) - : lprobparm(a_prob_parm), lpmfdata(a_pmf_data), m_nAux(a_nAux) {} + pele::physics::PMF::PmfData::DataContainer const* a_pmf_data, + pele::physics::eos::EosParm const* a_eos_parm, + int a_nAux) + : lprobparm(a_prob_parm), lpmfdata(a_pmf_data), leosparm(a_eos_parm), m_nAux(a_nAux) {} AMREX_GPU_DEVICE void operator()( @@ -394,7 +411,7 @@ struct PeleLMCCFillExtDirnE if ((bc[idir] == amrex::BCType::ext_dir) and (iv[idir] < domlo[idir])) { // bcnormal() is defined in pelelm_prob.H in problem directory in /Exec - bcnormal(x, m_nAux, s_ext, idir, 1, time, geom, *lprobparm, lpmfdata); + bcnormal(x, m_nAux, s_ext, idir, 1, time, geom, *lprobparm, lpmfdata, leosparm); nE(iv) = s_ext[NE]; @@ -402,7 +419,7 @@ struct PeleLMCCFillExtDirnE } else if ((bc[idir+AMREX_SPACEDIM] == amrex::BCType::ext_dir) and (iv[idir] > domhi[idir])) { // bcnormal() is defined in pelelm_prob.H in problem directory in /Exec - bcnormal(x, m_nAux, s_ext, idir, -1, time, geom, *lprobparm, lpmfdata); + bcnormal(x, m_nAux, s_ext, idir, -1, time, geom, *lprobparm, lpmfdata, leosparm); nE(iv) = s_ext[NE]; @@ -418,12 +435,15 @@ struct PeleLMCCFillExtDirPhiV ProbParm const* lprobparm; pele::physics::PMF::PmfData::DataContainer const* lpmfdata; + pele::physics::eos::EosParm const* leosparm; const int m_nAux; AMREX_GPU_HOST constexpr PeleLMCCFillExtDirPhiV(ProbParm const* a_prob_parm, - pele::physics::PMF::PmfData::DataContainer const* a_pmf_data, int a_nAux) - : lprobparm(a_prob_parm), lpmfdata(a_pmf_data), m_nAux(a_nAux) {} + pele::physics::PMF::PmfData::DataContainer const* a_pmf_data, + pele::physics::eos::EosParm const* a_eos_parm, + int a_nAux) + : lprobparm(a_prob_parm), lpmfdata(a_pmf_data), leosparm(a_eos_parm), m_nAux(a_nAux) {} AMREX_GPU_DEVICE void operator()( @@ -458,7 +478,7 @@ struct PeleLMCCFillExtDirPhiV if ((bc[idir] == amrex::BCType::ext_dir) and (iv[idir] < domlo[idir])) { // bcnormal() is defined in pelelm_prob.H in problem directory in /Exec - bcnormal(x, m_nAux, s_ext, idir, 1, time, geom, *lprobparm, lpmfdata); + bcnormal(x, m_nAux, s_ext, idir, 1, time, geom, *lprobparm, lpmfdata, leosparm); phiV(iv) = s_ext[PHIV]; @@ -466,7 +486,7 @@ struct PeleLMCCFillExtDirPhiV } else if ((bc[idir+AMREX_SPACEDIM] == amrex::BCType::ext_dir) and (iv[idir] > domhi[idir])) { // bcnormal() is defined in pelelm_prob.H in problem directory in /Exec - bcnormal(x, m_nAux, s_ext, idir, -1, time, geom, *lprobparm, lpmfdata); + bcnormal(x, m_nAux, s_ext, idir, -1, time, geom, *lprobparm, lpmfdata, leosparm); phiV(iv) = s_ext[PHIV]; @@ -515,10 +535,10 @@ struct umacFill const int numcomp, amrex::GeometryData const& /*geom*/, const amrex::Real /*time*/, - const amrex::BCRec* bcr, + const amrex::BCRec* bcr, const int bcomp, const int /*orig_comp*/) const - { + { // Abort if this function is expected to fill an ext_dir BC. for (int n = bcomp; n < bcomp+numcomp; ++n) { const amrex::BCRec& bc = bcr[n]; @@ -526,7 +546,7 @@ struct umacFill || bc.lo(1) == amrex::BCType::ext_dir || bc.hi(1) == amrex::BCType::ext_dir, || bc.lo(2) == amrex::BCType::ext_dir || bc.hi(2) == amrex::BCType::ext_dir ) ) { amrex::Abort("PeleLM::umacFill: umac should not have BCType::ext_dir"); - } - } - } + } + } + } }; diff --git a/Source/PeleLMInit.cpp b/Source/PeleLMInit.cpp index 10c33192e..f76de53d4 100644 --- a/Source/PeleLMInit.cpp +++ b/Source/PeleLMInit.cpp @@ -102,7 +102,7 @@ void PeleLM::MakeNewLevelFromScratch( int lev, if ( lev == 0 && m_signDistNeeded) { // Set up CC signed distance container to control EB refinement m_signedDist0.reset(new MultiFab(grids[lev], dmap[lev], 1, 1, MFInfo(), *m_factory[lev])); - + // Estimate the maximum distance we need in terms of level 0 dx: Real extentFactor = static_cast(nErrorBuf(0)); for (int ilev = 1; ilev <= max_level; ++ilev) { @@ -358,6 +358,7 @@ void PeleLM::initLevelData(int lev) { // Prob/PMF datas ProbParm const* lprobparm = prob_parm_d; pele::physics::PMF::PmfData::DataContainer const* lpmfdata = pmf_data.getDeviceData(); + auto const* leosparm = eos_parms.device_eos_parm(); #ifdef AMREX_USE_OMP #pragma omp parallel if (Gpu::notInLaunchRegion()) @@ -377,9 +378,9 @@ void PeleLM::initLevelData(int lev) { { pelelm_initdata(i, j, k, m_incompressible, state_arr, aux_arr, #ifdef PELE_USE_EFIELD - ne_arr, phiV_arr, + ne_arr, phiV_arr, #endif - geomdata, *lprobparm, lpmfdata); + geomdata, *lprobparm, lpmfdata, leosparm); }); } From 20ab368d90b4783870d9e822d6d8614ff5a31b4b Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Tue, 21 Jun 2022 11:34:22 -0600 Subject: [PATCH 07/27] include manfunc parms in init --- Source/PeleLM.H | 45 +++++++++++++++++++++--------------------- Source/PeleLM.cpp | 10 +++++++--- Source/PeleLMSetup.cpp | 42 ++++++++++++++++++++++++++++++--------- 3 files changed, 63 insertions(+), 34 deletions(-) diff --git a/Source/PeleLM.H b/Source/PeleLM.H index 383f2c44f..972ad9458 100644 --- a/Source/PeleLM.H +++ b/Source/PeleLM.H @@ -66,17 +66,17 @@ class PeleLM : public amrex::AmrCore { amrex::Real time, bool initial = false) override; - virtual void MakeNewLevelFromScratch( int lev, + virtual void MakeNewLevelFromScratch( int lev, amrex::Real time, const amrex::BoxArray& ba, const amrex::DistributionMapping& dm) override; - virtual void ErrorEst( int lev, + virtual void ErrorEst( int lev, amrex::TagBoxArray& tags, amrex::Real time, int ng) override; - virtual void MakeNewLevelFromCoarse( int lev, + virtual void MakeNewLevelFromCoarse( int lev, amrex::Real time, const amrex::BoxArray& ba, const amrex::DistributionMapping& dm) override; @@ -145,7 +145,7 @@ class PeleLM : public amrex::AmrCore { LevelData (amrex::BoxArray const& ba, amrex::DistributionMapping const& dm, amrex::FabFactory const& factory, - int a_incompressible, int a_has_divu, + int a_incompressible, int a_has_divu, int a_nAux, int a_nGrowState); // cell-centered state multifabs @@ -270,7 +270,7 @@ class PeleLM : public amrex::AmrCore { // MAC PROJECTION void resetMacProjector(); - + // Predict face velocity using Godunov void predictVelocity(std::unique_ptr &advData); @@ -307,7 +307,7 @@ class PeleLM : public amrex::AmrCore { // compute cell-centered diffusivity (stored in LevelData) void calcViscosity(const PeleLM::TimeStamp &a_time); void calcDiffusivity(const PeleLM::TimeStamp &a_time); - + // get edge-centered diffusivity on a per level / per comp basis amrex::Array getDiffusivity(int lev, int beta_comp, int ncomp, int doZeroVisc, @@ -326,7 +326,7 @@ class PeleLM : public amrex::AmrCore { // add wbar term to the species fluxes void addWbarTerm(const amrex::Vector> &a_fluxes, - const amrex::Vector> &a_wbarfluxes, + const amrex::Vector> &a_wbarfluxes, amrex::Vector const &a_spec, amrex::Vector const &a_rho, amrex::Vector const &a_beta); @@ -338,7 +338,7 @@ class PeleLM : public amrex::AmrCore { // Compute enthalpy flux due to species diffusion void computeSpeciesEnthalpyFlux(const amrex::Vector> &a_fluxes, amrex::Vector const &a_temp); - + void differentialDiffusionUpdate(std::unique_ptr &advData, std::unique_ptr &diffData); @@ -413,7 +413,7 @@ class PeleLM : public amrex::AmrCore { const amrex::Vector &a_velForce, int nGrowForce, int add_gradP); - + void getVelForces(const TimeStamp &a_time, int lev, amrex::MultiFab* a_divTau, @@ -722,7 +722,7 @@ class PeleLM : public amrex::AmrCore { // Typical values void setTypicalValues(const PeleLM::TimeStamp &a_time, int is_init = 0); void updateTypicalValuesChem(); - + void checkMemory(const std::string &a_message); // Mixture fraction & Progress variable @@ -768,14 +768,14 @@ class PeleLM : public amrex::AmrCore { void getEBDistance(int a_lev, - amrex::MultiFab &a_signDistLev); + amrex::MultiFab &a_signDistLev); void extendSignedDistance( amrex::MultiFab *a_signDist, amrex::Real a_extendFactor); // static_cast factory into ebfactory - amrex::EBFArrayBoxFactory const& - EBFactory (int lev) const noexcept { + amrex::EBFArrayBoxFactory const& + EBFactory (int lev) const noexcept { return static_cast(*m_factory[lev]); } @@ -789,10 +789,10 @@ class PeleLM : public amrex::AmrCore { void advanceSetup(); - void oneSDC(int sdcIter, + void oneSDC(int sdcIter, std::unique_ptr &advData, std::unique_ptr &diffData); - + void copyTransportOldToNew(); void copyStateNewToOld(int nGhost = 0); void copyPressNewToOld(); @@ -876,7 +876,7 @@ class PeleLM : public amrex::AmrCore { amrex::Real &norm); int testExitNewton(int newtonIter, - const amrex::Real &max_res); + const amrex::Real &max_res); void updateNLState(const amrex::Vector &a_update); @@ -1048,6 +1048,7 @@ class PeleLM : public amrex::AmrCore { // EOS pointer static pele::physics::eos::EosParams< pele::physics::PhysicsType::eos_type> eos_parms; + static std::unique_ptr manfunc_par; // Reactor pointer std::string m_chem_integrator; @@ -1057,7 +1058,7 @@ class PeleLM : public amrex::AmrCore { pele::physics::turbinflow::TurbInflow turb_inflow; // Diagnostics - amrex::Vector> m_diagnostics; + amrex::Vector> m_diagnostics; // Linear solvers std::unique_ptr m_diffusion_op; @@ -1103,7 +1104,7 @@ class PeleLM : public amrex::AmrCore { amrex::Real m_dtChangeMax = 1.1; amrex::Real m_stop_time = -1.0; amrex::Real m_max_dt = 1e12; - int m_nstep = -1; + int m_nstep = -1; int m_max_step = -1; int m_regrid_int = -1; @@ -1125,7 +1126,7 @@ class PeleLM : public amrex::AmrCore { // DivU dt int m_divu_checkFlag = 1; amrex::Real m_divu_dtFactor = 0.5; - amrex::Real m_divu_rhoMin = 0.1; + amrex::Real m_divu_rhoMin = 0.1; // Aux data size int m_nAux = 0; @@ -1191,7 +1192,7 @@ class PeleLM : public amrex::AmrCore { amrex::Array m_domainRhoHFlux; amrex::Array m_domainRhoYFlux; amrex::Array m_domainUmacFlux; - + std::ofstream tmpStateFile; std::ofstream tmpMassFile; std::ofstream tmpSpecFile; @@ -1259,14 +1260,14 @@ class PeleLM : public amrex::AmrCore { // Nodal projection int m_nodal_mg_max_coarsening_level = 100; - amrex::Real m_nodal_mg_rtol = 1.0e-11; + amrex::Real m_nodal_mg_rtol = 1.0e-11; amrex::Real m_nodal_mg_atol = 1.0e-14; std::string m_hypre_namespace_nodal = "nodal_hypre"; // MAC projection int m_mac_mg_verbose = 0; int m_mac_mg_max_coarsening_level = 100; - amrex::Real m_mac_mg_rtol = 1.0e-11; + amrex::Real m_mac_mg_rtol = 1.0e-11; amrex::Real m_mac_mg_atol = 1.0e-14; std::string m_hypre_namespace_mac = "mac_hypre"; //----------------------------------------------------------------------------- diff --git a/Source/PeleLM.cpp b/Source/PeleLM.cpp index dd8a9aa83..5caaca55b 100644 --- a/Source/PeleLM.cpp +++ b/Source/PeleLM.cpp @@ -8,6 +8,7 @@ pele::physics::transport::TransportParams< pele::physics::eos::EosParams< pele::physics::PhysicsType::eos_type> PeleLM::eos_parms; +std::unique_ptr PeleLM::manfunc_par; PeleLM::PeleLM() = default; @@ -16,12 +17,15 @@ PeleLM::~PeleLM() for (int lev = 0; lev <= finest_level; ++lev) { ClearLevel(lev); } - + if (!m_incompressible) { eos_parms.deallocate(); +#ifdef USE_MANIFOLD_EOS + manfunc_par->deallocate(); +#endif trans_parms.deallocate(); m_reactor->close(); - } + } closeTempFile(); typical_values.clear(); @@ -39,7 +43,7 @@ PeleLM::LevelData* PeleLM::getLevelDataPtr(int lev, const PeleLM::TimeStamp &a_time, int /*useUMac*/) { AMREX_ASSERT(a_time==AmrOldTime || a_time==AmrNewTime || a_time==AmrHalfTime); - if ( a_time == AmrOldTime ) { + if ( a_time == AmrOldTime ) { return m_leveldata_old[lev].get(); } else if ( a_time == AmrNewTime ) { return m_leveldata_new[lev].get(); diff --git a/Source/PeleLMSetup.cpp b/Source/PeleLMSetup.cpp index a6eddb4e3..359b4ca68 100644 --- a/Source/PeleLMSetup.cpp +++ b/Source/PeleLMSetup.cpp @@ -77,9 +77,33 @@ void PeleLM::Setup() { // Initialize EOS and others if (!m_incompressible) { amrex::Print() << " Initialization of EOS (CPP)... \n"; +#ifdef USE_MANIFOLD_EOS + amrex::ParmParse pp("manifold"); + std::string manifold_model; + pp.get("model", manifold_model); + if(manifold_model == "Table") + { + manfunc_par.reset(new pele::physics::TabFuncParams()); + amrex::Print() << " Initialization of Table (CPP)... \n"; + } + else if(manifold_model == "NeuralNet") + { +#ifdef USE_LIBTORCH + manfunc_par.reset(new pele::physics::NNFuncParams()); + amrex::Print() << " Initialization of Neural Net Func. (CPP)... \n"; +#else + amrex::Error("Must set USE_LIBTORCH = TRUE to run with neural net manifold model."); +#endif + } + manfunc_par->initialize(); + eos_parms.allocate(manfunc_par->device_manfunc_data()); + amrex::Print() << " Initialization of Transport ... \n"; + trans_parms.allocate(manfunc_par->device_manfunc_data()); +#else eos_parms.allocate(); amrex::Print() << " Initialization of Transport ... \n"; trans_parms.allocate(); +#endif if (m_do_react) { int reactor_type = 2; int ncells_chem = 1; @@ -133,7 +157,7 @@ void PeleLM::Setup() { m_pNew = prob_parm->P_mean; // Copy problem parameters into device copy - Gpu::copy(Gpu::hostToDevice, prob_parm, prob_parm+1,prob_parm_d); + Gpu::copy(Gpu::hostToDevice, prob_parm, prob_parm+1,prob_parm_d); } void PeleLM::readParameters() { @@ -226,7 +250,7 @@ void PeleLM::readParameters() { // Get the phiV bc ppef.getarr("phiV_lo_bc",lo_bc_char,0,AMREX_SPACEDIM); ppef.getarr("phiV_hi_bc",hi_bc_char,0,AMREX_SPACEDIM); - for (int idim = 0; idim < AMREX_SPACEDIM; idim++) + for (int idim = 0; idim < AMREX_SPACEDIM; idim++) { if (lo_bc_char[idim] == "Interior"){ m_phiV_bc.setLo(idim,0); @@ -236,7 +260,7 @@ void PeleLM::readParameters() { m_phiV_bc.setLo(idim,2); } else { amrex::Abort("Wrong PhiV bc. Should be : Interior, Dirichlet or Neumann"); - } + } if (hi_bc_char[idim] == "Interior"){ m_phiV_bc.setHi(idim,0); } else if (hi_bc_char[idim] == "Dirichlet") { @@ -297,12 +321,12 @@ void PeleLM::readParameters() { pp.queryarr("gravity", grav, 0, AMREX_SPACEDIM); Vector gp0(AMREX_SPACEDIM,0); pp.queryarr("gradP0", gp0, 0, AMREX_SPACEDIM); - for (int idim = 0; idim < AMREX_SPACEDIM; idim++) + for (int idim = 0; idim < AMREX_SPACEDIM; idim++) { m_background_gp[idim] = gp0[idim]; m_gravity[idim] = grav[idim]; } - + // ----------------------------------------- // diffusion @@ -311,7 +335,7 @@ void PeleLM::readParameters() { pp.query("deltaT_iterMax",m_deltaTIterMax); pp.query("deltaT_tol",m_deltaT_norm_max); pp.query("deltaT_crashIfFailing",m_crashOnDeltaTFail); - + // ----------------------------------------- // initialization @@ -358,7 +382,7 @@ void PeleLM::readParameters() { m_Godunov_ppm = 0; } else { Abort("Unknown 'advection_scheme'. Recognized options are: Godunov_PLM, Godunov_PPM or Godunov_BDS"); - } + } m_predict_advection_type = "Godunov"; // Only option at this point. This will disapear when predict_velocity support BDS. // ----------------------------------------- @@ -409,7 +433,7 @@ void PeleLM::readParameters() { m_EB_refine_type != "Adaptive" ) { Abort("refine_EB_type can only be 'Static' or 'Adaptive'"); } - // Default EB refinement level is max_level + // Default EB refinement level is max_level m_EB_refine_LevMax = max_level; pp.query("refine_EB_max_level",m_EB_refine_LevMax); pp.query("refine_EB_buffer",m_derefineEBBuffer); @@ -507,7 +531,7 @@ void PeleLM::variablesSetup() { std::string PrettyLine = std::string(78, '=') + "\n"; //---------------------------------------------------------------- - // Variables ordering is defined through compiler macro in PeleLM_Index.H + // Variables ordering is defined through compiler macro in PeleLM_Index.H // Simply print on screen the state layout and append to the stateComponents list Print() << "\n"; Print() << PrettyLine; From 5b2ab08540796b8c6966fc269dbe9640fb0b152d Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Tue, 21 Jun 2022 13:37:51 -0600 Subject: [PATCH 08/27] don't compute Z or C with manifold yet --- Source/PeleLM.cpp | 2 +- Source/PeleLMSetup.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/PeleLM.cpp b/Source/PeleLM.cpp index 5caaca55b..7206ff4b4 100644 --- a/Source/PeleLM.cpp +++ b/Source/PeleLM.cpp @@ -21,7 +21,7 @@ PeleLM::~PeleLM() if (!m_incompressible) { eos_parms.deallocate(); #ifdef USE_MANIFOLD_EOS - manfunc_par->deallocate(); + manfunc_par->deallocate(); #endif trans_parms.deallocate(); m_reactor->close(); diff --git a/Source/PeleLMSetup.cpp b/Source/PeleLMSetup.cpp index 359b4ca68..bd8098114 100644 --- a/Source/PeleLMSetup.cpp +++ b/Source/PeleLMSetup.cpp @@ -134,8 +134,11 @@ void PeleLM::Setup() { } // Mixture fraction & Progress variable + // TODO: Take mixture fraction and progress variable if they are manifold coordinates +#ifndef USE_MANIFOLD_EOS initMixtureFraction(); initProgressVariable(); +#endif // Initiliaze turbulence injection turb_inflow.init(Geom(0)); From 454147ae9e09c0476111cf665b07391d435d5070 Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Tue, 21 Jun 2022 16:09:03 -0600 Subject: [PATCH 09/27] updates for Advection for Manifold --- Source/PeleLMAdvection.cpp | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/Source/PeleLMAdvection.cpp b/Source/PeleLMAdvection.cpp index 5468aa202..d2f787ef2 100644 --- a/Source/PeleLMAdvection.cpp +++ b/Source/PeleLMAdvection.cpp @@ -37,7 +37,7 @@ void PeleLM::computeVelocityAdvTerm(std::unique_ptr &advData) int add_gradP = 1; getVelForces(AmrOldTime,GetVecOfPtrs(divtau),GetVecOfPtrs(velForces),nGrow_force,add_gradP); - auto bcRecVel = fetchBCRecArray(VELX,AMREX_SPACEDIM); + auto bcRecVel = fetchBCRecArray(VELX,AMREX_SPACEDIM); auto bcRecVel_d = convertToDeviceVector(bcRecVel); auto AdvTypeVel = fetchAdvTypeArray(VELX,AMREX_SPACEDIM); auto AdvTypeVel_d = convertToDeviceVector(AdvTypeVel); @@ -102,11 +102,11 @@ void PeleLM::computeVelocityAdvTerm(std::unique_ptr &advData) m_advection_type); } #ifdef AMREX_USE_EB - EB_set_covered_faces(GetArrOfPtrs(fluxes[lev]),0.); - EB_set_covered_faces(GetArrOfPtrs(faces[lev]),0.); + EB_set_covered_faces(GetArrOfPtrs(fluxes[lev]),0.); + EB_set_covered_faces(GetArrOfPtrs(faces[lev]),0.); #endif } - + //---------------------------------------------------------------- // Average down fluxes to ensure C/F consistency for (int lev = finest_level; lev > 0; --lev) { @@ -284,11 +284,11 @@ void PeleLM::computeScalarAdvTerms(std::unique_ptr &advData) auto bcRecSpec_d = convertToDeviceVector(bcRecSpec); auto AdvTypeSpec = fetchAdvTypeArray(FIRSTSPEC,NUM_SPECIES); auto AdvTypeSpec_d = convertToDeviceVector(AdvTypeSpec); - auto bcRecTemp = fetchBCRecArray(TEMP,1); + auto bcRecTemp = fetchBCRecArray(TEMP,1); auto bcRecTemp_d = convertToDeviceVector(bcRecTemp); auto AdvTypeTemp = fetchAdvTypeArray(TEMP,1); auto AdvTypeTemp_d = convertToDeviceVector(AdvTypeTemp); - auto bcRecRhoH = fetchBCRecArray(RHOH,1); + auto bcRecRhoH = fetchBCRecArray(RHOH,1); auto bcRecRhoH_d = convertToDeviceVector(bcRecRhoH); auto AdvTypeRhoH = fetchAdvTypeArray(RHOH,1); auto AdvTypeRhoH_d = convertToDeviceVector(AdvTypeRhoH); @@ -305,7 +305,7 @@ void PeleLM::computeScalarAdvTerms(std::unique_ptr &advData) //---------------------------------------------------------------- // Loop over levels and get the fluxes - for (int lev = 0; lev <= finest_level; ++lev) { + for (int lev = 0; lev <= finest_level; ++lev) { // Get level data ptr Old auto ldata_p = getLevelDataPtr(lev,AmrOldTime); @@ -460,16 +460,21 @@ void PeleLM::computeScalarAdvTerms(std::unique_ptr &advData) { rho_ed(i,j,k) = 0.0; }); - } else if (flagfab.getType(ebx) != FabType::regular ) { // EB containing boxes + } else if (flagfab.getType(ebx) != FabType::regular ) { // EB containing boxes const auto& afrac = areafrac[idim]->array(mfi); amrex::ParallelFor(ebx, [rho_ed, rhoY_ed, afrac] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { rho_ed(i,j,k) = 0.0; if (afrac(i,j,k) > 0.0) { // Uncovered faces +#ifndef USE_MANIFOLD_EOS + //TODO BAP: Get rid of ifdef for this and below for (int n = 0; n < NUM_SPECIES; n++) { rho_ed(i,j,k) += rhoY_ed(i,j,k,n); } +#else + rho_ed(i,j,k) = rhoY_ed(i,j,k,NUM_SPECIES-1); +#endif } }); } else // Regular boxes @@ -478,9 +483,13 @@ void PeleLM::computeScalarAdvTerms(std::unique_ptr &advData) AMREX_GPU_DEVICE (int i, int j, int k) noexcept { rho_ed(i,j,k) = 0.0; +#ifndef USE_MANIFOLD_EOS for (int n = 0; n < NUM_SPECIES; n++) { rho_ed(i,j,k) += rhoY_ed(i,j,k,n); } +#else + rho_ed(i,j,k) = rhoY_ed(i,j,k,NUM_SPECIES-1); +#endif }); } } @@ -550,7 +559,7 @@ void PeleLM::computeScalarAdvTerms(std::unique_ptr &advData) { rhoHm(i,j,k) = 0.0; }); - } else if (flagfab.getType(ebx) != FabType::regular ) { // EB containing boxes + } else if (flagfab.getType(ebx) != FabType::regular ) { // EB containing boxes const auto& afrac = areafrac[idim]->array(mfi); amrex::ParallelFor(ebx, [rho, rhoY, T, rhoHm, afrac, leosparm] AMREX_GPU_DEVICE (int i, int j, int k) noexcept @@ -612,7 +621,7 @@ void PeleLM::computeScalarAdvTerms(std::unique_ptr &advData) m_advection_type); } #ifdef AMREX_USE_EB - EB_set_covered_faces(GetArrOfPtrs(fluxes[lev]),0.); + EB_set_covered_faces(GetArrOfPtrs(fluxes[lev]),0.); #endif } @@ -722,10 +731,15 @@ void PeleLM::computeScalarAdvTerms(std::unique_ptr &advData) amrex::ParallelFor(bx, [aofrho, aofrhoY] AMREX_GPU_DEVICE (int i, int j, int k) noexcept { +#ifndef USE_MANIFOLD_EOS + //TODO BAP: get rid of ifdef here aofrho(i,j,k) = 0.0; for (int n = 0; n < NUM_SPECIES; n++) { aofrho(i,j,k) += aofrhoY(i,j,k,n); } +#else + aofrho(i,j,k) = aofrhoY(i,j,k,NUM_SPECIES-1); +#endif }); } } @@ -783,7 +797,7 @@ void PeleLM::computePassiveAdvTerms(std::unique_ptr &advData, //---------------------------------------------------------------- // Loop over levels and get the fluxes - for (int lev = 0; lev <= finest_level; ++lev) { + for (int lev = 0; lev <= finest_level; ++lev) { // Get level data ptr Old auto ldata_p = getLevelDataPtr(lev,AmrOldTime); From f6fd86f8268d53174ded20f8a78ff3478622ec48 Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Tue, 21 Jun 2022 16:09:59 -0600 Subject: [PATCH 10/27] Some error checking for Manifold EOS --- Source/PeleLMPlot.cpp | 42 +++++++++++++++++++++++------------------- Source/PeleLMSetup.cpp | 5 +++++ 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/Source/PeleLMPlot.cpp b/Source/PeleLMPlot.cpp index 67e15f66d..376c611f1 100644 --- a/Source/PeleLMPlot.cpp +++ b/Source/PeleLMPlot.cpp @@ -353,36 +353,36 @@ void PeleLM::WriteHeader(const std::string& name, bool is_checkpoint) const void PeleLM::WriteCheckPointFile() { BL_PROFILE("PeleLM::WriteCheckPointFile()"); - + const std::string& checkpointname = amrex::Concatenate(m_check_file, m_nstep, m_ioDigits); - + if (m_verbose) { amrex::Print() << "\n Writting checkpoint file: " << checkpointname << "\n"; } - + amrex::PreBuildDirectorHierarchy(checkpointname, level_prefix, finest_level + 1, true); bool is_checkpoint = true; WriteHeader(checkpointname, is_checkpoint); WriteJobInfo(checkpointname); - + for(int lev = 0; lev <= finest_level; ++lev) - { + { VisMF::Write(m_leveldata_new[lev]->state, amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "state")); - + VisMF::Write(m_leveldata_new[lev]->gp, amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "gradp")); - + VisMF::Write(m_leveldata_new[lev]->press, amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "p")); - + if (!m_incompressible) { if (m_has_divu) { VisMF::Write(m_leveldata_new[lev]->divu, amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "divU")); } - + if (m_do_react) { VisMF::Write(m_leveldatareact[lev]->I_R, amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "I_R")); @@ -395,7 +395,7 @@ void PeleLM::WriteCheckPointFile() VisMF::Write(m_leveldata_new[lev]->nE, amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "nE")); #endif - } + } } #ifdef PELELM_USE_SPRAY if (theSprayPC() != nullptr && do_spray_particles) { @@ -431,12 +431,12 @@ void PeleLM::ReadCheckPointFile() Vector fileCharPtr; ParallelDescriptor::ReadAndBcastFile(File, fileCharPtr); std::string fileCharPtrString(fileCharPtr.dataPtr()); - std::istringstream is(fileCharPtrString, std::istringstream::in); + std::istringstream is(fileCharPtrString, std::istringstream::in); std::string line, word; - // Start reading from checkpoint file - + // Start reading from checkpoint file + // Title line std::getline(is, line); @@ -578,6 +578,10 @@ void PeleLM::initLevelDataFromPlt(int a_lev, Abort(" initializing data from a pltfile only available for low-Mach simulations"); } +#ifdef USE_MANIFOLD_EOS + Abort(" initializing data from a pltfile not available for Manifold Eos"); +#endif + amrex::Print() << " initData on level " << a_lev << " from pltfile " << a_dataPltFile << "\n"; // Use PelePhysics PltFileManager @@ -590,8 +594,8 @@ void PeleLM::initLevelDataFromPlt(int a_lev, int idT = -1, idV = -1, idY = -1, nSpecPlt = 0; for (int i = 0; i < plt_vars.size(); ++i) { std::string firstChars = plt_vars[i].substr(0, 2); - if (plt_vars[i] == "temp") idT = i; - if (plt_vars[i] == "x_velocity") idV = i; + if (plt_vars[i] == "temp") idT = i; + if (plt_vars[i] == "x_velocity") idV = i; if (firstChars == "Y(" && idY < 0 ) { // species might not be ordered in the order of the current mech. idY = i; } @@ -657,11 +661,11 @@ void PeleLM::initLevelDataFromPlt(int a_lev, auto eos = pele::physics::PhysicsType::eos(leosparm); Real massfrac[NUM_SPECIES] = {0.0}; Real sumYs = 0.0; - // TODO : is this the best way to Init when species don't add to 1? + // TODO : is this the best way to Init when species don't add to 1? for (int n = 0; n < NUM_SPECIES; n++){ massfrac[n] = rhoY_arr(i,j,k,n); sumYs += massfrac[n]; - } + } for (int n = 0; n < NUM_SPECIES; n++){ massfrac[n] /= sumYs; } @@ -676,7 +680,7 @@ void PeleLM::initLevelDataFromPlt(int a_lev, Real h_cgs = 0.0; eos.TY2H(temp_arr(i,j,k), massfrac, h_cgs); rhoH_arr(i,j,k) = h_cgs * 1.0e-4 * rho_arr(i,j,k); - + // Fill rhoYs for (int n = 0; n < NUM_SPECIES; n++){ rhoY_arr(i,j,k,n) = massfrac[n] * rho_arr(i,j,k); @@ -768,7 +772,7 @@ void PeleLM::WriteJobInfo(const std::string& path) const jobInfoFile << " maximum zones = "; for(int idim = 0; idim < AMREX_SPACEDIM; idim++) { - jobInfoFile << geom[lev].Domain().length(idim) << " "; + jobInfoFile << geom[lev].Domain().length(idim) << " "; } jobInfoFile << "\n\n"; } diff --git a/Source/PeleLMSetup.cpp b/Source/PeleLMSetup.cpp index bd8098114..4761f1eaa 100644 --- a/Source/PeleLMSetup.cpp +++ b/Source/PeleLMSetup.cpp @@ -334,6 +334,11 @@ void PeleLM::readParameters() { // ----------------------------------------- // diffusion pp.query("use_wbar",m_use_wbar); +#ifdef USE_MANIFOLD_EOS + if (m_use_wbar) { + amrex::Abort("Use of Wbar fluxes is incompatible with Manifold EOS"); + } +#endif pp.query("deltaT_verbose",m_deltaT_verbose); pp.query("deltaT_iterMax",m_deltaTIterMax); pp.query("deltaT_tol",m_deltaT_norm_max); From 2835c9d0c57b117b1bed547ce12e982521dc0b3d Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Tue, 21 Jun 2022 17:07:35 -0600 Subject: [PATCH 11/27] more error checking --- Source/PeleLMSetup.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/PeleLMSetup.cpp b/Source/PeleLMSetup.cpp index 4761f1eaa..939a955e5 100644 --- a/Source/PeleLMSetup.cpp +++ b/Source/PeleLMSetup.cpp @@ -246,6 +246,11 @@ void PeleLM::readParameters() { if (verbose && m_closed_chamber) { Print() << " Simulation performed with the closed chamber algorithm \n"; } +#ifdef USE_MANIFOLD_EOS + if (m_closed_chamber) { + amrex::Abort("Simulation with closed chamber not supported for Manifold EOS"); + } +#endif #ifdef PELE_USE_EFIELD ParmParse ppef("ef"); From 8f79f16f717de91cd50b07604b120f0ad6ee1375 Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Tue, 21 Jun 2022 17:09:04 -0600 Subject: [PATCH 12/27] Don't enforce diffusion fluxes sum to 0 for Manifold --- Source/PeleLMDiffusion.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Source/PeleLMDiffusion.cpp b/Source/PeleLMDiffusion.cpp index 52d807489..53e20bf2a 100644 --- a/Source/PeleLMDiffusion.cpp +++ b/Source/PeleLMDiffusion.cpp @@ -88,13 +88,13 @@ void PeleLM::computeDifferentialDiffusionTerms(const TimeStamp &a_time, GetVecOfPtrs(diffData->Dn), 0, GetVecOfArrOfPtrs(fluxes), 0, NUM_SPECIES, 1, bcRecSpec_d.dataPtr(), -1.0, m_dt); - auto bcRecTemp = fetchBCRecArray(TEMP,1); + auto bcRecTemp = fetchBCRecArray(TEMP,1); auto bcRecTemp_d = convertToDeviceVector(bcRecTemp); fluxDivergenceRD(GetVecOfConstPtrs(getTempVect(AmrOldTime)), 0, GetVecOfPtrs(diffData->Dn), NUM_SPECIES, GetVecOfArrOfPtrs(fluxes), NUM_SPECIES, 1, 1, bcRecTemp_d.dataPtr(), -1.0, m_dt); - auto bcRecRhoH = fetchBCRecArray(RHOH,1); + auto bcRecRhoH = fetchBCRecArray(RHOH,1); auto bcRecRhoH_d = convertToDeviceVector(bcRecRhoH); fluxDivergenceRD(GetVecOfConstPtrs(getRhoHVect(AmrOldTime)), 0, GetVecOfPtrs(diffData->Dn), NUM_SPECIES+1, @@ -111,13 +111,13 @@ void PeleLM::computeDifferentialDiffusionTerms(const TimeStamp &a_time, GetVecOfPtrs(diffData->Dnp1), 0, GetVecOfArrOfPtrs(fluxes), 0, NUM_SPECIES, 1, bcRecSpec_d.dataPtr(), -1.0, m_dt); - auto bcRecTemp = fetchBCRecArray(TEMP,1); + auto bcRecTemp = fetchBCRecArray(TEMP,1); auto bcRecTemp_d = convertToDeviceVector(bcRecTemp); fluxDivergenceRD(GetVecOfConstPtrs(getTempVect(AmrNewTime)), 0, GetVecOfPtrs(diffData->Dnp1), NUM_SPECIES, GetVecOfArrOfPtrs(fluxes), NUM_SPECIES, 1, 1, bcRecTemp_d.dataPtr(), -1.0, m_dt); - auto bcRecRhoH = fetchBCRecArray(RHOH,1); + auto bcRecRhoH = fetchBCRecArray(RHOH,1); auto bcRecRhoH_d = convertToDeviceVector(bcRecRhoH); fluxDivergenceRD(GetVecOfConstPtrs(getRhoHVect(AmrNewTime)), 0, GetVecOfPtrs(diffData->Dnp1), NUM_SPECIES+1, @@ -197,8 +197,11 @@ void PeleLM::computeDifferentialDiffusionFluxes(const TimeStamp &a_time, } // Adjust species diffusion fluxes to ensure their sum is zero + // TODO BAP: Make this more elegant +#ifndef USE_MANIFOLD_EOS adjustSpeciesFluxes(a_fluxes, GetVecOfConstPtrs(getSpeciesVect(a_time))); +#endif //---------------------------------------------------------------- //---------------------------------------------------------------- @@ -509,7 +512,7 @@ void PeleLM::computeSpeciesEnthalpyFlux(const Vector &advDat fillPatchSpecies(AmrNewTime); // Adjust species diffusion fluxes to ensure their sum is zero + // TODO BAP: Make this more elegant +#ifndef USE_MANIFOLD_EOS adjustSpeciesFluxes(GetVecOfArrOfPtrs(fluxes), GetVecOfConstPtrs(getSpeciesVect(AmrNewTime))); +#endif // Average down fluxes^{np1,kp1} getDiffusionOp()->avgDownFluxes(GetVecOfArrOfPtrs(fluxes),0,NUM_SPECIES); From 5e9f3bb2b5cde60bcb270552872efafaba0708bd Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Tue, 21 Jun 2022 17:11:10 -0600 Subject: [PATCH 13/27] Kernel changes for getting Rho from RhoY --- Source/PeleLM_K.H | 43 ++++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/Source/PeleLM_K.H b/Source/PeleLM_K.H index 053dc1bb9..d6851b6f2 100644 --- a/Source/PeleLM_K.H +++ b/Source/PeleLM_K.H @@ -123,15 +123,13 @@ getVelViscosity(int i, int j, int k, using namespace amrex::literals; // Get rho & Y from rhoY - amrex::Real rho = 0.0_rt; - for (int n = 0; n < NUM_SPECIES; n++) { - rho += rhoY(i,j,k,n); - } - amrex::Real rhoinv = 1.0_rt / rho; + amrex::Real rho, rhoinv; amrex::Real y[NUM_SPECIES] = {0.0}; + amrex::Real massdens[NUM_SPECIES]; for (int n = 0; n < NUM_SPECIES; n++) { - y[n] = rhoY(i,j,k,n) * rhoinv; + massdens[n] = rhoY(i,j,k,n); } + pele::physics::PhysicsType::eos_type::RY2RRinvY(massdens, rho, rhoinv, y); rho *= 1.0e-3_rt; // MKS -> CGS conversion amrex::Real temp = T(i,j,k); @@ -781,15 +779,13 @@ reactionRateRhoY(int i, int j, int k, auto eos = pele::physics::PhysicsType::eos(eosparm); // Get rho & Ys from rhoYs. - amrex::Real rho = 0.0_rt; - for (int n = 0; n < NUM_SPECIES; n++) { - rho += rhoY(i,j,k,n); - } - amrex::Real rhoinv = 1.0_rt / rho; - amrex::Real y[NUM_SPECIES] = {0.0_rt}; + amrex::Real rho, rhoinv; + amrex::Real y[NUM_SPECIES] = {0.0}; + amrex::Real massdens[NUM_SPECIES]; for (int n = 0; n < NUM_SPECIES; n++) { - y[n] = rhoY(i,j,k,n) * rhoinv; + massdens[n] = rhoY(i,j,k,n); } + eos.RY2RRinvY(massdens, rho, rhoinv, y); // Get T from Y/H. amrex::Real Tloc = T(i,j,k); @@ -848,15 +844,14 @@ linearChemForcing(const amrex::Box& bx, /* T update with rho, H and Y */ // Get updated rho - amrex::Real rho_loc = 0.0; - for (int n = 0; n < NUM_SPECIES; n++) { - rho_loc += rY_in(i,j,k,n); - } - // Get updated Ys + amrex::Real rho_loc = 0.0, rho_loc_inv = 0.0; amrex::Real Y_loc[NUM_SPECIES] = {0.0}; + amrex::Real massdens[NUM_SPECIES]; for (int n = 0; n < NUM_SPECIES; n++) { - Y_loc[n] = rY_in(i,j,k,n) / rho_loc; + massdens[n] = rY_in(i,j,k,n); } + pele::physics::PhysicsType::eos_type::RY2RRinvY(massdens, rho_loc, rho_loc_inv, Y_loc); + // Get updated H amrex::Real H_loc = rhoH_in(i,j,k,0) / rho_loc; @@ -906,15 +901,13 @@ getGammaInv(int i, int j, int k, auto eos = pele::physics::PhysicsType::eos(eosparm); // Get rho & Y from rhoY - amrex::Real rho = 0.0_rt; - for (int n = 0; n < NUM_SPECIES; n++) { - rho += rhoY(i,j,k,n); - } - amrex::Real rhoinv = 1.0_rt / rho; + amrex::Real rho, rhoinv; amrex::Real y[NUM_SPECIES] = {0.0}; + amrex::Real massdens[NUM_SPECIES]; for (int n = 0; n < NUM_SPECIES; n++) { - y[n] = rhoY(i,j,k,n) * rhoinv; + massdens[n] = rhoY(i,j,k,n); } + pele::physics::PhysicsType::eos_type::RY2RRinvY(massdens, rho, rhoinv, y); amrex::Real cpmix = 0.0_rt; eos.TY2Cp(T(i,j,k), y, cpmix); From 71864c9a179974052ad37c92534b4325e17ec2cf Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Wed, 22 Jun 2022 14:04:12 -0600 Subject: [PATCH 14/27] add different chi correction methods --- Source/PeleLM.H | 3 ++ Source/PeleLMSetup.cpp | 4 +++ Source/PeleLMUMac.cpp | 69 +++++++++++++++++++++++++++++++----------- 3 files changed, 58 insertions(+), 18 deletions(-) diff --git a/Source/PeleLM.H b/Source/PeleLM.H index 972ad9458..60690bfdf 100644 --- a/Source/PeleLM.H +++ b/Source/PeleLM.H @@ -1128,6 +1128,9 @@ class PeleLM : public amrex::AmrCore { amrex::Real m_divu_dtFactor = 0.5; amrex::Real m_divu_rhoMin = 0.1; + // Chi correction + std::string m_chi_correction_type = "DivuEveryIter"; + // Aux data size int m_nAux = 0; diff --git a/Source/PeleLMSetup.cpp b/Source/PeleLMSetup.cpp index 939a955e5..9511cc3c3 100644 --- a/Source/PeleLMSetup.cpp +++ b/Source/PeleLMSetup.cpp @@ -175,6 +175,10 @@ void PeleLM::readParameters() { // ----------------------------------------- pp.query("run_mode",m_run_mode); pp.query("v", m_verbose); + pp.query("chi_correction_type", m_chi_correction_type); + AMREX_ASSERT(chi_correction_type == "DivuEveryIter" || + chi_correction_type == "DivuFirstIter" || + chi_correction_type == "NoDivu"); // ----------------------------------------- // Boundary conditions diff --git a/Source/PeleLMUMac.cpp b/Source/PeleLMUMac.cpp index 44692241f..447fa8721 100644 --- a/Source/PeleLMUMac.cpp +++ b/Source/PeleLMUMac.cpp @@ -10,7 +10,7 @@ void PeleLM::predictVelocity(std::unique_ptr &advData) { BL_PROFILE("PeleLM::predictVelocity()"); - // set umac boundaries to zero + // set umac boundaries to zero if ( advData->umac[0][0].nGrow() > 0 ) { for (int lev=0; lev <= finest_level; ++lev) { @@ -40,7 +40,7 @@ void PeleLM::predictVelocity(std::unique_ptr &advData) //---------------------------------------------------------------- // Predict face velocities at t^{n+1/2} with Godunov - auto bcRecVel = fetchBCRecArray(VELX,AMREX_SPACEDIM); + auto bcRecVel = fetchBCRecArray(VELX,AMREX_SPACEDIM); auto bcRecVel_d = convertToDeviceVector(bcRecVel); for (int lev = 0; lev <= finest_level; ++lev) { @@ -106,16 +106,49 @@ void PeleLM::addChiIncrement(int a_sdcIter, auto const& chiInc_ar = chiIncr[lev].const_array(mfi); auto const& chi_ar = advData->chi[lev].array(mfi); auto const& mac_divu_ar = advData->mac_divu[lev].array(mfi); - amrex::ParallelFor(gbx, [chi_ar, chiInc_ar, mac_divu_ar, a_sdcIter] - AMREX_GPU_DEVICE (int i, int j, int k) noexcept - { - if ( a_sdcIter == 1 ) { - chi_ar(i,j,k) = chiInc_ar(i,j,k); - } else { - chi_ar(i,j,k) += chiInc_ar(i,j,k); - } - mac_divu_ar(i,j,k) += chi_ar(i,j,k); - }); + if (m_chi_correction_type == "DivuEveryIter") { + amrex::ParallelFor(gbx, [chi_ar, chiInc_ar, mac_divu_ar, a_sdcIter] + AMREX_GPU_DEVICE (int i, int j, int k) noexcept + { + if ( a_sdcIter == 1 ) { + chi_ar(i,j,k) = chiInc_ar(i,j,k); + } else { + chi_ar(i,j,k) += chiInc_ar(i,j,k); + } + mac_divu_ar(i,j,k) += chi_ar(i,j,k); + }); + } else if (m_chi_correction_type == "DivuFirstIter") { + amrex::ParallelFor(gbx, [chi_ar, chiInc_ar, mac_divu_ar, a_sdcIter] + AMREX_GPU_DEVICE (int i, int j, int k) noexcept + { + if ( a_sdcIter == 1 ) { + chi_ar(i,j,k) = chiInc_ar(i,j,k) + mac_divu_ar(i,j,k); + } else { + chi_ar(i,j,k) += chiInc_ar(i,j,k); + } + mac_divu_ar(i,j,k) = chi_ar(i,j,k); + }); + } else if (m_chi_correction_type == "NoDivu") { + amrex::ParallelFor(gbx, [chi_ar, chiInc_ar, mac_divu_ar, a_sdcIter] + AMREX_GPU_DEVICE (int i, int j, int k) noexcept + { + if ( a_sdcIter == 1 ) { + chi_ar(i,j,k) = chiInc_ar(i,j,k); + } else { + chi_ar(i,j,k) += chiInc_ar(i,j,k); + } + mac_divu_ar(i,j,k) = chi_ar(i,j,k); + }); + } else { + amrex::Abort("Invalid chi_correction_type"); + } + + } + if (m_verbose > 1) { + amrex::Real max_corr = chiIncr[lev].norm0() * m_dt/m_dpdtFactor; + amrex::Print() << " Level " << lev << " SDC " << a_sdcIter + << ": Calculating Chi corr (" << m_chi_correction_type + << ") max relative P mismatch is " << max_corr << std::endl; } } } @@ -215,7 +248,7 @@ PeleLM::create_constrained_umac_grown(int a_lev, int a_nGrow, // Divergence preserving interp Interpolater* mapper = &face_divfree_interp; - + // Set BCRec for Umac Vector bcrec(1); for (int idim = 0; idim < AMREX_SPACEDIM; idim++) { @@ -225,16 +258,16 @@ PeleLM::create_constrained_umac_grown(int a_lev, int a_nGrow, } else { bcrec[0].setLo(idim,BCType::foextrap); bcrec[0].setHi(idim,BCType::foextrap); - } - } + } + } Array,AMREX_SPACEDIM> bcrecArr = {AMREX_D_DECL(bcrec,bcrec,bcrec)}; - + PhysBCFunct> crse_bndry_func(*crse_geom, bcrec, umacFill{}); Array>,AMREX_SPACEDIM> cbndyFuncArr = {AMREX_D_DECL(crse_bndry_func,crse_bndry_func,crse_bndry_func)}; - + PhysBCFunct> fine_bndry_func(*fine_geom, bcrec, umacFill{}); Array>,AMREX_SPACEDIM> fbndyFuncArr = {AMREX_D_DECL(fine_bndry_func,fine_bndry_func,fine_bndry_func)}; - + // Use piecewise constant interpolation in time, so create dummy variable for time Real dummy = 0.; FillPatchTwoLevels(u_mac_fine, IntVect(a_nGrow), dummy, From e230d1392ac6d94cb1a5708534bf7280d001d2bf Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Wed, 22 Jun 2022 14:05:37 -0600 Subject: [PATCH 15/27] density correction for manifold redundant density --- Source/PeleLMAdvection.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Source/PeleLMAdvection.cpp b/Source/PeleLMAdvection.cpp index d2f787ef2..f1d8f6781 100644 --- a/Source/PeleLMAdvection.cpp +++ b/Source/PeleLMAdvection.cpp @@ -769,6 +769,32 @@ void PeleLM::updateDensity(std::unique_ptr &advData) } } averageDownDensity(AmrNewTime); + + // TODO BAP: figure out root cause of why this is needed + // ensure no deviation between redundant density +#ifdef USE_MANIFOLD_EOS + for (int lev = 0; lev <= finest_level; ++lev) { + // Get level data ptr + auto ldataOld_p = getLevelDataPtr(lev,AmrOldTime); + auto ldataNew_p = getLevelDataPtr(lev,AmrNewTime); +#ifdef AMREX_USE_OMP +#pragma omp parallel if (Gpu::notInLaunchRegion()) +#endif + for (MFIter mfi(ldataNew_p->state,TilingIfNotGPU()); mfi.isValid(); ++mfi) { + Box const& bx = mfi.tilebox(); + auto const& rhoNew_arr = ldataNew_p->state.const_array(mfi,DENSITY); + auto const& rhoRedundantNew_arr = ldataNew_p->state.array(mfi,FIRSTSPEC+NUM_SPECIES-1); + auto const& rhoOld_arr = ldataOld_p->state.const_array(mfi,DENSITY); + auto const& rhoRedundantOld_arr = ldataOld_p->state.array(mfi,FIRSTSPEC+NUM_SPECIES-1); + amrex::ParallelFor(bx, [rhoNew_arr, rhoRedundantNew_arr, rhoOld_arr, rhoRedundantOld_arr] + AMREX_GPU_DEVICE (int i, int j, int k) noexcept + { + rhoRedundantNew_arr(i,j,k) = rhoNew_arr(i,j,k); + rhoRedundantOld_arr(i,j,k) = rhoOld_arr(i,j,k); + }); + } + } +#endif } void PeleLM::computePassiveAdvTerms(std::unique_ptr &advData, From 152ca12f407620c11d6301363f1583e54b4931c1 Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Wed, 22 Jun 2022 14:54:24 -0600 Subject: [PATCH 16/27] PMF test case for manifold model --- .../FlameSheet/get_pmfdat_and_ctable.py | 106 ++++++++++++++++++ Exec/RegTests/FlameSheet/input.drm19 | 103 +++++++++++++++++ Exec/RegTests/FlameSheet/input.manifold | 103 +++++++++++++++++ Exec/RegTests/FlameSheet/pelelm_prob.H | 45 +++++++- 4 files changed, 353 insertions(+), 4 deletions(-) create mode 100644 Exec/RegTests/FlameSheet/get_pmfdat_and_ctable.py create mode 100644 Exec/RegTests/FlameSheet/input.drm19 create mode 100644 Exec/RegTests/FlameSheet/input.manifold diff --git a/Exec/RegTests/FlameSheet/get_pmfdat_and_ctable.py b/Exec/RegTests/FlameSheet/get_pmfdat_and_ctable.py new file mode 100644 index 000000000..6508bec4c --- /dev/null +++ b/Exec/RegTests/FlameSheet/get_pmfdat_and_ctable.py @@ -0,0 +1,106 @@ +import cantera as ct +import numpy as np +import pandas as pd +from collections import OrderedDict +import ctable_tools as ctable + +# Thermo Conditions +press = ct.one_atm +temp = 298.0 +phi = 1.0 +fuel = 'CH4' +oxid = 'O2:1.0, N2:3.76' +mechanism = 'drm19.cti' +trans = 'Mix' +progvars = ["CO2","H2O","CO","H2"] +ctable_specs = ["CO2","H2O","CO","H2","N2","O2","OH"] +outfile_prefix = 'prem_drm19_phi1_p1_t298' + +# Flame Numerics +width = 0.1 +loglevel = 1 +ratio = 2 +slope = 0.05 +curve = 0.05 +prune = 0.02 +max_points = 10000 + +# Set up the flame +gas = ct.Solution(mechanism) +gas.set_equivalence_ratio(phi, fuel, oxid) +flame = ct.FreeFlame(gas, width=width) +flame.set_refine_criteria(ratio=ratio, slope=slope, curve=curve, prune=prune) +flame.set_max_grid_points(1,max_points) +flame.transport_model = trans + +# Solve Flame +flame.solve() + +# Get all desired data +data = pd.DataFrame() +data["X"] = flame.grid +data["T"] = flame.T +data["VEL"] = flame.velocity +data["RHO"] = flame.density_mass +data["DIFF"] = flame.thermal_conductivity / flame.cp_mass +data["VISC"] = flame.viscosity + +specXdata = pd.DataFrame(flame.X.T, + columns=gas.species_names) +specYdata = pd.DataFrame(flame.Y.T, + columns=gas.species_names) + +rxnrates = flame.net_production_rates.T * list(gas.molecular_weights) +specRRdata = pd.DataFrame(rxnrates,columns=gas.species_names) + +# Compute prgress variable and its source +# ensure prog is monotonic +# source term 0 at min and max to ensure boundedness +data["PROG"] = specYdata[progvars].sum(axis=1) +prog = np.array(data["PROG"]) +mono = np.min((prog[1:] - prog[:-1]) >= 0) +if not mono: print("WARNING: Nonmonotonic progress variable") +data["SRC_PROG"] = specRRdata[progvars].sum(axis=1) +data.loc[0,"SRC_PROG"] = 0.0 +data.loc[len(flame.grid)-1,"SRC_PROG"] = 0.0 + +# Convert to CGS units +ctable.convert_chemtable_units(data) + +# Make the chemtable +chemtable = data.drop(columns=["VEL","X","PROG"]) +for spec in ctable_specs: + chemtable["Y-"+spec] = specYdata[spec] +chemtable = pd.DataFrame(chemtable.values, + columns = chemtable.columns, + index = pd.MultiIndex.from_product([data["PROG"]],names=['PROG'])) +ctable.write_chemtable_binary(outfile_prefix+'.ctb', chemtable, "1DFGM") +ctable.print_chemtable(chemtable) +#print(chemtable) + +chemtable.drop(columns=["SRC_PROG"], inplace=True) +ctable.write_chemtable_binary(outfile_prefix+'_norxn.ctb', chemtable, "1DFGM") +ctable.print_chemtable(chemtable) + +# Make the PMF dat files: Detailed Chem and Manifold +def write_dat_file(fname, df): + with open(fname,'w') as fi: + line1 = "".join(["VARIABLES ="] + + [' "{}"'.format(var.split(' ')[0]) for var in df.columns[:4]] + + [' "{}"'.format(var.upper()) for var in df.columns[4:]]) + fi.write(line1+'\n') + line2 = " ZONE I={} FORMAT=POINT\n".format(df.shape[1]) + fi.write(line2) + print('Reformated file has these variables:') + print(line1) + for idex, row in df.iterrows(): + linen = "".join(['{:<26.15g}'.format(x) for x in row])+'\n' + fi.write(linen) + +rename = {'VEL':'u', 'T':'temp','RHO':'rho'} +keepvars = ["X","T","VEL","RHO"] +df = data[keepvars].rename(columns=rename) +df2 = pd.DataFrame(data[["PROG","RHO"]].values, columns=['X0','XRHO']) + +write_dat_file(outfile_prefix+'.dat', pd.concat([df,specXdata],axis=1)) +write_dat_file(outfile_prefix+'_mani.dat', pd.concat([df,df2],axis=1)) diff --git a/Exec/RegTests/FlameSheet/input.drm19 b/Exec/RegTests/FlameSheet/input.drm19 new file mode 100644 index 000000000..4a67b41c6 --- /dev/null +++ b/Exec/RegTests/FlameSheet/input.drm19 @@ -0,0 +1,103 @@ +#----------------------DOMAIN DEFINITION------------------------ +geometry.is_periodic = 1 0 # For each dir, 0: non-perio, 1: periodic +geometry.coord_sys = 0 # 0 => cart, 1 => RZ +geometry.prob_lo = 0.0 0.0 0.0 # x_lo y_lo (z_lo) +geometry.prob_hi = 0.003 0.012 0.016 # x_hi y_hi (z_hi) + +# >>>>>>>>>>>>> BC FLAGS <<<<<<<<<<<<<<<< +# Interior, Inflow, Outflow, Symmetry, +# SlipWallAdiab, NoSlipWallAdiab, SlipWallIsotherm, NoSlipWallIsotherm +peleLM.lo_bc = Interior Inflow +peleLM.hi_bc = Interior Outflow + + +#-------------------------AMR CONTROL---------------------------- +amr.n_cell = 32 128 32 # Level 0 number of cells in each direction +amr.v = 1 # AMR verbose +amr.max_level = 2 # maximum level number allowed +amr.ref_ratio = 2 2 2 2 # refinement ratio +amr.regrid_int = 5 # how often to regrid +amr.n_error_buf = 1 1 2 2 # number of buffer cells in error est +amr.grid_eff = 0.7 # what constitutes an efficient grid +amr.blocking_factor = 16 # block factor in grid generation (min box size) +amr.max_grid_size = 64 # max box size + + +#--------------------------- Problem ------------------------------- +prob.P_mean = 101325.0 +prob.standoff = -.03 +prob.pertmag = 0.00005 +pmf.datafile = "prem_drm19_phi1_p1_t298.dat" + +#-------------------------PeleLM CONTROL---------------------------- +peleLM.v = 3 +peleLM.incompressible = 0 +peleLM.rho = 1.17 +peleLM.mu = 0.0 +peleLM.sdc_iterMax = 2 +peleLM.floor_species = 0 + +peleLM.do_temporals = 1 +peleLM.temporal_int = 2 +peleLM.mass_balance = 1 + +#amr.restart = chk00005 +#amr.check_int = 20 +amr.plot_int = 100 +amr.max_step = 20000 +amr.dt_shrink = 0.01 +amr.stop_time = 0.01 +#amr.stop_time = 1.00 +amr.cfl = 0.5 +amr.derive_plot_vars = avg_pressure mag_vort mass_fractions # mixture_fraction progress_variable + +# ------------------- INPUTS DERIVED DIAGS ------------------ +peleLM.fuel_name = CH4 +#peleLM.mixtureFraction.format = Cantera +#peleLM.mixtureFraction.type = mass +#peleLM.mixtureFraction.oxidTank = O2:0.233 N2:0.767 +#peleLM.mixtureFraction.fuelTank = H2:0.5 CH4:0.5 +#peleLM.progressVariable.format = Cantera +#peleLM.progressVariable.weights = CO:1.0 CO2:1.0 +#peleLM.progressVariable.coldState = CO:0.0 CO2:0.0 +#peleLM.progressVariable.hotState = CO:0.000002 CO2:0.0666 + +# --------------- INPUTS TO CHEMISTRY REACTOR --------------- +peleLM.chem_integrator = "ReactorCvode" +peleLM.use_typ_vals_chem = 1 # Use species/temp typical values in CVODE +ode.rtol = 1.0e-6 # Relative tolerance of the chemical solve +ode.atol = 1.0e-5 # Absolute tolerance factor applied on typical values +cvode.solve_type = denseAJ_direct # CVODE Linear solve type (for Newton direction) +cvode.max_order = 4 # CVODE max BDF order. + +mac_proj.verbose = 2 +nodal_proj.verbose = 2 + +#--------------------REFINEMENT CONTROL------------------------ +amr.refinement_indicators = temp +amr.temp.max_level = 3 +amr.temp.adjacent_difference_greater = 10 +amr.temp.field_name = temp + +#amr.refinement_indicators = magVort +#amr.magVort.max_level = 1 +#amr.magVort.value_greater = 500.0 +#amr.magVort.field_name = mag_vort + +#amr.refinement_indicators = yH +#amr.yH.max_level = 3 +#amr.yH.value_greater = 1.0e-6 +#amr.yH.field_name = Y(H) + +#amrex.fpe_trap_invalid = 1 +#amrex.fpe_trap_zero = 1 +#amrex.fpe_trap_overflow = 1 + +#----------------------- Manifold Stuff ---------------------------- +#manifold.model = Table +#manifold.v = 1 +#manifold.filename = prem_drm19_phi1_p1_t298.ctb +#eos.nominal_pressure_cgs = 1013250.0 +#peleLM.use_wbar = 0 +#peleLM.chi_correction_type = "DivuFirstIter" +#peleLM.chi_correction_type = "NoDivu" \ No newline at end of file diff --git a/Exec/RegTests/FlameSheet/input.manifold b/Exec/RegTests/FlameSheet/input.manifold new file mode 100644 index 000000000..80b9652b0 --- /dev/null +++ b/Exec/RegTests/FlameSheet/input.manifold @@ -0,0 +1,103 @@ +#----------------------DOMAIN DEFINITION------------------------ +geometry.is_periodic = 1 0 # For each dir, 0: non-perio, 1: periodic +geometry.coord_sys = 0 # 0 => cart, 1 => RZ +geometry.prob_lo = 0.0 0.0 0.0 # x_lo y_lo (z_lo) +geometry.prob_hi = 0.003 0.012 0.016 # x_hi y_hi (z_hi) + +# >>>>>>>>>>>>> BC FLAGS <<<<<<<<<<<<<<<< +# Interior, Inflow, Outflow, Symmetry, +# SlipWallAdiab, NoSlipWallAdiab, SlipWallIsotherm, NoSlipWallIsotherm +peleLM.lo_bc = Interior Inflow +peleLM.hi_bc = Interior Outflow + + +#-------------------------AMR CONTROL---------------------------- +amr.n_cell = 32 128 32 # Level 0 number of cells in each direction +amr.v = 1 # AMR verbose +amr.max_level = 2 # maximum level number allowed +amr.ref_ratio = 2 2 2 2 # refinement ratio +amr.regrid_int = 5 # how often to regrid +amr.n_error_buf = 1 1 2 2 # number of buffer cells in error est +amr.grid_eff = 0.7 # what constitutes an efficient grid +amr.blocking_factor = 16 # block factor in grid generation (min box size) +amr.max_grid_size = 64 # max box size + + +#--------------------------- Problem ------------------------------- +prob.P_mean = 101325.0 +prob.standoff = -.03 +prob.pertmag = 0.00005 +pmf.datafile = "prem_drm19_phi1_p1_t298_mani.dat" + +#-------------------------PeleLM CONTROL---------------------------- +peleLM.v = 3 +peleLM.incompressible = 0 +peleLM.rho = 1.17 +peleLM.mu = 0.0 +peleLM.sdc_iterMax = 4 +peleLM.floor_species = 0 + +peleLM.do_temporals = 1 +peleLM.temporal_int = 2 +peleLM.mass_balance = 1 + +#amr.restart = chk00005 +#amr.check_int = 20 +amr.plot_int = 100 +amr.max_step = 20202 +amr.dt_shrink = 0.01 +amr.stop_time = 0.01 +#amr.stop_time = 1.00 +amr.cfl = 0.5 +amr.derive_plot_vars = avg_pressure mag_vort mass_fractions # mixture_fraction progress_variable + +# ------------------- INPUTS DERIVED DIAGS ------------------ +peleLM.fuel_name = CH4 +#peleLM.mixtureFraction.format = Cantera +#peleLM.mixtureFraction.type = mass +#peleLM.mixtureFraction.oxidTank = O2:0.233 N2:0.767 +#peleLM.mixtureFraction.fuelTank = H2:0.5 CH4:0.5 +#peleLM.progressVariable.format = Cantera +#peleLM.progressVariable.weights = CO:1.0 CO2:1.0 +#peleLM.progressVariable.coldState = CO:0.0 CO2:0.0 +#peleLM.progressVariable.hotState = CO:0.000002 CO2:0.0666 + +# --------------- INPUTS TO CHEMISTRY REACTOR --------------- +peleLM.chem_integrator = "ReactorRK64" +peleLM.use_typ_vals_chem = 1 # Use species/temp typical values in CVODE +ode.rtol = 1.0e-6 # Relative tolerance of the chemical solve +ode.atol = 1.0e-5 # Absolute tolerance factor applied on typical values +cvode.solve_type = denseAJ_direct # CVODE Linear solve type (for Newton direction) +cvode.max_order = 4 # CVODE max BDF order. + +mac_proj.verbose = 2 +nodal_proj.verbose = 2 + +#--------------------REFINEMENT CONTROL------------------------ +amr.refinement_indicators = temp +amr.temp.max_level = 3 +amr.temp.adjacent_difference_greater = 10 +amr.temp.field_name = temp + +#amr.refinement_indicators = magVort +#amr.magVort.max_level = 1 +#amr.magVort.value_greater = 500.0 +#amr.magVort.field_name = mag_vort + +#amr.refinement_indicators = yH +#amr.yH.max_level = 3 +#amr.yH.value_greater = 1.0e-6 +#amr.yH.field_name = Y(H) + +#amrex.fpe_trap_invalid = 1 +#amrex.fpe_trap_zero = 1 +#amrex.fpe_trap_overflow = 1 + +#----------------------- Manifold Stuff ---------------------------- +manifold.model = Table +manifold.v = 1 +manifold.filename = prem_drm19_phi1_p1_t298.ctb +eos.nominal_pressure_cgs = 1013250.0 +peleLM.use_wbar = 0 +peleLM.chi_correction_type = "DivuFirstIter" +#peleLM.chi_correction_type = "NoDivu" \ No newline at end of file diff --git a/Exec/RegTests/FlameSheet/pelelm_prob.H b/Exec/RegTests/FlameSheet/pelelm_prob.H index 063a51b83..c5d0fff8f 100644 --- a/Exec/RegTests/FlameSheet/pelelm_prob.H +++ b/Exec/RegTests/FlameSheet/pelelm_prob.H @@ -36,7 +36,7 @@ void pelelm_initdata(int i, int j, int k, constexpr amrex::Real Pi = 3.14159265358979323846264338327950288; - auto eos = pele::physics::PhysicsType::eos(); + auto eos = pele::physics::PhysicsType::eos(eosparm); amrex::GpuArray pmf_vals = {0.0}; amrex::Real molefrac[NUM_SPECIES] = {0.0}; amrex::Real massfrac[NUM_SPECIES] = {0.0}; @@ -74,12 +74,35 @@ void pelelm_initdata(int i, int j, int k, amrex::Real y1 = (z - prob_parm.standoff - 0.5*dx[2] + pert)*100; amrex::Real y2 = (z - prob_parm.standoff + 0.5*dx[2] + pert)*100; #endif + pele::physics::PMF::pmf(pmf_data,y1, y2, pmf_vals); + state(i,j,k,TEMP) = pmf_vals[0]; +#ifdef USE_MANIFOLD_EOS +// Setup for manifold EOS + for (int n = 0; n < NUM_SPECIES-1; n++){ + massfrac[n] = pmf_vals[3 + n]; + } - pele::physics::PMF::pmf(pmf_data,y1, y2, pmf_vals); + state(i,j,k,VELX) = 0; +#if ( AMREX_SPACEDIM == 2 ) + state(i,j,k,VELY) = pmf_vals[1]*1e-2; +#elif ( AMREX_SPACEDIM == 3 ) + state(i,j,k,VELY) = 0; + state(i,j,k,VELZ) = pmf_vals[1]*1e-2; +#endif - state(i,j,k,TEMP) = pmf_vals[0];; + amrex::Real rho_temp, tmp1, tmp2; + eos.PYT2R(tmp1, massfrac, tmp2, rho_temp); + rho_temp *= 1.0e3; // CGS -> MKS conversion + state(i,j,k,DENSITY) = rho_temp; + state(i,j,k,RHOH) = 0.0; // No RhoH transport in manifold model + for (int n = 0; n < NUM_SPECIES-1; n++) { + state(i,j,k,FIRSTSPEC+n) = massfrac[n] * rho_temp; + } + state(i,j,k,FIRSTSPEC+NUM_SPECIES-1) = rho_temp; +#else +// setup for detailed chem for (int n = 0; n < NUM_SPECIES; n++){ molefrac[n] = pmf_vals[3 + n]; } @@ -109,6 +132,7 @@ void pelelm_initdata(int i, int j, int k, for (int n = 0; n < NUM_SPECIES; n++) { state(i,j,k,FIRSTSPEC+n) = massfrac[n] * state(i,j,k,DENSITY); } +#endif } AMREX_GPU_DEVICE @@ -131,7 +155,7 @@ bcnormal( amrex::Real molefrac[NUM_SPECIES] = {0.0}; amrex::Real massfrac[NUM_SPECIES] = {0.0}; - auto eos = pele::physics::PhysicsType::eos(); + auto eos = pele::physics::PhysicsType::eos(eosparm); if (sgn == 1) { pele::physics::PMF::pmf(pmf_data,prob_lo[idir], prob_lo[idir], pmf_vals); @@ -144,6 +168,8 @@ bcnormal( #endif s_ext[TEMP] = pmf_vals[0]; + +#ifndef USE_MANIFOLD_EOS for (int n = 0; n < NUM_SPECIES; n++){ molefrac[n] = pmf_vals[3 + n]; } @@ -161,6 +187,17 @@ bcnormal( for (int n = 0; n < NUM_SPECIES; n++) { s_ext[FIRSTSPEC+n] = massfrac[n] * s_ext[DENSITY]; } +#else + for (int n = 0; n < NUM_SPECIES; n++){ + massfrac[n] = pmf_vals[3 + n]; + } + s_ext[DENSITY] = pmf_vals[3 + NUM_SPECIES -1] *1.0e3; + s_ext[RHOH] = 0.0; + for (int n = 0; n < NUM_SPECIES-1; n++) { + s_ext[FIRSTSPEC+n] = massfrac[n] * s_ext[DENSITY]; + } + s_ext[FIRSTSPEC+NUM_SPECIES-1] = s_ext[DENSITY]; +#endif } } From 3bcda07a0a4a07731e9bc9fe1c4053ebe825099c Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Thu, 23 Jun 2022 16:48:46 -0600 Subject: [PATCH 17/27] Cleaning things up --- Source/PeleLM.H | 1 + Source/PeleLMAdvection.cpp | 26 -------------------------- Source/PeleLMDiffusion.cpp | 25 +++++++++++++++++-------- 3 files changed, 18 insertions(+), 34 deletions(-) diff --git a/Source/PeleLM.H b/Source/PeleLM.H index 60690bfdf..0988c0c91 100644 --- a/Source/PeleLM.H +++ b/Source/PeleLM.H @@ -332,6 +332,7 @@ class PeleLM : public amrex::AmrCore { amrex::Vector const &a_beta); // Ensure sum of specues diffusion fluxes sum to 0 + template void adjustSpeciesFluxes(const amrex::Vector> &a_fluxes, amrex::Vector const &a_spec); diff --git a/Source/PeleLMAdvection.cpp b/Source/PeleLMAdvection.cpp index f1d8f6781..d2f787ef2 100644 --- a/Source/PeleLMAdvection.cpp +++ b/Source/PeleLMAdvection.cpp @@ -769,32 +769,6 @@ void PeleLM::updateDensity(std::unique_ptr &advData) } } averageDownDensity(AmrNewTime); - - // TODO BAP: figure out root cause of why this is needed - // ensure no deviation between redundant density -#ifdef USE_MANIFOLD_EOS - for (int lev = 0; lev <= finest_level; ++lev) { - // Get level data ptr - auto ldataOld_p = getLevelDataPtr(lev,AmrOldTime); - auto ldataNew_p = getLevelDataPtr(lev,AmrNewTime); -#ifdef AMREX_USE_OMP -#pragma omp parallel if (Gpu::notInLaunchRegion()) -#endif - for (MFIter mfi(ldataNew_p->state,TilingIfNotGPU()); mfi.isValid(); ++mfi) { - Box const& bx = mfi.tilebox(); - auto const& rhoNew_arr = ldataNew_p->state.const_array(mfi,DENSITY); - auto const& rhoRedundantNew_arr = ldataNew_p->state.array(mfi,FIRSTSPEC+NUM_SPECIES-1); - auto const& rhoOld_arr = ldataOld_p->state.const_array(mfi,DENSITY); - auto const& rhoRedundantOld_arr = ldataOld_p->state.array(mfi,FIRSTSPEC+NUM_SPECIES-1); - amrex::ParallelFor(bx, [rhoNew_arr, rhoRedundantNew_arr, rhoOld_arr, rhoRedundantOld_arr] - AMREX_GPU_DEVICE (int i, int j, int k) noexcept - { - rhoRedundantNew_arr(i,j,k) = rhoNew_arr(i,j,k); - rhoRedundantOld_arr(i,j,k) = rhoOld_arr(i,j,k); - }); - } - } -#endif } void PeleLM::computePassiveAdvTerms(std::unique_ptr &advData, diff --git a/Source/PeleLMDiffusion.cpp b/Source/PeleLMDiffusion.cpp index 53e20bf2a..21e0a65e7 100644 --- a/Source/PeleLMDiffusion.cpp +++ b/Source/PeleLMDiffusion.cpp @@ -198,10 +198,10 @@ void PeleLM::computeDifferentialDiffusionFluxes(const TimeStamp &a_time, // Adjust species diffusion fluxes to ensure their sum is zero // TODO BAP: Make this more elegant -#ifndef USE_MANIFOLD_EOS - adjustSpeciesFluxes(a_fluxes, + //#ifndef USE_MANIFOLD_EOS + adjustSpeciesFluxes(a_fluxes, GetVecOfConstPtrs(getSpeciesVect(a_time))); -#endif + //#endif //---------------------------------------------------------------- //---------------------------------------------------------------- @@ -377,8 +377,9 @@ void PeleLM::addWbarTerm(const Vector > &a_spflu } } -void PeleLM::adjustSpeciesFluxes(const Vector > &a_spfluxes, - Vector const &a_spec) +template +void PeleLM::adjustSpeciesFluxes(const Vector > & a_spfluxes, + Vector const & a_spec) { BL_PROFILE("PeleLM::adjustSpeciesFluxes()"); @@ -473,6 +474,14 @@ void PeleLM::adjustSpeciesFluxes(const Vector > } } +template <> +void PeleLM::adjustSpeciesFluxes(const Vector > & /*a_spfluxes*/, + Vector const & /*a_spec*/) +{ + // Manifold Model: "Species" don't sum to unity, so no need to adjust the fluxes + BL_PROFILE("PeleLM::adjustSpeciesFluxes()"); +} + void PeleLM::computeSpeciesEnthalpyFlux(const Vector > &a_fluxes, Vector const &a_temp) { @@ -658,10 +667,10 @@ void PeleLM::differentialDiffusionUpdate(std::unique_ptr &advDat // Adjust species diffusion fluxes to ensure their sum is zero // TODO BAP: Make this more elegant -#ifndef USE_MANIFOLD_EOS - adjustSpeciesFluxes(GetVecOfArrOfPtrs(fluxes), + //#ifndef USE_MANIFOLD_EOS + adjustSpeciesFluxes(GetVecOfArrOfPtrs(fluxes), GetVecOfConstPtrs(getSpeciesVect(AmrNewTime))); -#endif + //#endif // Average down fluxes^{np1,kp1} getDiffusionOp()->avgDownFluxes(GetVecOfArrOfPtrs(fluxes),0,NUM_SPECIES); From 02e9a87cc3d6ab6df9943f3567f17ba5237aa283 Mon Sep 17 00:00:00 2001 From: Kiran Eiden Date: Thu, 30 Jun 2022 10:14:50 -0600 Subject: [PATCH 18/27] Ostensibly working PeleLMeX setup with neural net --- Exec/RegTests/FlameSheet/GNUmakefile | 14 +- Exec/RegTests/FlameSheet/drm19.yaml | 606 ++++++++++++++++++ .../FlameSheet/get_pmfdat_and_ctable.py | 80 ++- Exec/RegTests/FlameSheet/input.manifold | 2 +- Exec/RegTests/FlameSheet/input.nn | 114 ++++ Source/PeleLM.H | 7 +- Utils/Make.PeleLMeX | 16 + 7 files changed, 831 insertions(+), 8 deletions(-) create mode 100644 Exec/RegTests/FlameSheet/drm19.yaml create mode 100644 Exec/RegTests/FlameSheet/input.nn diff --git a/Exec/RegTests/FlameSheet/GNUmakefile b/Exec/RegTests/FlameSheet/GNUmakefile index d3f82f9aa..8ca1771e8 100644 --- a/Exec/RegTests/FlameSheet/GNUmakefile +++ b/Exec/RegTests/FlameSheet/GNUmakefile @@ -18,13 +18,21 @@ USE_MPI = TRUE USE_OMP = FALSE USE_CUDA = FALSE USE_HIP = FALSE +FUEGO_GAS = TRUE +USE_LIBTORCH = FALSE # PeleLMeX USE_EFIELD = FALSE # PelePhysics -Chemistry_Model = drm19 -Eos_Model = Fuego -Transport_Model = Simple +ifeq ($(FUEGO_GAS), TRUE) + Chemistry_Model = drm19 + Eos_Model = Fuego + Transport_Model = Simple +else + Eos_Model = Manifold + Chemistry_Model = Manifold + Transport_Model = Manifold +endif include $(PELELMEX_HOME)/Utils/Make.PeleLMeX diff --git a/Exec/RegTests/FlameSheet/drm19.yaml b/Exec/RegTests/FlameSheet/drm19.yaml new file mode 100644 index 000000000..c013836b8 --- /dev/null +++ b/Exec/RegTests/FlameSheet/drm19.yaml @@ -0,0 +1,606 @@ +description: |- + <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> OH + M # Reaction 1 + type: three-body + rate-constant: {A: 5.0e+17, b: -1.0, Ea: 0.0} + efficiencies: {H2: 2.0, H2O: 6.0, CH4: 2.0, CO: 1.5, CO2: 2.0, C2H6: 3.0, + AR: 0.7} +- equation: O + H2 <=> H + OH # Reaction 2 + rate-constant: {A: 5.0e+04, b: 2.67, Ea: 6290.0} +- equation: O + HO2 <=> OH + O2 # Reaction 3 + rate-constant: {A: 2.0e+13, b: 0.0, Ea: 0.0} +- equation: O + CH2 <=> H + HCO # Reaction 4 + rate-constant: {A: 8.0e+13, b: 0.0, Ea: 0.0} +- equation: O + CH2(S) <=> H + HCO # Reaction 5 + rate-constant: {A: 1.5e+13, b: 0.0, Ea: 0.0} +- equation: O + CH3 <=> H + CH2O # Reaction 6 + rate-constant: {A: 8.43e+13, b: 0.0, Ea: 0.0} +- equation: O + CH4 <=> OH + CH3 # Reaction 7 + rate-constant: {A: 1.02e+09, b: 1.5, Ea: 8600.0} +- equation: O + CO + M <=> CO2 + M # Reaction 8 + type: three-body + rate-constant: {A: 6.02e+14, b: 0.0, Ea: 3000.0} + efficiencies: {H2: 2.0, O2: 6.0, H2O: 6.0, CH4: 2.0, CO: 1.5, CO2: 3.5, + C2H6: 3.0, AR: 0.5} +- equation: O + HCO <=> OH + CO # Reaction 9 + rate-constant: {A: 3.0e+13, b: 0.0, Ea: 0.0} +- equation: O + HCO <=> H + CO2 # Reaction 10 + rate-constant: {A: 3.0e+13, b: 0.0, Ea: 0.0} +- equation: O + CH2O <=> OH + HCO # Reaction 11 + rate-constant: {A: 3.9e+13, b: 0.0, Ea: 3540.0} +- equation: O + C2H4 <=> CH3 + HCO # Reaction 12 + rate-constant: {A: 1.92e+07, b: 1.83, Ea: 220.0} +- equation: O + C2H5 <=> CH3 + CH2O # Reaction 13 + rate-constant: {A: 1.32e+14, b: 0.0, Ea: 0.0} +- equation: O + C2H6 <=> OH + C2H5 # Reaction 14 + rate-constant: {A: 8.98e+07, b: 1.92, Ea: 5690.0} +- equation: O2 + CO <=> O + CO2 # Reaction 15 + rate-constant: {A: 2.5e+12, b: 0.0, Ea: 4.78e+04} +- equation: O2 + CH2O <=> HO2 + HCO # Reaction 16 + rate-constant: {A: 1.0e+14, b: 0.0, Ea: 4.0e+04} +- equation: H + O2 + M <=> HO2 + M # Reaction 17 + type: three-body + rate-constant: {A: 2.8e+18, b: -0.86, Ea: 0.0} + efficiencies: {O2: 0.0, H2O: 0.0, CO: 0.75, CO2: 1.5, C2H6: 1.5, N2: 0.0, + AR: 0.0} +- equation: H + 2 O2 <=> HO2 + O2 # Reaction 18 + rate-constant: {A: 3.0e+20, b: -1.72, Ea: 0.0} +- equation: H + O2 + H2O <=> HO2 + H2O # Reaction 19 + rate-constant: {A: 9.38e+18, b: -0.76, Ea: 0.0} +- equation: H + O2 + N2 <=> HO2 + N2 # Reaction 20 + rate-constant: {A: 3.75e+20, b: -1.72, Ea: 0.0} +- equation: H + O2 + AR <=> HO2 + AR # Reaction 21 + rate-constant: {A: 7.0e+17, b: -0.8, Ea: 0.0} +- equation: H + O2 <=> O + OH # Reaction 22 + rate-constant: {A: 8.3e+13, b: 0.0, Ea: 1.4413e+04} +- equation: 2 H + M <=> H2 + M # Reaction 23 + type: three-body + rate-constant: {A: 1.0e+18, b: -1.0, Ea: 0.0} + efficiencies: {H2: 0.0, H2O: 0.0, CH4: 2.0, CO2: 0.0, C2H6: 3.0, AR: 0.63} +- equation: 2 H + H2 <=> 2 H2 # Reaction 24 + rate-constant: {A: 9.0e+16, b: -0.6, Ea: 0.0} +- equation: 2 H + H2O <=> H2 + H2O # Reaction 25 + rate-constant: {A: 6.0e+19, b: -1.25, Ea: 0.0} +- equation: 2 H + CO2 <=> H2 + CO2 # Reaction 26 + rate-constant: {A: 5.5e+20, b: -2.0, Ea: 0.0} +- equation: H + OH + M <=> H2O + M # Reaction 27 + type: three-body + rate-constant: {A: 2.2e+22, b: -2.0, Ea: 0.0} + efficiencies: {H2: 0.73, H2O: 3.65, CH4: 2.0, C2H6: 3.0, AR: 0.38} +- equation: H + HO2 <=> O2 + H2 # Reaction 28 + rate-constant: {A: 2.8e+13, b: 0.0, Ea: 1068.0} +- equation: H + HO2 <=> 2 OH # Reaction 29 + rate-constant: {A: 1.34e+14, b: 0.0, Ea: 635.0} +- equation: H + CH2 (+M) <=> CH3 (+M) # Reaction 30 + type: falloff + low-P-rate-constant: {A: 3.2e+27, b: -3.14, Ea: 1230.0} + high-P-rate-constant: {A: 2.5e+16, b: -0.8, Ea: 0.0} + Troe: {A: 0.68, T3: 78.0, T1: 1995.0, T2: 5590.0} + efficiencies: {H2: 2.0, H2O: 6.0, CH4: 2.0, CO: 1.5, CO2: 2.0, C2H6: 3.0, + AR: 0.7} +- equation: H + CH3 (+M) <=> CH4 (+M) # Reaction 31 + type: falloff + low-P-rate-constant: {A: 2.477e+33, b: -4.76, Ea: 2440.0} + high-P-rate-constant: {A: 1.27e+16, b: -0.63, Ea: 383.0} + Troe: {A: 0.783, T3: 74.0, T1: 2941.0, T2: 6964.0} + efficiencies: {H2: 2.0, H2O: 6.0, CH4: 2.0, CO: 1.5, CO2: 2.0, C2H6: 3.0, + AR: 0.7} +- equation: H + CH4 <=> CH3 + H2 # Reaction 32 + rate-constant: {A: 6.6e+08, b: 1.62, Ea: 1.084e+04} +- equation: H + HCO (+M) <=> CH2O (+M) # Reaction 33 + type: falloff + low-P-rate-constant: {A: 1.35e+24, b: -2.57, Ea: 1425.0} + high-P-rate-constant: {A: 1.09e+12, b: 0.48, Ea: -260.0} + Troe: {A: 0.7824, T3: 271.0, T1: 2755.0, T2: 6570.0} + efficiencies: {H2: 2.0, H2O: 6.0, CH4: 2.0, CO: 1.5, CO2: 2.0, C2H6: 3.0, + AR: 0.7} +- equation: H + HCO <=> H2 + CO # Reaction 34 + rate-constant: {A: 7.34e+13, b: 0.0, Ea: 0.0} +- equation: H + CH2O (+M) <=> CH3O (+M) # Reaction 35 + type: falloff + low-P-rate-constant: {A: 2.2e+30, b: -4.8, Ea: 5560.0} + high-P-rate-constant: {A: 5.4e+11, b: 0.454, Ea: 2600.0} + Troe: {A: 0.758, T3: 94.0, T1: 1555.0, T2: 4200.0} + efficiencies: {H2: 2.0, H2O: 6.0, CH4: 2.0, CO: 1.5, CO2: 2.0, C2H6: 3.0} +- equation: H + CH2O <=> HCO + H2 # Reaction 36 + rate-constant: {A: 2.3e+10, b: 1.05, Ea: 3275.0} +- equation: H + CH3O <=> OH + CH3 # Reaction 37 + rate-constant: {A: 3.2e+13, b: 0.0, Ea: 0.0} +- equation: H + C2H4 (+M) <=> C2H5 (+M) # Reaction 38 + type: falloff + low-P-rate-constant: {A: 1.2e+42, b: -7.62, Ea: 6970.0} + high-P-rate-constant: {A: 1.08e+12, b: 0.454, Ea: 1820.0} + Troe: {A: 0.9753, T3: 210.0, T1: 984.0, T2: 4374.0} + efficiencies: {H2: 2.0, H2O: 6.0, CH4: 2.0, CO: 1.5, CO2: 2.0, C2H6: 3.0, + AR: 0.7} +- equation: H + C2H5 (+M) <=> C2H6 (+M) # Reaction 39 + type: falloff + low-P-rate-constant: {A: 1.99e+41, b: -7.08, Ea: 6685.0} + high-P-rate-constant: {A: 5.21e+17, b: -0.99, Ea: 1580.0} + Troe: {A: 0.8422, T3: 125.0, T1: 2219.0, T2: 6882.0} + efficiencies: {H2: 2.0, H2O: 6.0, CH4: 2.0, CO: 1.5, CO2: 2.0, C2H6: 3.0, + AR: 0.7} +- equation: H + C2H6 <=> C2H5 + H2 # Reaction 40 + rate-constant: {A: 1.15e+08, b: 1.9, Ea: 7530.0} +- equation: H2 + CO (+M) <=> CH2O (+M) # Reaction 41 + type: falloff + low-P-rate-constant: {A: 5.07e+27, b: -3.42, Ea: 8.435e+04} + high-P-rate-constant: {A: 4.3e+07, b: 1.5, Ea: 7.96e+04} + Troe: {A: 0.932, T3: 197.0, T1: 1540.0, T2: 1.03e+04} + efficiencies: {H2: 2.0, H2O: 6.0, CH4: 2.0, CO: 1.5, CO2: 2.0, C2H6: 3.0, + AR: 0.7} +- equation: OH + H2 <=> H + H2O # Reaction 42 + rate-constant: {A: 2.16e+08, b: 1.51, Ea: 3430.0} +- equation: 2 OH <=> O + H2O # Reaction 43 + rate-constant: {A: 3.57e+04, b: 2.4, Ea: -2110.0} +- equation: OH + HO2 <=> O2 + H2O # Reaction 44 + rate-constant: {A: 2.9e+13, b: 0.0, Ea: -500.0} +- equation: OH + CH2 <=> H + CH2O # Reaction 45 + rate-constant: {A: 2.0e+13, b: 0.0, Ea: 0.0} +- equation: OH + CH2(S) <=> H + CH2O # Reaction 46 + rate-constant: {A: 3.0e+13, b: 0.0, Ea: 0.0} +- equation: OH + CH3 <=> CH2 + H2O # Reaction 47 + rate-constant: {A: 5.6e+07, b: 1.6, Ea: 5420.0} +- equation: OH + CH3 <=> CH2(S) + H2O # Reaction 48 + rate-constant: {A: 2.501e+13, b: 0.0, Ea: 0.0} +- equation: OH + CH4 <=> CH3 + H2O # Reaction 49 + rate-constant: {A: 1.0e+08, b: 1.6, Ea: 3120.0} +- equation: OH + CO <=> H + CO2 # Reaction 50 + rate-constant: {A: 4.76e+07, b: 1.228, Ea: 70.0} +- equation: OH + HCO <=> H2O + CO # Reaction 51 + rate-constant: {A: 5.0e+13, b: 0.0, Ea: 0.0} +- equation: OH + CH2O <=> HCO + H2O # Reaction 52 + rate-constant: {A: 3.43e+09, b: 1.18, Ea: -447.0} +- equation: OH + C2H6 <=> C2H5 + H2O # Reaction 53 + rate-constant: {A: 3.54e+06, b: 2.12, Ea: 870.0} +- equation: HO2 + CH2 <=> OH + CH2O # Reaction 54 + rate-constant: {A: 2.0e+13, b: 0.0, Ea: 0.0} +- equation: HO2 + CH3 <=> O2 + CH4 # Reaction 55 + rate-constant: {A: 1.0e+12, b: 0.0, Ea: 0.0} +- equation: HO2 + CH3 <=> OH + CH3O # Reaction 56 + rate-constant: {A: 2.0e+13, b: 0.0, Ea: 0.0} +- equation: HO2 + CO <=> OH + CO2 # Reaction 57 + rate-constant: {A: 1.5e+14, b: 0.0, Ea: 2.36e+04} +- equation: CH2 + O2 <=> OH + HCO # Reaction 58 + rate-constant: {A: 1.32e+13, b: 0.0, Ea: 1500.0} +- equation: CH2 + H2 <=> H + CH3 # Reaction 59 + rate-constant: {A: 5.0e+05, b: 2.0, Ea: 7230.0} +- equation: CH2 + CH3 <=> H + C2H4 # Reaction 60 + rate-constant: {A: 4.0e+13, b: 0.0, Ea: 0.0} +- equation: CH2 + CH4 <=> 2 CH3 # Reaction 61 + rate-constant: {A: 2.46e+06, b: 2.0, Ea: 8270.0} +- equation: CH2(S) + N2 <=> CH2 + N2 # Reaction 62 + rate-constant: {A: 1.5e+13, b: 0.0, Ea: 600.0} +- equation: CH2(S) + AR <=> CH2 + AR # Reaction 63 + rate-constant: {A: 9.0e+12, b: 0.0, Ea: 600.0} +- equation: CH2(S) + O2 <=> H + OH + CO # Reaction 64 + rate-constant: {A: 2.8e+13, b: 0.0, Ea: 0.0} +- equation: CH2(S) + O2 <=> CO + H2O # Reaction 65 + rate-constant: {A: 1.2e+13, b: 0.0, Ea: 0.0} +- equation: CH2(S) + H2 <=> CH3 + H # Reaction 66 + rate-constant: {A: 7.0e+13, b: 0.0, Ea: 0.0} +- equation: CH2(S) + H2O <=> CH2 + H2O # Reaction 67 + rate-constant: {A: 3.0e+13, b: 0.0, Ea: 0.0} +- equation: CH2(S) + CH3 <=> H + C2H4 # Reaction 68 + rate-constant: {A: 1.2e+13, b: 0.0, Ea: -570.0} +- equation: CH2(S) + CH4 <=> 2 CH3 # Reaction 69 + rate-constant: {A: 1.6e+13, b: 0.0, Ea: -570.0} +- equation: CH2(S) + CO <=> CH2 + CO # Reaction 70 + rate-constant: {A: 9.0e+12, b: 0.0, Ea: 0.0} +- equation: CH2(S) + CO2 <=> CH2 + CO2 # Reaction 71 + rate-constant: {A: 7.0e+12, b: 0.0, Ea: 0.0} +- equation: CH2(S) + CO2 <=> CO + CH2O # Reaction 72 + rate-constant: {A: 1.4e+13, b: 0.0, Ea: 0.0} +- equation: CH3 + O2 <=> O + CH3O # Reaction 73 + rate-constant: {A: 2.675e+13, b: 0.0, Ea: 2.88e+04} +- equation: CH3 + O2 <=> OH + CH2O # Reaction 74 + rate-constant: {A: 3.6e+10, b: 0.0, Ea: 8940.0} +- equation: 2 CH3 (+M) <=> C2H6 (+M) # Reaction 75 + type: falloff + low-P-rate-constant: {A: 1.77e+50, b: -9.67, Ea: 6220.0} + high-P-rate-constant: {A: 2.12e+16, b: -0.97, Ea: 620.0} + Troe: {A: 0.5325, T3: 151.0, T1: 1038.0, T2: 4970.0} + efficiencies: {H2: 2.0, H2O: 6.0, CH4: 2.0, CO: 1.5, CO2: 2.0, C2H6: 3.0, + AR: 0.7} +- equation: 2 CH3 <=> H + C2H5 # Reaction 76 + rate-constant: {A: 4.99e+12, b: 0.1, Ea: 1.06e+04} +- equation: CH3 + HCO <=> CH4 + CO # Reaction 77 + rate-constant: {A: 2.648e+13, b: 0.0, Ea: 0.0} +- equation: CH3 + CH2O <=> HCO + CH4 # Reaction 78 + rate-constant: {A: 3320.0, b: 2.81, Ea: 5860.0} +- equation: CH3 + C2H6 <=> C2H5 + CH4 # Reaction 79 + rate-constant: {A: 6.14e+06, b: 1.74, Ea: 1.045e+04} +- equation: HCO + H2O <=> H + CO + H2O # Reaction 80 + rate-constant: {A: 2.244e+18, b: -1.0, Ea: 1.7e+04} +- equation: HCO + M <=> H + CO + M # Reaction 81 + type: three-body + rate-constant: {A: 1.87e+17, b: -1.0, Ea: 1.7e+04} + efficiencies: {H2: 2.0, H2O: 0.0, CH4: 2.0, CO: 1.5, CO2: 2.0, C2H6: 3.0} +- equation: HCO + O2 <=> HO2 + CO # Reaction 82 + rate-constant: {A: 7.6e+12, b: 0.0, Ea: 400.0} +- equation: CH3O + O2 <=> HO2 + CH2O # Reaction 83 + rate-constant: {A: 4.28e-13, b: 7.6, Ea: -3530.0} +- equation: C2H5 + O2 <=> HO2 + C2H4 # Reaction 84 + rate-constant: {A: 8.4e+11, b: 0.0, Ea: 3875.0} diff --git a/Exec/RegTests/FlameSheet/get_pmfdat_and_ctable.py b/Exec/RegTests/FlameSheet/get_pmfdat_and_ctable.py index 6508bec4c..5b8cb3fa5 100644 --- a/Exec/RegTests/FlameSheet/get_pmfdat_and_ctable.py +++ b/Exec/RegTests/FlameSheet/get_pmfdat_and_ctable.py @@ -1,7 +1,7 @@ import cantera as ct import numpy as np import pandas as pd -from collections import OrderedDict +from collections import OrderedDict, defaultdict import ctable_tools as ctable # Thermo Conditions @@ -10,11 +10,71 @@ phi = 1.0 fuel = 'CH4' oxid = 'O2:1.0, N2:3.76' -mechanism = 'drm19.cti' +mechanism = 'drm19.yaml' trans = 'Mix' progvars = ["CO2","H2O","CO","H2"] ctable_specs = ["CO2","H2O","CO","H2","N2","O2","OH"] outfile_prefix = 'prem_drm19_phi1_p1_t298' +# net_info_file = None +net_info_file = "cmlm_net_info.txt" + +def load_info_file(filename): + """Load neural net info file.""" + + with open(filename, 'r') as file: + umap = defaultdict(list) + line_cont = False + for line in file: + try: + com_idx = line.index("#") + except: + com_idx = None + if com_idx is not None: + line = line[:com_idx].strip() + if (not line): continue + if not line_cont: + varname, value = line.split(' = ') + else: + value = line + umap[varname.strip()] += value.strip().rstrip("\\").split() + line_cont = line.strip().endswith("\\") + + dimnames = umap["dimnames"] + defn0 = "def_" + dimnames[0] + coeff = np.zeros((len(dimnames), len(umap[defn0])), dtype=np.float32) + varidx = np.zeros((len(dimnames), len(umap[defn0])), dtype=np.int32) + srcidx = np.zeros((len(dimnames), len(umap[defn0])), dtype=np.int32) + combmap = dict() + + for i in range(len(dimnames)): + defn = "def_" + dimnames[i] + for j, item in enumerate(umap[defn]): + + c, v = item.split("*") + c = c.strip() + v = v.strip() + + coeff[i,j] = float(c) + varidx[i,j] = umap["varnames"].index(v) + combmap[v] = j + + if v.startswith("Y-"): + v = v[2:] + try: + idx = umap["varnames"].index("SRC_" + v) + except ValueError: + idx = -1 + srcidx[i,j] = idx + + manibiases = np.fromiter(map(float, umap["manibiases"]), dtype=np.float32) + return umap, coeff, varidx, srcidx, manibiases, combmap + +# Read info file if one is provided +if net_info_file is not None: + umap, coeff, varidx, srcidx, manibiases, combmap = load_info_file(net_info_file) +else: + coeff, varidx, srcidx, manibiases, combmap = (None,)*5 + umap = defaultdict(list) # Flame Numerics width = 0.1 @@ -28,6 +88,7 @@ # Set up the flame gas = ct.Solution(mechanism) gas.set_equivalence_ratio(phi, fuel, oxid) +gas.TP = temp, press flame = ct.FreeFlame(gas, width=width) flame.set_refine_criteria(ratio=ratio, slope=slope, curve=curve, prune=prune) flame.set_max_grid_points(1,max_points) @@ -64,11 +125,21 @@ data.loc[0,"SRC_PROG"] = 0.0 data.loc[len(flame.grid)-1,"SRC_PROG"] = 0.0 +# Compute manifold variable(s) for net +for i, dim in enumerate(umap["dimnames"]): + data[dim] = 0.0 + for k in combmap: + if k.startswith('Y-'): + k_slc = k[2:] + else: + k_slc = k + data[dim] += coeff[i, combmap[k]] * specYdata[k_slc] + # Convert to CGS units ctable.convert_chemtable_units(data) # Make the chemtable -chemtable = data.drop(columns=["VEL","X","PROG"]) +chemtable = data.drop(columns=(["VEL","X","PROG"]+umap["dimnames"])) for spec in ctable_specs: chemtable["Y-"+spec] = specYdata[spec] chemtable = pd.DataFrame(chemtable.values, @@ -101,6 +172,9 @@ def write_dat_file(fname, df): keepvars = ["X","T","VEL","RHO"] df = data[keepvars].rename(columns=rename) df2 = pd.DataFrame(data[["PROG","RHO"]].values, columns=['X0','XRHO']) +df3 = pd.DataFrame(data[umap["dimnames"]+["RHO"]].values, + columns=([f"X{i}" for i in range(len(umap["dimnames"]))]+['XRHO'])) write_dat_file(outfile_prefix+'.dat', pd.concat([df,specXdata],axis=1)) write_dat_file(outfile_prefix+'_mani.dat', pd.concat([df,df2],axis=1)) +write_dat_file(outfile_prefix+'_mani_nn.dat', pd.concat([df,df3],axis=1)) diff --git a/Exec/RegTests/FlameSheet/input.manifold b/Exec/RegTests/FlameSheet/input.manifold index 80b9652b0..250a82b36 100644 --- a/Exec/RegTests/FlameSheet/input.manifold +++ b/Exec/RegTests/FlameSheet/input.manifold @@ -100,4 +100,4 @@ manifold.filename = prem_drm19_phi1_p1_t298.ctb eos.nominal_pressure_cgs = 1013250.0 peleLM.use_wbar = 0 peleLM.chi_correction_type = "DivuFirstIter" -#peleLM.chi_correction_type = "NoDivu" \ No newline at end of file +#peleLM.chi_correction_type = "NoDivu" diff --git a/Exec/RegTests/FlameSheet/input.nn b/Exec/RegTests/FlameSheet/input.nn new file mode 100644 index 000000000..57c1ca117 --- /dev/null +++ b/Exec/RegTests/FlameSheet/input.nn @@ -0,0 +1,114 @@ +#----------------------DOMAIN DEFINITION------------------------ +geometry.is_periodic = 1 0 # For each dir, 0: non-perio, 1: periodic +geometry.coord_sys = 0 # 0 => cart, 1 => RZ +geometry.prob_lo = 0.0 0.0 0.0 # x_lo y_lo (z_lo) +geometry.prob_hi = 0.003 0.012 0.016 # x_hi y_hi (z_hi) + +# >>>>>>>>>>>>> BC FLAGS <<<<<<<<<<<<<<<< +# Interior, Inflow, Outflow, Symmetry, +# SlipWallAdiab, NoSlipWallAdiab, SlipWallIsotherm, NoSlipWallIsotherm +peleLM.lo_bc = Interior Inflow +peleLM.hi_bc = Interior Outflow + + +#-------------------------AMR CONTROL---------------------------- +amr.n_cell = 32 128 32 # Level 0 number of cells in each direction +amr.v = 1 # AMR verbose +amr.max_level = 2 # maximum level number allowed +amr.ref_ratio = 2 2 2 2 # refinement ratio +amr.regrid_int = 5 # how often to regrid +amr.n_error_buf = 1 1 2 2 # number of buffer cells in error est +amr.grid_eff = 0.7 # what constitutes an efficient grid +amr.blocking_factor = 16 # block factor in grid generation (min box size) +amr.max_grid_size = 64 # max box size + + +#--------------------------- Problem ------------------------------- +prob.P_mean = 101325.0 +prob.standoff = -.03 +prob.pertmag = 0.00005 +pmf.datafile = "prem_drm19_phi1_p1_t298_mani_nn.dat" + +#-------------------------PeleLM CONTROL---------------------------- +peleLM.v = 3 +peleLM.incompressible = 0 +peleLM.rho = 1.17 +peleLM.mu = 0.0 +peleLM.sdc_iterMax = 4 +peleLM.floor_species = 0 + +peleLM.do_temporals = 1 +peleLM.temporal_int = 2 +peleLM.mass_balance = 1 + +#amr.restart = chk00005 +#amr.check_int = 20 +amr.plot_int = 10 +amr.max_step = 20000 +amr.dt_shrink = 0.01 +amr.stop_time = 0.01 +#amr.stop_time = 1.00 +amr.cfl = 0.5 +amr.derive_plot_vars = avg_pressure mag_vort mass_fractions # mixture_fraction progress_variable + +# ------------------- INPUTS DERIVED DIAGS ------------------ +peleLM.fuel_name = CH4 +#peleLM.mixtureFraction.format = Cantera +#peleLM.mixtureFraction.type = mass +#peleLM.mixtureFraction.oxidTank = O2:0.233 N2:0.767 +#peleLM.mixtureFraction.fuelTank = H2:0.5 CH4:0.5 +#peleLM.progressVariable.format = Cantera +#peleLM.progressVariable.weights = CO:1.0 CO2:1.0 +#peleLM.progressVariable.coldState = CO:0.0 CO2:0.0 +#peleLM.progressVariable.hotState = CO:0.000002 CO2:0.0666 + +# --------------- INPUTS TO CHEMISTRY REACTOR --------------- +peleLM.chem_integrator = "ReactorRK64" +peleLM.use_typ_vals_chem = 1 # Use species/temp typical values in CVODE +ode.rtol = 1.0e-6 # Relative tolerance of the chemical solve +ode.atol = 1.0e-5 # Absolute tolerance factor applied on typical values +ode.rk64_nsubsteps_guess = 1 +ode.rk64_nsubsteps_min = 1 +cvode.solve_type = denseAJ_direct # CVODE Linear solve type (for Newton direction) +cvode.max_order = 4 # CVODE max BDF order. + +mac_proj.verbose = 2 +mac_proj.rtol = 1e-8 +mac_proj.atol = 1e-8 +nodal_proj.verbose = 2 +nodal_proj.rtol = 1e-8 +nodal_proj.atol = 1e-8 +diffusion.rtol = 1e-8 +diffusion.atol = 1e-8 +tensor_diffusion.rtol = 1e-8 +tensor_diffusion.atol = 1e-8 + +#--------------------REFINEMENT CONTROL------------------------ +amr.refinement_indicators = temp +amr.temp.max_level = 3 +amr.temp.adjacent_difference_greater = 10 +amr.temp.field_name = temp + +#amr.refinement_indicators = magVort +#amr.magVort.max_level = 1 +#amr.magVort.value_greater = 500.0 +#amr.magVort.field_name = mag_vort + +#amr.refinement_indicators = yH +#amr.yH.max_level = 3 +#amr.yH.value_greater = 1.0e-6 +#amr.yH.field_name = Y(H) + +#amrex.fpe_trap_invalid = 1 +#amrex.fpe_trap_zero = 1 +#amrex.fpe_trap_overflow = 1 + +#----------------------- Manifold Stuff ---------------------------- +manifold.model = NeuralNet +manifold.v = 1 +manifold.filename = cmlm_net.pt +manifold.info_filename = cmlm_net_info.txt +eos.nominal_pressure_cgs = 1013250.0 +peleLM.use_wbar = 0 +peleLM.chi_correction_type = "DivuFirstIter" +#peleLM.chi_correction_type = "NoDivu" diff --git a/Source/PeleLM.H b/Source/PeleLM.H index 0988c0c91..00a3576da 100644 --- a/Source/PeleLM.H +++ b/Source/PeleLM.H @@ -331,10 +331,15 @@ class PeleLM : public amrex::AmrCore { amrex::Vector const &a_rho, amrex::Vector const &a_beta); - // Ensure sum of specues diffusion fluxes sum to 0 + // Ensure species diffusion fluxes sum to 0 template void adjustSpeciesFluxes(const amrex::Vector> &a_fluxes, amrex::Vector const &a_spec); + + // Ensure species diffusion fluxes sum to 0 + template <> + void adjustSpeciesFluxes(const amrex::Vector> &a_fluxes, + amrex::Vector const &a_spec); // Compute enthalpy flux due to species diffusion void computeSpeciesEnthalpyFlux(const amrex::Vector> &a_fluxes, diff --git a/Utils/Make.PeleLMeX b/Utils/Make.PeleLMeX index 5abccf08f..a71234b3f 100644 --- a/Utils/Make.PeleLMeX +++ b/Utils/Make.PeleLMeX @@ -64,6 +64,22 @@ ifeq ($(Eos_Model),$(filter $(Eos_Model),Manifold)) DEFINES += -DUSE_MANIFOLD_EOS endif +TORCH_LIBPATH = $(LIBTORCH_HOME)/lib + +ifeq ($(USE_CUDA),TRUE) + TORCH_LIBS = -ltorch -ltorch_cpu -lc10 -lc10_cuda -lcuda +else + TORCH_LIBS = -ltorch -ltorch_cpu -lc10 +endif + +ifeq ($(USE_LIBTORCH), TRUE) + DEFINES += -DUSE_LIBTORCH + INCLUDE_LOCATIONS += $(LIBTORCH_HOME)/include \ + $(LIBTORCH_HOME)/include/torch/csrc/api/include + LIBRARY_LOCATIONS += $(TORCH_LIBPATH) + LDFLAGS += -Wl,-rpath $(TORCH_LIBPATH) $(TORCH_LIBS) +endif + # Transport model switches ifeq ($(Transport_Model), Simple) DEFINES += -DUSE_SIMPLE_TRANSPORT From a983713108bc4a7817889641b592564c9f6f0e0b Mon Sep 17 00:00:00 2001 From: Kiran Eiden Date: Mon, 11 Jul 2022 22:43:29 -0600 Subject: [PATCH 19/27] Allow for neural network models without LibTorch --- Source/PeleLMSetup.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Source/PeleLMSetup.cpp b/Source/PeleLMSetup.cpp index 9511cc3c3..eb56ebe46 100644 --- a/Source/PeleLMSetup.cpp +++ b/Source/PeleLMSetup.cpp @@ -88,12 +88,8 @@ void PeleLM::Setup() { } else if(manifold_model == "NeuralNet") { -#ifdef USE_LIBTORCH manfunc_par.reset(new pele::physics::NNFuncParams()); amrex::Print() << " Initialization of Neural Net Func. (CPP)... \n"; -#else - amrex::Error("Must set USE_LIBTORCH = TRUE to run with neural net manifold model."); -#endif } manfunc_par->initialize(); eos_parms.allocate(manfunc_par->device_manfunc_data()); From 09ac2a54ee01442670c39f509a5247fc738874cd Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Tue, 12 Jul 2022 11:52:27 -0600 Subject: [PATCH 20/27] change in deriv lookup for log rho --- Source/PeleLM_K.H | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/PeleLM_K.H b/Source/PeleLM_K.H index d6851b6f2..1c35fa338 100644 --- a/Source/PeleLM_K.H +++ b/Source/PeleLM_K.H @@ -259,12 +259,13 @@ compute_divu(int i, int j, int k, auto eos = pele::physics::PhysicsType::eos(eosparm); eos.RY2RRinvY(massdens, rho, rhoinv, y); amrex::Real derivs[NUM_SPECIES-1] = {0.0}; - eos.Y2dRdY(y, derivs); + rho /= 1.0e3; // Unit conversion MKS -> CGS + eos.RY2dRdY(rho, y, derivs); divu(i,j,k) = 0.0; for (int n = 0; n < NUM_SPECIES-1; n++) { - derivs[n] *= 1.0e3; + derivs[n] *= 1.0e3; // Unit conversion CGS -> MKS divu(i,j,k) += derivs[n] * specDiff(i,j,k,n); if (do_react) divu(i,j,k) += derivs[n] * (rhoYdot(i,j,k,n)); // TODO: deal with extRhoY (below is not correct because derivs are dRho/dY, not dRho/dRhoY) From fe0c8f73675c273f15d81bc08092f69eeae94561 Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Tue, 12 Jul 2022 11:55:57 -0600 Subject: [PATCH 21/27] fix bcnormal so inlet density is consistent with EOS --- Exec/RegTests/FlameSheet/pelelm_prob.H | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Exec/RegTests/FlameSheet/pelelm_prob.H b/Exec/RegTests/FlameSheet/pelelm_prob.H index c5d0fff8f..f522aed61 100644 --- a/Exec/RegTests/FlameSheet/pelelm_prob.H +++ b/Exec/RegTests/FlameSheet/pelelm_prob.H @@ -191,7 +191,9 @@ bcnormal( for (int n = 0; n < NUM_SPECIES; n++){ massfrac[n] = pmf_vals[3 + n]; } - s_ext[DENSITY] = pmf_vals[3 + NUM_SPECIES -1] *1.0e3; + amrex::Real P_dummy, rho_cgs; + eos.PYT2R(P_dummy, massfrac, s_ext[TEMP], rho_cgs); + s_ext[DENSITY] = rho_cgs *1.0e3; s_ext[RHOH] = 0.0; for (int n = 0; n < NUM_SPECIES-1; n++) { s_ext[FIRSTSPEC+n] = massfrac[n] * s_ext[DENSITY]; From a3175300c3a020a6554ed0c7263b197ace9ea601 Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Tue, 12 Jul 2022 11:56:31 -0600 Subject: [PATCH 22/27] fix compile bug --- Source/PeleLM.H | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Source/PeleLM.H b/Source/PeleLM.H index 00a3576da..29a70e3cb 100644 --- a/Source/PeleLM.H +++ b/Source/PeleLM.H @@ -335,11 +335,6 @@ class PeleLM : public amrex::AmrCore { template void adjustSpeciesFluxes(const amrex::Vector> &a_fluxes, amrex::Vector const &a_spec); - - // Ensure species diffusion fluxes sum to 0 - template <> - void adjustSpeciesFluxes(const amrex::Vector> &a_fluxes, - amrex::Vector const &a_spec); // Compute enthalpy flux due to species diffusion void computeSpeciesEnthalpyFlux(const amrex::Vector> &a_fluxes, @@ -1282,6 +1277,10 @@ class PeleLM : public amrex::AmrCore { //----------------------------------------------------------------------------- }; +template <> +void PeleLM::adjustSpeciesFluxes(const amrex::Vector> &a_fluxes, + amrex::Vector const &a_spec); + void writeBuildInfo(); #endif From 106486189f6624ec63cbfe17f560a40cf3e9067b Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Wed, 6 Jul 2022 13:52:45 -0600 Subject: [PATCH 23/27] Add derived quantities from manifold model --- Source/PeleLMDeriveFunc.H | 7 +++++ Source/PeleLMDeriveFunc.cpp | 57 +++++++++++++++++++++++++++++++++++++ Source/PeleLMSetup.cpp | 51 ++++++++++++++++++++++----------- 3 files changed, 98 insertions(+), 17 deletions(-) diff --git a/Source/PeleLMDeriveFunc.H b/Source/PeleLMDeriveFunc.H index 55d21270d..38c199534 100644 --- a/Source/PeleLMDeriveFunc.H +++ b/Source/PeleLMDeriveFunc.H @@ -11,6 +11,13 @@ // Forward declaration of PeleLM class PeleLM; +#ifdef USE_MANIFOLD_EOS +void pelelm_dermaniout (PeleLM* a_pelelm, const amrex::Box& bx, amrex::FArrayBox& derfab, int dcomp, int ncomp, + const amrex::FArrayBox& statefab, const amrex::FArrayBox& pressfab, + const amrex::Geometry& geomdata, + amrex::Real time, const amrex::Vector &bcrec, int level); +#endif + void pelelm_dermassfrac (PeleLM* a_pelelm, const amrex::Box& bx, amrex::FArrayBox& derfab, int dcomp, int ncomp, const amrex::FArrayBox& statefab, const amrex::FArrayBox& pressfab, const amrex::Geometry& geomdata, diff --git a/Source/PeleLMDeriveFunc.cpp b/Source/PeleLMDeriveFunc.cpp index f5c8e0657..448f67411 100644 --- a/Source/PeleLMDeriveFunc.cpp +++ b/Source/PeleLMDeriveFunc.cpp @@ -29,6 +29,63 @@ void pelelm_dertemp (PeleLM* a_pelelm, const Box& bx, FArrayBox& derfab, int dco }); } +// +// Extract output quantities from manifold +// +#ifdef USE_MANIFOLD_EOS +void pelelm_dermaniout (PeleLM* a_pelelm, const Box& bx, FArrayBox& derfab, int dcomp, int ncomp, + const FArrayBox& statefab, const FArrayBox& /*pressfab*/, + const Geometry& /*geomdata*/, + Real /*time*/, const Vector& /*bcrec*/, int /*level*/) + +{ + auto manf_data = &a_pelelm->manfunc_par->host_manfunc_data(); + int nmanivar = manf_data->Nvar; + AMREX_ASSERT(derfab.box().contains(bx)); + AMREX_ASSERT(statefab.box().contains(bx)); + AMREX_ASSERT(derfab.nComp() >= dcomp + ncomp); + AMREX_ASSERT(statefab.nComp() >= NUM_SPECIES+1); + AMREX_ASSERT(ncomp == nmanivar); + AMREX_ASSERT(!a_pelelm->m_incompressible); + auto const in_dat = statefab.array(); + auto der = derfab.array(dcomp); + amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept + { + amrex::Real rhoinv = 1.0 / in_dat(i,j,k,DENSITY); + amrex::Real maniparm[NUM_SPECIES-1]; + for (int n = 0; n < NUM_SPECIES - 1; n++) { + maniparm[n] = in_dat(i,j,k,FIRSTSPEC+n) * rhoinv; + } + + // TODO: revisit this implementation on GPU + amrex::Real maniout[nmanivar]; + int manioutidx[nmanivar]; + for (int n = 0; n < nmanivar; n++) { + manioutidx[n] = n; + } + std::unique_ptr manfunc; + if(manf_data->manmodel == pele::physics::ManifoldModel::TABLE) + { + pele::physics::TabFuncParams::TabFuncData* tf_data = + static_cast(manf_data); + manfunc.reset(new pele::physics::TabFunc(tf_data)); + } + else + { + pele::physics::NNFuncParams::NNFuncData* nnf_data = + static_cast(manf_data); + manfunc.reset(new pele::physics::NNFunc(nnf_data)); + } + manfunc->get_values(nmanivar, manioutidx, maniparm, maniout); + + for (int n = 0; n < nmanivar; n++) { + der(i,j,k,n) = maniout[n]; + } + } + ); +} +#endif + // // Extract species mass fractions Y_n // diff --git a/Source/PeleLMSetup.cpp b/Source/PeleLMSetup.cpp index eb56ebe46..7d02778b8 100644 --- a/Source/PeleLMSetup.cpp +++ b/Source/PeleLMSetup.cpp @@ -49,6 +49,24 @@ void PeleLM::Setup() { // Setup the state variables variablesSetup(); + // Load manifold if needed (must happen before derived setup) +#ifdef USE_MANIFOLD_EOS + amrex::ParmParse pp("manifold"); + std::string manifold_model; + pp.get("model", manifold_model); + if(manifold_model == "Table") + { + manfunc_par.reset(new pele::physics::TabFuncParams()); + amrex::Print() << " Initialization of Table (CPP)... \n"; + } + else if(manifold_model == "NeuralNet") + { + manfunc_par.reset(new pele::physics::NNFuncParams()); + amrex::Print() << " Initialization of Neural Net Func. (CPP)... \n"; + } + manfunc_par->initialize(); +#endif + // Derived variables derivedSetup(); @@ -78,20 +96,6 @@ void PeleLM::Setup() { if (!m_incompressible) { amrex::Print() << " Initialization of EOS (CPP)... \n"; #ifdef USE_MANIFOLD_EOS - amrex::ParmParse pp("manifold"); - std::string manifold_model; - pp.get("model", manifold_model); - if(manifold_model == "Table") - { - manfunc_par.reset(new pele::physics::TabFuncParams()); - amrex::Print() << " Initialization of Table (CPP)... \n"; - } - else if(manifold_model == "NeuralNet") - { - manfunc_par.reset(new pele::physics::NNFuncParams()); - amrex::Print() << " Initialization of Neural Net Func. (CPP)... \n"; - } - manfunc_par->initialize(); eos_parms.allocate(manfunc_par->device_manfunc_data()); amrex::Print() << " Initialization of Transport ... \n"; trans_parms.allocate(manfunc_par->device_manfunc_data()); @@ -172,9 +176,9 @@ void PeleLM::readParameters() { pp.query("run_mode",m_run_mode); pp.query("v", m_verbose); pp.query("chi_correction_type", m_chi_correction_type); - AMREX_ASSERT(chi_correction_type == "DivuEveryIter" || - chi_correction_type == "DivuFirstIter" || - chi_correction_type == "NoDivu"); + AMREX_ASSERT(m_chi_correction_type == "DivuEveryIter" || + m_chi_correction_type == "DivuFirstIter" || + m_chi_correction_type == "NoDivu"); // ----------------------------------------- // Boundary conditions @@ -751,6 +755,19 @@ void PeleLM::derivedSetup() derive_lst.add("mole_fractions",IndexType::TheCellType(),NUM_SPECIES, var_names_massfrac,pelelm_dermolefrac,the_same_box); +#ifdef USE_MANIFOLD_EOS + // Output quantities from manifold + auto manf_data = &manfunc_par->host_manfunc_data(); + int nmanivar = manf_data->Nvar; + Vector var_names_maniout(nmanivar); + for (int n = 0 ; n < nmanivar; n++) { + std::string nametmp = std::string(&(manf_data->varnames)[n*manf_data->len_str], manf_data->len_str); + var_names_maniout[n] = "MANI_" + amrex::trim(nametmp); + } + derive_lst.add("maniout",IndexType::TheCellType(),nmanivar, + var_names_maniout,pelelm_dermaniout,the_same_box); +#endif + // Species diffusion coefficients for (int n = 0 ; n < NUM_SPECIES; n++) { var_names_massfrac[n] = "D_"+spec_names[n]; From 8ed0bce9e8341c55390f9cb61ebb79ffe3a3ed61 Mon Sep 17 00:00:00 2001 From: Kiran Eiden Date: Mon, 25 Jul 2022 16:23:50 -0600 Subject: [PATCH 24/27] Make pelelm_dermaniout GPU-compatible (hopefully) --- Source/PeleLMDeriveFunc.cpp | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/Source/PeleLMDeriveFunc.cpp b/Source/PeleLMDeriveFunc.cpp index 448f67411..b9066005c 100644 --- a/Source/PeleLMDeriveFunc.cpp +++ b/Source/PeleLMDeriveFunc.cpp @@ -41,12 +41,18 @@ void pelelm_dermaniout (PeleLM* a_pelelm, const Box& bx, FArrayBox& derfab, int { auto manf_data = &a_pelelm->manfunc_par->host_manfunc_data(); int nmanivar = manf_data->Nvar; + int manioutidx[nmanivar]; + for (int n = 0; n < nmanivar; n++) { + manioutidx[n] = n; + } + AMREX_ASSERT(derfab.box().contains(bx)); AMREX_ASSERT(statefab.box().contains(bx)); AMREX_ASSERT(derfab.nComp() >= dcomp + ncomp); AMREX_ASSERT(statefab.nComp() >= NUM_SPECIES+1); AMREX_ASSERT(ncomp == nmanivar); AMREX_ASSERT(!a_pelelm->m_incompressible); + auto const in_dat = statefab.array(); auto der = derfab.array(dcomp); amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept @@ -57,30 +63,23 @@ void pelelm_dermaniout (PeleLM* a_pelelm, const Box& bx, FArrayBox& derfab, int maniparm[n] = in_dat(i,j,k,FIRSTSPEC+n) * rhoinv; } - // TODO: revisit this implementation on GPU - amrex::Real maniout[nmanivar]; - int manioutidx[nmanivar]; - for (int n = 0; n < nmanivar; n++) { - manioutidx[n] = n; - } - std::unique_ptr manfunc; + // TODO: make this more elegant + pele::physics::ManifoldFunc* manfunc; if(manf_data->manmodel == pele::physics::ManifoldModel::TABLE) { pele::physics::TabFuncParams::TabFuncData* tf_data = static_cast(manf_data); - manfunc.reset(new pele::physics::TabFunc(tf_data)); + manfunc = new pele::physics::TabFunc(tf_data); } else { pele::physics::NNFuncParams::NNFuncData* nnf_data = static_cast(manf_data); - manfunc.reset(new pele::physics::NNFunc(nnf_data)); + manfunc = new pele::physics::NNFunc(nnf_data); } - manfunc->get_values(nmanivar, manioutidx, maniparm, maniout); - - for (int n = 0; n < nmanivar; n++) { - der(i,j,k,n) = maniout[n]; - } + manfunc->get_values(nmanivar, manioutidx, maniparm, der.ptr(i,j,k)); + + delete manfunc; } ); } From c61e828e79f990f9571d6e056e97446c31a2d2da Mon Sep 17 00:00:00 2001 From: Kiran Eiden Date: Mon, 25 Jul 2022 18:16:09 -0600 Subject: [PATCH 25/27] Use 'get_all_values' function so we don't need index array --- Source/PeleLMDeriveFunc.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Source/PeleLMDeriveFunc.cpp b/Source/PeleLMDeriveFunc.cpp index b9066005c..eab92dd4f 100644 --- a/Source/PeleLMDeriveFunc.cpp +++ b/Source/PeleLMDeriveFunc.cpp @@ -41,10 +41,6 @@ void pelelm_dermaniout (PeleLM* a_pelelm, const Box& bx, FArrayBox& derfab, int { auto manf_data = &a_pelelm->manfunc_par->host_manfunc_data(); int nmanivar = manf_data->Nvar; - int manioutidx[nmanivar]; - for (int n = 0; n < nmanivar; n++) { - manioutidx[n] = n; - } AMREX_ASSERT(derfab.box().contains(bx)); AMREX_ASSERT(statefab.box().contains(bx)); @@ -77,7 +73,7 @@ void pelelm_dermaniout (PeleLM* a_pelelm, const Box& bx, FArrayBox& derfab, int static_cast(manf_data); manfunc = new pele::physics::NNFunc(nnf_data); } - manfunc->get_values(nmanivar, manioutidx, maniparm, der.ptr(i,j,k)); + manfunc->get_all_values(maniparm, der.ptr(i,j,k)); delete manfunc; } From 951560cb2b177185519bcee45555b143680643ee Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Tue, 2 Aug 2022 13:15:13 -0600 Subject: [PATCH 26/27] update enclosed flame case for manifold models --- Exec/RegTests/EnclosedFlame/GNUmakefile | 23 +- Exec/RegTests/EnclosedFlame/gen_hit_ic.py | 463 ++++++++++++++++++ Exec/RegTests/EnclosedFlame/input.manifold | 126 +++++ Exec/RegTests/EnclosedFlame/input.nn | 130 +++++ Exec/RegTests/EnclosedFlame/input.posf | 115 +++++ Exec/RegTests/EnclosedFlame/pelelm_prob.H | 223 ++++++++- Exec/RegTests/EnclosedFlame/pelelm_prob.cpp | 213 +++++++- .../RegTests/EnclosedFlame/pelelm_prob_parm.H | 27 + 8 files changed, 1294 insertions(+), 26 deletions(-) create mode 100755 Exec/RegTests/EnclosedFlame/gen_hit_ic.py create mode 100644 Exec/RegTests/EnclosedFlame/input.manifold create mode 100644 Exec/RegTests/EnclosedFlame/input.nn create mode 100644 Exec/RegTests/EnclosedFlame/input.posf diff --git a/Exec/RegTests/EnclosedFlame/GNUmakefile b/Exec/RegTests/EnclosedFlame/GNUmakefile index df5b0dc3d..613ea121b 100644 --- a/Exec/RegTests/EnclosedFlame/GNUmakefile +++ b/Exec/RegTests/EnclosedFlame/GNUmakefile @@ -1,16 +1,15 @@ -TOP = ../../../.. +TOP = ../../../Submodules AMREX_HOME ?= ${TOP}/amrex -PELELMEX_HOME ?= ${TOP}/PeleLMeX +PELELMEX_HOME ?= ${TOP}/.. PELE_PHYSICS_HOME ?= ${TOP}/PelePhysics AMREX_HYDRO_HOME ?= ${TOP}/AMReX-Hydro - # AMReX DIM = 2 DEBUG = FALSE PRECISION = DOUBLE VERBOSE = FALSE -TINY_PROFILE = FALSE +TINY_PROFILE = TRUE # Compilation COMP = gnu @@ -21,9 +20,19 @@ USE_HIP = FALSE # PeleLMeX +FUEGO_GAS = FALSE # PelePhysics -Chemistry_Model = drm19 -Eos_Model = Fuego -Transport_Model = Simple +ifeq ($(FUEGO_GAS), TRUE) + Chemistry_Model = posf10325 + Eos_Model = Fuego + Transport_Model = Simple +else + USE_LIBTORCH = FALSE + LIBTORCH_HOME = /home/bperry/libtorch + DEFINES += -D_GLIBCXX_USE_CXX11_ABI=0 + Eos_Model = Manifold + Chemistry_Model = Manifold2d + Transport_Model = Manifold +endif include $(PELELMEX_HOME)/Utils/Make.PeleLMeX diff --git a/Exec/RegTests/EnclosedFlame/gen_hit_ic.py b/Exec/RegTests/EnclosedFlame/gen_hit_ic.py new file mode 100755 index 000000000..cd3bf6543 --- /dev/null +++ b/Exec/RegTests/EnclosedFlame/gen_hit_ic.py @@ -0,0 +1,463 @@ +#!/usr/bin/env python +# +# Generate a table of the velocity fluctuations for the homogeneous +# isotropic turbulence case at a specific k0 (default to 4) +# +# Order of operations: +# 1. velocity fluctuations generated on a 512^3 grid in wavenumber space +# 2. Coefficients associated to wavenumbers that cannot be represented on +# the desired grid are set to 0 (sharp wavenumber cutoff) +# 3. inverse Fourier transform of the velocity fluctuations (512^3 grid) +# 4. velocity fluctuations resampled on the desired grid (N^3) +# +# The velocity fluctuations are normalized by urms0 so to get the +# actual velocity fluctuations, one must multiply these velocities by +# the appropriate urms0. +# +# + +# ======================================================================== +# +# Imports +# +# ======================================================================== +import argparse +import sys +import time +from datetime import timedelta +import numpy as np +import scipy.interpolate as spi +import matplotlib as mpl + +mpl.use("Agg") +import matplotlib.pyplot as plt + + +# ======================================================================== +# +# Parse arguments +# +# ======================================================================== +parser = argparse.ArgumentParser( + description="Generate the velocity fluctuations for the HIT IC" +) +parser.add_argument( + "-k0", help="Wave number containing highest energy", type=float, default=4.0 +) +parser.add_argument("-N", help="Resolution", type=int, default=16) +parser.add_argument( + "-s", "--seed", help="Random number generator seed", type=int, default=42 +) +parser.add_argument( + "-p", "--plot", help="Save a plot of the x-velocity", action="store_true" +) +args = parser.parse_args() + +# =============================================================================== +# +# Some defaults variables +# +# =============================================================================== +plt.rc("text", usetex=True) +plt.rc("font", family="serif", serif="Times") +cmap_med = [ + "#F15A60", + "#7AC36A", + "#5A9BD4", + "#FAA75B", + "#9E67AB", + "#CE7058", + "#D77FB4", + "#737373", +] +cmap = [ + "#EE2E2F", + "#008C48", + "#185AA9", + "#F47D23", + "#662C91", + "#A21D21", + "#B43894", + "#010202", +] +dashseq = [ + (None, None), + [10, 5], + [10, 4, 3, 4], + [3, 3], + [10, 4, 3, 4, 3, 4], + [3, 3], + [3, 3], +] +markertype = ["s", "d", "o", "p", "h"] + +# ======================================================================== +# +# Function definitions +# +# ======================================================================== +def div0(a, b): + """ Ignore division by 0, just replace it by 0, + + From: http://stackoverflow.com/questions/26248654/numpy-return-0-with-divide-by-zero + e.g. div0( [-1, 0, 1], 0 ) -> [0, 0, 0] + """ + with np.errstate(divide="ignore", invalid="ignore"): + c = np.true_divide(a, b) + c[~np.isfinite(c)] = 0 # -inf inf NaN + return c + + +# ======================================================================== +def abs2(x): + """This is equivalent to np.abs(x)**2 or x*np.conj(x) + + To make it faster, add this right before the function definition + import numba + @numba.vectorize([numba.float64(numba.complex128),numba.float32(numba.complex64)]) + """ + return x.real ** 2 + x.imag ** 2 + + +# ======================================================================== +# +# Main +# +# ======================================================================== + +# Timer +start = time.time() + +# ======================================================================== +# 1. velocity fluctuations generated on a 512^3 grid in wavenumber space + +# Dimension of the large cube +N = 256 +halfN = int(round(0.5 * N)) +xs = 0 +xe = 2.0 * np.pi * 0.01 +L = xe - xs +dx = L / N + +# Only work if N and args.N are even +if not ((args.N % 2 == 0) and N % 2 == 0): + print("N or args.N is not even. Exiting") + sys.exit(1) + +# Get cell centered values and meshed grid +x = np.linspace(xs, xe, N + 1) +xc = (x[1:] + x[:-1]) / 2 # get cell center coordinates +X, Y, Z = np.meshgrid(xc, xc, xc, indexing="ij") + +# Get the wave numbers and associated quantities +k = np.concatenate((np.arange(halfN), np.arange(-halfN, 0, 1)), axis=0) +khalf = np.arange(halfN + 1) +k1, k2, k3 = np.meshgrid(k, k, khalf, indexing="ij") +kmag = np.sqrt(k1 ** 2 + k2 ** 2 + k3 ** 2) +k12 = np.sqrt(k1 ** 2 + k2 ** 2) +k1k12 = div0(k1, k12) +k2k12 = div0(k2, k12) +k3kmag = div0(k3, kmag) +k12kmag = div0(k12, kmag) + +# Generate data + +# # Toy Fourier data corresponding to uo = cos(X) * cos(2*Y) * cos(3*Z) +# uo = np.cos(X) * np.cos(2*Y) * np.cos(3*Z) +# uf = np.fft.rfftn(uo) +# vf = np.copy(uf) +# wf = np.copy(uf) + +# Energy spectrum +Ek = ( + 16.0 + * np.sqrt(2.0 / np.pi) + * (kmag ** 4) + / (args.k0 ** 5) + * np.exp(-2.0 * (kmag ** 2) / (args.k0 ** 2)) +) + +# Draw random numbers +np.random.seed(args.seed) +phi1 = np.random.uniform(0, 2 * np.pi, np.shape(kmag)) +phi2 = np.random.uniform(0, 2 * np.pi, np.shape(kmag)) +phi3 = np.random.uniform(0, 2 * np.pi, np.shape(kmag)) + +# the random quantities +prefix = np.sqrt(2.0 * div0(Ek, 4.0 * np.pi * (kmag ** 2))) +a = prefix * np.exp(1j * phi1) * np.cos(phi3) +b = prefix * np.exp(1j * phi2) * np.sin(phi3) + +# the random velocities +uf = k2k12 * a + k1k12 * k3kmag * b +vf = k2k12 * k3kmag * b - k1k12 * a +wf = -k12kmag * b + +# Impose the 3D spherical symmetry (to ensure we have a real signal) +# equiv: uf[-l,-m,0] = np.conj(uf[ l, m,0]) for l=0..N/2 and m=0..N/2 +uf[N:halfN:-1, N:halfN:-1, 0] = np.conj(uf[1:halfN, 1:halfN, 0]) +# symmetry on first column +uf[N:halfN:-1, 0, 0] = np.conj(uf[1:halfN, 0, 0]) +# symmetry on first row +uf[0, N:halfN:-1, 0] = np.conj(uf[0, 1:halfN, 0]) +# symmetry about the (halfN,halfN) element +uf[halfN - 1 : 0 : -1, N : halfN - 1 : -1, 0] = np.conj( + uf[halfN + 1 : N, 1 : halfN + 1, 0] +) + +vf[N:halfN:-1, N:halfN:-1, 0] = np.conj(vf[1:halfN, 1:halfN, 0]) +vf[halfN - 1 : 0 : -1, N : halfN - 1 : -1, 0] = np.conj( + vf[halfN + 1 : N, 1 : halfN + 1, 0] +) +vf[N:halfN:-1, 0, 0] = np.conj(vf[1:halfN, 0, 0]) +vf[0, N:halfN:-1, 0] = np.conj(vf[0, 1:halfN, 0]) + +wf[N:halfN:-1, N:halfN:-1, 0] = np.conj(wf[1:halfN, 1:halfN, 0]) +wf[halfN - 1 : 0 : -1, N : halfN - 1 : -1, 0] = np.conj( + wf[halfN + 1 : N, 1 : halfN + 1, 0] +) +wf[N:halfN:-1, 0, 0] = np.conj(wf[1:halfN, 0, 0]) +wf[0, N:halfN:-1, 0] = np.conj(wf[0, 1:halfN, 0]) + +# Normalize. Because we are generating the data in wavenumber space, +# we have to multiply by N**3 because in the definition of the numpy +# ifftn there is a 1/N**n. +uf = uf * N ** 3 +vf = vf * N ** 3 +wf = wf * N ** 3 + +# # Quick check on energy content (make sure you add both the current +# # contribution and the one we are neglecting because we are assuming +# # real input data) +# print('Energy = int E(k) dk = 0.5 * int (uf**2 + vf**2 wf**2) dk1 dk2 dk3 = {0:.10f} ~= 3/2'.format( +# (np.sum(abs2(uf ) + abs2(vf ) + abs2(wf )) + +# np.sum(abs2(uf[:,:,1:-1]) + abs2(vf[:,:,1:-1]) + abs2(wf[:,:,1:-1]))) +# * 0.5 / N**6)) + +# if plotting, save the original field (before filtering) +if args.plot: + uo = np.fft.irfftn(uf) + Eko = ( + 16.0 + * np.sqrt(2.0 / np.pi) + * (khalf ** 4) + / (args.k0 ** 5) + * np.exp(-2.0 * (khalf ** 2) / (args.k0 ** 2)) + ) + + # Get the spectrum from 3D velocity field + kbins = np.arange(1, halfN + 1) + Nbins = len(kbins) + whichbin = np.digitize(kmag.flat, kbins) + ncount = np.bincount(whichbin) + + KI = (abs2(uf) + abs2(vf) + abs2(wf)) * 0.5 / N ** 6 + KI[:, :, 1:-1] += ( + (abs2(uf[:, :, 1:-1]) + abs2(vf[:, :, 1:-1]) + abs2(wf[:, :, 1:-1])) + * 0.5 + / N ** 6 + ) + + Eku = np.zeros(len(ncount) - 1) + for n in range(1, len(ncount)): + Eku[n - 1] = np.sum(KI.flat[whichbin == n]) + + ku = 0.5 * (kbins[0 : Nbins - 1] + kbins[1:Nbins]) + 1 + Eku = Eku[1:Nbins] + + +# ======================================================================== +# 2. Coefficients associated to wavenumbers that cannot be represented +# on the desired grid are set to 0 (sharp wavenumber cutoff) +kmagc = 0.5 * args.N +uf[kmag > kmagc] = 0.0 +vf[kmag > kmagc] = 0.0 +wf[kmag > kmagc] = 0.0 + + +# ======================================================================== +# 3. inverse Fourier transform of the velocity fluctuations (512^3 grid) +u = np.fft.irfftn(uf, s=(N, N, N)) +v = np.fft.irfftn(vf, s=(N, N, N)) +w = np.fft.irfftn(wf, s=(N, N, N)) + +# Another energy content check +print( + "Energy = 1/V * int E(x,y,z) dV = 0.5/V * int (u**2 + v**2 + w**2) dx dy dz = {0:.10f} ~= 3/2".format( + np.sum(u ** 2 + v ** 2 + w ** 2) * 0.5 * (dx / L) ** 3 + ) +) + +# # Enstrophy check +# _, dudy, dudz = np.gradient(u, dx) +# dvdx, _, dvdz = np.gradient(v, dx) +# dwdx, dwdy, _ = np.gradient(w, dx) +# wx = dwdy-dvdz +# wy = dudz-dwdx +# wz = dvdx-dudy +# lambda0 = 2.0/args.k0 +# print('Enstrophy = 0.5/V * int (wx**2 + wy**2 + wz**2) dx dy dz= +# {0:.10f} ~= '.format(np.sum(wx**2+wy**2+wz**2) * 0.5 * (dx/L)**3 * +# lambda0**2)) + +# ======================================================================== +# 4. velocity fluctuations re-sampled on the desired grid (N^3) +xr = np.linspace(xs, xe, args.N + 1) +xrc = (xr[1:] + xr[:-1]) / 2 +Xr, Yr, Zr = np.meshgrid(xrc, xrc, xrc, indexing="ij") + +Xr = Xr.reshape(-1, order="F") +Yr = Yr.reshape(-1, order="F") +Zr = Zr.reshape(-1, order="F") + +ur = spi.interpn((xc, xc, xc), u, (Xr, Yr, Zr), method="linear") +vr = spi.interpn((xc, xc, xc), v, (Xr, Yr, Zr), method="linear") +wr = spi.interpn((xc, xc, xc), w, (Xr, Yr, Zr), method="linear") + + +# ======================================================================== +# Save the data in Fortran ordering +fname = "hit_ic_{0:d}_{1:d}.dat".format(int(args.k0), args.N) +data = np.vstack((Xr, Yr, Zr, ur, vr, wr)).T +np.savetxt(fname, data, fmt="%.18e", delimiter=",", header="x, y, z, u, v, w") + + +# ======================================================================== +# plot (only u fluctuations) +if args.plot: + import matplotlib as mpl + + mpl.use("Agg") + import matplotlib.pyplot as plt + + datmin = u.min() + datmax = u.max() + # print("min/max u =",datmin,datmax) + + # Original data + # transpose and origin change bc I used meshgrid with ij and not xy + fig, ax = plt.subplots(nrows=3, ncols=3, figsize=(14, 14)) + ax[0, 0].imshow( + uo[:, :, 0].T, + origin="lower", + extent=[xs, xe, xs, xe], + cmap="RdBu_r", + vmin=datmin, + vmax=datmax, + ) + ax[0, 0].set_title("Original data (x,y)") + ax[0, 1].imshow( + uo[:, 0, :].T, + origin="lower", + extent=[xs, xe, xs, xe], + cmap="RdBu_r", + vmin=datmin, + vmax=datmax, + ) + ax[0, 1].set_title("Original data (x,z)") + ax[0, 2].imshow( + uo[0, :, :].T, + origin="lower", + extent=[xs, xe, xs, xe], + cmap="RdBu_r", + vmin=datmin, + vmax=datmax, + ) + ax[0, 2].set_title("Original data (y,z)") + + # Filtered original data + ax[1, 0].imshow( + u[:, :, 0].T, + origin="lower", + extent=[xs, xe, xs, xe], + cmap="RdBu_r", + vmin=datmin, + vmax=datmax, + ) + ax[1, 0].set_title("Filtered original data (x,y)") + ax[1, 1].imshow( + u[:, 0, :].T, + origin="lower", + extent=[xs, xe, xs, xe], + cmap="RdBu_r", + vmin=datmin, + vmax=datmax, + ) + ax[1, 1].set_title("Filtered original data (x,z)") + ax[1, 2].imshow( + u[0, :, :].T, + origin="lower", + extent=[xs, xe, xs, xe], + cmap="RdBu_r", + vmin=datmin, + vmax=datmax, + ) + ax[1, 2].set_title("Filtered original data (y,z)") + + # Downsampled filtered data + ur = ur.reshape(args.N, args.N, args.N, order="F") + ax[2, 0].imshow( + ur[:, :, 0].T, + origin="lower", + extent=[xs, xe, xs, xe], + cmap="RdBu_r", + vmin=datmin, + vmax=datmax, + ) + ax[2, 0].set_title("Downsampled data (x,y)") + ax[2, 1].imshow( + ur[:, 0, :].T, + origin="lower", + extent=[xs, xe, xs, xe], + cmap="RdBu_r", + vmin=datmin, + vmax=datmax, + ) + ax[2, 1].set_title("Downsampled data (x,z)") + ax[2, 2].imshow( + ur[0, :, :].T, + origin="lower", + extent=[xs, xe, xs, xe], + cmap="RdBu_r", + vmin=datmin, + vmax=datmax, + ) + ax[2, 2].set_title("Downsampled data (y,z)") + + plt.savefig("hit_ic_u_{0:d}_{1:d}.png".format(int(args.k0), args.N), format="png") + + # Fourier coefficients of original data + fig, ax = plt.subplots(nrows=2, ncols=3, figsize=(14, 8)) + ax[0, 0].imshow(np.real(uf[:, :, 0].T), origin="lower", cmap="RdBu_r") + ax[0, 0].set_title("Real Fourier coefficients (x,y)") + ax[0, 1].imshow(np.real(uf[:, 0, :].T), origin="lower", cmap="RdBu_r") + ax[0, 1].set_title("Real Fourier coefficients (x,z)") + ax[0, 2].imshow(np.real(uf[0, :, :].T), origin="lower", cmap="RdBu_r") + ax[0, 2].set_title("Real Fourier coefficients (y,z)") + ax[1, 0].imshow(np.imag(uf[:, :, 0].T), origin="lower", cmap="RdBu_r") + ax[1, 0].set_title("Imag Fourier coefficients (x,y)") + ax[1, 1].imshow(np.imag(uf[:, 0, :].T), origin="lower", cmap="RdBu_r") + ax[1, 1].set_title("Imag Fourier coefficients (x,z)") + ax[1, 2].imshow(np.imag(uf[0, :, :].T), origin="lower", cmap="RdBu_r") + ax[1, 2].set_title("Imag Fourier coefficients (y,z)") + plt.savefig("hit_ic_uf_{0:d}_{1:d}.png".format(int(args.k0), args.N), format="png") + + # Spectrum + plt.figure(20) + ax = plt.gca() + p = plt.loglog(khalf, Eko, color=cmap[-1], lw=2) + p[0].set_dashes(dashseq[0]) + p = plt.loglog(ku, Eku, color=cmap[0], lw=2) + p[0].set_dashes(dashseq[1]) + plt.ylim([1e-16, 10]) + plt.xlabel(r"$k$", fontsize=22, fontweight="bold") + plt.ylabel(r"$E(k)$", fontsize=22, fontweight="bold") + plt.setp(ax.get_xmajorticklabels(), fontsize=18, fontweight="bold") + plt.setp(ax.get_ymajorticklabels(), fontsize=18, fontweight="bold") + plt.savefig( + "hit_ic_spectrum_{0:d}_{1:d}.png".format(int(args.k0), args.N), format="png" + ) + +# output timer +end = time.time() - start +print("Elapsed time " + str(timedelta(seconds=end)) + " (or {0:f} seconds)".format(end)) diff --git a/Exec/RegTests/EnclosedFlame/input.manifold b/Exec/RegTests/EnclosedFlame/input.manifold new file mode 100644 index 000000000..c7691527c --- /dev/null +++ b/Exec/RegTests/EnclosedFlame/input.manifold @@ -0,0 +1,126 @@ +#----------------------DOMAIN DEFINITION------------------------ +#geometry.is_periodic = 1 1 # For each dir, 0: non-perio, 1: periodi +geometry.is_periodic = 0 0 # For each dir, 0: non-perio, 1: periodic +geometry.coord_sys = 0 # 0 => cart, 1 => RZ +geometry.prob_lo = 0.0 0.0 0.0 # x_lo y_lo (z_lo) +geometry.prob_hi = 0.02 0.02 0.016 # x_hi y_hi (z_hi) + +# >>>>>>>>>>>>> BC FLAGS <<<<<<<<<<<<<<<< +# Interior, Inflow, Outflow, Symmetry, +# SlipWallAdiab, NoSlipWallAdiab, SlipWallIsotherm, NoSlipWallIsotherm +#peleLM.lo_bc = Interior Interior +#peleLM.hi_bc = Interior Interior + +peleLM.lo_bc = Outflow Outflow +peleLM.hi_bc = Outflow Outflow + + + +#-------------------------AMR CONTROL---------------------------- +amr.n_cell = 128 128 32 # Level 0 number of cells in each direction +amr.v = 1 # AMR verbose +amr.max_level = 2 # maximum level number allowed +amr.ref_ratio = 2 2 2 2 2 # refinement ratio +amr.regrid_int = 5 # how often to regrid +amr.n_error_buf = 6 6 6 6 # number of buffer cells in error est +amr.grid_eff = 0.7 # what constitutes an efficient grid +amr.blocking_factor = 16 # block factor in grid generation (min box size) +amr.max_grid_size = 128 # max box size + + +#--------------------------- Problem ------------------------------- +prob.P_mean = 101325.0 +prob.standoff = -0.0175 +prob.flame_radius = 0.001 +prob.pertmag = 0.0 #0.0002 +pmf.datafile = "prem_posf10325_phi1_p1_t298_mani.dat" +pmf.do_cellAverage = 0 + +#-------------------------PeleLM CONTROL---------------------------- +peleLM.v = 2 +peleLM.incompressible = 0 +peleLM.rho = 1.17 +peleLM.mu = 0.0 +peleLM.sdc_iterMax = 2 +peleLM.floor_species = 0 +peleLM.num_divu_iter = 1 #1 +peleLM.num_init_iter = 3 # 3 + +peleLM.do_temporals = 0 +peleLM.temporal_int = 2 +peleLM.mass_balance = 1 + +#amr.restart = chk00005 +amr.check_int = 100 +amr.plot_int = 10 +amr.max_step = 1000 +amr.dt_shrink = 0.01 +amr.stop_time = 1.0 +#amr.stop_time = 1.00 +amr.cfl = 0.15 +amr.derive_plot_vars = avg_pressure mag_vort mass_fractions maniout + +# --------------- INPUTS TO CHEMISTRY REACTOR --------------- +peleLM.chem_integrator = "ReactorRK64" +ode.rk64_nsubsteps_guess = 1 +ode.rk64_nsubsteps_min = 1 +peleLM.use_typ_vals_chem = 1 # Use species/temp typical values in CVODE +ode.rtol = 1.0e-6 # Relative tolerance of the chemical solve +ode.atol = 1.0e-5 # Absolute tolerance factor applied on typical values +cvode.solve_type = denseAJ_direct # CVODE Linear solve type (for Newton direction) +cvode.max_order = 4 # CVODE max BDF order. + +#--------------------REFINEMENT CONTROL------------------------ + +amr.refinement_indicators = magVort temp tempval +amr.magVort.max_level = 1 +amr.magVort.value_greater = 5000.0 +amr.magVort.field_name = mag_vort + +amr.temp.max_level = 3 +amr.temp.adjacent_difference_greater = 25 +amr.temp.field_name = temp + +amr.tempval.max_level = 2 +amr.tempval.value_greater = 150000 +amr.tempval.field_name = temp + +#amr.refinement_indicators = yH +#amr.yH.max_level = 5 +#amr.yH.value_greater = 3.0e-7 +#amr.yH.field_name = Y(H) + +#--------------------LINEAR SOLVER CONTROL------------------------ +nodal_proj.verbose = 2 +nodal_proj.rtol = 2.0e-11 +amrex.fpe_trap_invalid = 1 +amrex.fpe_trap_zero = 1 +amrex.fpe_trap_overflow = 1 + +#----------------------- Manifold Stuff ---------------------------- +manifold.model = Table +manifold.v = 1 +manifold.filename = prem_posf10325_phi1_p1_t298.ctb +eos.nominal_pressure_cgs = 1013250.0 +peleLM.use_wbar = 0 +peleLM.chi_correction_type = "DivuFirstIter" +#peleLM.chi_correction_type = "NoDivu" +eos.has_mani_src = true +eos.use_log_density = false +eos.compute_temperature = true + +# ----------------------- HIT IC ---------------------------- +ic.hitIC = 1 +ic.input_resolution = 128 +ic.uin_norm = 1.0 +ic.urms0 = 4.0 +ic.lscale = 25.132741228718 +#ic.lscale = 12.56637061436 +#ic.lscale = 6.283185307179 +#ic.lscale = 3.1415926536 +#ic.lscale = 1.57079632679489662 +ic.offset = 0 #-0.031415926536 +ic.input_name = hit_ic_4_128.dat +ic.input_binaryformat = 0 +ic.buffer_boundary = 0.25 +ic.buffer_thickness = 0.03 \ No newline at end of file diff --git a/Exec/RegTests/EnclosedFlame/input.nn b/Exec/RegTests/EnclosedFlame/input.nn new file mode 100644 index 000000000..3cc5ec0d7 --- /dev/null +++ b/Exec/RegTests/EnclosedFlame/input.nn @@ -0,0 +1,130 @@ +#----------------------DOMAIN DEFINITION------------------------ +#geometry.is_periodic = 1 1 # For each dir, 0: non-perio, 1: periodi +geometry.is_periodic = 0 0 # For each dir, 0: non-perio, 1: periodic +geometry.coord_sys = 0 # 0 => cart, 1 => RZ +geometry.prob_lo = 0.0 0.0 0.0 # x_lo y_lo (z_lo) +geometry.prob_hi = 0.02 0.02 0.016 # x_hi y_hi (z_hi) + +# >>>>>>>>>>>>> BC FLAGS <<<<<<<<<<<<<<<< +# Interior, Inflow, Outflow, Symmetry, +# SlipWallAdiab, NoSlipWallAdiab, SlipWallIsotherm, NoSlipWallIsotherm +#peleLM.lo_bc = Interior Interior +#peleLM.hi_bc = Interior Interior + +peleLM.lo_bc = Outflow Outflow +peleLM.hi_bc = Outflow Outflow + + + +#-------------------------AMR CONTROL---------------------------- +amr.n_cell = 128 128 32 # Level 0 number of cells in each direction +amr.v = 1 # AMR verbose +amr.max_level = 3 # maximum level number allowed +amr.ref_ratio = 2 2 2 2 2 # refinement ratio +amr.regrid_int = 5 # how often to regrid +amr.n_error_buf = 6 6 6 6 # number of buffer cells in error est +amr.grid_eff = 0.7 # what constitutes an efficient grid +amr.blocking_factor = 16 # block factor in grid generation (min box size) +amr.max_grid_size = 128 # max box size + + +#--------------------------- Problem ------------------------------- +prob.P_mean = 101325.0 +prob.standoff = -0.0175 +prob.flame_radius = 0.001 +prob.pertmag = 0.0 #0.0002 +pmf.datafile = "prem_posf10325_phi1_p1_t298_mani_nn.dat" +pmf.do_cellAverage = 0 + +#-------------------------PeleLM CONTROL---------------------------- +peleLM.v = 2 +peleLM.incompressible = 0 +peleLM.rho = 1.17 +peleLM.mu = 0.0 +peleLM.sdc_iterMax = 2 +peleLM.floor_species = 0 +peleLM.num_divu_iter = 1 #1 +peleLM.num_init_iter = 3 # 3 + +peleLM.do_temporals = 0 +peleLM.temporal_int = 2 +peleLM.mass_balance = 1 + +#amr.restart = chk00005 +amr.check_int = 100 +amr.plot_int = 10 +amr.max_step = 1000 +amr.dt_shrink = 0.01 +amr.stop_time = 1.0 +#amr.stop_time = 1.00 +amr.cfl = 0.15 +amr.derive_plot_vars = avg_pressure mag_vort mass_fractions maniout + +# --------------- INPUTS TO CHEMISTRY REACTOR --------------- +peleLM.chem_integrator = "ReactorRK64" +ode.rk64_nsubsteps_guess = 1 +ode.rk64_nsubsteps_min = 1 +peleLM.use_typ_vals_chem = 1 # Use species/temp typical values in CVODE +ode.rtol = 1.0e-6 # Relative tolerance of the chemical solve +ode.atol = 1.0e-5 # Absolute tolerance factor applied on typical values +cvode.solve_type = denseAJ_direct # CVODE Linear solve type (for Newton direction) +cvode.max_order = 4 # CVODE max BDF order. + +#--------------------REFINEMENT CONTROL------------------------ + +amr.refinement_indicators = magVort temp tempval +amr.magVort.max_level = 1 +amr.magVort.value_greater = 5000.0 +amr.magVort.field_name = mag_vort + +amr.temp.max_level = 3 +amr.temp.adjacent_difference_greater = 25 +amr.temp.field_name = temp + +amr.tempval.max_level = 2 +amr.tempval.value_greater = 150000 +amr.tempval.field_name = temp + +#amr.refinement_indicators = yH +#amr.yH.max_level = 5 +#amr.yH.value_greater = 3.0e-7 +#amr.yH.field_name = Y(H) + +#--------------------LINEAR SOLVER CONTROL------------------------ +nodal_proj.verbose = 2 +nodal_proj.rtol = 2.0e-11 +amrex.fpe_trap_invalid = 1 +amrex.fpe_trap_zero = 1 +amrex.fpe_trap_overflow = 1 + +#----------------------- Manifold Stuff ---------------------------- +manifold.model = NeuralNet +manifold.v = 1 +#manifold.filename = fgm_net.pnn +manifold.filename = cmlm_net.pnn +manifold.cmlm_net = 1 +#manifold.info_filename = fgm_net_info.txt +manifold.info_filename = cmlm_net_info.txt +eos.nominal_pressure_cgs = 1013250.0 +peleLM.use_wbar = 0 +peleLM.chi_correction_type = "DivuFirstIter" +#peleLM.chi_correction_type = "NoDivu" +eos.has_mani_src = true +eos.density_lookup_type = inverse +eos.compute_temperature = true + +# ----------------------- HIT IC ---------------------------- +ic.hitIC = 1 +ic.input_resolution = 128 +ic.uin_norm = 1.0 +ic.urms0 = 4.0 +ic.lscale = 25.132741228718 +#ic.lscale = 12.56637061436 +#ic.lscale = 6.283185307179 +#ic.lscale = 3.1415926536 +#ic.lscale = 1.57079632679489662 +ic.offset = 0 #-0.031415926536 +ic.input_name = hit_ic_4_128.dat +ic.input_binaryformat = 0 +ic.buffer_boundary = 0.25 +ic.buffer_thickness = 0.03 \ No newline at end of file diff --git a/Exec/RegTests/EnclosedFlame/input.posf b/Exec/RegTests/EnclosedFlame/input.posf new file mode 100644 index 000000000..9b56efa7f --- /dev/null +++ b/Exec/RegTests/EnclosedFlame/input.posf @@ -0,0 +1,115 @@ +#----------------------DOMAIN DEFINITION------------------------ +#geometry.is_periodic = 1 1 # For each dir, 0: non-perio, 1: periodi +geometry.is_periodic = 0 0 # For each dir, 0: non-perio, 1: periodic +geometry.coord_sys = 0 # 0 => cart, 1 => RZ +geometry.prob_lo = 0.0 0.0 0.0 # x_lo y_lo (z_lo) +geometry.prob_hi = 0.02 0.02 0.016 # x_hi y_hi (z_hi) + +# >>>>>>>>>>>>> BC FLAGS <<<<<<<<<<<<<<<< +# Interior, Inflow, Outflow, Symmetry, +# SlipWallAdiab, NoSlipWallAdiab, SlipWallIsotherm, NoSlipWallIsotherm +#peleLM.lo_bc = Interior Interior +#peleLM.hi_bc = Interior Interior + +peleLM.lo_bc = Outflow Outflow +peleLM.hi_bc = Outflow Outflow + + +#-------------------------AMR CONTROL---------------------------- +amr.n_cell = 128 128 32 # Level 0 number of cells in each direction +amr.v = 1 # AMR verbose +amr.max_level = 3 # maximum level number allowed +amr.ref_ratio = 2 2 2 2 2 # refinement ratio +amr.regrid_int = 5 # how often to regrid +amr.n_error_buf = 2 2 2 2 # number of buffer cells in error est +amr.grid_eff = 0.7 # what constitutes an efficient grid +amr.blocking_factor = 16 # block factor in grid generation (min box size) +amr.max_grid_size = 128 # max box size + + +#--------------------------- Problem ------------------------------- +prob.P_mean = 101325.0 +prob.standoff = -0.0175 +prob.flame_radius = 0.001 +prob.pertmag = 0.0 #0.0002 +pmf.datafile = "prem_posf10325_phi1_p1_t298.dat" +pmf.do_cellAverage = 0 + +#-------------------------PeleLM CONTROL---------------------------- +peleLM.v = 2 +peleLM.incompressible = 0 +peleLM.rho = 1.17 +peleLM.mu = 0.0 +peleLM.sdc_iterMax = 2 +peleLM.floor_species = 0 +peleLM.num_divu_iter = 1 +peleLM.num_init_iter = 3 + +peleLM.do_temporals = 0 +peleLM.temporal_int = 2 +peleLM.mass_balance = 1 + +# amr.restart = chk00100 +amr.check_int = 100 +amr.plot_int = 10 +amr.max_step = 1000 +amr.dt_shrink = 0.01 +amr.stop_time = 1.0 +#amr.stop_time = 1.00 +amr.cfl = 0.15 +amr.derive_plot_vars = avg_pressure mag_vort mass_fractions inst_src_spec + +# --------------- INPUTS TO CHEMISTRY REACTOR --------------- +peleLM.chem_integrator = "ReactorCvode" +peleLM.use_typ_vals_chem = 1 # Use species/temp typical values in CVODE +ode.rtol = 1.0e-6 # Relative tolerance of the chemical solve +ode.atol = 1.0e-5 # Absolute tolerance factor applied on typical values +cvode.solve_type = denseAJ_direct # CVODE Linear solve type (for Newton direction) +cvode.max_order = 4 # CVODE max BDF order. + +#--------------------REFINEMENT CONTROL------------------------ + +amr.refinement_indicators = magVort temp tempval +amr.magVort.max_level = 1 +amr.magVort.value_greater = 5000.0 +amr.magVort.field_name = mag_vort + +amr.temp.max_level = 3 +amr.temp.adjacent_difference_greater = 25 +amr.temp.field_name = temp + +amr.tempval.max_level = 2 +amr.tempval.value_greater = 150000 +amr.tempval.field_name = temp + +#amr.refinement_indicators = yH +#amr.yH.max_level = 5 +#amr.yH.value_greater = 3.0e-7 +#amr.yH.field_name = Y(H) + + +#--------------------LINEAR SOLVER CONTROL------------------------ +nodal_proj.verbose = 2 +nodal_proj.rtol = 2.0e-11 +amrex.fpe_trap_invalid = 1 +amrex.fpe_trap_zero = 1 +amrex.fpe_trap_overflow = 1 + +peleLM.use_wbar = 1 + + +# ----------------------- HIT IC ---------------------------- +ic.hitIC = 1 +ic.input_resolution = 128 +ic.uin_norm = 1.0 +ic.urms0 = 2.0 +#ic.lscale = 25.132741228718 +ic.lscale = 12.56637061436 +#ic.lscale = 6.283185307179 +#ic.lscale = 3.1415926536 +#ic.lscale = 1.57079632679489662 +ic.offset = 0 #-0.031415926536 +ic.input_name = hit_ic_4_128.dat +ic.input_binaryformat = 0 +ic.buffer_boundary = 0.25 +ic.buffer_thickness = 0.03 \ No newline at end of file diff --git a/Exec/RegTests/EnclosedFlame/pelelm_prob.H b/Exec/RegTests/EnclosedFlame/pelelm_prob.H index 1125cf48e..18e0acee8 100644 --- a/Exec/RegTests/EnclosedFlame/pelelm_prob.H +++ b/Exec/RegTests/EnclosedFlame/pelelm_prob.H @@ -11,6 +11,48 @@ #include #include +// ----------------------------------------------------------- +// Search for the closest index in an array to a given value +// using the bisection technique. +// INPUTS/OUTPUTS: +// xtable(0:n-1) => array to search in (ascending order) +// n => array size +// x => x location +// idxlo <=> output st. xtable(idxlo) <= x < xtable(idxlo+1) +// ----------------------------------------------------------- +AMREX_GPU_DEVICE +AMREX_FORCE_INLINE +void +locate(const amrex::Real* xtable, const int n, const amrex::Real& x, int& idxlo) +{ + // If x is out of bounds, return boundary index + if (x >= xtable[n - 1]) { + idxlo = n - 1; + return; + } + if (x <= xtable[0]) { + idxlo = 0; + return; + } + + // Do the bisection + idxlo = 0; + int idxhi = n - 1; + bool notdone = true; + while (notdone) { + if (idxhi - idxlo <= 1) { + notdone = false; + } else { + const int idxmid = (idxhi + idxlo) / 2; + if (x >= xtable[idxmid]) { + idxlo = idxmid; + } else { + idxhi = idxmid; + } + } + } +} + AMREX_GPU_DEVICE AMREX_FORCE_INLINE void pelelm_initdata(int i, int j, int k, @@ -19,16 +61,19 @@ void pelelm_initdata(int i, int j, int k, amrex::Array4 const& aux, amrex::GeometryData const& geomdata, ProbParm const& prob_parm, - pele::physics::PMF::PmfData::DataContainer const * pmf_data) + pele::physics::PMF::PmfData::DataContainer const * pmf_data, + const pele::physics::eos::EosParm* eosparm) { const amrex::Real* prob_lo = geomdata.ProbLo(); const amrex::Real* prob_hi = geomdata.ProbHi(); const amrex::Real* dx = geomdata.CellSize(); - AMREX_D_TERM(const amrex::Real x = prob_lo[0] + (i+0.5)*dx[0];, + /*AMREX_D_TERM(const amrex::Real x = prob_lo[0] + (i+0.5)*dx[0];, const amrex::Real y = prob_lo[1] + (j+0.5)*dx[1];, - const amrex::Real z = prob_lo[2] + (k+0.5)*dx[2];); - + const amrex::Real z = prob_lo[2] + (k+0.5)*dx[2];); */ + const amrex::Real x = prob_lo[0] + (i+0.5)*dx[0]; + const amrex::Real y = prob_lo[1] + (j+0.5)*dx[1]; + const amrex::Real z = prob_lo[2] + (k+0.5)*dx[2]; AMREX_D_TERM(const amrex::Real Lx = prob_hi[0] - prob_lo[0];, const amrex::Real Ly = prob_hi[1] - prob_lo[1];, const amrex::Real Lz = prob_hi[2] - prob_lo[2]); @@ -39,7 +84,7 @@ void pelelm_initdata(int i, int j, int k, constexpr amrex::Real Pi = 3.14159265358979323846264338327950288; - auto eos = pele::physics::PhysicsType::eos(); + auto eos = pele::physics::PhysicsType::eos(eosparm); amrex::GpuArray pmf_vals = {0.0}; amrex::Real molefrac[NUM_SPECIES] = {0.0}; amrex::Real massfrac[NUM_SPECIES] = {0.0}; @@ -49,12 +94,12 @@ void pelelm_initdata(int i, int j, int k, + (y-yc)*(y-yc), + (z-zc)*(z-zc))); - if (prob_parm.pertmag > 0.0) + if (prob_parm.pertmag > 0.0) { #if ( AMREX_SPACEDIM == 2 ) amrex::Real angle = std::atan2((y-yc),(x-xc)); - pert = prob_parm.pertmag * + pert = prob_parm.pertmag * (0.7 * std::sin(2*angle) + //1.023 * std::sin(2 * (angle - 0.004598) / radius2) + //0.945 * std::sin(3 * (angle - 0.00712435) / radius2) + @@ -62,14 +107,14 @@ void pelelm_initdata(int i, int j, int k, 1.4 * std::sin(4 * angle )); } - amrex::Real y1 = 3.6 - (radius - prob_parm.standoff - 0.5*dx[1] + pert)*100; - amrex::Real y2 = 3.6 - (radius - prob_parm.standoff + 0.5*dx[1] + pert)*100; + amrex::Real y1 = (prob_parm.flame_radius - (radius + prob_parm.standoff - 0.5*dx[1] + pert))*100; + amrex::Real y2 = (prob_parm.flame_radius - (radius + prob_parm.standoff + 0.5*dx[1] + pert))*100; #elif ( AMREX_SPACEDIM == 3 ) amrex::Real angle = std::atan2((y-yc),(x-xc)); amrex::Real angle2 = std::atan2((z-zc),(y-yc)); - pert = prob_parm.pertmag * + pert = prob_parm.pertmag * (0.5 * std::sin(2*angle) + 0.7 * std::sin(8 * angle ) + 1.1 * std::sin(4 * angle )) * @@ -78,8 +123,8 @@ void pelelm_initdata(int i, int j, int k, 0.85 * std::sin(16 * angle2 )); } - amrex::Real y1 = 3.6 - (radius - prob_parm.standoff - 1.42*dx[1] + pert)*100; - amrex::Real y2 = 3.6 - (radius - prob_parm.standoff + 1.42*dx[1] + pert)*100; +amrex::Real y1 = (prob_parm.flame_radius - (radius + prob_parm.standoff - 1.42*dx[1] + pert))*100; +amrex::Real y2 = (prob_parm.flame_radius - (radius + prob_parm.standoff + 1.42*dx[1] + pert))*100; #endif amrex::Real ymean = 0.5 * (y1+y2); @@ -87,11 +132,6 @@ void pelelm_initdata(int i, int j, int k, state(i,j,k,TEMP) = pmf_vals[0];; - for (int n = 0; n < NUM_SPECIES; n++){ - molefrac[n] = pmf_vals[3 + n]; - } - eos.X2Y(molefrac, massfrac); - state(i,j,k,VELX) = 0.0; #if ( AMREX_SPACEDIM == 2 ) state(i,j,k,VELY) = pmf_vals[1]*1e-2; @@ -100,6 +140,25 @@ void pelelm_initdata(int i, int j, int k, state(i,j,k,VELZ) = pmf_vals[1]*1e-2; #endif +#ifdef USE_MANIFOLD_EOS + for (int n = 0; n < NUM_SPECIES-1; n++){ + massfrac[n] = pmf_vals[3 + n]; + } + amrex::Real rho_temp, tmp1, tmp2; + eos.PYT2R(tmp1, massfrac, tmp2, rho_temp); + rho_temp *= 1.0e3; // CGS -> MKS conversion + state(i,j,k,DENSITY) = rho_temp; + state(i,j,k,RHOH) = 0.0; // No RhoH transport in manifold model + + for (int n = 0; n < NUM_SPECIES-1; n++) { + state(i,j,k,FIRSTSPEC+n) = massfrac[n] * rho_temp; + } + state(i,j,k,FIRSTSPEC+NUM_SPECIES-1) = rho_temp; +#else + for (int n = 0; n < NUM_SPECIES; n++){ + molefrac[n] = pmf_vals[3 + n]; + } + eos.X2Y(molefrac, massfrac); amrex::Real P_cgs = prob_parm.P_mean * 10.0; // Density @@ -116,6 +175,133 @@ void pelelm_initdata(int i, int j, int k, for (int n = 0; n < NUM_SPECIES; n++) { state(i,j,k,FIRSTSPEC+n) = massfrac[n] * state(i,j,k,DENSITY); } +#endif + + // Read in turbulent fluctuations from MarcHDF + if (prob_parm.hitIC) { + amrex::Real xtf[3] = {x, y, z}; + amrex::Real u[3] = {0.0}; + amrex::Real mod[3] = {0.0}; + int idx[3] = {0}; + int idxp1[3] = {0}; + amrex::Real slp[3] = {0.0}; + for (int cnt = 0; cnt < 3; cnt++) { + mod[cnt] = std::fmod(xtf[cnt], prob_parm.Linput); + locate(prob_parm.d_xarray, prob_parm.input_resolution, mod[cnt], idx[cnt]); + idxp1[cnt] = (idx[cnt] + 1) % prob_parm.input_resolution; + slp[cnt] = (mod[cnt] - prob_parm.d_xarray[idx[cnt]]) / prob_parm.d_xdiff[idx[cnt]]; + } + int inSize = prob_parm.input_resolution; + + const amrex::Real f0 = (1 - slp[0]) * (1 - slp[1]) * (1 - slp[2]); + const amrex::Real f1 = slp[0] * (1 - slp[1]) * (1 - slp[2]); + const amrex::Real f2 = (1 - slp[0]) * slp[1] * (1 - slp[2]); + const amrex::Real f3 = (1 - slp[0]) * (1 - slp[1]) * slp[2]; + const amrex::Real f4 = slp[0] * (1 - slp[1]) * slp[2]; + const amrex::Real f5 = (1 - slp[0]) * slp[1] * slp[2]; + const amrex::Real f6 = slp[0] * slp[1] * (1 - slp[2]); + const amrex::Real f7 = slp[0] * slp[1] * slp[2]; + u[0] = + prob_parm.d_uinput + [idx[0] + inSize * (idx[1] + inSize * idx[2])] * + f0 + + prob_parm.d_uinput + [idxp1[0] + inSize * (idx[1] + inSize * idx[2])] * + f1 + + prob_parm.d_uinput + [idx[0] + inSize * (idxp1[1] + inSize * idx[2])] * + f2 + + prob_parm.d_uinput + [idx[0] + inSize * (idx[1] + inSize * idxp1[2])] * + f3 + + prob_parm.d_uinput + [idxp1[0] + inSize * (idx[1] + inSize * idxp1[2])] * + f4 + + prob_parm.d_uinput + [idx[0] + inSize * (idxp1[1] + inSize * idxp1[2])] * + f5 + + prob_parm.d_uinput + [idxp1[0] + inSize * (idxp1[1] + inSize * idx[2])] * + f6 + + prob_parm.d_uinput + [idxp1[0] + + inSize * (idxp1[1] + inSize * idxp1[2])] * + f7; + u[1] = + prob_parm.d_vinput + [idx[0] + inSize * (idx[1] + inSize * idx[2])] * + f0 + + prob_parm.d_vinput + [idxp1[0] + inSize * (idx[1] + inSize * idx[2])] * + f1 + + prob_parm.d_vinput + [idx[0] + inSize * (idxp1[1] + inSize * idx[2])] * + f2 + + prob_parm.d_vinput + [idx[0] + inSize * (idx[1] + inSize * idxp1[2])] * + f3 + + prob_parm.d_vinput + [idxp1[0] + inSize * (idx[1] + inSize * idxp1[2])] * + f4 + + prob_parm.d_vinput + [idx[0] + inSize * (idxp1[1] + inSize * idxp1[2])] * + f5 + + prob_parm.d_vinput + [idxp1[0] + inSize * (idxp1[1] + inSize * idx[2])] * + f6 + + prob_parm.d_vinput + [idxp1[0] + + inSize * (idxp1[1] + inSize * idxp1[2])] * + f7; + u[2] = + prob_parm.d_winput + [idx[0] + inSize * (idx[1] + inSize * idx[2])] * + f0 + + prob_parm.d_winput + [idxp1[0] + inSize * (idx[1] + inSize * idx[2])] * + f1 + + prob_parm.d_winput + [idx[0] + inSize * (idxp1[1] + inSize * idx[2])] * + f2 + + prob_parm.d_winput + [idx[0] + inSize * (idx[1] + inSize * idxp1[2])] * + f3 + + prob_parm.d_winput + [idxp1[0] + inSize * (idx[1] + inSize * idxp1[2])] * + f4 + + prob_parm.d_winput + [idx[0] + inSize * (idxp1[1] + inSize * idxp1[2])] * + f5 + + prob_parm.d_winput + [idxp1[0] + inSize * (idxp1[1] + inSize * idx[2])] * + f6 + + prob_parm.d_winput + [idxp1[0] + + inSize * (idxp1[1] + inSize * idxp1[2])] * + f7; + AMREX_D_TERM( + const amrex::Real decayx = + (0.5 * + (std::tanh((xtf[0] - (prob_lo[0] + prob_parm.buffer_boundary*Lx)) / (prob_parm.buffer_thickness*Lx)) + 1)) * + (0.5 * + (std::tanh(-(xtf[0] - (prob_hi[0] - prob_parm.buffer_boundary*Lx)) / (prob_parm.buffer_thickness*Lx)) + 1));, + const amrex::Real decayy = + (0.5 * + (std::tanh((xtf[1] - (prob_lo[1] + prob_parm.buffer_boundary*Ly)) / (prob_parm.buffer_thickness*Ly)) + 1)) * + (0.5 * + (std::tanh(-(xtf[1] - (prob_hi[1] - prob_parm.buffer_boundary*Ly)) / (prob_parm.buffer_thickness*Ly)) + 1));, + const amrex::Real decayz = + (0.5 * + (std::tanh((xtf[2] - (prob_lo[2] + prob_parm.buffer_boundary*Lz)) / (prob_parm.buffer_thickness*Lz)) + 1)) * + (0.5 * + (std::tanh(-(xtf[2] - (prob_hi[2] - prob_parm.buffer_boundary*Lz)) / (prob_parm.buffer_thickness*Lz)) + 1));) + u[0] *= AMREX_D_TERM(decayx, * decayy, * decayz); + u[1] *= AMREX_D_TERM(decayx, * decayy, * decayz); + u[2] *= AMREX_D_TERM(decayx, * decayy, * decayz); + AMREX_D_TERM(state(i,j,k,VELX) = u[0];, + state(i,j,k,VELY) = u[1];, + state(i,j,k,VELZ) = u[2];) + } } AMREX_GPU_DEVICE @@ -130,7 +316,8 @@ bcnormal( const amrex::Real time, amrex::GeometryData const& geomdata, ProbParm const& prob_parm, - pele::physics::PMF::PmfData::DataContainer const *pmf_data) + pele::physics::PMF::PmfData::DataContainer const *pmf_data, + const pele::physics::eos::EosParm* eosparm) { const amrex::Real* prob_lo = geomdata.ProbLo(); } diff --git a/Exec/RegTests/EnclosedFlame/pelelm_prob.cpp b/Exec/RegTests/EnclosedFlame/pelelm_prob.cpp index e9d50dd88..59b289382 100644 --- a/Exec/RegTests/EnclosedFlame/pelelm_prob.cpp +++ b/Exec/RegTests/EnclosedFlame/pelelm_prob.cpp @@ -1,14 +1,225 @@ #include #include + +// ----------------------------------------------------------- +// Read a binary file +// INPUTS/OUTPUTS: +// iname => filename +// nx => input resolution +// ny => input resolution +// nz => input resolution +// data <= output data +// ----------------------------------------------------------- +void +read_binary( + const std::string& iname, + const size_t nx, + const size_t ny, + const size_t nz, + const size_t ncol, + amrex::Vector& data /*needs to be double*/) +{ + std::ifstream infile(iname, std::ios::in | std::ios::binary); + if (not infile.is_open()) { + amrex::Abort("Unable to open input file " + iname); + } + + for (size_t i = 0; i < nx * ny * nz * ncol; i++) { + infile.read(reinterpret_cast(&data[i]), sizeof(data[i])); + } + infile.close(); +} + +AMREX_FORCE_INLINE +std::string +read_file(std::ifstream& in) +{ + return static_cast( + std::stringstream() << in.rdbuf()) + .str(); +} + +// ----------------------------------------------------------- +// Read a csv file +// INPUTS/OUTPUTS: +// iname => filename +// nx => input resolution +// ny => input resolution +// nz => input resolution +// data <= output data +// ----------------------------------------------------------- +void +read_csv( + const std::string& iname, + const size_t nx, + const size_t ny, + const size_t nz, + amrex::Vector& data) +{ + std::ifstream infile(iname, std::ios::in); + const std::string memfile = read_file(infile); + if (not infile.is_open()) { + amrex::Abort("Unable to open input file " + iname); + } + infile.close(); + std::istringstream iss(memfile); + + // Read the file + size_t nlines = 0; + std::string firstline; + std::string line; + std::getline(iss, firstline); // skip header + while (getline(iss, line)) { + ++nlines; + } + + // Quick sanity check + if (nlines != nx * ny * nz) { + amrex::Abort( + "Number of lines in the input file (= " + std::to_string(nlines) + + ") does not match the input resolution (=" + std::to_string(nx) + ")"); + } + + // Read the data from the file + iss.clear(); + iss.seekg(0, std::ios::beg); + std::getline(iss, firstline); // skip header + int cnt = 0; + while (std::getline(iss, line)) { + std::istringstream linestream(line); + std::string value; + while (getline(linestream, value, ',')) { + std::istringstream sinput(value); + sinput >> data[cnt]; + cnt++; + } + } +} + + void PeleLM::readProbParm() { amrex::ParmParse pp("prob"); - + std::string type; pp.query("P_mean", PeleLM::prob_parm->P_mean); pp.query("standoff", PeleLM::prob_parm->standoff); pp.query("pertmag", PeleLM::prob_parm->pertmag); + pp.query("flame_radius", PeleLM::prob_parm->flame_radius); PeleLM::pmf_data.initialize(); + + // HIT init + amrex::ParmParse ppic("ic"); + ppic.query("hitIC", PeleLM::prob_parm->hitIC); + ppic.query("input_resolution", PeleLM::prob_parm->input_resolution); + ppic.query("uin_norm", PeleLM::prob_parm->uin_norm); + ppic.query("lscale", PeleLM::prob_parm->lscale); + ppic.query("offset", PeleLM::prob_parm->offset); + ppic.query("urms0", PeleLM::prob_parm->urms0); + /* + amrex::Vector win_lo( + AMREX_SPACEDIM, std::numeric_limits::lowest()); + amrex::Vector win_hi( + AMREX_SPACEDIM, std::numeric_limits::max()); + ppic.queryarr("win_lo", win_lo, 0, AMREX_SPACEDIM); + ppic.queryarr("win_hi", win_hi, 0, AMREX_SPACEDIM); + for (int i = 0; i < AMREX_SPACEDIM; i++) { + PeleLM::prob_parm->win_lo[i] = win_lo[i]; + PeleLM::prob_parm->win_hi[i] = win_hi[i]; + } + ppic.query("win_slope", PeleLM::prob_parm->win_slope); */ + + if (PeleLM::prob_parm->hitIC) { + amrex::Print() << "Initializing HIT data \n"; + + std::string datafile; + ppic.query("input_name",datafile); + int binfmt = 0; // Default is ASCII format + ppic.query("input_binaryformat",binfmt); + ppic.query("buffer_boundary",PeleLM::prob_parm->buffer_boundary); + ppic.query("buffer_thickness",PeleLM::prob_parm->buffer_thickness); + + // Read initial velocity field + const size_t nx = PeleLM::prob_parm->input_resolution; + const size_t ny = PeleLM::prob_parm->input_resolution; + const size_t nz = PeleLM::prob_parm->input_resolution; + amrex::Vector data(nx * ny * nz * 6); /* this needs to be double */ + if (binfmt) { + read_binary(datafile, nx, ny, nz, 6, data); + } else { + read_csv(datafile, nx, ny, nz, data); + } + + // Extract position and velocities + amrex::Vector xinput; + amrex::Vector uinput; + amrex::Vector vinput; + amrex::Vector winput; + amrex::Vector xdiff; + amrex::Vector xarray; + + xinput.resize(nx * ny * nz); + uinput.resize(nx * ny * nz); + vinput.resize(nx * ny * nz); + winput.resize(nx * ny * nz); + + for (int i = 0; i < xinput.size(); i++) { + xinput[i] = (data[0 + i * 6] + PeleLM::prob_parm->offset) / + PeleLM::prob_parm->lscale; + uinput[i] = data[3 + i * 6] * PeleLM::prob_parm->urms0 / PeleLM::prob_parm->uin_norm; + vinput[i] = data[4 + i * 6] * PeleLM::prob_parm->urms0 / PeleLM::prob_parm->uin_norm; + winput[i] = data[5 + i * 6] * PeleLM::prob_parm->urms0 / PeleLM::prob_parm->uin_norm; + } + + // Get the xarray table and the differences. + xarray.resize(nx); + for (int i = 0; i < xarray.size(); i++) { + xarray[i] = xinput[i]; + } + xdiff.resize(nx); + std::adjacent_difference( + xarray.begin(), + xarray.end(), + xdiff.begin()); + xdiff[0] = xdiff[1]; + + // Make sure the search array is increasing + if (not std::is_sorted( + xarray.begin(), + xarray.end())) { + amrex::Abort("Error: non ascending x-coordinate array."); + } + PeleLM::prob_parm->Linput = (xarray[nx - 1] + 0.5 * xdiff[nx - 1]) - (xarray[0] - 0.5 * xdiff[0]); + + // Initialize PeleLM::prob_parm containers + PeleLM::prob_parm->d_xarray = (amrex::Real*) amrex::The_Arena()->alloc(nx*sizeof(amrex::Real)); + PeleLM::prob_parm->d_xdiff = (amrex::Real*) amrex::The_Arena()->alloc(nx*sizeof(amrex::Real)); + PeleLM::prob_parm->d_uinput = (amrex::Real*) amrex::The_Arena()->alloc(nx*ny*nz*sizeof(amrex::Real)); + PeleLM::prob_parm->d_vinput = (amrex::Real*) amrex::The_Arena()->alloc(nx*ny*nz*sizeof(amrex::Real)); + PeleLM::prob_parm->d_winput = (amrex::Real*) amrex::The_Arena()->alloc(nx*ny*nz*sizeof(amrex::Real)); + + // Copy into PeleLM::prob_parm + amrex::Gpu::copy(amrex::Gpu::hostToDevice, + xarray.begin(), + xarray.end(), + PeleLM::prob_parm->d_xarray); + amrex::Gpu::copy(amrex::Gpu::hostToDevice, + xdiff.begin(), + xdiff.end(), + PeleLM::prob_parm->d_xdiff); + amrex::Gpu::copy(amrex::Gpu::hostToDevice, + uinput.begin(), + uinput.end(), + PeleLM::prob_parm->d_uinput); + amrex::Gpu::copy(amrex::Gpu::hostToDevice, + vinput.begin(), + vinput.end(), + PeleLM::prob_parm->d_vinput); + amrex::Gpu::copy(amrex::Gpu::hostToDevice, + winput.begin(), + winput.end(), + PeleLM::prob_parm->d_winput); + } } diff --git a/Exec/RegTests/EnclosedFlame/pelelm_prob_parm.H b/Exec/RegTests/EnclosedFlame/pelelm_prob_parm.H index e09474c39..15e480e72 100644 --- a/Exec/RegTests/EnclosedFlame/pelelm_prob_parm.H +++ b/Exec/RegTests/EnclosedFlame/pelelm_prob_parm.H @@ -11,6 +11,33 @@ struct ProbParm amrex::Real P_mean = 101325.0_rt; amrex::Real standoff = 0.0_rt; amrex::Real pertmag = 0.0004_rt; + amrex::Real flame_radius = 0.036_rt; int meanFlowDir = 1; + + // stuff for HIT init + int hitIC = 0; + int input_resolution = 0; + amrex::Real uin_norm = 1.0; + amrex::Real lscale = 1.0; + amrex::Real offset = 0.0; + amrex::Real urms0 = 0.0; + amrex::Real Linput = 0.0; + amrex::Real buffer_boundary = 1.0/6.0; + amrex::Real buffer_thickness = 1.0/24.0; + /* +#if (AMREX_SPACEDIM == 2) + amrex::GpuArray win_lo = {0.0, 0.0}; + amrex::GpuArray win_hi = {0.0, 0.0}; +#elif (AMREX_SPACEDIM == 3) + amrex::GpuArray win_lo = {0.0, 0.0, 0.0}; + amrex::GpuArray win_hi = {0.0, 0.0, 0.0}; +#endif + amrex::Real win_slope = 1.0; + */ + amrex::Real* d_uinput = nullptr; + amrex::Real* d_vinput = nullptr; + amrex::Real* d_winput = nullptr; + amrex::Real* d_xarray = nullptr; + amrex::Real* d_xdiff = nullptr; }; #endif From 5160978ef9cbfa863bb871e52373239c25b1c3ce Mon Sep 17 00:00:00 2001 From: Bruce Perry Date: Tue, 2 Aug 2022 16:58:13 -0600 Subject: [PATCH 27/27] updated case files for 2D kernel case --- Exec/RegTests/EnclosedFlame/README | 39 ++ Exec/RegTests/EnclosedFlame/cmlm_net.pnn | Bin 0 -> 70588 bytes Exec/RegTests/EnclosedFlame/cmlm_net.pt | Bin 0 -> 89927 bytes Exec/RegTests/EnclosedFlame/cmlm_net_info.txt | 22 + Exec/RegTests/EnclosedFlame/fgm_net.pnn | Bin 0 -> 15380 bytes Exec/RegTests/EnclosedFlame/fgm_net.pt | Bin 0 -> 33598 bytes Exec/RegTests/EnclosedFlame/fgm_net_info.txt | 18 + Exec/RegTests/EnclosedFlame/input.nn | 7 +- .../prem_posf10325_phi1_p1_t298_mani_cmlm.dat | 467 ++++++++++++++++++ .../prem_posf10325_phi1_p1_t298_mani_fgm.dat | 467 ++++++++++++++++++ 10 files changed, 1017 insertions(+), 3 deletions(-) create mode 100644 Exec/RegTests/EnclosedFlame/README create mode 100644 Exec/RegTests/EnclosedFlame/cmlm_net.pnn create mode 100644 Exec/RegTests/EnclosedFlame/cmlm_net.pt create mode 100644 Exec/RegTests/EnclosedFlame/cmlm_net_info.txt create mode 100644 Exec/RegTests/EnclosedFlame/fgm_net.pnn create mode 100644 Exec/RegTests/EnclosedFlame/fgm_net.pt create mode 100644 Exec/RegTests/EnclosedFlame/fgm_net_info.txt create mode 100644 Exec/RegTests/EnclosedFlame/prem_posf10325_phi1_p1_t298_mani_cmlm.dat create mode 100644 Exec/RegTests/EnclosedFlame/prem_posf10325_phi1_p1_t298_mani_fgm.dat diff --git a/Exec/RegTests/EnclosedFlame/README b/Exec/RegTests/EnclosedFlame/README new file mode 100644 index 000000000..0057421f9 --- /dev/null +++ b/Exec/RegTests/EnclosedFlame/README @@ -0,0 +1,39 @@ +Stuff that needs to be done to run this case + +-- COMPILE -- +make TPL -j +make -j + +-- Generate Turbulence initial condition -- +python gen_hit_ic.py -k0 4 -N 128 + +-- Run -- +srun -n 36 PeleLMeX2d.gnu.TPROF.MPI.ex input.nn + + +#### NOTES #### +Contained are files to run with an FGM+ANN model trained on a 1D premixed flame +and a CMLM model trained from corresponding 3D DNS + +Required files include a pmf data file used to set the initial condition. +In the input.nn file, set either + + pmf.datafile = "prem_posf10325_phi1_p1_t298_mani_cmlm.dat" +-- OR -- + pmf.datafile = "prem_posf10325_phi1_p1_t298_mani_fgm.dat" + +Also required are a neural network file and info file. +In the input.nn file, set either + + manifold.filename = cmlm_net.pnn + manifold.info_filename = cmlm_net_info.txt +-- OR -- + manifold.filename = fgm_net.pnn + manifold.info_filename = fgm_net_info.txt + +Note that the CMLM model is a 2D manifold while the FGM one is a 1D manifold. +This means the correct setting must be used in the GNUmakefile when compiling: + + Chemistry_Model = Manifold2d +-- OR -- + Chemistry_Model = Manifold \ No newline at end of file diff --git a/Exec/RegTests/EnclosedFlame/cmlm_net.pnn b/Exec/RegTests/EnclosedFlame/cmlm_net.pnn new file mode 100644 index 0000000000000000000000000000000000000000..606d0505d50b988feab0cae996958092a7c66a13 GIT binary patch literal 70588 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

vF8pRZn(wS4L^Y>es{E&FNCA(bntWR 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

oTUr*pXi5(%xw~VwzQEJ9lcg8i&A55j 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 F{{>+sdJF&n literal 0 HcmV?d00001 diff --git a/Exec/RegTests/EnclosedFlame/fgm_net.pt b/Exec/RegTests/EnclosedFlame/fgm_net.pt new file mode 100644 index 0000000000000000000000000000000000000000..19ef339b68ed87e06b02a19db9964767203ca225 GIT binary patch literal 33598 zcmbrm2UrwM);3JeQ3M1*2_}@Rq?ta|BoRSSf&oyG42s~845)yBfP#n^kYJ7=1`r0O zy9z;6Kt%yDClJM~hza9A`aHY)?(X;Qf4$Gnbrmy3pL6Qe-FH=WovP|t?&1<6A~G@} z|MpQ587>kYu{1CyEWs=^C?Uwq+{IBv#Q$GD3VjQRQq~k&GcvWo*?ernqK_gCy?aF( z#!j8eZj#F+ahAuxP;qQS@}-o=ijFZ^I(` z)2l&}W!z3|HfYhq;-zTs0ZzlwMGKJo0+EKQ8UYDw=>t)jbfVuftHE-maf1x-$>9sK z4LyTfk;oX62IIc>c$T}gOwmZr-WDMaztXJ#fD84{xLCM2+Ws3Z{{qwhn!QzL*kE(r zjI25`t05`Ip@Dy69rb+SS~!1xMuS1^XzGA#2+8oYYuHnL4ObXVqHBKKK0!`>8nX-<7RNn62KN%sYcQcn>8u8gp(XV5ea^J*(J{2S<5c?1h-8TbqMS^hWp8Mfunm)9lH zg#n3luXhH$KVmT*U6MnK`tayuo{Q-N;kk67R~(%`EtRIFvT1RfrF8a!SlYEQmiGFQ zOG}kU({Vr6(_Wsd=~FG4w02?&9TJgAr+bFeg_`T>ZR*Quc4Qj8Mi5D_+z?Bj>0D0d zJla4@y-1}q1kv=CXUTNjyJ)&iHHnV6o<}EEmD1U!%jm;jV(G;vgXvEyTj|G5QFJwA z(4!5~=-j*6^sukl^qt4qG%q=a{u;TSK7S&M{)OLp#;R^7>pGGXkoew*4 z_1vdSugWkI)U1JbhHs#vLwCS0{3hYCDt`gLDgOF^HJKJW@Iat|Zv zxm9Spk~mx7Y=R~vsWJtoBFMyh4B9Jfzb8_}=vO;;qL}HU>A>xg^ydpDl=4@mE;jDA+Lu8_wO5d*Cf`%^_XU*!upeADu zl9_KnZ!tWHPB}V2%B()Bsy&_d>6K>Z?8t(^TREJA!uFf)`Gpx#EK7fSnuxCC=%HK^ zN5{1}faTL%^h8me{e_>a)$5SIfS=WWgP+)2J)3QvZs?X`Ci+ns1_vH+QQ9_lbT8tE zWwGLP2))^8HEzKgSbk?d>?=J$O{n!k6+a8$;>#{{F1>`Vyt{%ce)Bcj9u)!k9=#AA zX-b&}2BXkT`t;WwO0*zo4DB!~4~AK#TDH2>pv*1HX}680=!H+Zwel=3wbSkq80}y1ka#nrF#zzkQV&yLyq$fu dssz6@`GsqT%%B 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