Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make sure that DensityEnergyFromPressureTemperature works for all equations of state #449

Merged
merged 45 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
4a210b5
add RhoE of PT to base class and implement where relevant
jonahm-LANL Dec 23, 2024
50c08e7
formatting
jonahm-LANL Dec 23, 2024
acd9def
Tests pass on macbook. Still need to add a few tests
jonahm-LANL Dec 24, 2024
608a772
swap default to findRoot because it respects bounds
jonahm-LANL Dec 24, 2024
0bdead9
tests complete for everything except eospac and spiner
jonahm-LANL Dec 24, 2024
0a4b61a
update zsplit
jonahm-LANL Dec 24, 2024
f1d1fc4
changelog
jonahm-LANL Dec 24, 2024
c5f75a5
missing auto
jonahm-LANL Dec 24, 2024
838b720
it works on my machine...
jonahm-LANL Dec 24, 2024
365dd65
output on failure please
jonahm-LANL Dec 24, 2024
376223f
just change tests to output-on-failure... and also loosen EPS in case…
jonahm-LANL Dec 24, 2024
9b083fa
lets try this...
jonahm-LANL Dec 24, 2024
c3182ff
make default implementation more robust maybe I hope
jonahm-LANL Dec 25, 2024
1034b38
put default rho gues close to STP
jonahm-LANL Dec 25, 2024
d6e80ee
put default rho gues close to STP
jonahm-LANL Dec 25, 2024
5f79307
re-tighten bounds after initial guess problem resolved
jonahm-LANL Dec 25, 2024
0e61f98
oops missing boolean not
jonahm-LANL Dec 25, 2024
cc8db8f
add several comments based on Jeff's feedback
jonahm-LANL Jan 3, 2025
58bc9c9
try 1e-8 MINR_DEFAULT
jonahm-LANL Jan 3, 2025
99aa6cd
Make base class use MaximumDensity
jonahm-LANL Jan 3, 2025
1e72ff5
update docs
jonahm-LANL Jan 3, 2025
26dd8f5
typo
jonahm-LANL Jan 3, 2025
be2f5b9
ok... lets try this
jonahm-LANL Jan 3, 2025
44e738b
make tests more effectively check for accurate pressure
jonahm-LANL Jan 3, 2025
fed072a
residual
jonahm-LANL Jan 3, 2025
8cc2508
formatting
jonahm-LANL Jan 3, 2025
6dc8cae
namespace
jonahm-LANL Jan 3, 2025
bc5f10e
tweak how to check this residual to work when P = 0
jonahm-LANL Jan 3, 2025
813e773
oops need to do magnitudes
jonahm-LANL Jan 3, 2025
e0e587c
come up with sensible bounds for power series EOSs.
jonahm-LANL Jan 3, 2025
cf286de
I give up. 1e-4 it is
jonahm-LANL Jan 3, 2025
2cdcc08
comment
jonahm-LANL Jan 3, 2025
1d1873f
remove unused variable
jonahm-LANL Jan 3, 2025
ceec265
tighter tolerances
jonahm-LANL Jan 3, 2025
8424efc
relax tolerance for chekc
jonahm-LANL Jan 3, 2025
7d9c90a
remove the maximum densities... we're never going to hit those bounds…
jonahm-LANL Jan 3, 2025
ee5e374
also add sanity check for rhomax vs rhomin
jonahm-LANL Jan 3, 2025
38bba46
missed leading underscore
jonahm-LANL Jan 3, 2025
a74bd26
stupid C++ deleting copy constructor for const correctness
jonahm-LANL Jan 3, 2025
5f8e8e0
choose worse, but more generic guess... does that help?
jonahm-LANL Jan 3, 2025
74ee377
is it because rho is uninitialized?
jonahm-LANL Jan 3, 2025
9e4f844
update docs
jonahm-LANL Jan 4, 2025
1cd2f9d
add introspection for pressure
jonahm-LANL Jan 8, 2025
6967e55
add default nullptr argument
jonahm-LANL Jan 9, 2025
144b90b
change name
jonahm-LANL Jan 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests_minimal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ jobs:
..
make -j4
make install
make test
ctest --output-on-failure
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd like to leave this permanently. You still get a summary out at the end, but here we can see what went wrong with a test. Useful for debugging, especially when the CI machine and your laptop disagree...

2 changes: 1 addition & 1 deletion .github/workflows/tests_minimal_kokkos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ jobs:
..
make -j4
make install
make test
ctest --output-on-failure
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- [[PR444]](https://github.com/lanl/singularity-eos/pull/444) Add Z split modifier and electron ideal gas EOS

### Fixed (Repair bugs, etc)
- [[PR449]](https://github.com/lanl/singularity-eos/pull/449) Ensure that DensityEnergyFromPressureTemperature works for all equations of state and is properly tested
- [[PR439]](https://github.com/lanl/singularity-eos/pull/439) Add mean atomic mass and number to EOS API
- [[PR437]](https://github.com/lanl/singularity-eos/pull/437) Fix segfault on HIP, clean up warnings, add strict sanitizer test

Expand Down
65 changes: 61 additions & 4 deletions doc/sphinx/src/using-eos.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1143,18 +1143,75 @@ quantities as outputs.
Methods Used for Mixed Cell Closures
--------------------------------------

Several methods were developed in support of mixed cell closures. In particular:
Several methods were developed in support of mixed cell closures. In particular the function

.. cpp:function:: Real MinimumDensity() const;

and
the function

.. cpp:function:: Real MinimumTemperature() const;

and the function

.. cpp:function:: Real MaximumDensity() const;

provide bounds for valid inputs into a table, which can be used by a
root finder to meaningful bound the root search. Similarly,
root finder to meaningful bound the root search.

.. warning::

For unbounded equations of state, ``MinimumDensity`` and
``MinimumTemperature`` will return zero, while ``MaximumDensity``
will return a very large finite number. Which number you get,
however, is not guaranteed. You may wish to apply more sensible
bounds in your own code.

Similarly,

.. cpp:function:: Real RhoPmin(const Real temp) const;

returns the density at which pressure is minimized for a given
temperature. This is again useful for root finds.
temperature. The function

.. cpp:function:: Real MinimumPressure() const;

provides the minimum pressure an equation of state supports, which may
be the most negative tension state. The function

.. cpp:function:: Real MaximumPressureFromTemperature() const;

provides a maximum possible pressure an equation of state
supports. (Most models are unbounded in pressure.) This is again
useful for root finds.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're modeling this off of what I did in the Gruneisen EOS, this isn't correct. This gives the maximum pressure at a given temperature. If you increase the temperature, the maximum pressure will increase.

For a sesame table, this would simply be $P(\rho_\mathrm{max}, T)$ assuming that $\rho_\mathrm{max}$ is not inside the vapor dome for some strange reason.

I know our convention is to use "From" in our lookup functions, but I'd argue this is subtly different and should be "At" instead. I'd argue our lookups are saying "Return a quantity given the provided state" ("Given" would be more accurate in the naming, but I'm not proposing it here). By contrast, this function does not take a complete thermodynamic state, instead taking a single variable and returning the information to form a complete state. Temperature by itself is not a complete thermodynamic state.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completely agree--I change the name. For the tabulated equations of state, I actually do pull it out and record it now (thinking ahead), but I let this float as the table interpolation does extrapolate off the top of the table. It's not particularly trustworthy, but it's not a hard limit. Only Gruneisen currently reports a maximum pressure bound. On the other hand, I do have each table report the minimum pressure, which is important.


The function

.. code-block:: cpp

template <typename Indexer_t = Real*>
void DensityEnergyFromPressureTemperature(const Real press, const Real temp,
Indexer_t &&lambda, Real &rho, Real &sie) const;

is designed for working in Pressure-Temperature space. Given a
pressure ``press`` and temperature ``temp``, it sets a density ``rho``
and specific internal energy ``sie``.

.. note::

Note that ``lambda`` must be passed in, whether or not a given
equation of state requires one. You may pass in ``nullptr`` safely,
however.
Yurlungur marked this conversation as resolved.
Show resolved Hide resolved

Typically this operation requires a root find. You may pass in an
initial guess for the density ``rho`` in-place and most EOS models
will use it.

.. warning::

Pressure is not necessarily monotone in density and it may be double
valued. Thus you are not guaranteed to find the correct root and the
value of your initial guess may determine correctness. The fact that
``rho`` may be used as an initial guess means you **must** pass in
an initialized variable, even if it is zero-initialized. Do not pass
uninitialized memory into this function!

9 changes: 6 additions & 3 deletions eospac-wrapper/eospac_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
namespace EospacWrapper {

void eosGetMetadata(int matid, SesameMetadata &metadata, Verbosity eospacWarn) {
constexpr int NT = 2;
constexpr int NT = 3;
EOS_INTEGER tableHandle[NT];
EOS_INTEGER tableType[NT] = {EOS_Info, EOS_Ut_DT};
EOS_INTEGER tableType[NT] = {EOS_Info, EOS_Ut_DT, EOS_Pt_DT};

EOS_INTEGER commentsHandle[1];
EOS_INTEGER commentsType[1] = {EOS_Comment};
Expand All @@ -48,7 +48,8 @@ void eosGetMetadata(int matid, SesameMetadata &metadata, Verbosity eospacWarn) {
}
}

eosSafeLoad(NT, matid, tableType, tableHandle, {"EOS_Info", "EOS_Ut_DT"}, eospacWarn);
eosSafeLoad(NT, matid, tableType, tableHandle, {"EOS_Info", "EOS_Ut_DT", "EOS_Pt_DT"},
eospacWarn);

for (int i = 0; i < numInfoTables; i++) {
eosSafeTableInfo(&(tableHandle[i]), NI[i], infoItems[i].data(), infoVals[i].data(),
Expand All @@ -66,6 +67,8 @@ void eosGetMetadata(int matid, SesameMetadata &metadata, Verbosity eospacWarn) {
metadata.TMax = temperatureFromSesame(infoVals[1][3]);
metadata.sieMin = sieFromSesame(infoVals[1][4]);
metadata.sieMax = sieFromSesame(infoVals[1][5]);
metadata.PMin = pressureFromSesame(infoVals[2][4]);
metadata.PMax = pressureFromSesame(infoVals[2][5]);
metadata.numRho = static_cast<int>(infoVals[1][6]);
metadata.numT = static_cast<int>(infoVals[1][7]);
metadata.rhoConversionFactor = infoVals[1][8];
Expand Down
1 change: 1 addition & 0 deletions eospac-wrapper/eospac_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class SesameMetadata {
double rhoMin, rhoMax;
double TMin, TMax;
double sieMin, sieMax;
double PMin, PMax;
double rhoConversionFactor;
double TConversionFactor;
double sieConversionFactor;
Expand Down
66 changes: 66 additions & 0 deletions singularity-eos/eos/eos_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@
#define _SINGULARITY_EOS_EOS_EOS_BASE_

#include <cstring>
#include <limits>
#include <string>

#include <ports-of-call/portability.hpp>
#include <ports-of-call/portable_errors.hpp>
#include <singularity-eos/base/constants.hpp>
#include <singularity-eos/base/robust_utils.hpp>
#include <singularity-eos/base/root-finding-1d/root_finding.hpp>
#include <singularity-eos/base/variadic_utils.hpp>

namespace singularity {
Expand Down Expand Up @@ -781,6 +784,25 @@ class EosBase {
PORTABLE_FORCEINLINE_FUNCTION
Real MinimumTemperature() const { return 0; }

// Report maximum value of density. Default is unbounded.
// JMM: Should we use actual infinity, the largest real, or just a
// big number? For comparisons, actual infinity is better. It also
// has the advantage of being projective with modifiers that modify
// the max. On the other hand, it's more fraught if someone tries to
// put it into a formula without guarding against it.
PORTABLE_FORCEINLINE_FUNCTION
Real MaximumDensity() const { return 1e100; }

// These are for the PT space PTE solver to bound the iterations in
// a safe range.
PORTABLE_FORCEINLINE_FUNCTION
Real MinimumPressure() const { return 0; }
// Gruneisen EOS's often have a maximum density, which implies a maximum pressure.
PORTABLE_FORCEINLINE_FUNCTION
Real MaximumPressureFromTemperature([[maybe_unused]] const Real T) const {
return 1e100;
}

PORTABLE_INLINE_FUNCTION
Real RhoPmin(const Real temp) const { return 0.0; }

Expand Down Expand Up @@ -836,6 +858,50 @@ class EosBase {
PORTABLE_ALWAYS_THROW_OR_ABORT(msg);
}

// JMM: This method is often going to be overloaded for special cases.
template <typename Indexer_t = Real *>
PORTABLE_INLINE_FUNCTION void
DensityEnergyFromPressureTemperature(const Real press, const Real temp,
Indexer_t &&lambda, Real &rho, Real &sie) const {
Comment on lines +859 to +863
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This default implementation is usually not optimal but does seem to work if a given EOS doesn't need special cases. In general, though, an EOS should overload and implement its own.

using RootFinding1D::findRoot; // more robust but slower. Better default.
using RootFinding1D::Status;

// Pressure is not monotone in density at low densities, which can
// prevent convergence. We want to approach tension from above,
// not below. Choose close to, but above, normal density for a
// metal like copper.
constexpr Real DEFAULT_RHO_GUESS = 12;

CRTP copy = *(static_cast<CRTP const *>(this));

// P(rho) not monotone. When relevant, bound rhopmin.
Real rhomin = std::max(copy.RhoPmin(temp), copy.MinimumDensity());
Real rhomax = copy.MaximumDensity();
PORTABLE_REQUIRE(rhomax > rhomin, "max bound > min bound");

auto PofRT = [&](const Real r) {
return copy.PressureFromDensityTemperature(r, temp, lambda);
};
Real rhoguess = rho; // use input density
if ((rhoguess <= rhomin) || (rhoguess >= rhomax)) { // avoid edge effects
if ((rhomin < DEFAULT_RHO_GUESS) && (DEFAULT_RHO_GUESS < rhomax)) {
rhoguess = DEFAULT_RHO_GUESS;
} else {
rhoguess = 0.5 * (rhomin + rhomax);
}
}
auto status = findRoot(PofRT, press, rhoguess, rhomin, rhomax, robust::EPS(),
robust::EPS(), rho);
// JMM: This needs to not fail and instead return something sane.
// If root find failed to converge, density will at least be
// within bounds.
if (status != Status::SUCCESS) {
PORTABLE_WARN("DensityEnergyFromPressureTemperature failed to find root\n");
}
sie = copy.InternalEnergyFromDensityTemperature(rho, temp, lambda);
return;
}

// Serialization
/*
The methodology here is there are *three* size methods all EOS's provide:
Expand Down
4 changes: 4 additions & 0 deletions singularity-eos/eos/eos_eospac.hpp
Yurlungur marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,7 @@ class EOSPAC : public EosBase<EOSPAC> {
}
PORTABLE_FORCEINLINE_FUNCTION Real MinimumDensity() const { return rho_min_; }
PORTABLE_FORCEINLINE_FUNCTION Real MinimumTemperature() const { return temp_min_; }
PORTABLE_FORCEINLINE_FUNCTION Real MinimumPressure() const { return press_min_; }

private:
static constexpr const unsigned long _preferred_input =
Expand All @@ -1128,6 +1129,7 @@ class EOSPAC : public EosBase<EOSPAC> {
Real dvdt_ref_ = 1;
Real rho_min_ = 0;
Real temp_min_ = 0;
Real press_min_ = 0;
// TODO(JMM): Is the fact that EOS_INTEGER isn't a size_t a
// problem? Could it ever realistically overflow?
EOS_INTEGER shared_size_, packed_size_;
Expand Down Expand Up @@ -1199,6 +1201,8 @@ inline EOSPAC::EOSPAC(const int matid, bool invert_at_setup, Real insert_data,
rho_ref_ = m.normalDensity;
rho_min_ = m.rhoMin;
temp_min_ = m.TMin;
press_min_ = m.PMin;

// use std::max to hydrogen, in case of bad table
AZbar_.Abar = std::max(1.0, m.meanAtomicMass);
AZbar_.Zbar = std::max(1.0, m.meanAtomicNumber);
Expand Down
11 changes: 11 additions & 0 deletions singularity-eos/eos/eos_gruneisen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,17 @@ class Gruneisen : public EosBase<Gruneisen> {
s1, _C0, _s1, _s2, _s3, _G0, _b, _rho0, _T0, _P0, _Cv, _rho_max);
_AZbar.PrintParams();
}

// Report minimum values of density and temperature
PORTABLE_FORCEINLINE_FUNCTION
Real MaximumDensity() const { return _rho_max; }
PORTABLE_FORCEINLINE_FUNCTION
Real MinimumPressure() const { return PressureFromDensityInternalEnergy(0, 0); }
PORTABLE_FORCEINLINE_FUNCTION
Real MaximumPressureFromTemperature(const Real T) const {
return MaxStableDensityAtTemperature(T);
}

template <typename Indexer_t>
PORTABLE_INLINE_FUNCTION void
DensityEnergyFromPressureTemperature(const Real press, const Real temp,
Expand Down
38 changes: 33 additions & 5 deletions singularity-eos/eos/eos_helmholtz.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,12 @@ class HelmElectrons {
std::size_t numTemp() const { return NTEMP; }
PORTABLE_FORCEINLINE_FUNCTION
std::size_t numRho() const { return NRHO; }
PORTABLE_FORCEINLINE_FUNCTION
Real MinimumDensity() const { return rhoMin(); }
PORTABLE_FORCEINLINE_FUNCTION
Real MinimumTemperature() const { return TMin_; }
PORTABLE_FORCEINLINE_FUNCTION
Real MaximumDensity() const { return rhoMax(); }

private:
inline void InitDataFile_(const std::string &filename);
Expand Down Expand Up @@ -676,16 +682,38 @@ class Helmholtz : public EosBase<Helmholtz> {
ValuesAtReferenceState(Real &rho, Real &temp, Real &sie, Real &press, Real &cv,
Real &bmod, Real &dpde, Real &dvdt,
Indexer_t &&lambda = static_cast<Real *>(nullptr)) const {
// JMM: I'm not sure what to put here or if it matters. Some
// reference state, maybe stellar denity, would be appropriate.
PORTABLE_ALWAYS_ABORT("Stub");
// JMM: Conditions for an oxygen burning shell in a stellar
// core. Not sure if that's the best choice.
rho = 1e7;
temp = 1.5e9;
Yurlungur marked this conversation as resolved.
Show resolved Hide resolved
FillEos(rho, temp, sie, press, cv, bmod,
thermalqs::specific_internal_energy | thermalqs::pressure |
thermalqs::specific_heat | thermalqs::bulk_modulus,
lambda);
dpde = GruneisenParamFromDensityTemperature(rho, temp, lambda) * rho;
dvdt = 0;
}
template <typename Indexer_t = Real *>
PORTABLE_INLINE_FUNCTION void
DensityEnergyFromPressureTemperature(const Real press, const Real temp,
Indexer_t &&lambda, Real &rho, Real &sie) const {
// This is only used for mixed cell closures. Stubbing it out for now.
PORTABLE_ALWAYS_ABORT("Stub");
using RootFinding1D::regula_falsi;
using RootFinding1D::Status;
PORTABLE_REQUIRE(temp > = 0, "Non-negative temperature required");
auto PofRT = [&](const Real r) {
return PressureFromDensityTemperature(r, temp, lambda);
};
auto status = regula_falsi(PofRT, press, 1e7, electrons_.rhoMin(),
electrons_.rhoMax(), 1.0e-8, 1.0e-8, rho);
if (status != Status::SUCCESS) {
PORTABLE_THROW_OR_ABORT("Helmholtz::DensityEnergyFromPressureTemperature: "
"Root find failed to find a solution given P, T\n");
}
if (rho < 0.) {
PORTABLE_THROW_OR_ABORT("Helmholtz::DensityEnergyFromPressureTemperature: "
"Root find produced negative energy solution\n");
}
sie = InternalEnergyFromDensityTemperature(rho, temp, lambda);
}
static std::string EosType() { return std::string("Helmholtz"); }
static std::string EosPyType() { return EosType(); }
Expand Down
Loading
Loading