Skip to content

Commit 98b05ca

Browse files
Fix resampled cooling internal energy with MHD (#1886)
## Summary - Pass face-centered MHD state into `ResampledCooling::computeCooling`. - Recover gas internal energy in the resampled cooling module as total gas energy minus kinetic and magnetic energy. - Convert `ResampledCoolingTest` to exercise the MHD path in 3D with a uniform magnetic field initialized at plasma beta ~ 1. Fixes #1885. ## Validation - `scripts/bash/quokka config -d 3d-debug` - `cmake --build build/3d-debug --target ResampledCoolingTest` - `ctest --test-dir build/3d-debug -R ResampledCoolingTest --output-on-failure` --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent d7ab681 commit 98b05ca

3 files changed

Lines changed: 54 additions & 20 deletions

File tree

src/QuokkaSimulation.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,8 +1006,8 @@ auto QuokkaSimulation<problem_t>::addStrangSplitSourcesWithBuiltin(amrex::MultiF
10061006
coolingTableType_ = "resampled";
10071007
}
10081008
if (coolingTableType_ == "resampled") {
1009-
cool_success =
1010-
quokka::ResampledCooling::computeCooling<problem_t>(state, dt, resampledTables_, tempFloor_, const_heating_rate_per_H);
1009+
cool_success = quokka::ResampledCooling::computeCooling<problem_t>(state, state_fc, dt, resampledTables_, tempFloor_,
1010+
const_heating_rate_per_H);
10111011
} else {
10121012
amrex::Abort("Invalid cooling table type! Only 'resampled' is supported.");
10131013
}

src/cooling/ResampledCooling.hpp

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
#include "math/FastMath.hpp"
2020
#include "math/ODEIntegrate.hpp"
2121
#include "math/root_finding.hpp"
22-
#include "radiation/radiation_system.hpp"
2322
#include "util/DataTable.hpp"
2423
#include <format>
2524

@@ -195,8 +194,8 @@ struct ResampledCoolingFunctor {
195194

196195
// const_heating_rate_per_H: unit erg/s/H
197196
template <typename problem_t>
198-
auto computeCooling(amrex::MultiFab &mf, const Real dt_in, resampled_tables &resampledTables, const Real temp_floor, const Real const_heating_rate_per_H)
199-
-> bool
197+
auto computeCooling(amrex::MultiFab &mf, std::array<amrex::MultiFab, AMREX_SPACEDIM> const &mf_fc, const Real dt_in, resampled_tables &resampledTables,
198+
const Real temp_floor, const Real const_heating_rate_per_H) -> bool
200199
{
201200
const BL_PROFILE("quokka::ResampledCooling::computeCooling()");
202201

@@ -219,21 +218,26 @@ auto computeCooling(amrex::MultiFab &mf, const Real dt_in, resampled_tables &res
219218
auto const &state = mf.array(iter);
220219
auto const &nsubsteps = nsubstepsMF.array(iter);
221220

221+
std::array<amrex::Array4<const amrex::Real>, AMREX_SPACEDIM> state_fc{};
222+
if constexpr (Physics_Traits<problem_t>::is_mhd_enabled) {
223+
for (int dir = 0; dir < AMREX_SPACEDIM; ++dir) {
224+
state_fc[dir] = mf_fc[dir].const_array(iter);
225+
}
226+
}
227+
222228
amrex::ParallelFor(indexRange, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
229+
// cooling function
223230
const Real rho = state(i, j, k, HydroSystem<problem_t>::density_index);
224-
const Real x1Mom = state(i, j, k, HydroSystem<problem_t>::x1Momentum_index);
225-
const Real x2Mom = state(i, j, k, HydroSystem<problem_t>::x2Momentum_index);
226-
const Real x3Mom = state(i, j, k, HydroSystem<problem_t>::x3Momentum_index);
227-
const Real Egas = state(i, j, k, HydroSystem<problem_t>::energy_index);
231+
const Real nH = rho * tables.cloudy_H_mass_fraction / C::m_p; // unit: cm^-3
232+
const ResampledCoolingFunctor user_rhs(rho, tables, const_heating_rate_per_H * nH); // unit: erg/cm^3/s
228233

229-
// number density
230-
const Real nH = rho * tables.cloudy_H_mass_fraction / C::m_p; // unit: cm^-3
234+
// state vector
235+
const Real Eint = HydroSystem<problem_t>::ComputeInternalEnergy(state, i, j, k, &state_fc);
236+
quokka::valarray<Real, 1> y = {Eint};
231237

232-
const Real Eint = RadSystem<problem_t>::ComputeEintFromEgas(rho, x1Mom, x2Mom, x3Mom, Egas);
238+
// integration tolerance
233239
const Real Eint_floor = (temp_floor > 0.0) ? ComputeEgasFromTgas(rho, temp_floor, tables) : 0.0;
234240
const Real abstol_floor = amrex::max(Eint_floor, std::numeric_limits<Real>::min());
235-
const ResampledCoolingFunctor user_rhs(rho, tables, const_heating_rate_per_H * nH); // unit: erg/cm^3/s
236-
quokka::valarray<Real, 1> y = {Eint};
237241
quokka::valarray<Real, 1> const abstol = {reltol_floor * abstol_floor};
238242

239243
// do integration with RK2 (Heun's method)

src/problems/ResampledCoolingTest/testResampledCoolingTest.cpp

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,13 @@
1515
#include "cooling/ResampledCooling.hpp"
1616
#include "math/interpolate.hpp"
1717
#include "util/BC.hpp"
18+
#include <algorithm>
19+
#include <array>
20+
#include <cmath>
1821
#include <format>
1922
#include <fstream>
2023
#include <iomanip>
24+
#include <limits>
2125
#include <sstream>
2226
#include <vector>
2327

@@ -97,14 +101,25 @@ template <> struct Physics_Traits<ResampledCoolingTest> {
97101
static constexpr bool is_radiation_enabled = false;
98102
static constexpr bool is_dust_enabled = false;
99103
static constexpr int nDustGroups = 1; // number of dust groups
100-
static constexpr bool is_mhd_enabled = false;
104+
static constexpr bool is_mhd_enabled = (AMREX_SPACEDIM == 3);
101105
static constexpr int nGroups = 1; // number of radiation groups
102106
static constexpr UnitSystem unit_system = UnitSystem::CGS;
103107
};
104108

105109
// Initial conditions: hot gas that will cool down
106110
constexpr double T_initial = 1.0e7; // K
107111
constexpr double rho_initial = 1.0e-26; // g cm^-3 (constant density for isochoric)
112+
constexpr double mu_initial = 0.6 * quokka::EOS_Traits<ResampledCoolingTest>::mean_molecular_weight;
113+
constexpr double pressure_initial = rho_initial * C::k_B * T_initial / mu_initial;
114+
constexpr double Bx_initial = 5.264491941623788e-06; // chosen so plasma beta = P_gas / (B^2 / 2) ~= 1
115+
constexpr double magnetic_energy_initial = 0.5 * Bx_initial * Bx_initial;
116+
constexpr double active_magnetic_energy_initial = Physics_Traits<ResampledCoolingTest>::is_mhd_enabled ? magnetic_energy_initial : 0.0;
117+
118+
auto computeInitialInternalEnergy() -> double
119+
{
120+
constexpr double gamma = quokka::EOS_Traits<ResampledCoolingTest>::gamma;
121+
return rho_initial * C::k_B * T_initial / ((gamma - 1.0) * mu_initial);
122+
}
108123

109124
template <> void QuokkaSimulation<ResampledCoolingTest>::setInitialConditionsOnGrid(quokka::grid const &grid_elem)
110125
{
@@ -114,25 +129,40 @@ template <> void QuokkaSimulation<ResampledCoolingTest>::setInitialConditionsOnG
114129
// Compute initial internal energy from temperature
115130
const double k_B = C::k_B;
116131
const double gamma = quokka::EOS_Traits<ResampledCoolingTest>::gamma;
117-
const double mu = 0.6 * quokka::EOS_Traits<ResampledCoolingTest>::mean_molecular_weight;
118132

119133
// For ideal gas: P = (gamma - 1) * rho * e_int
120134
// and P = rho * k_B * T / (mu * m_u)
121135
// Therefore: e_int = k_B * T / ((gamma - 1) * mu * m_u)
122-
const double e_int_initial = k_B * T_initial / ((gamma - 1.0) * mu);
136+
const double e_int_initial = k_B * T_initial / ((gamma - 1.0) * mu_initial);
123137
const double Eint_initial = rho_initial * e_int_initial;
138+
const double Egas_initial = Eint_initial + active_magnetic_energy_initial;
124139

125140
// loop over the grid and set the initial condition
126141
amrex::ParallelFor(indexRange, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
127142
state_cc(i, j, k, HydroSystem<ResampledCoolingTest>::density_index) = rho_initial;
128143
state_cc(i, j, k, HydroSystem<ResampledCoolingTest>::x1Momentum_index) = 0.;
129144
state_cc(i, j, k, HydroSystem<ResampledCoolingTest>::x2Momentum_index) = 0.;
130145
state_cc(i, j, k, HydroSystem<ResampledCoolingTest>::x3Momentum_index) = 0.;
131-
state_cc(i, j, k, HydroSystem<ResampledCoolingTest>::energy_index) = Eint_initial;
146+
state_cc(i, j, k, HydroSystem<ResampledCoolingTest>::energy_index) = Egas_initial;
132147
state_cc(i, j, k, HydroSystem<ResampledCoolingTest>::internalEnergy_index) = Eint_initial;
133148
});
134149
}
135150

151+
template <> void QuokkaSimulation<ResampledCoolingTest>::setInitialConditionsOnGridFaceVars(quokka::grid const &grid_elem)
152+
{
153+
const amrex::Array4<double> &state_fc = grid_elem.array_;
154+
const amrex::Box &indexRange = grid_elem.indexRange_;
155+
const quokka::direction dir = grid_elem.dir_;
156+
157+
amrex::ParallelFor(indexRange, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
158+
if (dir == quokka::direction::x) {
159+
state_fc(i, j, k, Physics_Indices<ResampledCoolingTest>::mhdFirstIndex) = Bx_initial;
160+
} else {
161+
state_fc(i, j, k, Physics_Indices<ResampledCoolingTest>::mhdFirstIndex) = 0.0;
162+
}
163+
});
164+
}
165+
136166
template <> void QuokkaSimulation<ResampledCoolingTest>::computeAfterTimestep()
137167
{
138168
// Extract solution at center of domain
@@ -143,8 +173,8 @@ template <> void QuokkaSimulation<ResampledCoolingTest>::computeAfterTimestep()
143173

144174
const amrex::Real Etot = values.at(HydroSystem<ResampledCoolingTest>::energy_index)[0];
145175
const amrex::Real rho = values.at(HydroSystem<ResampledCoolingTest>::density_index)[0];
146-
// For isochoric cooling with no kinetic energy, Eint = Etot
147-
const amrex::Real Eint = Etot;
176+
// For isochoric MHD cooling with no kinetic energy and a uniform magnetic field, subtract the constant magnetic energy.
177+
const amrex::Real Eint = Etot - active_magnetic_energy_initial;
148178

149179
// Get temperature from tables
150180
amrex::Real T = NAN;

0 commit comments

Comments
 (0)