From 950f86e9e7a81235d4b80981280010a4781989a1 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Wed, 7 Oct 2026 16:15:04 -0400 Subject: [PATCH] feat(equations/arithmetic): added sum and different equation types these are needed for things like the displacment force which is the sum of the rotational, gravitational, and pressure forces --- src/include/serif/equations/arithmetic.hpp | 186 +++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 src/include/serif/equations/arithmetic.hpp diff --git a/src/include/serif/equations/arithmetic.hpp b/src/include/serif/equations/arithmetic.hpp new file mode 100644 index 00000000..f8ef6a0f --- /dev/null +++ b/src/include/serif/equations/arithmetic.hpp @@ -0,0 +1,186 @@ +#pragma once + +/** + * @file arithmetic.hpp + * @author Emily M. Boudreaux + * + * @brief File containing the EquationScale and EquationSum templates, which allow for scaling and summing equations. + * + * The astute observer will notice that none of the code in this file or indeed in in any part of the equations module actually implements summation or scaled + * addition more generally. Rather, the code to manage that is implemented in the operator/backend/mfem module. Effectively these + * templates are compile time tags telling that system how to combine weak contributions. The reason for this comes down to we dont really want to sum + * at the point wise level; rather, we generally want to sum already integrated contributions. Since equations, by design, do not know any details about + * the global system they are unable to manage this themselves. + * + * There is another, as yet unexplored case, where we introduce some point-wise summation equation. This could be implemented in a much more + * local fashion and would probably be easier to trace; however, it would be semantically different in its meaning. + */ + +#include +#include +#include +#include +#include +#include + +#include "serif/equations/concepts.hpp" +#include "serif/utils/error/errors.hpp" + +namespace serif::equations { + + /** + * @brief Scale an equation by a constant coefficient. + * + * This class is a compile-time tag that indicates that the weak contributions from a given equation should be scaled by the given coefficient. + * The actual scaling is performed by the backend. A simple example would be if the scale is set to 1 then the weak contributions would be integrated unchanged; + * whereas if the scale were set to -1 then the weak contributions would be integrated and then negated. + * + * @tparam EquationT The equation type to scale. + */ + template + class EquationScale final { + public: + using Test = typename EquationT::Test; ///< The test type of the scaled equation is the same as the test type of the original equation. + + static constexpr std::string_view name = "scaled equation"; ///< Compile time name for the scaled equation, used for debugging and logging. + + /** + * @brief Construct a scaled equation. + * + * @param equation The equation to scale. + * @param coefficient The coefficient to scale the equation by. Must be finite. + * + * @throws std::invalid_argument If the coefficient is not finite. + */ + explicit EquationScale( + EquationT equation, + const double coefficient + ) : + m_equation(std::move(equation)), + m_coefficient(coefficient) { + using namespace utils::errors; + + serif_ensure(std::isfinite(coefficient), "An equation coefficient must be finite."); + } + + + /** + * @brief Return the contributions of the scaled equation. + * + * @return A tuple containing the contributions of the scaled equation. + */ + [[nodiscard]] auto contributions() const noexcept { + return std::tie(m_equation); + } + + /** + * @brief Return the coefficient of the scaled equation. + * + * @return The coefficient of the scaled equation. + */ + [[nodiscard]] double coefficient() const noexcept { + return m_coefficient; + } + + private: + EquationT m_equation; ///< The equation being scaled. + double m_coefficient; ///< The coefficient to scale the equation by. Must be finite. + }; + + /** + * @brief Sum multiple equations together. + * + * This class is a compile-time tag that indicates that the weak contributions from multiple equations should be summed together. The actual summation is performed by the backend. + * A simple example would be if two equations are summed together, then the weak contributions from both equations would be integrated and then added together to form the final residual. + * Note also that we do not define a coefficient here. The backend only ever sums things, as you can see in EquationDifference to subtract we locally + * introduce a -1 scaling factor. + * + * @requires The FirstT and RestTs equations must share the same test type, otherwise the summation is not well-defined. + * + * @note Only equations sharing a residual row can or should be summed together. + * + * @tparam FirstT The first equation type to sum. + * @tparam RestTs The remaining equation types to sum. + */ + template + requires (std::same_as && ...) + class EquationSum final { + public: + using Test = typename FirstT::Test; ///< The test type of the summed equations is the same as the test type of the first equation. + + static constexpr std::string_view name = "equation sum"; ///< Compile time name for the summed equations, used for debugging and logging. + + /** + * @brief Construct a summed equation. + * + * @param first The first equation to sum. + * @param rest Pack of the remaining equations to sum. + * + * @note the constructor uses the initializer list for m_equations, note that the coefficient defaults to 1.0 + */ + explicit EquationSum( + FirstT first, + RestTs... rest + ) : + m_equations(std::move(first), std::move(rest)...) {} + + /** + * @brief Return the contributions of the summed equations. + * + * @return A tuple containing the contributions of the summed equations. + */ + [[nodiscard]] const auto& contributions() const noexcept { + return m_equations; + } + + private: + std::tuple m_equations; ///< The equations being summed together. + }; + + /** + * @brief Subtract one equation from another. + * + * This class is a compile-time tag that indicates that the weak contributions from the right equation should be subtracted from the weak contributions of the left equation. + * The actual subtraction is performed by the backend. A simple example would be if the left equation has a weak contribution of 1 and the right equation has a weak contribution of 2, + * then the final residual would be -1. + * + * @requires The left and right equations must share the same test type, otherwise the subtraction is not well-defined. + * + * @note Only equations sharing a residual row can or should be subtracted from one another. + * + * @tparam LeftT The left equation type to subtract from. + * @tparam RightT The right equation type to subtract. + */ + template + requires std::same_as + class EquationDifference final { + public: + using Test = typename LeftT::Test; ///< The test type of the difference equation is the same as the test type of the left equation. + + static constexpr std::string_view name = "equation difference"; ///< Compile time name for the difference equation, used for debugging and logging. + + /** + * @brief Construct a difference equation. + * + * @param left The left equation to subtract from. + * @param right The right equation to subtract. + */ + explicit EquationDifference( + LeftT left, + RightT right + ) : + m_equation(std::move(left), EquationScale{std::move(right), -1.0}) {} + + /** + * @brief Return the contributions of the difference equation. + * + * @return A tuple containing the contributions of the difference equation. + */ + [[nodiscard]] const auto& contributions() const noexcept { + return m_equation.contributions(); + } + + private: + EquationSum> m_equation; ///< The equation representing the difference, which is a sum of the left equation and the scaled right equation (scaled by -1). + }; +} // namespace serif::equations