Skip to content

Commit

Permalink
Merge pull request #311 from lanl/jmm/build-rewrite
Browse files Browse the repository at this point in the history
EOSBuilder rewrite
  • Loading branch information
Yurlungur authored Nov 29, 2023
2 parents d233d12 + b3d40b3 commit 6968515
Show file tree
Hide file tree
Showing 18 changed files with 431 additions and 512 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ Date: 11/28/2023
- [[PR177]](https://github.com/lanl/singularity-eos/pull/177) added EOSPAC vector functions

### Changed (changing behavior/API/variables/...)
- [[PR311]](https://github.com/lanl/singularity-eos/pull/311) Refactor EOSBuilder for flexibility and extensibility
- [[PR310]](https://github.com/lanl/singularity-eos/pull/310) Speed up and clean up tests
- [[PR295]](https://github.com/lanl/singularity-eos/pull/295) Add fast logs to singularity-eos
- [[PR246]](https://github.com/lanl/singularity-eos/pull/246) Update CMake with upstream changes
Expand Down
13 changes: 13 additions & 0 deletions doc/sphinx/src/modifiers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,19 @@ Modifiers can be composed. For example:
using namespace singularity;
auto my_eos = ShiftedEOS<ScaledEOS<IdealGas>>(ScaledEOS(IdealGas(gm1, Cv), scale), shift);
You can build modifiers up iteratively by, for example:

.. code-block:: cpp
using namespace singularity;
EOS eos = IdealGas(gm1, cv);
if (do_shift) {
eos = eos.template Modify<ShiftedEOS>(shift);
}
if (do_scale) {
eos = eos.template Modify<ScaledEOS>(scale);
}
Undoing Modifiers
------------------

Expand Down
74 changes: 49 additions & 25 deletions doc/sphinx/src/using-eos.rst
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,34 @@ modifiers. However, note that modifiers do not commute, and only one
order is supported. The ordering, inside-out, is ``UnitSystem`` or
``RelativisticEOS``, then ``ScaledEOS``, then ``ShiftedEOS``.

A modified equation of state can be built up iteratively. To check if
the equation of state currently stored in the variant can modified,
you may call

.. cpp:function:: bool ModifiedInVariant<Mod>() const;

where ``Mod`` is the type of the modifier you want to apply, for
example ``ShiftedEOS``. If this function returns true, then you can
apply a modifier with the function

.. cpp:function:: Variant Modify<Mod>(Args &&..args) const;

where again ``Mod`` is the modifier you wish to apply, and ``args``
are the arguments to the constructor for that modifier, e.g., the
shift. For example, one might build up a shifted or scaled eos with a
code block like this:

.. code-block:: cpp
using namespace singularity;
EOS eos = IdealGas(gm1, cv);
if (do_shift) {
eos = eos.template Modify<ShiftedEOS>(shift);
}
if (do_scale) {
eos = eos.template Modify<ScaledEOS>(scale);
}
Relevant to the broad ``singularity-eos`` API, EOS models provide
introspection. To check if an EOS is modified, call

Expand Down Expand Up @@ -470,38 +498,34 @@ temperature or density and specific internal energy.
EOS Builder
------------

The inclusion of modifiers can make building a desired equation of
state somewhat cumbersome. To handle this, we have implemented the
``EOSBuilder`` machinery. ``EOSBuilder`` is a set of functions that
provides a declarative interface for building an equation of state
object.
The iterative construction of modifiers described above and in the
:ref:`modifiers<modifiers>` section is object oriented. For
convenience, we also provide a procedural, dispatch-based approach in
the ``EOSBuilder`` namespace and header. The key function is

The EOS Builder functions and types are defined in the
``singularity::EOSBuilder`` namespace. The key function is

.. cpp:function:: EOS EOSBuilder::buildEOS(EOSBuilder::EOSType t, EOSBuilder::params_t base_params, EOSBuilder::modifiers_t modifiers)
.. code-block:: cpp
* ``EOSBuilder::EOSType`` is an enum class with names that match the various EOS classes defined in :ref:`the models section <models>`; for example, ``EOSBuilder::EOSType::IdealGas``.
* ``EOSBuilder::params_t`` is a dictionary object with some type erasure, which maps strings to the types ``std::string``, ``int``, or ``Real``. It is used to map parameter names to their values for class constructors.
* ``EOSBuilder::modifiers_t`` is a dictionary from the ``EOSModifier`` enum class, which works identically to the ``EOSType`` enum but for modifiers, to ``params_t`` objects, specifying the constructor values for each modifier.
template <template <class> typename Mod, typename... Ts, typename... Args>
singularity::Variant<Ts...> Modify(const Variant<Ts...> &eos, Args &&...args);
Putting it all together, initializing an ``IdealGas`` with
``EOSBuilder`` looks something like this:
where ``Mod`` is an EOS modifier, ``Variant`` is either your
user-defined custom EOS variant type, or the pre-defined ``EOS`` type,
the ``eos`` object is an EOS you'd like to modify (stored as a
variant), and ``args`` are the additional arguments to the constructor
of ``Mod`` beyond the object to modify. For example, initializing an
``IdealGas`` equation of state that is optionally shifted and scaled
might look something like this:

.. code-block:: cpp
using namespace singularity;
EOSBuilder::EOSType type = EOSBuilder::EOSType::IdealGas;
EOSBuilder::modifiers_t modifiers;
EOSBuilder::params_t base_params, shifted_params, scaled_params;
base_params["Cv"].emplace<Real>(Cv);
base_params["gm1"].emplace<Real>(gm1);
shifted_params["shift"].emplace<Real>(shift);
scaled_params["scale"].emplace<Real>(scale);
modifiers[EOSBuilder::EOSModifier::Shifted] = shifted_params;
modifiers[EOSBuilder::EOSModifier::Scaled] = scaled_params;
EOS eos = EOSBuilder::buildEOS(type, base_params, modifiers);
EOS eos = IdealGas(gm1, cv);
if (do_shift) {
eos = EOSBuilder::Modify<ShiftedEOS>(eos, shift);
}
if (do_scale) {
eos = EOSBuilder::Modify<ScaledEOS>(eos, scale);
}
.. _eos methods reference section:

Expand Down
4 changes: 1 addition & 3 deletions singularity-eos/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,7 @@ set(EOS_HEADERS
eos/eos_stiff.hpp
)

set(EOS_SRCS
eos/eos_builder.cpp
)
set(EOS_SRCS eos/eos.cpp)

if (SINGULARITY_BUILD_CLOSURE)
list(APPEND EOS_HEADERS
Expand Down
32 changes: 30 additions & 2 deletions singularity-eos/base/variadic_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,35 @@
#include <type_traits>

namespace singularity {
namespace detail {
namespace variadic_utils {

// Some generic variatic utilities
// ======================================================================

// Backport of C++17 bool_constant.
// With C++17, can be replaced with
// using std::bool_constant
template <bool B>
using bool_constant = std::integral_constant<bool, B>;

// Implementation of std::conjunction/std::disjunction without C++17
// With C++17, can be replaced with
// using std::disjunction
template <bool...>
struct bool_pack {};
template <bool... Bs>
using conjunction = std::is_same<bool_pack<true, Bs...>, bool_pack<Bs..., true>>;
template <bool... Bs>
struct disjunction : bool_constant<!conjunction<!Bs...>::value> {};

// Checks if T is contained in the pack Ts
template <typename T, typename... Ts>
using contains = disjunction<std::is_same<T, Ts>::value...>;

template <typename T, typename... Ts>
constexpr bool contains_v() {
return contains<T, Ts...>::value;
}

// variadic list
template <typename... Ts>
Expand Down Expand Up @@ -156,7 +184,7 @@ constexpr auto pack_size(type_list<Ts...>) {
return sizeof...(Ts);
}

} // namespace detail
} // namespace variadic_utils
} // namespace singularity

#endif // SINGULARITY_EOS_BASE_VARIADIC_UTILS_HPP_
45 changes: 45 additions & 0 deletions singularity-eos/eos/eos.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//------------------------------------------------------------------------------
// © 2021-2023. Triad National Security, LLC. All rights reserved. This
// program was produced under U.S. Government contract 89233218CNA000001
// for Los Alamos National Laboratory (LANL), which is operated by Triad
// National Security, LLC for the U.S. Department of Energy/National
// Nuclear Security Administration. All rights in the program are
// reserved by Triad National Security, LLC, and the U.S. Department of
// Energy/National Nuclear Security Administration. The Government is
// granted for itself and others acting on its behalf a nonexclusive,
// paid-up, irrevocable worldwide license in this material to reproduce,
// prepare derivative works, distribute copies to the public, perform
// publicly and display publicly, and to permit others to do so.
//------------------------------------------------------------------------------

// JMM: This is a dummy file to provide a source file for cmake to
// compile when built without fortran.
//
// The main reason to do this is that when we build without fortran
// bindings, the library is completely header only. However WITH
// fortran bindings, it is not.
//
// cmake supports a build mode for header only libraries by marking
// those libraries INTERFACE. Without this flag, a few things will go
// wrong:
// 1. CMake will be unable to infer a proper linker
// so one must be specified by hand.
// 2. no dynamic library file (e.g., "*.a") will be generated,
// but dependencies that link against it, such as another library,
// or in our case, tests, will look for it. This will lead to
// a failure of the downstream library at link time.
//
// On the other hand, a library with implementation files
// CANNOT be marked INTERFACE, as otherwise source files will
// not be compiled.
//
// Unfortunately, switching the INTERFACE tag on and off in cmake
// is very cumbersome, as if a library is marked INTERFACE
// all its dependencies must ALSO be slurped in with an INTERFACE
// flag. This introduces significant branching in the cmake code
// and a lot of builer plate. For now, then, I simply include this
// empty source file.
//
// In the future, we could include centralized code here that we
// always want compiled, such as template instantiations or
// convenience functions.
18 changes: 9 additions & 9 deletions singularity-eos/eos/eos.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ namespace singularity {

// recreate variadic list
template <typename... Ts>
using tl = singularity::detail::type_list<Ts...>;
using tl = singularity::variadic_utils::type_list<Ts...>;

template <template <typename> class... Ts>
using al = singularity::detail::adapt_list<Ts...>;
using al = singularity::variadic_utils::adapt_list<Ts...>;

// transform variadic list: applies modifiers to eos's
using singularity::detail::transform_variadic_list;
using singularity::variadic_utils::transform_variadic_list;

// all eos's
static constexpr const auto full_eos_list =
Expand Down Expand Up @@ -117,31 +117,31 @@ static constexpr const auto scaled_1 =
transform_variadic_list(full_eos_list, al<ScaledEOS>{});
// relativistic and unit system modifiers
static constexpr const auto unit_or_rel =
singularity::detail::concat(unit_system, relativistic);
singularity::variadic_utils::concat(unit_system, relativistic);
// variadic list of eos with shifted, relativistic or unit system modifiers
static constexpr const auto shifted_of_unit_or_rel =
transform_variadic_list(unit_or_rel, al<ShiftedEOS>{});
// combined list of all shifted EOS
static constexpr const auto shifted =
singularity::detail::concat(shifted_1, shifted_of_unit_or_rel);
singularity::variadic_utils::concat(shifted_1, shifted_of_unit_or_rel);
// variadic list of eos with scaled, relativistic or unit system modifiers
static constexpr const auto scaled_of_unit_or_rel =
transform_variadic_list(unit_or_rel, al<ScaledEOS>{});
// variadic list of Scaled<Shifted<T>>'s
static constexpr const auto scaled_of_shifted =
transform_variadic_list(shifted, al<ScaledEOS>{});
// combined list of all scaled EOS
static constexpr const auto scaled =
singularity::detail::concat(scaled_1, scaled_of_unit_or_rel, scaled_of_shifted);
static constexpr const auto scaled = singularity::variadic_utils::concat(
scaled_1, scaled_of_unit_or_rel, scaled_of_shifted);
// create combined list
static constexpr const auto combined_list_1 =
singularity::detail::concat(full_eos_list, shifted, scaled, unit_or_rel);
singularity::variadic_utils::concat(full_eos_list, shifted, scaled, unit_or_rel);
// make a ramped eos of everything
static constexpr const auto ramped_all =
transform_variadic_list(combined_list_1, al<BilinearRampEOS>{});
// final combined list
static constexpr const auto combined_list =
singularity::detail::concat(combined_list_1, ramped_all);
singularity::variadic_utils::concat(combined_list_1, ramped_all);
// a function that returns a Variant from a typelist
template <typename... Ts>
struct tl_to_Variant_struct {
Expand Down
34 changes: 34 additions & 0 deletions singularity-eos/eos/eos_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include <ports-of-call/portability.hpp>
#include <ports-of-call/portable_errors.hpp>
#include <singularity-eos/base/variadic_utils.hpp>

namespace singularity {
namespace mfuncname {
Expand Down Expand Up @@ -137,6 +138,39 @@ class EosBase {
f(copy);
}

// EOS builder helpers
// Checks if an EOS can be modified
template <template <class> typename Mod, typename... Ts>
constexpr bool ModifiedInList() const {
return variadic_utils::contains_v<Mod<CRTP>, Ts...>();
}
// Modifies an EOS object by pulling out underlying type and modifying it
// This one returns Mod<CRT>
template <template <class> typename Mod, typename... Args>
constexpr Mod<CRTP> Modify(Args &&...args) const {
CRTP unmodified = *(static_cast<CRTP const *>(this));
return Mod<CRTP>(std::move(unmodified), std::forward<Args>(args)...);
}
// These are overloads needed for the variant, as std::visit must be
// able to return a variant of the same type every time. This lets
// us do so, even though sometimes we don't modify the object.
template <template <class> typename Mod, typename... Args>
constexpr Mod<CRTP> ConditionallyModify(std::true_type, Args &&...args) const {
return Modify<Mod>(std::forward<Args>(args)...);
}
template <template <class> typename Mod, typename... Args>
constexpr CRTP ConditionallyModify(std::false_type, Args &&...args) const {
CRTP unmodified = *(static_cast<CRTP const *>(this));
return unmodified;
}
template <template <class> typename Mod, typename... Ts, typename... Args>
constexpr auto ConditionallyModify(const variadic_utils::type_list<Ts...> &tl,
Args &&...args) const {
constexpr bool do_mod = variadic_utils::contains_v<Mod<CRTP>, Ts...>();
return ConditionallyModify<Mod>(variadic_utils::bool_constant<do_mod>(),
std::forward<Args>(args)...);
}

// Vector member functions
template <typename RealIndexer, typename ConstRealIndexer, typename LambdaIndexer>
inline void
Expand Down
Loading

0 comments on commit 6968515

Please sign in to comment.