Files
MeanField_SERiF/src/include/serif/dimensions/partial.hpp

208 lines
9.9 KiB
C++

#pragma once
/**
* @file partial.hpp
* @author Emily M. Boudreaux
*
* @brief Compile time tools to represent a partial derivative of one quantity wrt. another quantity.
* Note that this system is not responsible for computing partials or in fact derivatives of any kind.
* Rather, it is responsible for forcing the developer --- or compiler at least --- to ask
* for a specific, explicit, partial and have to spell out what that is. That in turn gives
* the compiler enough information to do a lot of downstream things. A motivating example would be automatic verification
* of the provided EOS against all partials that might be needed to ensure they are all exported
* by the EOS.
*/
#include "serif/dimensions/quantities.hpp"
#include "serif/utils/misc/concepts/numeric.hpp"
namespace serif::dimensions {
/**
* @struct WithRespectTo
*
* @tparam Quantity A quantity satisfying @ref ThermodynamicQuantityType
* @brief A type that is empty at runtime but allows for the developer
* to specify to the compiler what quantity the partial is with respect to.
*/
template <ThermodynamicQuantityType Quantity>
struct WithRespectTo final {};
/** @brief
* A second EOS partial retains both differentiation quantities in its type.
*/
template <ThermodynamicQuantityType OutputQuantity, ThermodynamicQuantityType FirstInput, ThermodynamicQuantityType SecondInput>
class SecondPartialDerivative final {
public:
/** @brief Store an explicitly evaluated second partial; this type does not differentiate. */
explicit constexpr SecondPartialDerivative(double value) noexcept : m_value(value) {}
/** @brief Return the numeric partial in the caller's physical units. */
[[nodiscard]] constexpr double value() const noexcept { return m_value; }
private:
double m_value;
};
/**
* @class PartialDerivative
*
* @tparam OutputQuantity
* @tparam InputQuantity
*
* @brief Class to represent a compile time derivative relation between two quantities. Note that
* this class does not, in fact cannot, compute any derivatives itself. Rather, this is a way
* for the compiler to understand higher level mathematical relations. Unless you are really
* in the weeds of the development team you will likely not need to interact with this
* directly. Further note that this class does include (unlike the more basic dimension system)
* some basic dimensional analysis. Specifically, there are some overloaded operators that
* allow for certain specializations of PartialDerivative to operate on one and other
* conditioned on what their input and output types are. This does not do any thing like
* convert area to length^2. However, it will do more basic things like ensure that you
* can only add two partials which agree on their dimensions exactly. Or that while you can
* multiple a partial by any scalar, you cannot multiply arbitrary partials.
*/
template <ThermodynamicQuantityType OutputQuantity, ThermodynamicQuantityType InputQuantity>
class PartialDerivative final {
public:
explicit constexpr PartialDerivative(const double value) noexcept : m_value(value){}
/**
* @brief Get the underlying value as a compile time constant if possible. This
* allows the compiler, where the constructor may be determined at compile time,
* to fully optimize PartialDerivative operations as if they were doubles.
*
* @note calling value gets the underlying numeric value directly thereby bypassing
* the limitations on what a valid operation are (e.g. what the operator overloads
* enforce).
*
* @return m_value: This is whatever was passed at constructor time since there is
* no way to modify this value after construction without some truly horrific C++ tricks
*/
[[nodiscard]] constexpr double value() const noexcept {
return m_value;
}
/**
* @brief Overload the + operator between two partials which share the exact same input
* and output types
* @param lhs One partial
* @param rhs Other Partial
* @return A new partial with the same input and output types where m_value = lhs.m_value + rhs.m_value
*/
friend constexpr PartialDerivative<OutputQuantity, InputQuantity> operator+(
const PartialDerivative<OutputQuantity, InputQuantity>& lhs,
const PartialDerivative<OutputQuantity, InputQuantity>& rhs
) noexcept {
return PartialDerivative<OutputQuantity, InputQuantity>{lhs.m_value + rhs.m_value};
}
/**
* @brief Overload the - operator between two partials which share the exact same input
* and output types
* @param lhs One partial
* @param rhs Other Partial
* @return A new partial with the same input and output types where m_value = lhs.m_value - rhs.m_value
*/
friend constexpr PartialDerivative<OutputQuantity, InputQuantity> operator-(
const PartialDerivative<OutputQuantity, InputQuantity>& lhs,
const PartialDerivative<OutputQuantity, InputQuantity>& rhs
) noexcept {
return PartialDerivative<OutputQuantity, InputQuantity>{lhs.m_value - rhs.m_value};
}
/**
* @brief Overload of the * operator for a left partial and a right scalar
* and output types
* @tparam Scalar Any value satisfying the @ref IsNumeric concept (integral types and float types)
* @param lhs Partial with some input and some output type
* @param rhs Some numeric quantity
* @return A new partial with the same input and output types where m_value = lhs.m_value * rhs
*/
template <utils::misc::concepts::IsNumeric Scalar>
friend constexpr PartialDerivative<OutputQuantity, InputQuantity> operator*(
const PartialDerivative<OutputQuantity, InputQuantity>& lhs,
const Scalar& rhs
) noexcept {
return PartialDerivative<OutputQuantity, InputQuantity>{lhs.m_value * rhs};
}
/**
* @brief Overload of the * operator for a right partial and a left scalar
* and output types
* @tparam Scalar Any value satisfying the @ref IsNumeric concept (integral types and float types)
* @param lhs Some numeric quantity
* @param rhs Partial with some input and some output type
* @return A new partial with the same input and output types where m_value = lhs * rhs.m_value
*/
template <utils::misc::concepts::IsNumeric Scalar>
friend constexpr PartialDerivative<OutputQuantity, InputQuantity> operator*(
const Scalar& lhs,
const PartialDerivative<OutputQuantity, InputQuantity>& rhs
) noexcept {
return PartialDerivative<OutputQuantity, InputQuantity>{lhs * rhs.m_value};
}
/**
* @brief Overload of the / operator for a left partial and a right scalar
* and output types
* @tparam Scalar Any value satisfying the @ref IsNumeric concept (integral types and float types)
* @param lhs Partial with some input and some output type
* @param rhs Some numeric quantity
* @return A new partial with the same input and output types where m_value = lhs.m_value / rhs
*/
template <utils::misc::concepts::IsNumeric Scalar>
friend constexpr PartialDerivative<OutputQuantity, InputQuantity> operator/(
const PartialDerivative<OutputQuantity, InputQuantity>& lhs,
const Scalar& rhs
) noexcept {
return PartialDerivative<OutputQuantity, InputQuantity>{lhs.m_value / rhs};
}
/**
* @brief Partial ordering overload to compare a left partial and a right scalar
* @tparam Scalar Any value satisfying the @ref IsNumeric concept (integral types and float types)
* @param lhs Some numeric quantity
* @param rhs Some partial with an input and output type
* @return partial ordering comparing rhs.m_value to lhs
*/
template <utils::misc::concepts::IsNumeric Scalar>
friend constexpr std::partial_ordering operator<=>(
const PartialDerivative<OutputQuantity, InputQuantity>& lhs,
Scalar rhs
) noexcept {
return lhs.m_value <=> rhs;
}
/**
* @brief Partial ordering overload to compare a right partial and a left scalar
* @tparam Scalar Any value satisfying the @ref IsNumeric concept (integral types and float types)
* @param lhs Some numeric quantity
* @param rhs Some partial with an input and output type
* @return partial ordering comparing rhs.m_value to lhs
*/
template <utils::misc::concepts::IsNumeric Scalar>
friend constexpr std::partial_ordering operator<=>(
Scalar lhs,
const PartialDerivative<OutputQuantity, InputQuantity>& rhs
) noexcept {
return lhs <=> rhs.m_value;
}
/**
* @brief Partial ordering comparing two partials with exactly the same input and output types
* @param lhs Some Partial
* @param rhs Other Partial
* @return Partial ordering comparing lhs.m_value to rhs.m_value
*/
friend constexpr std::partial_ordering operator<=>(
const PartialDerivative<OutputQuantity, InputQuantity> &lhs,
const PartialDerivative<OutputQuantity, InputQuantity> &rhs
) noexcept {
return lhs.m_value <=> rhs.m_value;
}
private:
double m_value; ///< Numeric value associated with the partial. If this is known at compile time then the compiler can optimize operations with partial.value() to be equivalent to compile time double operations.
};
}