208 lines
9.9 KiB
C++
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.
|
|
};
|
|
}
|