diff --git a/EmilysRulesForSERiFCode.md b/EmilysRulesForSERiFCode.md index 28024f61..3f63a3b7 100644 --- a/EmilysRulesForSERiFCode.md +++ b/EmilysRulesForSERiFCode.md @@ -37,4 +37,6 @@ for correctness. - Prefer more and smaller functions over fewer and larger functions. - Do not include the full mfem.hpp header. Its slow, I will send you an angry email. - AI is a tool, its okay to use it, but use it as a tool not as a crutch. You must understand all the code you write. -- The less something is related to physics the more you may consider AI. A build system configuration for example is a good candidate for AI. A physics function is not. \ No newline at end of file +- The less something is related to physics the more you may consider AI. A build system configuration for example is a good candidate for AI. A physics function is not. +- Its not 1980, we are not working on dumb terminals connected to a PDP 11 with 80 charecters of width. Do not limit yourself to 80 charecter width lines if it limits the readability of that line +- At the same time monitors do still have a finite width, as a line gets long consider if it makes sense to split it into multiple lines. \ No newline at end of file diff --git a/build-config/magicenum/meson.build b/build-config/magicenum/meson.build index d68252b4..e13e0b54 100644 --- a/build-config/magicenum/meson.build +++ b/build-config/magicenum/meson.build @@ -1,6 +1,9 @@ magic_enum_include = include_directories('include') -magic_enum_args = [] +# SERiFErrorCode includes values up to 1000; magic_enum defaults to 127. +# Keep this definition on the public dependency so every translation unit +# instantiates the same reflection tables. +magic_enum_args = ['-DMAGIC_ENUM_RANGE_MAX=2000'] if get_option('magic_enum_hash') magic_enum_args += '-DMAGIC_ENUM_ENABLE_HASH' @@ -11,3 +14,4 @@ magic_enum_dep = declare_dependency( compile_args: magic_enum_args, ) +meson.override_dependency('magic_enum', magic_enum_dep) diff --git a/build-config/mfem/meson.build b/build-config/mfem/meson.build index ce0a82d5..044f550e 100644 --- a/build-config/mfem/meson.build +++ b/build-config/mfem/meson.build @@ -406,11 +406,12 @@ if effective_allow_preinstalled endforeach if system_compatible - system_compatible = cpp.compiles( + system_compatible = cpp.links( '#include \n' + - 'int main(){return 0;}', + '#include \n' + + 'int main(){std::istringstream input; mfem::Mesh mesh(input); return mesh.Dimension();}', dependencies: system_candidate_dependencies, - name: 'system MFEM public headers are consumable', + name: 'system MFEM matches the selected C++ ABI', ) endif @@ -597,6 +598,15 @@ else bundle_command += ['--c-link-arg=' + platform_link_arg] bundle_command += ['--cxx-link-arg=' + platform_link_arg] endforeach + # Meson does not include the native-file c_ld/cpp_ld selection in *_link_args. + # Carry that selection into the external CMake/Make dependency builds too. + linker_flags = {'ld.bfd': '-fuse-ld=bfd', 'ld.gold': '-fuse-ld=gold', 'ld.lld': '-fuse-ld=lld'} + if linker_flags.has_key(cc.get_linker_id()) + bundle_command += ['--c-link-arg=' + linker_flags[cc.get_linker_id()]] + endif + if linker_flags.has_key(cpp.get_linker_id()) + bundle_command += ['--cxx-link-arg=' + linker_flags[cpp.get_linker_id()]] + endif foreach i : range(mfem_feature_names.length()) bundle_command += [ '--feature', diff --git a/meson_options.txt b/meson_options.txt index 395eaa6c..651963d1 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -3,6 +3,7 @@ option('feature_profile', type: 'combo', choices: ['minimal', 'portable', 'full' option('build_python', type: 'boolean', value: false, description: 'Build the nanobind Python extension and install native assets inside the Python package.') option('build_tests', type: 'boolean', value: true, description: 'Register serial, parallel, and Python smoke tests when applicable.') option('build_checks', type: 'boolean', value: true, description: 'Build the compile-time checks for SERiF type invariants.') +option('sandbox_expected_errors', type: 'array', value: ['finite_sandbox:NON_FINITE_VALUE_ERROR', 'quadrature_sandbox:INVALID_QUADRATURE_ORDER'], description: 'Intentional error examples, as sandbox_name:ERROR_CODE. Other sandboxes must exit successfully.') option('install_mfem', type: 'boolean', value: true, description: 'Install the source-built MFEM bundle and its runtime dependencies.') option('dependency_prefix', type: 'string', value: '', description: 'Explicit prefix containing optional TPLs. Used even when allow_preinstalled=false.') option('jobs', type: 'integer', min: 1, max: 1024, value: 4, description: 'Parallel jobs for the CMake/Make source bundle.') diff --git a/native/clang.ini b/native/clang.ini new file mode 100644 index 00000000..21977ae9 --- /dev/null +++ b/native/clang.ini @@ -0,0 +1,3 @@ +[binaries] +c = 'clang' +cpp = 'clang++' diff --git a/native/gcc-libcxx.ini b/native/gcc-libcxx.ini new file mode 100644 index 00000000..a9de4787 --- /dev/null +++ b/native/gcc-libcxx.ini @@ -0,0 +1,5 @@ +# Linux GCC 16+ with libc++ development headers in the standard system location. +# Layer after gcc.ini. Override this include path for a non-system LLVM install. +[built-in options] +cpp_args = ['-nostdinc++', '-isystem', '/usr/include/c++/v1'] +cpp_link_args = ['-nostdlib++', '-lc++', '-lc++abi'] diff --git a/native/gcc.ini b/native/gcc.ini new file mode 100644 index 00000000..3b70f11b --- /dev/null +++ b/native/gcc.ini @@ -0,0 +1,3 @@ +[binaries] +c = 'gcc' +cpp = 'g++' diff --git a/native/ld.ini b/native/ld.ini new file mode 100644 index 00000000..b0fde818 --- /dev/null +++ b/native/ld.ini @@ -0,0 +1,3 @@ +[binaries] +c_ld = 'bfd' +cpp_ld = 'bfd' diff --git a/native/libcxx.ini b/native/libcxx.ini new file mode 100644 index 00000000..82fe10d6 --- /dev/null +++ b/native/libcxx.ini @@ -0,0 +1,4 @@ +# Layer after clang.ini. GCC does not support Clang's -stdlib option. +[built-in options] +cpp_args = ['-stdlib=libc++'] +cpp_link_args = ['-stdlib=libc++'] diff --git a/native/libstdcxx.ini b/native/libstdcxx.ini new file mode 100644 index 00000000..ae18519f --- /dev/null +++ b/native/libstdcxx.ini @@ -0,0 +1,5 @@ +# Layer after clang.ini to explicitly select libstdc++. +# GCC already uses libstdc++ by default and does not accept -stdlib. +[built-in options] +cpp_args = ['-stdlib=libstdc++'] +cpp_link_args = ['-stdlib=libstdc++'] diff --git a/native/lld.ini b/native/lld.ini new file mode 100644 index 00000000..00a3e0a3 --- /dev/null +++ b/native/lld.ini @@ -0,0 +1,3 @@ +[binaries] +c_ld = 'lld' +cpp_ld = 'lld' diff --git a/src/compile_time_checks/serif/discretization/blocks/fields.cpp b/src/compile_time_checks/serif/discretization/blocks/fields.cpp index 2e76e910..d00804ea 100644 --- a/src/compile_time_checks/serif/discretization/blocks/fields.cpp +++ b/src/compile_time_checks/serif/discretization/blocks/fields.cpp @@ -24,5 +24,5 @@ namespace { static_assert(DensitySpace::order == 2, "Density should be in a second order space"); static_assert(DensitySpace::rank::value == 0, "Density should be a scalar field"); static_assert(std::same_as, domain::StellarDomains>, "Density should be supported on stellar domains"); - static_assert(std::same_as, serif::dimensions::Density>, "Density should have a quantity of type Density"); + static_assert(std::same_as, serif::dimensions::MassDensity>, "Density should have a quantity of type Density"); } diff --git a/src/include/serif/dimensions/dimensions.hpp b/src/include/serif/dimensions/dimensions.hpp index 22746ee7..8f683e0d 100644 --- a/src/include/serif/dimensions/dimensions.hpp +++ b/src/include/serif/dimensions/dimensions.hpp @@ -1,154 +1,346 @@ #pragma once +/** + * @file dimensions.hpp + * @author Emily M. Boudreaux + * + * @brief File containing prue dimensions. Note that this is not intended to be (nor does it + * have the capability to be) a general dimensional analysis library. There is no + * understanding here that, for example, a dimension of length^2 is an area. Rather, + * the purpose of this is to force developers to be explicit about what values they + * are passing to what functions. Further, most of this system is built to be compile + * time constant. This means that the compiler can still apply many optimizations. Note + * that the dimensions here do not contain a value, rather these are *just* the concept + * of a dimension. To store a value you must use a @ref Quantity and @ref QuantityValue. + */ #include #include "serif/dimensions/quantities.hpp" namespace serif::dimensions { + /** + * @struct Dimensionless + * + * @brief Non dimensional type. + */ struct Dimensionless final : PhysicalQuantity { static constexpr std::string_view identifier = "dimensionless"; }; + /** + * @struct Mass + * + * @brief Dimension of mass + */ struct Mass final : PhysicalQuantity { static constexpr std::string_view identifier = "mass"; }; + /** + * @struct Length + * + * @brief Dimension of Length + */ struct Length final : PhysicalQuantity { static constexpr std::string_view identifier = "length"; }; + /** + * @struct Time + * + * @brief Dimension of Time + */ struct Time final : PhysicalQuantity { static constexpr std::string_view identifier = "time"; }; + /** + * @struct Area + * + * @brief Dimension of Area + */ struct Area final : PhysicalQuantity { static constexpr std::string_view identifier = "area"; }; + /** + * @struct Volume + * + * @brief Dimension of Volume + */ struct Volume final : PhysicalQuantity { static constexpr std::string_view identifier = "volume"; }; - struct Density final : ThermodynamicQuantity { + /** + * @struct MassDensity + * + * @brief Dimension of Mass Density + */ + struct MassDensity final : ThermodynamicQuantity { static constexpr std::string_view identifier = "density"; }; - struct SurfaceDensity final : PhysicalQuantity { - static constexpr std::string_view identifier = "surface_density"; - }; - + /** + * @struct NumberDensity + * + * @brief Dimension of Number Density + */ struct NumberDensity final : ThermodynamicQuantity { static constexpr std::string_view identifier = "number_density"; }; + /** + * @struct Pressure + * + * @brief Dimension of Pressure + */ struct Pressure final : ThermodynamicQuantity { static constexpr std::string_view identifier = "pressure"; }; + /** + * @struct Temperature + * + * @brief Dimension of Temperature + */ struct Temperature final : ThermodynamicQuantity { static constexpr std::string_view identifier = "temperature"; }; + /** + * @struct Entropy + * + * @brief Dimension of Entropy + */ struct Entropy final : ThermodynamicQuantity { static constexpr std::string_view identifier = "entropy"; }; + /** + * @struct SpecificEntropy + * + * @brief Dimension of Specific Entropy + */ struct SpecificEntropy final : ThermodynamicQuantity { static constexpr std::string_view identifier = "specific_entropy"; }; + /** + * @struct ChemicalPotential + * + * @brief Dimension of Chemical Potential + */ struct ChemicalPotential final : ThermodynamicQuantity { static constexpr std::string_view identifier = "chemical_potential"; }; + /** + * @struct Energy + * + * @brief Dimension of Energy + */ struct Energy final : PhysicalQuantity { static constexpr std::string_view identifier = "energy"; }; + /** + * @struct InternalEnergy + * + * @brief Dimension of Internal Energy + */ struct InternalEnergy final : ThermodynamicQuantity { static constexpr std::string_view identifier = "internal_energy"; }; + /** + * @struct SpecificEnergy + * + * @brief Dimension of Specific Energy + */ struct SpecificEnergy final : PhysicalQuantity { static constexpr std::string_view identifier = "specific_energy"; }; + /** + * @struct SpecificInternalEnergy + * + * @brief Dimension of Specific Internal Energy + */ struct SpecificInternalEnergy final : ThermodynamicQuantity { static constexpr std::string_view identifier = "specific_internal_energy"; }; + /** + * @struct SpecificEnthalpy + * + * @brief Dimension of Specific Enthalpy + */ struct SpecificEnthalpy final : ThermodynamicQuantity { static constexpr std::string_view identifier = "specific_enthalpy"; }; + /** + * @struct EnergyDensity + * + * @brief Dimension of Energy Density + */ struct EnergyDensity final : ThermodynamicQuantity { static constexpr std::string_view identifier = "energy_density"; }; + /** + * @struct GravitationalPotential + * + * @brief Dimension of Gravitational Potential + */ struct GravitationalPotential final : PhysicalQuantity { static constexpr std::string_view identifier = "gravitational_potential"; }; + /** + * @struct Velocity + * + * @brief Dimension of Velocity + */ struct Velocity final : PhysicalQuantity { static constexpr std::string_view identifier = "velocity"; }; + /** + * @struct Acceleration + * + * @brief Dimension of Acceleration + */ struct Acceleration final : PhysicalQuantity { static constexpr std::string_view identifier = "acceleration"; }; + /** + * @struct Frequency + * + * @brief Dimension of Frequency + */ struct Frequency final : PhysicalQuantity { static constexpr std::string_view identifier = "frequency"; }; + /** + * @struct AngularVelocity + * + * @brief Dimension of Angular Velocity + */ struct AngularVelocity final : PhysicalQuantity { static constexpr std::string_view identifier = "angular_velocity"; }; + /** + * @struct Momentum + * + * @brief Dimension of Momentum + */ struct Momentum final : PhysicalQuantity { static constexpr std::string_view identifier = "momentum"; }; + /** + * @struct AngularMomentum + * + * @brief Dimension of Angular Momentum + */ struct AngularMomentum final : PhysicalQuantity { static constexpr std::string_view identifier = "angular_momentum"; }; + /** + * @struct MomentOfInertia + * + * @brief Dimension of Moment Of Inertia + */ struct MomentOfInertia final : PhysicalQuantity { static constexpr std::string_view identifier = "moment_of_inertia"; }; + /** + * @struct Force + * + * @brief Dimension of Force + */ struct Force final : PhysicalQuantity { static constexpr std::string_view identifier = "force"; }; + /** + * @struct Torque + * + * @brief Dimension of Torque + */ struct Torque final : PhysicalQuantity { static constexpr std::string_view identifier = "torque"; }; + /** + * @struct Power + * + * @brief Dimension of Power + */ struct Power final : PhysicalQuantity { static constexpr std::string_view identifier = "power"; }; + /** + * @struct Luminosity + * + * @brief Dimension of Luminosity + */ struct Luminosity final : PhysicalQuantity { static constexpr std::string_view identifier = "luminosity"; }; + /** + * @struct MassFlowRate + * + * @brief Dimension of Mass Flow Rate + */ struct MassFlowRate final : PhysicalQuantity { static constexpr std::string_view identifier = "mass_flow_rate"; }; + /** + * @struct Opacity + * + * @brief Dimension of Opacity + * @note we are not currently using this in the code since we are still using a polytropic + * eos. However, when we actually get to using this we will likely want to break this out + * into more specific and meaningful opacity dimensions (RMO, per unit wavelength, etc...) + */ struct Opacity final : PhysicalQuantity { static constexpr std::string_view identifier = "opacity"; }; + /** + * @struct DynamicViscosity + * + * @brief Dimension of Dynamic Viscosity + */ struct DynamicViscosity final : PhysicalQuantity { static constexpr std::string_view identifier = "dynamic_viscosity"; }; + /** + * @struct KinematicViscosity + * + * @brief Dimension of Kinematic Viscosity + */ struct KinematicViscosity final : PhysicalQuantity { static constexpr std::string_view identifier = "kinematic_viscosity"; }; + /** + * @struct MagneticFluxDensity + * + * @brief Dimension of Magnetic Flux Density + */ struct MagneticFluxDensity final : PhysicalQuantity { static constexpr std::string_view identifier = "magnetic_flux_density"; }; diff --git a/src/include/serif/dimensions/partial.hpp b/src/include/serif/dimensions/partial.hpp index 52d14c9a..ffff99b6 100644 --- a/src/include/serif/dimensions/partial.hpp +++ b/src/include/serif/dimensions/partial.hpp @@ -1,21 +1,78 @@ #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 struct WithRespectTo final {}; + /** + * @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 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 operator+( const PartialDerivative& lhs, const PartialDerivative& rhs @@ -23,6 +80,13 @@ namespace serif::dimensions { return PartialDerivative{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 operator-( const PartialDerivative& lhs, const PartialDerivative& rhs @@ -30,6 +94,14 @@ namespace serif::dimensions { return PartialDerivative{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 friend constexpr PartialDerivative operator*( const PartialDerivative& lhs, @@ -38,6 +110,14 @@ namespace serif::dimensions { return PartialDerivative{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 friend constexpr PartialDerivative operator*( const Scalar& lhs, @@ -46,6 +126,14 @@ namespace serif::dimensions { return PartialDerivative{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 friend constexpr PartialDerivative operator/( const PartialDerivative& lhs, @@ -54,6 +142,13 @@ namespace serif::dimensions { return PartialDerivative{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 friend constexpr std::partial_ordering operator<=>( const PartialDerivative& lhs, @@ -62,6 +157,13 @@ namespace serif::dimensions { 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 friend constexpr std::partial_ordering operator<=>( Scalar lhs, @@ -70,6 +172,12 @@ namespace serif::dimensions { 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 &lhs, const PartialDerivative &rhs @@ -78,6 +186,6 @@ namespace serif::dimensions { } private: - double m_value; + 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. }; } \ No newline at end of file diff --git a/src/include/serif/dimensions/quantities.hpp b/src/include/serif/dimensions/quantities.hpp index a186cdb0..b8a8110b 100644 --- a/src/include/serif/dimensions/quantities.hpp +++ b/src/include/serif/dimensions/quantities.hpp @@ -1,4 +1,11 @@ #pragma once +/** + * @file quantities.hpp + * @author Emily M. Boudreaux + * + * @brief This file contains the code to associate numeric data to a dimension. We call + * this a Quantity and a QuantityValue. + */ #include #include @@ -14,19 +21,60 @@ namespace serif::dimensions { using utils::misc::concepts::IsNumeric; + /** + * @struct PhysicalQuantity + * + * @breif General type for any physical quantity. + */ struct PhysicalQuantity { }; + + /** + * @struct ThermodynamicQuantity + * + * @breif Slightly more specific type for thermodynamic types. Note both of these + * are empty at runtime, these are only useful as compile time tools to control + * what code the compiler generates. + */ struct ThermodynamicQuantity: PhysicalQuantity { }; + /** + * @concept PhysicalQuantityType + * + * @tparam QuantityCandidate The type that will be validated + * + * @brief Is compile-time true of the QuantityCandidate can have any const / volatile stripped + * and if its derived from PhysicalQuantity. Note all ThermodynamicQuantities will also + * satisfy this concept. + */ template concept PhysicalQuantityType = std::same_as> && std::derived_from; + /** + * @concept ThermodynamicQuantityType + * + * @tparam QuantityCandidate + * + * @brief Is compile-time true of the QuantityCandidate can have any const / volatile stripped + * and if its derived from PhysicalQuantity (both enforced through checking @ref PhysicalQuantityType) + * and then that is is also derived from ThermodynamicQuantity. Note not all physical quanties will + * satisfy this. + */ template concept ThermodynamicQuantityType = PhysicalQuantityType && std::derived_from; + /** + * @class QuantityValue + * @tparam Quantity Some valid PhysicalQuantityType. Note all the dimensions in @ref dimensions.hpp + * are of type PhysicalQuantity. There should be no other dimensions in the code. + * + * @brief This is the non derivative equivalent to @ref PartialDerivative. This associates + * a dimension with a value and, if that value can be determined at compile time, can be + * decomposed to a transparent double through .value() + */ template class QuantityValue final { public: @@ -36,11 +84,25 @@ namespace serif::dimensions { return m_value; } + + /** + * @breif Default comparison operator for two QuantityValues. Note that there is + * an implicit requirement these be of the same specialization. E.g. each have the same + * PhysicalQuantityType Quantity type parameter. Because this is the default and this + * class has only one trivially comparable member this just checks if m_value == m_value. + * @return true if the values in both are equal. + */ [[nodiscard]] friend constexpr bool operator==( const QuantityValue &, const QuantityValue & ) noexcept = default; + /** + * @breif overload of + operator for two quantities with the same PhysicalQuantityType + * @param lhs One Quantity + * @param rhs Other Quantity + * @return a new quantity with the same PhysicalQuantityType but with a value = lhs.m_value + rhs.m_value + */ friend constexpr QuantityValue operator+( const QuantityValue &lhs, const QuantityValue &rhs @@ -48,6 +110,12 @@ namespace serif::dimensions { return QuantityValue{lhs.m_value + rhs.m_value}; } + /** + * @breif overload of - operator for two quantities with the same PhysicalQuantityType + * @param lhs One Quantity + * @param rhs Other Quantity + * @return a new quantity with the same PhysicalQuantityType but with a value = lhs.m_value - rhs.m_value + */ friend constexpr QuantityValue operator-( const QuantityValue &lhs, const QuantityValue &rhs @@ -55,6 +123,13 @@ namespace serif::dimensions { return QuantityValue{lhs.m_value - rhs.m_value}; } + /** + * @breif Overload of the * operator for a left quantity and some right numeric, scalar, value. + * @tparam Scalar A value satisfying @ref IsNumeric + * @param lhs some quantity value + * @param rhs some scalar value + * @return some quantity value with the same type as lhs but with value = lhs.m_value * scalar + */ template friend constexpr QuantityValue operator*( const QuantityValue &lhs, @@ -63,6 +138,13 @@ namespace serif::dimensions { return QuantityValue{lhs.m_value * static_cast(rhs)}; } + /** + * @breif Overload of the * operator for a right quantity and some left numeric, scalar, value. + * @tparam Scalar A value satisfying @ref IsNumeric + * @param lhs some scalar value + * @param rhs some quantity value + * @return some quantity value with the same type as lhs but with value = scalar * lhs.m_value + */ template friend constexpr QuantityValue operator*( const Scalar lhs, @@ -71,6 +153,13 @@ namespace serif::dimensions { return QuantityValue{static_cast(lhs) * rhs.m_value}; } + /** + * @breif Overload of the / operator for a left quantity and some right numeric, scalar, value. + * @tparam Scalar A value satisfying @ref IsNumeric + * @param lhs some quantity value + * @param rhs some scalar value + * @return some quantity value with the same type as lhs but with value = lhs.m_value / scalar + */ template friend constexpr QuantityValue operator/( const QuantityValue &lhs, @@ -79,6 +168,13 @@ namespace serif::dimensions { return QuantityValue{lhs.m_value / static_cast(rhs)}; } + /** + * @breif Partial ordering overload for some left quantity and right scalar + * @tparam Scalar A value satisfying @ref IsNumeric + * @param lhs some quantity value + * @param rhs some scalar value + * @return The partial ordering comparing lhs.m_value to scalar + */ template friend constexpr std::partial_ordering operator<=>( const QuantityValue &lhs, @@ -87,6 +183,13 @@ namespace serif::dimensions { return lhs.m_value <=> static_cast(rhs); } + /** + * @breif Partial ordering overload for some right quantity and left scalar + * @tparam Scalar A value satisfying @ref IsNumeric + * @param lhs some scalar value + * @param rhs some quantity value + * @return The partial ordering comparing scalar to lhs.m_value + */ template friend constexpr std::partial_ordering operator<=>( const Scalar lhs, @@ -95,6 +198,12 @@ namespace serif::dimensions { return static_cast(lhs) <=> rhs.m_value; } + /** + * @breif partial ordering overload to compare two quantity values of the same type + * @param lhs Some quantity value + * @param rhs Other quantity value + * @return the partial ordering of lhs.m_value <=> rhs.m_value + */ friend constexpr std::partial_ordering operator<=>( const QuantityValue &lhs, const QuantityValue &rhs @@ -103,19 +212,43 @@ namespace serif::dimensions { } private: - double m_value; + double m_value; ///< Numeric value associated with the dimension of QuantityValue. }; + /** + * @struct IsQuantityValue + * @tparam Candidate some type that may be a QuantityValue + * + * @breif Most general specialization, defaults to compile time false. + */ template struct IsQuantityValue : std::false_type { }; + /** + * @struct IsQuantityValue + * @tparam Quantity Some quantity that satisfies the @ref PhysicalQuantityType concept + * + * @breif More specific so if a type satisfies this the compiler will select it first. This evaluates + * to compile time true if Quantity satisfies the PhysicalQuantityType concept which then + * creats a specialization of IsQuantityValue that is compile time true for any Quantity of that set. + */ template struct IsQuantityValue> : std::true_type { }; + /** + * @concept QuantityValueType + * @tparam Candidate Some type that might be a quantity value type + * + * @brief Turn those compile time structs into a concept that can be used to constrain + * a template parameter + */ template concept QuantityValueType = IsQuantityValue>::value; - + /** + * @struct QuantityOf + * @tparam Candidate Some type that may be able to report what its dimension is + */ template struct QuantityOf; diff --git a/src/include/serif/dimensions/runtime/concepts.hpp b/src/include/serif/dimensions/runtime/concepts.hpp index 5b7a4420..03d7a6ac 100644 --- a/src/include/serif/dimensions/runtime/concepts.hpp +++ b/src/include/serif/dimensions/runtime/concepts.hpp @@ -1,4 +1,13 @@ #pragma once +/** + * @file concepts.hpp + * @author Emily M. Boudreaux + * + * @brief Runtime concept for a type that can be projected to a runtime type. + * This is useful when converting from the compile time type system to something + * that can be inspected at runtime. + */ + #include #include @@ -6,12 +15,25 @@ #include "serif/dimensions/runtime/runtime.hpp" namespace serif::dimensions::runtime { + + /** + * @concept RuntimeIdentifiedThermodynamicQuantity + * @breif Concept to limit template arguments such that they are compatible with the expected + * ThermodynamicQuantity interface. Specifically, a valid type must contain a member + * identifier which can be converted to std::string_view and that member, when converted + * to a string_view must be longer than 0 length. + */ template concept RuntimeIdentifiedThermodynamicQuantity = ThermodynamicQuantityType && requires {{Quantity::identifier} -> std::convertible_to; } && (std::string_view{Quantity::identifier}.size() > 0); + /** + * @brief Extract a compile time identifier from a thermodynamic quantity and project it into a runtime + * string_view + * @tparam Quantity Type which @ref RuntimeIdentifiedThermodynamicQuantity allows + */ template inline constexpr ThermodynamicQuantityID thermodynamicQuantityID{std::string_view{Quantity::identifier}}; } \ No newline at end of file diff --git a/src/include/serif/dimensions/runtime/runtime.hpp b/src/include/serif/dimensions/runtime/runtime.hpp index 45015b42..263a6bf9 100644 --- a/src/include/serif/dimensions/runtime/runtime.hpp +++ b/src/include/serif/dimensions/runtime/runtime.hpp @@ -1,13 +1,30 @@ #pragma once +/** + * @file runtime.hpp + * @author Emily M. Boudreaux + * + * @brief runtime usable types representing the compile time dimension type system. + * Note that generally this is not a dimensional analysis library. Though perhaps + * in the future it will grow to something like that. Rather, this system + * is intended to ensure that developers are explicit which what numeric values mean. + * There is more detail on this in the file comment for @ref dimensions.hpp and @ref quantities.hpp + */ + #include #include #include namespace serif::dimensions::runtime { + /** + * @class ThermodynamicQuantityID + * + * @brief Runtime resolvable ID for a thermodynamic quantity. Used in conjunction with + * concepts to resolve compile time thermodynamic types to runtime values. + */ class ThermodynamicQuantityID final { public: - explicit constexpr ThermodynamicQuantityID(std::string_view name) noexcept : m_name(name) {}; + explicit constexpr ThermodynamicQuantityID(const std::string_view name) noexcept : m_name(name) {}; [[nodiscard]] constexpr std::string_view name() const noexcept { return m_name; @@ -18,9 +35,17 @@ namespace serif::dimensions::runtime { std::string_view m_name; // We can use a string view here since the constructor is constexpr and the string view will be valid for the lifetime of the program. }; + /** + * @struct RuntimeQuantityValue + * + * @brief Runtime resolved type for a quantity value. This holds both an ID and a value. This is the runtime + * equivalent to a @ref QuantityValue. Note that where these differ is that QuantityValue + * is built with template types. This allows the compiler to resolve it to a plain numeric type and still do optimizations + * That is generally not the case with RuntimeQuantityValue + */ struct RuntimeQuantityValue final { ThermodynamicQuantityID id; - double value; + double value{}; }; // TODO: This should be refactored into the eos module rather than the dimensions module diff --git a/src/include/serif/dimensions/type_alias.hpp b/src/include/serif/dimensions/type_alias.hpp index 56bb5328..473f294b 100644 --- a/src/include/serif/dimensions/type_alias.hpp +++ b/src/include/serif/dimensions/type_alias.hpp @@ -10,8 +10,7 @@ namespace serif::dimensions { using TimeValue = QuantityValue