Skip to content

Commit

Permalink
A robust numerical scheme for the dust-gas coupling model (#733)
Browse files Browse the repository at this point in the history
### Description
In this PR, we solve the dust coupling to the gas and radiation in two
different regimes: the well-coupled regime and the low-density,
poorly-coupled regime.

#### Changes to the code

- Defined `ComputeJacobianForGasAndDustDecoupled` to compute the
Jacobian for the gas-dust-radiation system in the regime where gas and
dust are decoupled.
- Added a new parameter `lambda_gd_times_dt` to
`SolveMatterRadiationEnergyExchange`. `lambda_gd_times_dt` is
$\Lambda_{\rm gd} \Delta t$, which is used to update gas energy as well
as constraining the sum of $R_g$.
- Added a new parameter `dust_model` to
`SolveMatterRadiationEnergyExchange`. Unfortunately, the energy-exchange
system becomes complicated and you can't deal with all three dust models
by passing functions to `SolveMatterRadiationEnergyExchange`.
`dust_model` takes three values, representing a gas-dust-radiation
system in different regimes:
- 0: dust and gas are perfectly coupled. In this case, `T_d = T_gas`,
and we solve a system that involves T_gas and Erad. This is the model
used in Wibking & Krumholz (2022), He, Wibking & Krumholz (2024a) and
He, Wibking & Krumholz (2024b).
- 1: dust and gas are well coupled. In this case, we solve a system that
involves T_gas, T_d, and Erad, where T_d is used as an implicit,
dependent variable.
- 2: dust and gas are poorly coupled. In this case, we solve a system
that involves only T_d and Erad.
- `dust_model = 0` is enabled by setting
`enable_dust_gas_thermal_coupling_model_ = false`. Otherwise,
`dust_model = 1` or `dust_model = 2` is determined automatically in the
code on a cell-by-cell basis. See the description below.

#### Alternative things to do

- Make `threshold_for_dust_gas_coupling` a runtime parameter. This is
the parameter to control when to switch between dust model 1 and 2.

#### Physics

In the well-coupled regime, we solve the following equations: 

$$
\begin{split}
\frac{\partial E_{\rm gas}}{\partial t} = - \Lambda_{{\rm gd}} \\
\frac{\partial E_g}{\partial t} = \hat{c} \left[ \chi_B \frac{4 \pi}{c}
B_g(T_d) - \chi_E E_g \right]
\end{split}
$$

where $\Lambda_{\mathrm{gd}}$ is the thermal interaction rate due to
collisions between the gas and the dust, which we take the rate of Keto
& Field (2005)

$$
\Lambda_{{\rm gd}} = \Theta_{{\rm gd}} ~n_{\mathrm{H}}^2 T^{1 /
2}\left(T-T_{{d}}\right) \quad{} (1)
$$

where $\Theta_{\mathrm{gd}} = 2.5 \times 10^{-34} \mathrm{erg \ cm^3 \
s^{-1} \ K^{-3/2}}$. This parameter can be specified in arbitrary units
by the user. As in most literature, we assume that the dust is in LTE
with the total radiation field (e.g. Bate & Keto 2015):

$$
c \sum_g \left[ \chi_B \frac{4 \pi}{c} B_g(T_d) - \chi_E E_g \right] =
\Lambda_{{\rm gd}}. \quad{} (2)
$$

We can then define a similar set of independent variables as in the IMEX
paper, $(E_{\rm gas}, R_g), g = 1, 2, ..., N_g$, where

$$
R_g \equiv \hat{c} \Delta t \left[ \chi_B \frac{4 \pi}{c} B_g(T_d) -
\chi_E E_g \right], \quad{} (3)
$$

and solve for the following set of equations using Newton-Raphson
iteration:

$$
\begin{split}
0 = F_G \equiv E_{\rm gas} - E_{\rm gas}^0 + \frac{c}{\hat{c}} \sum_g
R_g,\\
0 = F_{R,g} \equiv E_g - E_g^0 - R_g
\end{split}
$$

This is similar to the original gas-only scheme, except that the gas
temperature $T$ is replaced with $T_d$.

We still need to calculate the dust temperature $T_d$. In the first step
of the iteration, we solve for $T_d$ given $T$ and $E_g$ via combining
Equation (1) and (2). Then, later in the iteration, we solve for $T_d$
via a simple relation:

$$
\sum_g R_g = \frac{\hat{c}}{c} \Delta t \ \Lambda_{\rm gd} =
\frac{\hat{c}}{c} \Delta t \ \Theta_{{\rm gd}} ~n_{\mathrm{H}}^2 T^{1 /
2}\left(T-T_{{d}}\right) = N_{\rm gd} T^{1/2}(T - T_d) \quad{} (4)
$$

where we have defined $N_{\rm gd} \equiv (\hat{c}/c) \Delta t
\Theta_{\rm gd} n_H^2$. Apparently,

$$
T_d = T - \frac{\sum_g R_g}{N_{\rm gd} \sqrt{T}}. \quad{} (5)
$$

Equation (4) also gives a simple formula for $d T_d / d T$, which is
used in the computation of the Jacobian. Further, the Jacobian is no
longer sparse. However, I did a trick to analytically simplify the
Jacobian matrix via Gauss-Jordan elimination to make it sparse so that
we can use the numerical scheme in the multigroup paper. More details in
Overleaf document "quokka-thermochemistry".

In the decoupled regime where $N_{\rm gd}$ is small, however, this
scheme failed. This is obvious from Equation (5): even a small value of
$\sum_g R_g$ will result in a large, negative $T_d$. This is unavoidable
in the Newton-Raphson step; the valid domain for $\sum_g R_g$ is tiny:
$[0, N_{\rm gd} T^{3/2})$. Here I'm considering the case where the
radiation temperature is well below the gas temperature, so $T \gg T_r
\approx T_d$, therefore $\Lambda_{\rm gd} \ge 0$.

After extensive exploration, I constructed the following scheme for the
decoupled regime. First, we define the decoupled regime as when
$\max(N_{\rm gd} T^{3/2}, N_{\rm gd} T_d^{3/2}) < 10^{-6} \max(E_{\rm
gas}, \sum_g R_g)$. Since the gas temperature won't change much, it can
be updated via a simple forward Euler step of the following equation:

$$
C_V \frac{d T}{dt} = - \Lambda_{\rm gd} = - \Theta_{{\rm gd}}
~n_{\mathrm{H}}^2 \ T^{1 / 2}\left(T-T_{{d}}\right) \quad{} (6)
$$

One can also choose to solve this analytically. Thus we have obtained
$\Lambda_{\rm gd} \Delta t$.

Then, we want to deal with energy exchange between radiation groups, to
capture dust absorbing UV radiation and re-emitting in IR. At first, I
tried to update $E_g$ with fixed $T_d$, by solving

$$
E_g - E_g^0 = c \Delta t \ \chi_g (B_g(T_d) - E_g).
$$

This can be solved with simple algebra. However, I have shown that the
resulting $\sum_g R_g$ (the sum of the RHS over groups) does not
necessarily equal to the $\Lambda_{\rm gd} \Delta t$ used previously to
update gas temperature. This results in the failure of energy
conservation. To enforce $\sum_g R_g = \Lambda_{\rm gd} \Delta t$, we
have to include $T_d$ as an independent variable; otherwise, the system
is over-determined.

Then, here is the new scheme that I have shown to be very robust. After
updating $T$ via Equation (6), we solve the updated $T_d$ and $E_g$ via
the following set of equations:

$$
\begin{split}
\sum_g R_g = \Lambda_{\rm gd} \Delta t, \\
E_g - E_g^0 = R_g.
\end{split}
$$

We solve this equation via Newton-Raphson iteration on the base variable
$(T_d, R_g)$.

$$
\begin{split}
0 = F_0 = \sum_g R_g - \Lambda_{\rm gd} \Delta t \\
0 = F_g = E_g - E_g^0 - R_g
\end{split}
$$

and the Jacobian is 

$$
\begin{split}
J_{00} = 0 \\
J_{0g} = 1 \\
J_{g0} = \frac{\partial E_g}{\partial T_d} =
\frac{\chi_{B,g}}{\chi_{E,g}} \frac{\partial B_g(T_d)}{\partial T_d} \\
J_{gg} = \frac{\partial E_g}{\partial R_g} - 1 = -
\frac{\chi_{B,g}}{\chi_{E,g}} \frac{1}{\hat{c} \chi_{\rm B,g} \Delta t}
- 1
\end{split}
$$

This set of equations is very similar to the previous ones, so we can
use the same routine with some switches to reset different Jacobian for
different regimes. After this system is converged, the whole iteration
is done and energy is guaranteed to be conserved (to the degree of how
well the Newton-Raphson iteration converges).

#### Tests

`RadMarshakDust` is a Marshak wave problem where FUV radiation is
streaming from the left boundary, gets absorbed by dust which then
radiates in IR. The IR opacity is set to a large number so that
diffusion is extremely slow. I compare the (semi-)exact analytic
solution with the numerical calculation and find perfect agreement.
Figure shown below:

![CleanShot 2024-09-02 at 17 03
24](https://github.com/user-attachments/assets/cbc2b4d8-ea5f-45c8-8647-37fc69f54a8e)


### Related issues
None.

### Checklist
_Before this pull request can be reviewed, all of these tasks should be
completed. Denote completed tasks with an `x` inside the square brackets
`[ ]` in the Markdown source below:_
- [x] I have added a description (see above).
- [x] I have added a link to any related issues see (see above).
- [x] I have read the [Contributing
Guide](https://github.com/quokka-astro/quokka/blob/development/CONTRIBUTING.md).
- [x] I have added tests for any new physics that this PR adds to the
code.
- [x] I have tested this PR on my local computer and all tests pass.
- [x] I have manually triggered the GPU tests with the magic comment
`/azp run`.
- [x] I have requested a reviewer for this PR.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
chongchonghe and pre-commit-ci[bot] authored Sep 9, 2024
1 parent 4a7057f commit 2d273a2
Show file tree
Hide file tree
Showing 5 changed files with 298 additions and 124 deletions.
126 changes: 97 additions & 29 deletions src/problems/RadMarshakDust/test_radiation_marshak_dust.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,32 @@
struct StreamingProblem {
};

constexpr double c = 1.0; // speed of light
constexpr double chat = 1.0; // reduced speed of light
constexpr double kappa0 = 10.0; // opacity
AMREX_GPU_MANAGED double kappa1 = NAN; // dust opacity at IR
AMREX_GPU_MANAGED double kappa2 = NAN; // dust opacity at FUV

constexpr bool dust_on = true;

constexpr double c = 1.0; // speed of light
constexpr double chat = 1.0; // reduced speed of light
constexpr double rho0 = 1.0;
constexpr double CV = 1.0;
constexpr double mu = 1.5 / CV; // mean molecular weight
constexpr double initial_T = 1.0;
constexpr double a_rad = 1.0e10;
constexpr double erad_floor = 1.0e-10;
constexpr double initial_Erad = erad_floor;
constexpr double initial_Trad = 1.0e-5;
constexpr double T_rad_L = 1.0e-2; // so EradL = 1e2
constexpr double EradL = a_rad * T_rad_L * T_rad_L * T_rad_L * T_rad_L;
// constexpr double T_end_exact = 0.0031597766719577; // dust off; solution of 1 == a_rad * T^4 + T
constexpr double T_end_exact = initial_T; // dust on

// constexpr int n_group_ = 1;
// static constexpr amrex::GpuArray<double, n_group_ + 1> radBoundaries_{1e-10, 1e4};
// static constexpr OpacityModel opacity_model_ = OpacityModel::single_group;
constexpr int n_group_ = 2;
static constexpr amrex::GpuArray<double, n_group_ + 1> radBoundaries_{1e-10, 100, 1e4};
static constexpr OpacityModel opacity_model_ = OpacityModel::piecewise_constant_opacity;

template <> struct quokka::EOS_Traits<StreamingProblem> {
static constexpr double mean_molecular_weight = mu;
static constexpr double boltzmann_constant = 1.0;
Expand All @@ -45,7 +56,7 @@ template <> struct Physics_Traits<StreamingProblem> {
static constexpr bool is_radiation_enabled = true;
// face-centred
static constexpr bool is_mhd_enabled = false;
static constexpr int nGroups = 1; // number of radiation groups
static constexpr int nGroups = n_group_; // number of radiation groups
};

template <> struct RadSystem_Traits<StreamingProblem> {
Expand All @@ -54,31 +65,51 @@ template <> struct RadSystem_Traits<StreamingProblem> {
static constexpr double radiation_constant = a_rad;
static constexpr double Erad_floor = erad_floor;
static constexpr int beta_order = 0;
static constexpr bool enable_dust_gas_thermal_coupling_model = true;
static constexpr bool enable_dust_gas_thermal_coupling_model = dust_on;
static constexpr double energy_unit = 1.0;
static constexpr amrex::GpuArray<double, n_group_ + 1> radBoundaries = radBoundaries_;
static constexpr OpacityModel opacity_model = opacity_model_;
};

template <> AMREX_GPU_HOST_DEVICE auto RadSystem<StreamingProblem>::ComputePlanckOpacity(const double /*rho*/, const double /*Tgas*/) -> amrex::Real
{
return kappa0;
return kappa1;
}

template <> AMREX_GPU_HOST_DEVICE auto RadSystem<StreamingProblem>::ComputeFluxMeanOpacity(const double /*rho*/, const double /*Tgas*/) -> amrex::Real
{
return kappa0;
return kappa1;
}

template <>
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE auto
RadSystem<StreamingProblem>::DefineOpacityExponentsAndLowerValues(amrex::GpuArray<double, nGroups_ + 1> /*rad_boundaries*/, const double /*rho*/,
const double /*Tgas*/) -> amrex::GpuArray<amrex::GpuArray<double, nGroups_ + 1>, 2>
{
amrex::GpuArray<amrex::GpuArray<double, nGroups_ + 1>, 2> exponents_and_values{};
for (int i = 0; i < nGroups_ + 1; ++i) {
exponents_and_values[0][i] = 0.0;
if (i == 0) {
exponents_and_values[1][i] = kappa1;
} else {
exponents_and_values[1][i] = kappa2;
}
}
return exponents_and_values;
}

template <> void QuokkaSimulation<StreamingProblem>::setInitialConditionsOnGrid(quokka::grid const &grid_elem)
{
const amrex::Box &indexRange = grid_elem.indexRange_;
const amrex::Array4<double> &state_cc = grid_elem.array_;

const auto Erad0 = initial_Erad;
const auto Egas0 = initial_T * CV;
const auto Erads = RadSystem<StreamingProblem>::ComputeThermalRadiationMultiGroup(initial_Trad, radBoundaries_);

// loop over the grid and set the initial condition
amrex::ParallelFor(indexRange, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
for (int g = 0; g < Physics_Traits<StreamingProblem>::nGroups; ++g) {
state_cc(i, j, k, RadSystem<StreamingProblem>::radEnergy_index + Physics_NumVars::numRadVars * g) = Erad0;
state_cc(i, j, k, RadSystem<StreamingProblem>::radEnergy_index + Physics_NumVars::numRadVars * g) = Erads[g];
state_cc(i, j, k, RadSystem<StreamingProblem>::x1RadFlux_index + Physics_NumVars::numRadVars * g) = 0;
state_cc(i, j, k, RadSystem<StreamingProblem>::x2RadFlux_index + Physics_NumVars::numRadVars * g) = 0;
state_cc(i, j, k, RadSystem<StreamingProblem>::x3RadFlux_index + Physics_NumVars::numRadVars * g) = 0;
Expand Down Expand Up @@ -114,16 +145,18 @@ AMRSimulation<StreamingProblem>::setCustomBoundaryConditions(const amrex::IntVec
amrex::Box const &box = geom.Domain();
amrex::GpuArray<int, 3> lo = box.loVect3d();

// const auto Erads = RadSystem<StreamingProblem>::ComputeThermalRadiation(T_rad_L, radBoundaries_);
quokka::valarray<double, 2> const Erads = {erad_floor, EradL};
const double c_light = c;
const auto Frads = Erads * c_light;

if (i < lo[0]) {
// streaming inflow boundary
const double Erad = EradL;
const double Frad = c * Erad;

// multigroup radiation
// x1 left side boundary (Marshak)
for (int g = 0; g < Physics_Traits<StreamingProblem>::nGroups; ++g) {
consVar(i, j, k, RadSystem<StreamingProblem>::radEnergy_index + Physics_NumVars::numRadVars * g) = Erad;
consVar(i, j, k, RadSystem<StreamingProblem>::x1RadFlux_index + Physics_NumVars::numRadVars * g) = Frad;
consVar(i, j, k, RadSystem<StreamingProblem>::radEnergy_index + Physics_NumVars::numRadVars * g) = Erads[g];
consVar(i, j, k, RadSystem<StreamingProblem>::x1RadFlux_index + Physics_NumVars::numRadVars * g) = Frads[g];
consVar(i, j, k, RadSystem<StreamingProblem>::x2RadFlux_index + Physics_NumVars::numRadVars * g) = 0;
consVar(i, j, k, RadSystem<StreamingProblem>::x3RadFlux_index + Physics_NumVars::numRadVars * g) = 0;
}
Expand All @@ -148,6 +181,11 @@ auto problem_main() -> int
const double dt_max = 1;
const int max_timesteps = 5000;

// read user parameters
amrex::ParmParse pp("problem");
pp.query("kappa1", kappa1);
pp.query("kappa2", kappa2);

// Boundary conditions
constexpr int nvars = RadSystem<StreamingProblem>::nvar_;
amrex::Vector<amrex::BCRec> BCs_cc(nvars);
Expand Down Expand Up @@ -181,58 +219,88 @@ auto problem_main() -> int
const int nx = static_cast<int>(position.size());

// compute error norm
std::vector<double> erad(nx);
std::vector<double> xs(nx);
std::vector<double> T(nx);
std::vector<double> T_exact(nx);
std::vector<double> xs(nx);
std::vector<double> erad(nx);
std::vector<double> erad1(nx);
std::vector<double> erad2(nx);
std::vector<double> erad1_exact(nx);
std::vector<double> erad2_exact(nx);
for (int i = 0; i < nx; ++i) {
amrex::Real const x = position[i];
xs.at(i) = x;
double erad_sim = 0.0;
for (int g = 0; g < Physics_Traits<StreamingProblem>::nGroups; ++g) {
erad_sim += values.at(RadSystem<StreamingProblem>::radEnergy_index + Physics_NumVars::numRadVars * g)[i];
erad1.at(i) = values.at(RadSystem<StreamingProblem>::radEnergy_index + Physics_NumVars::numRadVars * 0)[i];
erad.at(i) = erad1.at(i);
if (n_group_ > 1) {
erad2.at(i) = values.at(RadSystem<StreamingProblem>::radEnergy_index + Physics_NumVars::numRadVars * 1)[i];
erad.at(i) += erad2.at(i);
}
erad.at(i) = erad_sim;
const double e_gas = values.at(RadSystem<StreamingProblem>::gasInternalEnergy_index)[i];
T.at(i) = quokka::EOS<StreamingProblem>::ComputeTgasFromEint(rho0, e_gas);
T_exact.at(i) = T_end_exact;

erad2_exact.at(i) = x < sim.tNew_[0] ? EradL * std::exp(-x * rho0 * kappa2) : erad_floor;
erad1_exact.at(i) = x < sim.tNew_[0] ? EradL * std::exp(-x * rho0 * kappa2) * (sim.tNew_[0] - x) : erad_floor;
}

double err_norm = 0.;
double sol_norm = 0.;
for (int i = 0; i < nx; ++i) {
for (int i = 1; i < nx; ++i) { // skip the first cell
err_norm += std::abs(T[i] - T_exact[i]);
err_norm += std::abs(erad1[i] - erad1_exact[i]);
err_norm += std::abs(erad2[i] - erad2_exact[i]);
sol_norm += std::abs(T_exact[i]);
sol_norm += std::abs(erad1_exact[i]);
sol_norm += std::abs(erad2_exact[i]);
}

const double rel_err_norm = err_norm / sol_norm;
const double rel_err_tol = 0.02;
const double rel_err_tol = 0.01;
int status = 1;
if (rel_err_norm < rel_err_tol) {
status = 0;
}
amrex::Print() << "Relative L1 norm = " << rel_err_norm << std::endl;

#ifdef HAVE_PYTHON
// Plot results
// Plot erad1
matplotlibcpp::clf();
std::map<std::string, std::string> plot_args;
std::map<std::string, std::string> plot_args2;
plot_args["label"] = "numerical solution";
matplotlibcpp::plot(xs, erad, plot_args);
plot_args2["label"] = "exact solution";
matplotlibcpp::plot(xs, erad1, plot_args);
matplotlibcpp::plot(xs, erad1_exact, plot_args2);
matplotlibcpp::xlabel("x");
matplotlibcpp::ylabel("E_rad");
matplotlibcpp::ylabel("E_rad_group1");
matplotlibcpp::legend();
matplotlibcpp::title(fmt::format("t = {:f}", sim.tNew_[0]));
matplotlibcpp::title(fmt::format("Marshak_dust test at t = {:.1f}", sim.tNew_[0]));
matplotlibcpp::tight_layout();
matplotlibcpp::save("./radiation_marshak_dust_Erad.pdf");
matplotlibcpp::save("./radiation_marshak_dust_Erad1.pdf");

// Plot erad2
if (n_group_ > 1) {
matplotlibcpp::clf();
matplotlibcpp::plot(xs, erad2, plot_args);
matplotlibcpp::plot(xs, erad2_exact, plot_args2);
matplotlibcpp::xlabel("x");
matplotlibcpp::ylabel("E_rad_group2");
matplotlibcpp::legend();
matplotlibcpp::title(fmt::format("Marshak_dust test at t = {:.1f}", sim.tNew_[0]));
matplotlibcpp::tight_layout();
matplotlibcpp::save("./radiation_marshak_dust_Erad2.pdf");
}

// plot temperature
matplotlibcpp::clf();
matplotlibcpp::ylim(0.0, 1.1);
matplotlibcpp::plot(xs, T, plot_args);
matplotlibcpp::plot(xs, T_exact, plot_args2);
matplotlibcpp::xlabel("x");
matplotlibcpp::ylabel("Temperature");
matplotlibcpp::title(fmt::format("t = {:f}", sim.tNew_[0]));
matplotlibcpp::legend();
matplotlibcpp::title(fmt::format("Marshak_dust test at t = {:.1f}", sim.tNew_[0]));
matplotlibcpp::tight_layout();
matplotlibcpp::save("./radiation_marshak_dust_temperature.pdf");
#endif // HAVE_PYTHON
Expand Down
9 changes: 6 additions & 3 deletions src/radiation/planck_integral.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,13 @@ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE auto interpolate_planck_integral(Real l
const int j = static_cast<int>((logx - LOG_X_MIN) / (LOG_X_MAX - LOG_X_MIN) * (arr_len - 1));
const Real gap = (LOG_X_MAX - LOG_X_MIN) / (arr_len - 1);

Real y = NAN;
if ((j < 0) || (j >= arr_len - 1)) {
return y;
if (j < 0) {
return 0.0;
}
if (j >= arr_len - 1) {
return 1.0;
}
Real y = NAN;
if constexpr (!USE_SECOND_ORDER) {
// linear interpolation
const Real slope = (Y_interp[j + 1] - Y_interp[j]) / gap;
Expand Down
52 changes: 29 additions & 23 deletions src/radiation/radiation_system.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ template <typename problem_t> struct RadSystem_Traits {
static constexpr OpacityModel opacity_model = OpacityModel::single_group;
};

// this struct is specialized by the user application code
//
template <typename problem_t> struct ISM_Traits {
static constexpr double gas_dust_coupling_threshold = 1.0e-6;
};

// A struct to hold the results of the ComputeRadPressure function.
struct RadPressureResult {
quokka::valarray<double, 4> F; // components of radiation pressure tensor
Expand Down Expand Up @@ -297,8 +303,7 @@ template <typename problem_t> class RadSystem : public HyperbolicSystem<problem_
AMREX_GPU_DEVICE static auto
ComputeDustTemperatureBateKeto(double T_gas, double T_d_init, double rho, quokka::valarray<double, nGroups_> const &Erad, double N_d, double dt,
double R_sum, int n_step,
amrex::GpuArray<double, nGroups_ + 1> const &rad_boundaries = amrex::GpuArray<double, nGroups_ + 1>{},
amrex::GpuArray<double, nGroups_> const &rad_boundary_ratios = amrex::GpuArray<double, nGroups_>{}) -> double;
amrex::GpuArray<double, nGroups_ + 1> const &rad_boundaries = amrex::GpuArray<double, nGroups_ + 1>{}) -> double;

AMREX_GPU_DEVICE static auto
ComputeDustTemperatureGasOnly(double T_gas, double T_d_init, double rho, quokka::valarray<double, nGroups_> const &Erad, double N_d, double dt,
Expand All @@ -308,25 +313,29 @@ template <typename problem_t> class RadSystem : public HyperbolicSystem<problem_

AMREX_GPU_DEVICE static auto ComputeJacobianForGas(double T_gas, double T_d, double Egas_diff, quokka::valarray<double, nGroups_> const &Erad_diff,
quokka::valarray<double, nGroups_> const &Rvec, quokka::valarray<double, nGroups_> const &Src,
double coeff_n, quokka::valarray<double, nGroups_> const &tau, double c_v,
double coeff_n, quokka::valarray<double, nGroups_> const &tau, double c_v, double lambda_gd_time_dt,
quokka::valarray<double, nGroups_> const &kappaPoverE,
quokka::valarray<double, nGroups_> const &d_fourpiboverc_d_t) -> JacobianResult<problem_t>;

AMREX_GPU_DEVICE static auto ComputeJacobianForGasAndDust(double T_gas, double T_d, double Egas_diff,
quokka::valarray<double, nGroups_> const &Erad_diff,
quokka::valarray<double, nGroups_> const &Rvec, quokka::valarray<double, nGroups_> const &Src,
double coeff_n, quokka::valarray<double, nGroups_> const &tau, double c_v,
quokka::valarray<double, nGroups_> const &kappaPoverE,
double lambda_gd_time_dt, quokka::valarray<double, nGroups_> const &kappaPoverE,
quokka::valarray<double, nGroups_> const &d_fourpiboverc_d_t) -> JacobianResult<problem_t>;

template <typename JacobianFunc, typename DustTempFunc>
AMREX_GPU_DEVICE static auto ComputeJacobianForGasAndDustDecoupled(
double T_gas, double T_d, double Egas_diff, quokka::valarray<double, nGroups_> const &Erad_diff, quokka::valarray<double, nGroups_> const &Rvec,
quokka::valarray<double, nGroups_> const &Src, double coeff_n, quokka::valarray<double, nGroups_> const &tau, double c_v, double lambda_gd_time_dt,
quokka::valarray<double, nGroups_> const &kappaPoverE, quokka::valarray<double, nGroups_> const &d_fourpiboverc_d_t) -> JacobianResult<problem_t>;

template <typename JacobianFunc>
AMREX_GPU_DEVICE static auto
SolveMatterRadiationEnergyExchange(double Egas0, quokka::valarray<double, nGroups_> const &Erad0Vec, double rho, double coeff_n, double dt,
amrex::GpuArray<Real, nmscalars_> const &massScalars, int n_outer_iter,
quokka::valarray<double, nGroups_> const &work, quokka::valarray<double, nGroups_> const &vel_times_F,
quokka::valarray<double, nGroups_> const &Src, amrex::GpuArray<double, nGroups_ + 1> const &radBoundaries_g_copy,
amrex::GpuArray<double, nGroups_> const &radBoundaryRatios_copy, JacobianFunc ComputeJacobian,
DustTempFunc ComputeDustTemperature, int *p_iteration_counter,
SolveMatterRadiationEnergyExchange(double Egas0, quokka::valarray<double, nGroups_> const &Erad0Vec, double rho, double T_d0, int dust_model,
double coeff_n, double lambda_gd_times_dt, double dt, amrex::GpuArray<Real, nmscalars_> const &massScalars,
int n_outer_iter, quokka::valarray<double, nGroups_> const &work,
quokka::valarray<double, nGroups_> const &vel_times_F, quokka::valarray<double, nGroups_> const &Src,
amrex::GpuArray<double, nGroups_ + 1> const &rad_boundaries, JacobianFunc ComputeJacobian, int *p_iteration_counter,
int *p_iteration_failure_counter) -> NewtonIterationResult<problem_t>;

template <FluxDir DIR>
Expand Down Expand Up @@ -1258,26 +1267,23 @@ AMREX_GPU_HOST_DEVICE auto RadSystem<problem_t>::ComputeFluxInDiffusionLimit(con
return flux;
}

template <typename problem_t>
AMREX_GPU_DEVICE auto RadSystem<problem_t>::ComputeDustTemperatureGasOnly(double const T_gas, double const /*T_d_init*/, double const /*rho*/,
quokka::valarray<double, nGroups_> const & /*Erad*/, double /*N_d*/, double /*dt*/,
double /*R_sum*/, int /*n_step*/,
amrex::GpuArray<double, nGroups_ + 1> const & /*rad_boundaries*/,
amrex::GpuArray<double, nGroups_> const & /*rad_boundary_ratios*/) -> double
{
return T_gas;
}

template <typename problem_t>
AMREX_GPU_DEVICE auto RadSystem<problem_t>::ComputeDustTemperatureBateKeto(double const T_gas, double const T_d_init, double const rho,
quokka::valarray<double, nGroups_> const &Erad, double N_d, double dt, double R_sum,
int n_step, amrex::GpuArray<double, nGroups_ + 1> const &rad_boundaries,
amrex::GpuArray<double, nGroups_> const &rad_boundary_ratios) -> double
int n_step, amrex::GpuArray<double, nGroups_ + 1> const &rad_boundaries) -> double
{
if (n_step > 0) {
return T_gas - R_sum / (N_d * std::sqrt(T_gas));
}

amrex::GpuArray<double, nGroups_> rad_boundary_ratios{};

if constexpr (nGroups_ > 1 && opacity_model_ != OpacityModel::piecewise_constant_opacity) {
for (int g = 0; g < nGroups_; ++g) {
rad_boundary_ratios[g] = rad_boundaries[g + 1] / rad_boundaries[g];
}
}

quokka::valarray<double, nGroups_> kappaPVec{};
quokka::valarray<double, nGroups_> kappaEVec{};

Expand Down
Loading

0 comments on commit 2d273a2

Please sign in to comment.