13 #include <yaml-cpp/yaml.h>
16 #include <athena/athena.hpp>
17 #include <athena/eos/eos.hpp>
18 #include <athena/hydro/hydro.hpp>
19 #include <athena/mesh/mesh.hpp>
20 #include <athena/parameter_input.hpp>
23 #include <application/application.hpp>
24 #include <application/exceptions.hpp>
28 #include <configure.hpp>
48 Application::Logger app(
"snap");
49 app->Log(
"Destroy Thermodynamics");
59 if (NCLOUD < (
NPHASE - 1) * NVAPOR) {
62 "NCLOUD < (NPHASE-1)*NVAPOR is not supported for legacy input");
86 for (
int i = 0; i <= NVAPOR; ++i) {
94 mythermo_->
Rd_ = pin->GetOrAddReal(
"thermodynamics",
"Rd", 1.);
113 throw RuntimeError(
"Thermodynamics",
"Thermodynamics has been initialized");
116 Application::Logger app(
"snap");
117 app->Log(
"Initialize Thermodynamics");
119 if (pin->DoesParameterExist(
"thermodynamics",
"thermo_file")) {
120 std::string filename = pin->GetString(
"thermodynamics",
"thermo_file");
121 std::ifstream stream(filename);
122 if (stream.good() ==
false) {
123 throw RuntimeError(
"Thermodynamics",
124 "Cannot open thermodynamic file: " + filename);
161 for (
int n = 0; n < mu_ratio.size(); ++n) {
162 inv_mu_ratio[n] = 1. / mu_ratio[n];
163 mu[n] = mu[0] * mu_ratio[n];
164 inv_mu[n] = 1. / mu[n];
165 cp_ratio_mole[n] = cp_ratio_mass[n] * mu_ratio[n];
169 for (
int n = 0; n <= NVAPOR; ++n) {
170 latent_energy_mass[n] = 0.;
171 latent_energy_mole[n] = 0.;
174 for (
int i = 1; i <= NVAPOR; ++i) {
176 int n = cloud_index_set[i][
j] + 1 + NVAPOR;
177 latent_energy_mass[n] = beta[n] *
Rd / mu_ratio[n] * t3[i];
178 latent_energy_mole[n] = latent_energy_mass[n] * mu[n];
183 for (
int n = 0; n <= NVAPOR; ++n) delta[n] = 0.;
184 for (
int i = 1; i <= NVAPOR; ++i) {
185 for (
int j = 0;
j < cloud_index_set[i].size(); ++
j) {
186 int n = cloud_index_set[i][
j] + 1 + NVAPOR;
187 delta[n] = (cp_ratio_mass[n] - cp_ratio_mass[i]) * mu_ratio[i] /
195 for (
int n = (
NPHASE - 1) * NVAPOR; n < NCLOUD; ++n) {
199 int j = 1 + NVAPOR + n;
201 inv_mu[
j] = 1. / mu[
j];
203 mu_ratio[
j] = mol.
mu() / mu[0];
204 inv_mu_ratio[
j] = 1. / mu_ratio[
j];
207 cp_ratio_mole[
j] = cp_ratio_mass[
j] * mu_ratio[
j];
209 latent_energy_mole[
j] = mol.
latent();
210 latent_energy_mass[
j] = mol.
latent() / mol.
mu();
218 for (
int n = 0; n <= NVAPOR; ++n) {
219 cv_ratio_mass[n] = gammad * cp_ratio_mass[n] + (1. - gammad) / mu_ratio[n];
220 cv_ratio_mole[n] = cv_ratio_mass[n] * mu_ratio[n];
223 for (
int n = 1 + NVAPOR; n <
Size; ++n) {
224 cv_ratio_mass[n] = gammad * cp_ratio_mass[n];
225 cv_ratio_mole[n] = cv_ratio_mass[n] * mu_ratio[n];
230 pin->GetOrAddReal(
"thermodynamics",
"sa.max_iter", 10);
247 Real gm1 = pmb->peos->GetGamma() - 1;
248 auto& u = pmb->phydro->u;
250 Real rho = 0., fsig = 0., feps = 0.;
251 #pragma omp simd reduction(+ : rho, fsig, feps)
252 for (
int n = 0; n <= NVAPOR; ++n) {
253 rho += u(n, k,
j, i);
261 (
sqr(u(IM1, k,
j, i)) +
sqr(u(IM2, k,
j, i)) +
sqr(u(IM3, k,
j, i))) /
263 return gm1 * (u(IEN, k,
j, i) - KE) * feps / fsig;
268 Real gammad = pmb->peos->GetGamma();
269 auto& w = pmb->phydro->w;
271 Real qsig = 1., feps = 1.;
272 #pragma omp simd reduction(+ : qsig, feps)
273 for (
int n = 1; n <= NVAPOR; ++n) {
278 return (gammad - 1.) / gammad * feps / qsig;
285 Real qsig = 1., feps = 1.;
286 #pragma omp simd reduction(+ : qsig)
287 for (
int n = 1; n <= NVAPOR; ++n) {
291 #pragma omp simd reduction(+ : qsig, feps)
292 for (
int n = 0; n < NCLOUD; ++n) {
297 return (gammad - 1.) / gammad / qsig;
302 Real gammad = pmb->peos->GetGamma();
306 Real fsig = 1., feps = 1.;
307 #pragma omp simd reduction(+ : fsig, feps)
308 for (
int n = 1; n <= NVAPOR; ++n) {
313 for (
int n = 0; n < NCLOUD; ++n) {
318 return 1. + (gammad - 1.) * feps / fsig;
323 Real fgas = 1., feps = 1.;
325 #pragma omp simd reduction(+ : feps)
326 for (
int n = 1; n <= NVAPOR; ++n) {
330 #pragma omp simd reduction(+ : fgas, feps)
331 for (
int n = 0; n < NCLOUD; ++n) {
340 int j,
int i)
const {
342 air.ToMassConcentration();
345 Real rho = 0., LE = 0., IE = 0.;
347 #pragma omp simd reduction(+ : IE, rho)
348 for (
int n = 0; n <= NVAPOR; ++n) {
353 #pragma omp simd reduction(+ : IE, LE, rho)
354 for (
int n = 0; n < NCLOUD; ++n) {
360 return (IE + LE) / rho +
gz;
366 Real gammad = pmb->peos->GetGamma();
367 auto& w = pmb->phydro->w;
370 #pragma omp simd reduction(+ : qsig)
371 for (
int n = 1; n <= NVAPOR; ++n)
373 return gammad / (gammad - 1.) *
Rd_ * qsig;
379 Real gammad = pmb->peos->GetGamma();
380 auto& w = pmb->phydro->w;
383 #pragma omp simd reduction(+ : qsig)
384 for (
int n = 1; n <= NVAPOR; ++n)
386 return 1. / (gammad - 1.) *
Rd_ * qsig;
391 Real gammad = pmb->peos->GetGamma();
392 auto& w = pmb->phydro->w;
395 #pragma omp simd reduction(+ : qsig)
396 for (
int n = 1; n <= NVAPOR; ++n)
398 return gammad / (gammad - 1.) *
Rd_ * qsig * w(IDN, k,
j, i);
402 auto& w = pmb->phydro->w;
405 #pragma omp simd reduction(+ : feps)
406 for (
int n = 1; n <= NVAPOR; ++n) feps += w(n, k,
j, i) * (
mu_ratio_[n] - 1.);
407 return mu_[0] * feps;
413 air.ToMoleFraction();
418 Real grav, Real userp)
const {
422 for (
int i = 1; i <= NVAPOR; ++i) {
426 qfrac->
w[i] += rates[0];
429 for (
int j = 1;
j < rates.size(); ++
j)
443 if (std::abs(rates[0]) < 1.E-8)
return 0.;
446 for (
int j = 1;
j < rates.size(); ++
j) {
451 return heat / std::abs(rates[0]);
456 int k,
int j,
int i)
const {
464 Real xg = 1., qd = 1.;
465 #pragma omp simd reduction(+ : xg, qd)
466 for (
int n = 0; n < NCLOUD; ++n) {
467 xg += -air_mole.
c[n];
468 qd += -air_mass.
c[n];
472 #pragma omp simd reduction(+ : xd, qd)
473 for (
int n = 1; n <= NVAPOR; ++n) {
474 xd += -air_mole.
w[n];
475 qd += -air_mass.
w[n];
478 Real
temp = air_mole.
w[IDN];
479 Real pres = air_mole.
w[IPR];
481 Real qv = air_mass.
w[v];
491 std::vector<Real> rates{-0.01, 0.01};
495 Real pd = xd / xg * pres;
496 Real cpt = cpd * qd + cl * qt;
498 return temp * pow(
p0 / pd,
Rd * qd / cpt) * pow(rh, -Rv * qv / cpt) *
499 exp(lv * qv / (cpt *
temp));
508 Real fsig = 1., qgas = 1.;
510 for (
int i = 1; i <= NVAPOR; ++i) {
516 int n =
j + 1 + NVAPOR;
517 Real qc = qfrac->
c[
j];
525 #pragma omp simd reduction(+ : qgas)
526 for (
int n = 0; n < NCLOUD; ++n) qgas += -qfrac->
c[n];
528 qfrac->
w[IDN] = umole / (cvd * fsig);
534 Real fsig = 1., LE = 0.;
536 for (
int i = 1; i <= NVAPOR; ++i) {
542 int n =
j + 1 + NVAPOR;
543 Real qc = qfrac.
c[
j];
550 return cvd * qfrac.
w[IDN] * fsig - LE;
555 #pragma omp simd reduction(+ : qgas)
556 for (
int n = 0; n < NCLOUD; ++n) qgas += -qfrac.
c[n];
562 for (
int i = 1; i <= NVAPOR; ++i)
564 qfrac->
w[i] += qfrac->
c[
j];
570 auto& indx = info.first;
571 auto& stoi = info.second;
573 qfrac->
w[indx[0]] += stoi[0] / stoi[2] * qfrac->
c[indx[2]];
574 qfrac->
c[indx[2]] = 0.;
575 qfrac->
w[indx[1]] += stoi[1] / stoi[2] * qfrac->
c[indx[2]];
576 qfrac->
c[indx[2]] = 0.;
AirParcel & ToMoleFraction()
static IndexMap const * GetInstance()
virtual double cp(double T) const
void LoadThermodynamicFile(std::string chemfile)
virtual double latent(double T) const
std::array< Real, Size > cp_ratio_mass_
ratio of specific heat capacities [J/mol] at constant pressure
RealArrayX TryEquilibriumTP_VaporCloud(AirParcel const &qfrac, int ivapor, Real cv_hat=0., bool misty=false) const
Calculate the equilibrium mole transfer by cloud reaction vapor -> cloud.
std::array< Real, Size > cp_ratio_mole_
ratio of specific heat capacities [J/mol] at constant pressure
std::array< Real, Size > cv_ratio_mass_
ratio of specific heat capacities [J/mol] at constant volume
std::array< Real, Size > inv_mu_ratio_
inverse ratio of mean molecular weights
std::array< Real, Size > beta_
Dimensionless latent heat.
Real getDensityMole(AirParcel const &qfrac) const
Real GetCpMassRef(int n) const
static constexpr Real RefTemp
void enrollVaporFunctions()
custom functions for vapor (to be overridden by user mods)
Real PotentialTemp(MeshBlock *pmb, Real p0, int k, int j, int i) const
Calculate potential temperature from primitive variable.
Real gammad_ref_
reference polytropic index of dry air
Real MoistStaticEnergy(MeshBlock const *pmb, Real gz, int k, int j, int i) const
Moist static energy.
std::array< Real, Size > latent_energy_mass_
latent energy at constant volume [J/kg]
std::map< IndexPair, ReactionInfo > cloud_reaction_map_
reaction information map
static Thermodynamics const * GetInstance()
Return a pointer to the one and only instance of Thermodynamics.
int sa_max_iter_
maximum saturation adjustment iterations
Real GetCvMass(AirParcel const &qfrac, int n) const
std::vector< IndexSet > cloud_index_set_
cloud index set
Real sa_ftol_
saturation adjustment temperature tolerance
static Thermodynamics * fromYAMLInput(YAML::Node node)
std::array< Real, Size > inv_mu_
inverse molecular weight [mol/kg]
std::array< Real, 1+NVAPOR > p3_
triple point pressure [pa]
Real GetLatentHeatMole(int i, std::vector< Real > const &rates, Real temp) const
Real RelativeHumidity(MeshBlock const *pmb, int n, int k, int j, int i) const
Relative humidity.
Real GetLatentEnergyMole(int n, Real temp=0.) const
Temperature dependent specific latent energy [J/mol] of condensates at constant volume.
void updateTPConservingU(AirParcel *qfrac, Real rmole, Real umole) const
update T/P
static Thermodynamics * fromLegacyInput(ParameterInput *pin)
SVPFunc1Container svp_func1_
saturation vapor pressure function: Vapor -> Cloud
void Extrapolate(AirParcel *qfrac, Real dzORdlnp, Method method, Real grav=0., Real userp=0.) const
std::array< Real, 1+NVAPOR > t3_
triple point temperature [K]
Real RovRd(AirParcel const &qfrac) const
std::array< Real, Size > mu_
molecular weight [kg/mol]
Real GetChi(AirParcel const &qfrac) const
std::array< Real, Size > delta_
Dimensionless differences in specific heat capacity.
static Thermodynamics * mythermo_
pointer to the single Thermodynamics instance
Real GetCpMass(AirParcel const &qfrac, int n) const
Real GetLatentHeatMass(int i, std::vector< Real > const &rates, Real temp) const
Real EquivalentPotentialTemp(MeshBlock *pmb, Real p0, int v, int k, int j, int i) const
Calculate equivalent potential temperature from primitive variable.
Real sa_relax_
saturation adjustment relaxation parameter
std::array< Real, Size > latent_energy_mole_
latent energy at constant volume [J/mol]
Real GetTemp(MeshBlock const *pmb, int k, int j, int i) const
Calculate temperature from primitive variable.
std::array< Real, Size > cv_ratio_mole_
ratio of specific heat capacities [J/mol] at constant volume
static void Destroy()
Destroy the one and only instance of Thermodynamics.
std::array< Real, Size > mu_ratio_
ratio of mean molecular weights
Real Rd_
ideal gas constant of dry air in J/kg
static Thermodynamics const * InitFromAthenaInput(ParameterInput *pin)
void rk4IntegrateLnp(AirParcel *qfrac, Real dlnp, Method method, Real adlnTdlnP) const
void setTotalEquivalentVapor(AirParcel *qfrac) const
Real GetEnthalpyMass(MeshBlock const *pmb, int k, int j, int i) const
specific enthalpy [J/kg] of the air parcel
Real GetPres(MeshBlock const *pmb, int k, int j, int i) const
Real GetGammad(AirParcel const &var) const
adiaibatic index of dry air [1]
Real GetGamma(MeshBlock const *pmb, int k, int j, int i) const
Polytropic index.
Real getInternalEnergyMole(AirParcel const &qfrac) const
void rk4IntegrateZ(AirParcel *qfrac, Real dlnp, Method method, Real grav, Real adlnTdlnP) const
AirParcel gather_from_primitive(MeshBlock const *pmb, int k, int j, int i)
void read_thermo_property(Real var[], char const name[], int len, Real v0, ParameterInput *pin)
Real __attribute__((weak)) Thermodynamics
static std::mutex thermo_mutex