2026-09-15 10:42:00 -04:00
# pragma once
2026-09-20 14:37:11 -04:00
/**
* @ 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 .
*/
2026-09-15 10:42:00 -04:00
# include "serif/dimensions/quantities.hpp"
# include "serif/utils/misc/concepts/numeric.hpp"
namespace serif : : dimensions {
2026-09-20 14:37:11 -04:00
/**
* @ 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 .
*/
2026-09-15 10:42:00 -04:00
template < ThermodynamicQuantityType Quantity >
struct WithRespectTo final { } ;
2026-09-20 14:37:11 -04:00
/**
* @ 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 .
*/
2026-09-15 10:42:00 -04:00
template < ThermodynamicQuantityType OutputQuantity , ThermodynamicQuantityType InputQuantity >
class PartialDerivative final {
public :
explicit constexpr PartialDerivative ( const double value ) noexcept : m_value ( value ) { }
2026-09-20 14:37:11 -04:00
/**
* @ 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
*/
2026-09-15 10:42:00 -04:00
[[nodiscard]] constexpr double value ( ) const noexcept {
return m_value ;
}
2026-09-20 14:37:11 -04:00
/**
* @ 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
*/
2026-09-15 10:42:00 -04:00
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 } ;
}
2026-09-20 14:37:11 -04:00
/**
* @ 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
*/
2026-09-15 10:42:00 -04:00
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 } ;
}
2026-09-20 14:37:11 -04:00
/**
* @ 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
*/
2026-09-15 10:42:00 -04:00
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 } ;
}
2026-09-20 14:37:11 -04:00
/**
* @ 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
*/
2026-09-15 10:42:00 -04:00
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 } ;
}
2026-09-20 14:37:11 -04:00
/**
* @ 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
*/
2026-09-15 10:42:00 -04:00
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 } ;
}
2026-09-20 14:37:11 -04:00
/**
* @ 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
*/
2026-09-15 10:42:00 -04:00
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 ;
}
2026-09-20 14:37:11 -04:00
/**
* @ 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
*/
2026-09-15 10:42:00 -04:00
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 ;
}
2026-09-20 14:37:11 -04:00
/**
* @ 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
*/
2026-09-15 10:42:00 -04:00
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 :
2026-09-20 14:37:11 -04:00
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.
2026-09-15 10:42:00 -04:00
} ;
}