diff --git a/src/algebra/TinyMatrix.hpp b/src/algebra/TinyMatrix.hpp index 0d3b9c0e5fef46f85fa576b68f940708c8ed529d..e347650b362e32a9e37ccc58841b02674967aead 100644 --- a/src/algebra/TinyMatrix.hpp +++ b/src/algebra/TinyMatrix.hpp @@ -14,6 +14,10 @@ template <size_t M, size_t N = M, typename T = double> class [[nodiscard]] TinyMatrix { public: + inline static constexpr size_t Dimension = M * N; + inline static constexpr size_t NumberOfRows = M; + inline static constexpr size_t NumberOfColumns = N; + using data_type = T; private: diff --git a/src/algebra/TinyVector.hpp b/src/algebra/TinyVector.hpp index 382741093f351cea80ff1e261399df9583dc4d0c..029b259f568866208919f5864980fdd3079dc975 100644 --- a/src/algebra/TinyVector.hpp +++ b/src/algebra/TinyVector.hpp @@ -15,7 +15,8 @@ class [[nodiscard]] TinyVector { public: inline static constexpr size_t Dimension = N; - using data_type = T; + + using data_type = T; private: T m_values[N]; diff --git a/src/language/modules/MathModule.cpp b/src/language/modules/MathModule.cpp index 69f6890fbc5e8c3e3515901acd5b5102100e8c35..2217424e43bb9b276cf2260ce88c547252f88982 100644 --- a/src/language/modules/MathModule.cpp +++ b/src/language/modules/MathModule.cpp @@ -70,6 +70,18 @@ MathModule::MathModule() this->_addBuiltinFunction("round", std::make_shared<BuiltinFunctionEmbedder<int64_t(double)>>( [](double x) -> int64_t { return std::lround(x); })); + this->_addBuiltinFunction("min", std::make_shared<BuiltinFunctionEmbedder<double(double, double)>>( + [](double x, double y) -> double { return std::min(x, y); })); + + this->_addBuiltinFunction("min", std::make_shared<BuiltinFunctionEmbedder<int64_t(int64_t, int64_t)>>( + [](int64_t x, int64_t y) -> int64_t { return std::min(x, y); })); + + this->_addBuiltinFunction("max", std::make_shared<BuiltinFunctionEmbedder<double(double, double)>>( + [](double x, double y) -> double { return std::max(x, y); })); + + this->_addBuiltinFunction("max", std::make_shared<BuiltinFunctionEmbedder<int64_t(int64_t, int64_t)>>( + [](int64_t x, int64_t y) -> int64_t { return std::max(x, y); })); + this->_addBuiltinFunction("dot", std::make_shared<BuiltinFunctionEmbedder<double(const TinyVector<1>, const TinyVector<1>)>>( [](const TinyVector<1> x, const TinyVector<1> y) -> double { return dot(x, y); })); diff --git a/src/language/node_processor/BinaryExpressionProcessor.hpp b/src/language/node_processor/BinaryExpressionProcessor.hpp index 8cc1a430f96d2892110afaff0b1ac8bc6ccaf347..7f278d4c7d4a007ad5db4c9f5d444665ccca56d6 100644 --- a/src/language/node_processor/BinaryExpressionProcessor.hpp +++ b/src/language/node_processor/BinaryExpressionProcessor.hpp @@ -260,7 +260,7 @@ struct BinaryExpressionProcessor<BinaryOpT, std::shared_ptr<ValueT>, std::shared return this->_eval(m_node.children[0]->execute(exec_policy), m_node.children[1]->execute(exec_policy)); } catch (const NormalError& error) { - throw ParseError(error.what(), m_node.begin()); + throw ParseError(error.what(), m_node.begin()); // LCOV_EXCL_LINE } } @@ -297,7 +297,7 @@ struct BinaryExpressionProcessor<BinaryOpT, std::shared_ptr<ValueT>, A_DataT, st return this->_eval(m_node.children[0]->execute(exec_policy), m_node.children[1]->execute(exec_policy)); } catch (const NormalError& error) { - throw ParseError(error.what(), m_node.begin()); + throw ParseError(error.what(), m_node.begin()); // LCOV_EXCL_LINE } } @@ -335,7 +335,7 @@ struct BinaryExpressionProcessor<BinaryOpT, std::shared_ptr<ValueT>, std::shared return this->_eval(m_node.children[0]->execute(exec_policy), m_node.children[1]->execute(exec_policy)); } catch (const NormalError& error) { - throw ParseError(error.what(), m_node.begin()); + throw ParseError(error.what(), m_node.begin()); // LCOV_EXCL_LINE } } diff --git a/src/language/utils/CMakeLists.txt b/src/language/utils/CMakeLists.txt index c8928003d045b7216b8edd8ea13b989d697d3268..cd3f8af2314e96e87bacec25ac1dfc08d015192b 100644 --- a/src/language/utils/CMakeLists.txt +++ b/src/language/utils/CMakeLists.txt @@ -23,8 +23,9 @@ add_library(PugsLanguageUtils BuiltinFunctionEmbedderUtils.cpp DataVariant.cpp EmbeddedData.cpp - EmbeddedIDiscreteFunctionOperators.cpp EmbeddedIDiscreteFunctionMathFunctions.cpp + EmbeddedIDiscreteFunctionOperators.cpp + EmbeddedIDiscreteFunctionUtils.cpp FunctionSymbolId.cpp IncDecOperatorRegisterForN.cpp IncDecOperatorRegisterForR.cpp diff --git a/src/language/utils/EmbeddedIDiscreteFunctionMathFunctions.cpp b/src/language/utils/EmbeddedIDiscreteFunctionMathFunctions.cpp index d4d10c6c7c531add6cd9f32c2ad376fcd97100a6..f49c4c033251bf46d7cd91e7c524af4d146db9d3 100644 --- a/src/language/utils/EmbeddedIDiscreteFunctionMathFunctions.cpp +++ b/src/language/utils/EmbeddedIDiscreteFunctionMathFunctions.cpp @@ -8,7 +8,7 @@ #include <scheme/IDiscreteFunction.hpp> #include <scheme/IDiscreteFunctionDescriptor.hpp> -#define DISCRETE_FUNCTION_CALL(FUNCTION, ARG) \ +#define DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(FUNCTION, ARG) \ if (ARG->dataType() == ASTNodeDataType::double_t and ARG->descriptor().type() == DiscreteFunctionType::P0) { \ switch (ARG->mesh()->dimension()) { \ case 1: { \ @@ -28,55 +28,55 @@ } \ } \ } else { \ - throw NormalError("invalid operand type " + operand_type_name(ARG)); \ + throw NormalError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(ARG)); \ } std::shared_ptr<const IDiscreteFunction> sqrt(const std::shared_ptr<const IDiscreteFunction>& f) { - DISCRETE_FUNCTION_CALL(sqrt, f); + DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(sqrt, f); } std::shared_ptr<const IDiscreteFunction> abs(const std::shared_ptr<const IDiscreteFunction>& f) { - DISCRETE_FUNCTION_CALL(abs, f); + DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(abs, f); } std::shared_ptr<const IDiscreteFunction> sin(const std::shared_ptr<const IDiscreteFunction>& f) { - DISCRETE_FUNCTION_CALL(sin, f); + DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(sin, f); } std::shared_ptr<const IDiscreteFunction> cos(const std::shared_ptr<const IDiscreteFunction>& f) { - DISCRETE_FUNCTION_CALL(cos, f); + DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(cos, f); } std::shared_ptr<const IDiscreteFunction> tan(const std::shared_ptr<const IDiscreteFunction>& f) { - DISCRETE_FUNCTION_CALL(tan, f); + DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(tan, f); } std::shared_ptr<const IDiscreteFunction> asin(const std::shared_ptr<const IDiscreteFunction>& f) { - DISCRETE_FUNCTION_CALL(asin, f); + DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(asin, f); } std::shared_ptr<const IDiscreteFunction> acos(const std::shared_ptr<const IDiscreteFunction>& f) { - DISCRETE_FUNCTION_CALL(acos, f); + DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(acos, f); } std::shared_ptr<const IDiscreteFunction> atan(const std::shared_ptr<const IDiscreteFunction>& f) { - DISCRETE_FUNCTION_CALL(atan, f); + DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(atan, f); } std::shared_ptr<const IDiscreteFunction> @@ -106,14 +106,14 @@ atan2(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<c return std::make_shared<const DiscreteFunctionType>( atan2(dynamic_cast<const DiscreteFunctionType&>(*f), dynamic_cast<const DiscreteFunctionType&>(*g))); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } else { - std::stringstream os; - os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(g); - throw NormalError(os.str()); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g)); } } @@ -134,14 +134,14 @@ atan2(const double a, const std::shared_ptr<const IDiscreteFunction>& f) using DiscreteFunctionType = DiscreteFunctionP0<3, double>; return std::make_shared<const DiscreteFunctionType>(atan2(a, dynamic_cast<const DiscreteFunctionType&>(*f))); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } else { - std::stringstream os; - os << "incompatible operand types " << operand_type_name(a) << " and " << operand_type_name(f); - throw NormalError(os.str()); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f)); } } @@ -162,63 +162,63 @@ atan2(const std::shared_ptr<const IDiscreteFunction>& f, const double a) using DiscreteFunctionType = DiscreteFunctionP0<3, double>; return std::make_shared<const DiscreteFunctionType>(atan2(dynamic_cast<const DiscreteFunctionType&>(*f), a)); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } else { - std::stringstream os; - os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(a); - throw NormalError(os.str()); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a)); } } std::shared_ptr<const IDiscreteFunction> sinh(const std::shared_ptr<const IDiscreteFunction>& f) { - DISCRETE_FUNCTION_CALL(sinh, f); + DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(sinh, f); } std::shared_ptr<const IDiscreteFunction> cosh(const std::shared_ptr<const IDiscreteFunction>& f) { - DISCRETE_FUNCTION_CALL(cosh, f); + DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(cosh, f); } std::shared_ptr<const IDiscreteFunction> tanh(const std::shared_ptr<const IDiscreteFunction>& f) { - DISCRETE_FUNCTION_CALL(tanh, f); + DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(tanh, f); } std::shared_ptr<const IDiscreteFunction> asinh(const std::shared_ptr<const IDiscreteFunction>& f) { - DISCRETE_FUNCTION_CALL(asinh, f); + DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(asinh, f); } std::shared_ptr<const IDiscreteFunction> acosh(const std::shared_ptr<const IDiscreteFunction>& f) { - DISCRETE_FUNCTION_CALL(acosh, f); + DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(acosh, f); } std::shared_ptr<const IDiscreteFunction> atanh(const std::shared_ptr<const IDiscreteFunction>& f) { - DISCRETE_FUNCTION_CALL(atanh, f); + DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(atanh, f); } std::shared_ptr<const IDiscreteFunction> exp(const std::shared_ptr<const IDiscreteFunction>& f) { - DISCRETE_FUNCTION_CALL(exp, f); + DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(exp, f); } std::shared_ptr<const IDiscreteFunction> log(const std::shared_ptr<const IDiscreteFunction>& f) { - DISCRETE_FUNCTION_CALL(log, f); + DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(log, f); } std::shared_ptr<const IDiscreteFunction> @@ -248,14 +248,14 @@ pow(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<con return std::make_shared<const DiscreteFunctionType>( pow(dynamic_cast<const DiscreteFunctionType&>(*f), dynamic_cast<const DiscreteFunctionType&>(*g))); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } else { - std::stringstream os; - os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(g); - throw NormalError(os.str()); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g)); } } @@ -276,14 +276,14 @@ pow(const double a, const std::shared_ptr<const IDiscreteFunction>& f) using DiscreteFunctionType = DiscreteFunctionP0<3, double>; return std::make_shared<const DiscreteFunctionType>(pow(a, dynamic_cast<const DiscreteFunctionType&>(*f))); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } else { - std::stringstream os; - os << "incompatible operand types " << operand_type_name(a) << " and " << operand_type_name(f); - throw NormalError(os.str()); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f)); } } @@ -304,14 +304,14 @@ pow(const std::shared_ptr<const IDiscreteFunction>& f, const double a) using DiscreteFunctionType = DiscreteFunctionP0<3, double>; return std::make_shared<const DiscreteFunctionType>(pow(dynamic_cast<const DiscreteFunctionType&>(*f), a)); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } else { - std::stringstream os; - os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(a); - throw NormalError(os.str()); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a)); } } @@ -361,9 +361,11 @@ dot(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<con return std::make_shared<const DiscreteFunctionResultType>( dot(dynamic_cast<const DiscreteFunctionType&>(*f), dynamic_cast<const DiscreteFunctionType&>(*g))); } + // LCOV_EXCL_START default: { - throw UnexpectedError("invalid data dimension " + operand_type_name(f)); + throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f)); } + // LCOV_EXCL_STOP } } } @@ -392,14 +394,14 @@ dot(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<con case 3: { return dot<3>(f, g); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } else { - std::stringstream os; - os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(g); - throw NormalError(os.str()); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g)); } } @@ -432,14 +434,14 @@ dot(const std::shared_ptr<const IDiscreteFunction>& f, const TinyVector<VectorDi case 3: { return dot<3, VectorDimension>(f, a); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } else { - std::stringstream os; - os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(a); - throw NormalError(os.str()); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a)); } } @@ -472,14 +474,14 @@ dot(const TinyVector<VectorDimension>& a, const std::shared_ptr<const IDiscreteF case 3: { return dot<3, VectorDimension>(a, f); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } else { - std::stringstream os; - os << "incompatible operand types " << operand_type_name(a) << " and " << operand_type_name(f); - throw NormalError(os.str()); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f)); } } @@ -518,12 +520,14 @@ min(const std::shared_ptr<const IDiscreteFunction>& f) using DiscreteFunctionType = DiscreteFunctionP0<3, double>; return min(dynamic_cast<const DiscreteFunctionType&>(*f)); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } else { - throw NormalError("invalid operand type " + operand_type_name(f)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f)); } } @@ -554,14 +558,14 @@ min(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<con return std::make_shared<const DiscreteFunctionType>( min(dynamic_cast<const DiscreteFunctionType&>(*f), dynamic_cast<const DiscreteFunctionType&>(*g))); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } else { - std::stringstream os; - os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(g); - throw NormalError(os.str()); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g)); } } @@ -582,14 +586,14 @@ min(const double a, const std::shared_ptr<const IDiscreteFunction>& f) using DiscreteFunctionType = DiscreteFunctionP0<3, double>; return std::make_shared<const DiscreteFunctionType>(min(a, dynamic_cast<const DiscreteFunctionType&>(*f))); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } else { - std::stringstream os; - os << "incompatible operand types " << operand_type_name(a) << " and " << operand_type_name(f); - throw NormalError(os.str()); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f)); } } @@ -610,14 +614,14 @@ min(const std::shared_ptr<const IDiscreteFunction>& f, const double a) using DiscreteFunctionType = DiscreteFunctionP0<3, double>; return std::make_shared<const DiscreteFunctionType>(min(dynamic_cast<const DiscreteFunctionType&>(*f), a)); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } else { - std::stringstream os; - os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(a); - throw NormalError(os.str()); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a)); } } @@ -638,12 +642,14 @@ max(const std::shared_ptr<const IDiscreteFunction>& f) using DiscreteFunctionType = DiscreteFunctionP0<3, double>; return max(dynamic_cast<const DiscreteFunctionType&>(*f)); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } else { - throw NormalError("invalid operand type " + operand_type_name(f)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f)); } } @@ -674,14 +680,14 @@ max(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<con return std::make_shared<const DiscreteFunctionType>( max(dynamic_cast<const DiscreteFunctionType&>(*f), dynamic_cast<const DiscreteFunctionType&>(*g))); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } else { - std::stringstream os; - os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(g); - throw NormalError(os.str()); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g)); } } @@ -702,14 +708,14 @@ max(const double a, const std::shared_ptr<const IDiscreteFunction>& f) using DiscreteFunctionType = DiscreteFunctionP0<3, double>; return std::make_shared<const DiscreteFunctionType>(max(a, dynamic_cast<const DiscreteFunctionType&>(*f))); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } else { - std::stringstream os; - os << "incompatible operand types " << operand_type_name(a) << " and " << operand_type_name(f); - throw NormalError(os.str()); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f)); } } @@ -730,14 +736,14 @@ max(const std::shared_ptr<const IDiscreteFunction>& f, const double a) using DiscreteFunctionType = DiscreteFunctionP0<3, double>; return std::make_shared<const DiscreteFunctionType>(max(dynamic_cast<const DiscreteFunctionType&>(*f), a)); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } else { - std::stringstream os; - os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(a); - throw NormalError(os.str()); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a)); } } @@ -759,12 +765,14 @@ sum_of(const std::shared_ptr<const IDiscreteFunction>& f) using DiscreteFunctionType = DiscreteFunctionP0<3, ValueT>; return sum(dynamic_cast<const DiscreteFunctionType&>(*f)); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } else { - throw NormalError("invalid operand type " + operand_type_name(f)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f)); } } @@ -800,12 +808,14 @@ integral_of(const std::shared_ptr<const IDiscreteFunction>& f) using DiscreteFunctionType = DiscreteFunctionP0<3, ValueT>; return integrate(dynamic_cast<const DiscreteFunctionType&>(*f)); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } else { - throw NormalError("invalid operand type " + operand_type_name(f)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f)); } } diff --git a/src/language/utils/EmbeddedIDiscreteFunctionOperators.cpp b/src/language/utils/EmbeddedIDiscreteFunctionOperators.cpp index 13def86d07990370bb7dce658dde09f8d06b565a..656164d6fc012ef2469f846d3e54f32a9c1d4927 100644 --- a/src/language/utils/EmbeddedIDiscreteFunctionOperators.cpp +++ b/src/language/utils/EmbeddedIDiscreteFunctionOperators.cpp @@ -9,16 +9,6 @@ #include <scheme/IDiscreteFunction.hpp> #include <utils/Exceptions.hpp> -template <typename LHS_T, typename RHS_T> -PUGS_INLINE std::string -invalid_operands(const LHS_T& f, const RHS_T& g) -{ - std::ostringstream os; - os << "undefined binary operator\n"; - os << "note: incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(g); - return os.str(); -} - // unary operators template <typename UnaryOperatorT, typename DiscreteFunctionT> std::shared_ptr<const IDiscreteFunction> @@ -52,9 +42,11 @@ applyUnaryOperation(const std::shared_ptr<const IDiscreteFunction>& f) auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyVector<3>>&>(*f); return applyUnaryOperation<UnaryOperatorT>(fh); } + // LCOV_EXCL_START default: { - throw UnexpectedError("invalid operand type " + operand_type_name(f)); + throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f)); } + // LCOV_EXCL_STOP } } case ASTNodeDataType::matrix_t: { @@ -72,14 +64,18 @@ applyUnaryOperation(const std::shared_ptr<const IDiscreteFunction>& f) auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>&>(*f); return applyUnaryOperation<UnaryOperatorT>(fh); } + // LCOV_EXCL_START default: { - throw UnexpectedError("invalid operand type " + operand_type_name(f)); + throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f)); } + // LCOV_EXCL_STOP } } + // LCOV_EXCL_START default: { - throw UnexpectedError("invalid operand type " + operand_type_name(f)); + throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f)); } + // LCOV_EXCL_STOP } break; } @@ -89,15 +85,19 @@ applyUnaryOperation(const std::shared_ptr<const IDiscreteFunction>& f) auto fh = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*f); return applyUnaryOperation<UnaryOperatorT>(fh); } + // LCOV_EXCL_START default: { - throw UnexpectedError("invalid operand type " + operand_type_name(f)); + throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f)); } + // LCOV_EXCL_STOP } break; } + // LCOV_EXCL_START default: { - throw UnexpectedError("invalid operand type " + operand_type_name(f)); + throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f)); } + // LCOV_EXCL_STOP } } @@ -115,9 +115,11 @@ applyUnaryOperation(const std::shared_ptr<const IDiscreteFunction>& f) case 3: { return applyUnaryOperation<UnaryOperatorT, 3>(f); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } @@ -137,7 +139,7 @@ innerCompositionLaw(const DiscreteFunctionT& lhs, const DiscreteFunctionT& rhs) using data_type = typename DiscreteFunctionT::data_type; if constexpr ((std::is_same_v<language::multiply_op, BinOperatorT> and is_tiny_vector_v<data_type>) or (std::is_same_v<language::divide_op, BinOperatorT> and not std::is_arithmetic_v<data_type>)) { - throw NormalError(invalid_operands(lhs, rhs)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(lhs, rhs)); } else { return std::make_shared<decltype(BinOp<BinOperatorT>{}.eval(lhs, rhs))>(BinOp<BinOperatorT>{}.eval(lhs, rhs)); } @@ -149,7 +151,7 @@ innerCompositionLaw(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<const IDiscreteFunction>& g) { Assert(f->mesh() == g->mesh()); - Assert(isSameDiscretization(f, g)); + Assert(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(f, g)); switch (f->dataType()) { case ASTNodeDataType::double_t: { @@ -166,15 +168,17 @@ innerCompositionLaw(const std::shared_ptr<const IDiscreteFunction>& f, auto gh = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*g); if (fh.size() != gh.size()) { - throw NormalError(operand_type_name(f) + " spaces have different sizes"); + throw NormalError(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(f) + " spaces have different sizes"); } return innerCompositionLaw<BinOperatorT>(fh, gh); } else { - throw NormalError(invalid_operands(f, g)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g)); } } else { - throw UnexpectedError(invalid_operands(f, g)); + // LCOV_EXCL_START + throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g)); + // LCOV_EXCL_STOP } } case ASTNodeDataType::vector_t: { @@ -198,9 +202,11 @@ innerCompositionLaw(const std::shared_ptr<const IDiscreteFunction>& f, return innerCompositionLaw<BinOperatorT>(fh, gh); } + // LCOV_EXCL_START default: { - throw NormalError(invalid_operands(f, g)); + throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g)); } + // LCOV_EXCL_STOP } } case ASTNodeDataType::matrix_t: { @@ -225,14 +231,18 @@ innerCompositionLaw(const std::shared_ptr<const IDiscreteFunction>& f, return innerCompositionLaw<BinOperatorT>(fh, gh); } + // LCOV_EXCL_START default: { - throw UnexpectedError("invalid data type " + operand_type_name(f)); + throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f)); } + // LCOV_EXCL_STOP } } + // LCOV_EXCL_START default: { - throw UnexpectedError("invalid data type " + operand_type_name(f)); + throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f)); } + // LCOV_EXCL_STOP } } @@ -246,9 +256,7 @@ innerCompositionLaw(const std::shared_ptr<const IDiscreteFunction>& f, throw NormalError("operands are defined on different meshes"); } - if (not isSameDiscretization(f, g)) { - throw NormalError(invalid_operands(f, g)); - } + Assert(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(f, g)); switch (mesh->dimension()) { case 1: { @@ -260,9 +268,11 @@ innerCompositionLaw(const std::shared_ptr<const IDiscreteFunction>& f, case 3: { return innerCompositionLaw<BinOperatorT, 3>(f, g); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } @@ -283,29 +293,23 @@ std::shared_ptr<const IDiscreteFunction> applyBinaryOperation(const DiscreteFunctionT& fh, const std::shared_ptr<const IDiscreteFunction>& g) { Assert(fh.mesh() == g->mesh()); - Assert(not isSameDiscretization(fh, *g)); + Assert(not EmbeddedIDiscreteFunctionUtils::isSameDiscretization(fh, *g)); using lhs_data_type = std::decay_t<typename DiscreteFunctionT::data_type>; switch (g->dataType()) { case ASTNodeDataType::double_t: { - if constexpr (not std::is_same_v<lhs_data_type, double>) { - if constexpr (not is_tiny_matrix_v<lhs_data_type>) { - auto gh = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*g); - - return applyBinaryOperation<BinOperatorT>(fh, gh); - } else { - throw NormalError(invalid_operands(fh, g)); - } - } else if constexpr (std::is_same_v<BinOperatorT, language::multiply_op> and - std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<Dimension, double>>) { + if constexpr (std::is_same_v<BinOperatorT, language::multiply_op> and + std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<Dimension, double>>) { if (g->descriptor().type() == DiscreteFunctionType::P0Vector) { auto gh = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*g); return applyBinaryOperation<BinOperatorT>(fh, gh); } else { - throw NormalError(invalid_operands(fh, g)); + // LCOV_EXCL_START + throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(fh, g)); + // LCOV_EXCL_STOP } } else { - throw UnexpectedError("should have called innerCompositionLaw"); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(fh, g)); } } case ASTNodeDataType::vector_t: { @@ -318,7 +322,7 @@ applyBinaryOperation(const DiscreteFunctionT& fh, const std::shared_ptr<const ID return applyBinaryOperation<BinOperatorT>(fh, gh); } else { - throw NormalError(invalid_operands(fh, g)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(fh, g)); } } case 2: { @@ -328,7 +332,7 @@ applyBinaryOperation(const DiscreteFunctionT& fh, const std::shared_ptr<const ID return applyBinaryOperation<BinOperatorT>(fh, gh); } else { - throw NormalError(invalid_operands(fh, g)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(fh, g)); } } case 3: { @@ -338,15 +342,17 @@ applyBinaryOperation(const DiscreteFunctionT& fh, const std::shared_ptr<const ID return applyBinaryOperation<BinOperatorT>(fh, gh); } else { - throw NormalError(invalid_operands(fh, g)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(fh, g)); } } + // LCOV_EXCL_START default: { - throw UnexpectedError("invalid rhs data type " + operand_type_name(g)); + throw UnexpectedError("invalid rhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(g)); } + // LCOV_EXCL_STOP } } else { - throw NormalError(invalid_operands(fh, g)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(fh, g)); } } case ASTNodeDataType::matrix_t: { @@ -368,17 +374,21 @@ applyBinaryOperation(const DiscreteFunctionT& fh, const std::shared_ptr<const ID return applyBinaryOperation<BinOperatorT>(fh, gh); } + // LCOV_EXCL_START default: { - throw UnexpectedError("invalid rhs data type " + operand_type_name(g)); + throw UnexpectedError("invalid rhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(g)); } + // LCOV_EXCL_STOP } } else { - throw NormalError(invalid_operands(fh, g)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(fh, g)); } } + // LCOV_EXCL_START default: { - throw UnexpectedError("invalid rhs data type " + operand_type_name(g)); + throw UnexpectedError("invalid rhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(g)); } + // LCOV_EXCL_STOP } } @@ -388,40 +398,45 @@ applyBinaryOperation(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<const IDiscreteFunction>& g) { Assert(f->mesh() == g->mesh()); - Assert(not isSameDiscretization(f, g)); - - switch (f->dataType()) { - case ASTNodeDataType::double_t: { - auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*f); - - return applyBinaryOperation<BinOperatorT, Dimension>(fh, g); - } - case ASTNodeDataType::matrix_t: { - Assert(f->dataType().numberOfRows() == f->dataType().numberOfColumns()); - switch (f->dataType().numberOfRows()) { - case 1: { - auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>&>(*f); + Assert(not EmbeddedIDiscreteFunctionUtils::isSameDiscretization(f, g)); + if (f->descriptor().type() == DiscreteFunctionType::P0) { + switch (f->dataType()) { + case ASTNodeDataType::double_t: { + auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*f); return applyBinaryOperation<BinOperatorT, Dimension>(fh, g); } - case 2: { - auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>&>(*f); + case ASTNodeDataType::matrix_t: { + Assert(f->dataType().numberOfRows() == f->dataType().numberOfColumns()); + switch (f->dataType().numberOfRows()) { + case 1: { + auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>&>(*f); - return applyBinaryOperation<BinOperatorT, Dimension>(fh, g); - } - case 3: { - auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>&>(*f); + return applyBinaryOperation<BinOperatorT, Dimension>(fh, g); + } + case 2: { + auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>&>(*f); - return applyBinaryOperation<BinOperatorT, Dimension>(fh, g); + return applyBinaryOperation<BinOperatorT, Dimension>(fh, g); + } + case 3: { + auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>&>(*f); + + return applyBinaryOperation<BinOperatorT, Dimension>(fh, g); + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("invalid lhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(f)); + } + // LCOV_EXCL_STOP + } } default: { - throw UnexpectedError("invalid lhs data type " + operand_type_name(f)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g)); } } - } - default: { - throw NormalError(invalid_operands(f, g)); - } + } else { + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g)); } } @@ -435,7 +450,7 @@ applyBinaryOperation(const std::shared_ptr<const IDiscreteFunction>& f, throw NormalError("operands are defined on different meshes"); } - Assert(not isSameDiscretization(f, g), "should call inner composition instead"); + Assert(not EmbeddedIDiscreteFunctionUtils::isSameDiscretization(f, g), "should call inner composition instead"); switch (mesh->dimension()) { case 1: { @@ -447,36 +462,38 @@ applyBinaryOperation(const std::shared_ptr<const IDiscreteFunction>& f, case 3: { return applyBinaryOperation<BinOperatorT, 3>(f, g); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } std::shared_ptr<const IDiscreteFunction> operator+(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<const IDiscreteFunction>& g) { - if (isSameDiscretization(f, g)) { + if (EmbeddedIDiscreteFunctionUtils::isSameDiscretization(f, g)) { return innerCompositionLaw<language::plus_op>(f, g); } else { - throw NormalError(invalid_operands(f, g)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g)); } } std::shared_ptr<const IDiscreteFunction> operator-(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<const IDiscreteFunction>& g) { - if (isSameDiscretization(f, g)) { + if (EmbeddedIDiscreteFunctionUtils::isSameDiscretization(f, g)) { return innerCompositionLaw<language::minus_op>(f, g); } else { - throw NormalError(invalid_operands(f, g)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g)); } } std::shared_ptr<const IDiscreteFunction> operator*(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<const IDiscreteFunction>& g) { - if (isSameDiscretization(f, g)) { + if (EmbeddedIDiscreteFunctionUtils::isSameDiscretization(f, g)) { return innerCompositionLaw<language::multiply_op>(f, g); } else { return applyBinaryOperation<language::multiply_op>(f, g); @@ -486,7 +503,7 @@ operator*(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_p std::shared_ptr<const IDiscreteFunction> operator/(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<const IDiscreteFunction>& g) { - if (isSameDiscretization(f, g)) { + if (EmbeddedIDiscreteFunctionUtils::isSameDiscretization(f, g)) { return innerCompositionLaw<language::divide_op>(f, g); } else { return applyBinaryOperation<language::divide_op>(f, g); @@ -507,7 +524,7 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const DiscreteFunctionT& (is_tiny_matrix_v<rhs_data_type> or is_tiny_vector_v<rhs_data_type>)) { return std::make_shared<decltype(BinOp<BinOperatorT>{}.eval(a, f))>(BinOp<BinOperatorT>{}.eval(a, f)); } else { - throw NormalError(invalid_operands(a, f)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f)); } } else if constexpr (std::is_same_v<language::plus_op, BinOperatorT> or std::is_same_v<language::minus_op, BinOperatorT>) { @@ -516,16 +533,18 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const DiscreteFunctionT& } else if constexpr (std::is_same_v<lhs_data_type, rhs_data_type>) { return std::make_shared<decltype(BinOp<BinOperatorT>{}.eval(a, f))>(BinOp<BinOperatorT>{}.eval(a, f)); } else { - throw NormalError(invalid_operands(a, f)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f)); } } else if constexpr (std::is_same_v<language::divide_op, BinOperatorT>) { if constexpr (std::is_same_v<lhs_data_type, double> and std::is_arithmetic_v<rhs_data_type>) { return std::make_shared<decltype(BinOp<BinOperatorT>{}.eval(a, f))>(BinOp<BinOperatorT>{}.eval(a, f)); } else { - throw NormalError(invalid_operands(a, f)); + // LCOV_EXCL_START + throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f)); + // LCOV_EXCL_STOP } } else { - throw NormalError(invalid_operands(a, f)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f)); } } @@ -543,10 +562,10 @@ applyBinaryOperationToVectorWithLeftConstant(const DataType& a, const DiscreteFu (is_tiny_matrix_v<rhs_data_type> or is_tiny_vector_v<rhs_data_type>)) { return std::make_shared<decltype(BinOp<BinOperatorT>{}.eval(a, f))>(BinOp<BinOperatorT>{}.eval(a, f)); } else { - throw NormalError(invalid_operands(a, f)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f)); } } else { - throw NormalError(invalid_operands(a, f)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f)); } } @@ -566,7 +585,9 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co auto fh = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*f); return applyBinaryOperationToVectorWithLeftConstant<BinOperatorT>(a, fh); } else { - throw NormalError(invalid_operands(a, f)); + // LCOV_EXCL_START + throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f)); + // LCOV_EXCL_STOP } } case ASTNodeDataType::vector_t: { @@ -577,7 +598,7 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyVector<1>>&>(*f); return applyBinaryOperationWithLeftConstant<BinOperatorT>(a, fh); } else { - throw NormalError(invalid_operands(a, f)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f)); } } case 2: { @@ -585,7 +606,7 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyVector<2>>&>(*f); return applyBinaryOperationWithLeftConstant<BinOperatorT>(a, fh); } else { - throw NormalError(invalid_operands(a, f)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f)); } } case 3: { @@ -593,12 +614,14 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyVector<3>>&>(*f); return applyBinaryOperationWithLeftConstant<BinOperatorT>(a, fh); } else { - throw NormalError(invalid_operands(a, f)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f)); } } + // LCOV_EXCL_START default: { - throw UnexpectedError("invalid lhs data type " + operand_type_name(f)); + throw UnexpectedError("invalid lhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(f)); } + // LCOV_EXCL_STOP } } else { switch (f->dataType().dimension()) { @@ -614,9 +637,11 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyVector<3>>&>(*f); return applyBinaryOperationWithLeftConstant<BinOperatorT>(a, fh); } + // LCOV_EXCL_START default: { - throw UnexpectedError("invalid lhs data type " + operand_type_name(f)); + throw UnexpectedError("invalid lhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(f)); } + // LCOV_EXCL_STOP } } } @@ -629,7 +654,7 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>&>(*f); return applyBinaryOperationWithLeftConstant<BinOperatorT>(a, fh); } else { - throw NormalError(invalid_operands(a, f)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f)); } } case 2: { @@ -637,7 +662,7 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>&>(*f); return applyBinaryOperationWithLeftConstant<BinOperatorT>(a, fh); } else { - throw NormalError(invalid_operands(a, f)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f)); } } case 3: { @@ -645,12 +670,14 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>&>(*f); return applyBinaryOperationWithLeftConstant<BinOperatorT>(a, fh); } else { - throw NormalError(invalid_operands(a, f)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f)); } } + // LCOV_EXCL_START default: { - throw UnexpectedError("invalid lhs data type " + operand_type_name(f)); + throw UnexpectedError("invalid lhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(f)); } + // LCOV_EXCL_STOP } } else { switch (f->dataType().numberOfRows()) { @@ -666,15 +693,19 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>&>(*f); return applyBinaryOperationWithLeftConstant<BinOperatorT>(a, fh); } + // LCOV_EXCL_START default: { - throw UnexpectedError("invalid lhs data type " + operand_type_name(f)); + throw UnexpectedError("invalid lhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(f)); } + // LCOV_EXCL_STOP } } } + // LCOV_EXCL_START default: { - throw NormalError(invalid_operands(a, f)); + throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f)); } + // LCOV_EXCL_STOP } } @@ -692,9 +723,11 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co case 3: { return applyBinaryOperationWithLeftConstant<BinOperatorT, 3>(a, f); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } @@ -709,13 +742,17 @@ applyBinaryOperationWithRightConstant(const DiscreteFunctionT& f, const DataType if constexpr (std::is_same_v<language::multiply_op, BinOperatorT>) { if constexpr (is_tiny_matrix_v<lhs_data_type> and is_tiny_matrix_v<rhs_data_type>) { - return std::make_shared<decltype(BinOp<BinOperatorT>{}.eval(f, a))>(BinOp<BinOperatorT>{}.eval(f, a)); + if constexpr (lhs_data_type::NumberOfColumns == rhs_data_type::NumberOfRows) { + return std::make_shared<decltype(BinOp<BinOperatorT>{}.eval(f, a))>(BinOp<BinOperatorT>{}.eval(f, a)); + } else { + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a)); + } } else if constexpr (std::is_same_v<lhs_data_type, double> and (is_tiny_matrix_v<rhs_data_type> or is_tiny_vector_v<rhs_data_type> or std::is_arithmetic_v<rhs_data_type>)) { return std::make_shared<decltype(BinOp<BinOperatorT>{}.eval(f, a))>(BinOp<BinOperatorT>{}.eval(f, a)); } else { - throw NormalError(invalid_operands(f, a)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a)); } } else if constexpr (std::is_same_v<language::plus_op, BinOperatorT> or std::is_same_v<language::minus_op, BinOperatorT>) { @@ -723,10 +760,10 @@ applyBinaryOperationWithRightConstant(const DiscreteFunctionT& f, const DataType (std::is_arithmetic_v<lhs_data_type> and std::is_arithmetic_v<rhs_data_type>)) { return std::make_shared<decltype(BinOp<BinOperatorT>{}.eval(f, a))>(BinOp<BinOperatorT>{}.eval(f, a)); } else { - throw NormalError(invalid_operands(f, a)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a)); } } else { - throw NormalError(invalid_operands(f, a)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a)); } } @@ -735,7 +772,7 @@ std::shared_ptr<const IDiscreteFunction> applyBinaryOperationWithRightConstant(const std::shared_ptr<const IDiscreteFunction>& f, const DataType& a) { if (f->descriptor().type() != DiscreteFunctionType::P0) { - throw NormalError(invalid_operands(f, a)); + throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a)); } switch (f->dataType()) { @@ -746,61 +783,54 @@ applyBinaryOperationWithRightConstant(const std::shared_ptr<const IDiscreteFunct auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*f); return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a); } + case ASTNodeDataType::vector_t: { + switch (f->dataType().dimension()) { + case 1: { + auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyVector<1>>&>(*f); + return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a); + } + case 2: { + auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyVector<2>>&>(*f); + return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a); + } + case 3: { + auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyVector<3>>&>(*f); + return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a); + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("invalid lhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(f)); + } + // LCOV_EXCL_STOP + } + } case ASTNodeDataType::matrix_t: { Assert(f->dataType().numberOfRows() == f->dataType().numberOfColumns()); - if constexpr (is_tiny_matrix_v<DataType>) { - switch (f->dataType().numberOfRows()) { - case 1: { - if constexpr (std::is_same_v<DataType, TinyMatrix<1>>) { - auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>&>(*f); - return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a); - } else { - throw NormalError(invalid_operands(f, a)); - } - } - case 2: { - if constexpr (std::is_same_v<DataType, TinyMatrix<2>>) { - auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>&>(*f); - return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a); - } else { - throw NormalError(invalid_operands(f, a)); - } - } - case 3: { - if constexpr (std::is_same_v<DataType, TinyMatrix<3>>) { - auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>&>(*f); - return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a); - } else { - throw NormalError(invalid_operands(f, a)); - } - } - default: { - throw UnexpectedError("invalid lhs data type " + operand_type_name(f)); - } - } - } else { - switch (f->dataType().numberOfRows()) { - case 1: { - auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>&>(*f); - return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a); - } - case 2: { - auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>&>(*f); - return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a); - } - case 3: { - auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>&>(*f); - return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a); - } - default: { - throw UnexpectedError("invalid lhs data type " + operand_type_name(f)); - } - } + switch (f->dataType().numberOfRows()) { + case 1: { + auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>&>(*f); + return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a); + } + case 2: { + auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>&>(*f); + return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a); + } + case 3: { + auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>&>(*f); + return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a); + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("invalid lhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(f)); + } + // LCOV_EXCL_STOP } } + // LCOV_EXCL_START default: { - throw NormalError(invalid_operands(f, a)); + throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a)); } + // LCOV_EXCL_STOP } } @@ -818,9 +848,11 @@ applyBinaryOperationWithRightConstant(const std::shared_ptr<const IDiscreteFunct case 3: { return applyBinaryOperationWithRightConstant<BinOperatorT, 3>(f, a); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } diff --git a/src/language/utils/EmbeddedIDiscreteFunctionUtils.cpp b/src/language/utils/EmbeddedIDiscreteFunctionUtils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..286988b9b2849fb34dc84948be05414ebbad6d5b --- /dev/null +++ b/src/language/utils/EmbeddedIDiscreteFunctionUtils.cpp @@ -0,0 +1,27 @@ +#include <language/utils/EmbeddedIDiscreteFunctionUtils.hpp> + +#include <utils/Exceptions.hpp> + +bool +EmbeddedIDiscreteFunctionUtils::isSameDiscretization(const IDiscreteFunction& f, const IDiscreteFunction& g) +{ + if ((f.dataType() == g.dataType()) and (f.descriptor().type() == g.descriptor().type())) { + switch (f.dataType()) { + case ASTNodeDataType::double_t: { + return true; + } + case ASTNodeDataType::vector_t: { + return f.dataType().dimension() == g.dataType().dimension(); + } + case ASTNodeDataType::matrix_t: { + return (f.dataType().numberOfRows() == g.dataType().numberOfRows()) and + (f.dataType().numberOfColumns() == g.dataType().numberOfColumns()); + } + default: { + throw UnexpectedError("invalid data type " + getOperandTypeName(f)); + } + } + } else { + return false; + } +} diff --git a/src/language/utils/EmbeddedIDiscreteFunctionUtils.hpp b/src/language/utils/EmbeddedIDiscreteFunctionUtils.hpp index 981cd8be9ca166038b5aa4b913dae63488868b64..1e61f917dfa448add18b562dd403a70101dbec3b 100644 --- a/src/language/utils/EmbeddedIDiscreteFunctionUtils.hpp +++ b/src/language/utils/EmbeddedIDiscreteFunctionUtils.hpp @@ -1,58 +1,50 @@ #ifndef EMBEDDED_I_DISCRETE_FUNCTION_UTILS_HPP #define EMBEDDED_I_DISCRETE_FUNCTION_UTILS_HPP +#include <language/utils/ASTNodeDataType.hpp> #include <scheme/IDiscreteFunction.hpp> #include <scheme/IDiscreteFunctionDescriptor.hpp> -#include <utils/Exceptions.hpp> -#include <sstream> #include <string> -template <typename T> -PUGS_INLINE std::string -operand_type_name(const T& t) +struct EmbeddedIDiscreteFunctionUtils { - if constexpr (is_shared_ptr_v<T>) { - Assert(t.use_count() > 0); - return operand_type_name(*t); - } else if constexpr (std::is_base_of_v<IDiscreteFunction, std::decay_t<T>>) { - return "Vh(" + name(t.descriptor().type()) + ':' + dataTypeName(t.dataType()) + ')'; - } else { - return dataTypeName(ast_node_data_type_from<T>); + template <typename T> + static PUGS_INLINE std::string + getOperandTypeName(const T& t) + { + if constexpr (is_shared_ptr_v<T>) { + Assert(t.use_count() > 0, "dangling shared_ptr"); + return getOperandTypeName(*t); + } else if constexpr (std::is_base_of_v<IDiscreteFunction, std::decay_t<T>>) { + return "Vh(" + name(t.descriptor().type()) + ':' + dataTypeName(t.dataType()) + ')'; + } else { + return dataTypeName(ast_node_data_type_from<T>); + } } -} -PUGS_INLINE -bool -isSameDiscretization(const IDiscreteFunction& f, const IDiscreteFunction& g) -{ - if ((f.dataType() == g.dataType()) and (f.descriptor().type() == g.descriptor().type())) { - switch (f.dataType()) { - case ASTNodeDataType::double_t: { - return true; - } - case ASTNodeDataType::vector_t: { - return f.dataType().dimension() == g.dataType().dimension(); - } - case ASTNodeDataType::matrix_t: { - return (f.dataType().numberOfRows() == g.dataType().numberOfRows()) and - (f.dataType().numberOfColumns() == g.dataType().numberOfColumns()); - } - default: { - throw UnexpectedError("invalid data type " + operand_type_name(f)); - } - } - } else { - return false; + static bool isSameDiscretization(const IDiscreteFunction& f, const IDiscreteFunction& g); + + static PUGS_INLINE bool + isSameDiscretization(const std::shared_ptr<const IDiscreteFunction>& f, + const std::shared_ptr<const IDiscreteFunction>& g) + { + return isSameDiscretization(*f, *g); } -} -PUGS_INLINE -bool -isSameDiscretization(const std::shared_ptr<const IDiscreteFunction>& f, - const std::shared_ptr<const IDiscreteFunction>& g) -{ - return isSameDiscretization(*f, *g); -} + template <typename T1, typename T2> + PUGS_INLINE static std::string + incompatibleOperandTypes(const T1& t1, const T2& t2) + { + return "incompatible operand types " + getOperandTypeName(t1) + " and " + getOperandTypeName(t2); + } + + template <typename T> + PUGS_INLINE static std::string + invalidOperandType(const T& t) + { + return "invalid operand type " + getOperandTypeName(t); + } +}; #endif // EMBEDDED_I_DISCRETE_FUNCTION_UTILS_HPP diff --git a/src/mesh/CartesianMeshBuilder.cpp b/src/mesh/CartesianMeshBuilder.cpp index d6a4589cced3958ca7401a5a80d881d23fe87749..6b7ef2b26f8aa8fbe1d78ab24f606eb8c335340d 100644 --- a/src/mesh/CartesianMeshBuilder.cpp +++ b/src/mesh/CartesianMeshBuilder.cpp @@ -125,14 +125,14 @@ CartesianMeshBuilder::CartesianMeshBuilder(const TinyVector<Dimension>& a, const TinyVector<Dimension>& b, const TinyVector<Dimension, uint64_t>& size) { - if (parallel::rank() == 0) { - TinyVector lenght = b - a; - for (size_t i = 0; i < Dimension; ++i) { - if (lenght[i] == 0) { - throw NormalError("invalid box definition corners share a component"); - } + TinyVector lenght = b - a; + for (size_t i = 0; i < Dimension; ++i) { + if (lenght[i] == 0) { + throw NormalError("invalid box definition corners share a component"); } + } + if (parallel::rank() == 0) { TinyVector<Dimension> corner0 = a; TinyVector<Dimension> corner1 = b; diff --git a/src/scheme/DiscreteFunctionInterpoler.cpp b/src/scheme/DiscreteFunctionInterpoler.cpp index 11b19835eab3af91c0ea125359364831916cbc18..c39b6ee9d7020b1ffee3582ee36bd461a0c03435 100644 --- a/src/scheme/DiscreteFunctionInterpoler.cpp +++ b/src/scheme/DiscreteFunctionInterpoler.cpp @@ -4,7 +4,7 @@ #include <scheme/DiscreteFunctionP0.hpp> #include <utils/Exceptions.hpp> -template <size_t Dimension, typename DataType> +template <size_t Dimension, typename DataType, typename ValueType> std::shared_ptr<IDiscreteFunction> DiscreteFunctionInterpoler::_interpolate() const { @@ -12,10 +12,25 @@ DiscreteFunctionInterpoler::_interpolate() const using MeshDataType = MeshData<Dimension>; MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); - return std::make_shared< - DiscreteFunctionP0<Dimension, DataType>>(mesh, - InterpolateItemValue<DataType(TinyVector<Dimension>)>:: - template interpolate<ItemType::cell>(m_function_id, mesh_data.xj())); + if constexpr (std::is_same_v<DataType, ValueType>) { + return std::make_shared< + DiscreteFunctionP0<Dimension, ValueType>>(mesh, + InterpolateItemValue<DataType(TinyVector<Dimension>)>:: + template interpolate<ItemType::cell>(m_function_id, mesh_data.xj())); + } else { + static_assert(std::is_convertible_v<DataType, ValueType>); + + CellValue<DataType> cell_data = + InterpolateItemValue<DataType(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(m_function_id, + mesh_data.xj()); + + CellValue<ValueType> cell_value{mesh->connectivity()}; + + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_data[cell_id]; }); + + return std::make_shared<DiscreteFunctionP0<Dimension, ValueType>>(mesh, cell_value); + } } template <size_t Dimension> @@ -29,13 +44,13 @@ DiscreteFunctionInterpoler::_interpolate() const switch (data_type) { case ASTNodeDataType::bool_t: { - return this->_interpolate<Dimension, bool>(); + return this->_interpolate<Dimension, bool, double>(); } case ASTNodeDataType::unsigned_int_t: { - return this->_interpolate<Dimension, uint64_t>(); + return this->_interpolate<Dimension, uint64_t, double>(); } case ASTNodeDataType::int_t: { - return this->_interpolate<Dimension, int64_t>(); + return this->_interpolate<Dimension, int64_t, double>(); } case ASTNodeDataType::double_t: { return this->_interpolate<Dimension, double>(); @@ -51,12 +66,14 @@ DiscreteFunctionInterpoler::_interpolate() const case 3: { return this->_interpolate<Dimension, TinyVector<3>>(); } + // LCOV_EXCL_START default: { std::ostringstream os; os << "invalid vector dimension " << rang::fgB::red << data_type.dimension() << rang::style::reset; throw UnexpectedError(os.str()); } + // LCOV_EXCL_STOP } } case ASTNodeDataType::matrix_t: { @@ -71,20 +88,24 @@ DiscreteFunctionInterpoler::_interpolate() const case 3: { return this->_interpolate<Dimension, TinyMatrix<3>>(); } + // LCOV_EXCL_START default: { std::ostringstream os; os << "invalid vector dimension " << rang::fgB::red << data_type.dimension() << rang::style::reset; throw UnexpectedError(os.str()); } + // LCOV_EXCL_STOP } } + // LCOV_EXCL_START default: { std::ostringstream os; os << "invalid interpolation value type: " << rang::fgB::red << dataTypeName(data_type) << rang::style::reset; throw UnexpectedError(os.str()); } + // LCOV_EXCL_STOP } } @@ -102,9 +123,10 @@ DiscreteFunctionInterpoler::interpolate() const case 3: { return this->_interpolate<3>(); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid dimension"); } + // LCOV_EXCL_STOP } - return nullptr; } diff --git a/src/scheme/DiscreteFunctionInterpoler.hpp b/src/scheme/DiscreteFunctionInterpoler.hpp index 758ff2454ec673ba2cc46c0c561eca1a4a256b51..e5724e3fec3ea75098772827537a1267f68328b7 100644 --- a/src/scheme/DiscreteFunctionInterpoler.hpp +++ b/src/scheme/DiscreteFunctionInterpoler.hpp @@ -15,7 +15,7 @@ class DiscreteFunctionInterpoler std::shared_ptr<const IDiscreteFunctionDescriptor> m_discrete_function_descriptor; const FunctionSymbolId m_function_id; - template <size_t Dimension, typename DataType> + template <size_t Dimension, typename DataType, typename ValueType = DataType> std::shared_ptr<IDiscreteFunction> _interpolate() const; template <size_t Dimension> diff --git a/src/scheme/DiscreteFunctionUtils.cpp b/src/scheme/DiscreteFunctionUtils.cpp index 6d2e62da4fcdd9d1c785b01894ca3d4776dfae8c..f7057c4abcf920eea37d678eb410e7edf58006b3 100644 --- a/src/scheme/DiscreteFunctionUtils.cpp +++ b/src/scheme/DiscreteFunctionUtils.cpp @@ -10,6 +10,10 @@ std::shared_ptr<const IDiscreteFunction> shallowCopy(const std::shared_ptr<const Mesh<Connectivity<Dimension>>>& mesh, const std::shared_ptr<const DiscreteFunctionP0<Dimension, DataType>>& discrete_function) { + Assert(mesh->shared_connectivity() == + dynamic_cast<const Mesh<Connectivity<Dimension>>&>(*discrete_function->mesh()).shared_connectivity(), + "connectivities should be the same"); + return std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh, discrete_function->cellValues()); } @@ -22,7 +26,7 @@ shallowCopy(const std::shared_ptr<const Mesh<Connectivity<Dimension>>>& mesh, std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(discrete_function->mesh()); if (mesh->shared_connectivity() != function_mesh->shared_connectivity()) { - throw NormalError("incompatible connectivities"); + throw NormalError("cannot shallow copy when connectivity changes"); } switch (discrete_function->descriptor().type()) { @@ -46,17 +50,21 @@ shallowCopy(const std::shared_ptr<const Mesh<Connectivity<Dimension>>>& mesh, return shallowCopy(mesh, std::dynamic_pointer_cast<const DiscreteFunctionP0<Dimension, TinyVector<3>>>( discrete_function)); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid data vector dimension: " + std::to_string(discrete_function->dataType().dimension())); } + // LCOV_EXCL_STOP } } case ASTNodeDataType::matrix_t: { if (discrete_function->dataType().numberOfRows() != discrete_function->dataType().numberOfColumns()) { + // LCOV_EXCL_START throw UnexpectedError( "invalid data matrix dimensions: " + std::to_string(discrete_function->dataType().numberOfRows()) + "x" + std::to_string(discrete_function->dataType().numberOfColumns())); + // LCOV_EXCL_STOP } switch (discrete_function->dataType().numberOfRows()) { case 1: { @@ -71,21 +79,27 @@ shallowCopy(const std::shared_ptr<const Mesh<Connectivity<Dimension>>>& mesh, return shallowCopy(mesh, std::dynamic_pointer_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>( discrete_function)); } + // LCOV_EXCL_START default: { throw UnexpectedError( "invalid data matrix dimensions: " + std::to_string(discrete_function->dataType().numberOfRows()) + "x" + std::to_string(discrete_function->dataType().numberOfColumns())); } + // LCOV_EXCL_STOP } } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid kind of P0 function: invalid data type"); } + // LCOV_EXCL_STOP } } + // LCOV_EXCL_START default: { - throw NormalError("invalid discretization type"); + throw UnexpectedError("invalid discretization type"); } + // LCOV_EXCL_STOP } } @@ -108,8 +122,10 @@ shallowCopy(const std::shared_ptr<const IMesh>& mesh, const std::shared_ptr<cons case 3: { return shallowCopy(std::dynamic_pointer_cast<const Mesh<Connectivity<3>>>(mesh), discrete_function); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid mesh dimension"); } + // LCOV_EXCL_STOP } } diff --git a/src/scheme/DiscreteFunctionVectorInterpoler.cpp b/src/scheme/DiscreteFunctionVectorInterpoler.cpp index 4827345a5864ccae5425f6463e00163d455a4aa8..98d6513c9a896b81d8e6909e057a8300b66d6dc7 100644 --- a/src/scheme/DiscreteFunctionVectorInterpoler.cpp +++ b/src/scheme/DiscreteFunctionVectorInterpoler.cpp @@ -38,7 +38,7 @@ DiscreteFunctionVectorInterpoler::_interpolate() const default: { std::ostringstream os; os << "vector functions require scalar value type.\n" - << "Invalid interpolation value type:" << rang::fgB::red << dataTypeName(data_type) << rang::style::reset; + << "Invalid interpolation value type: " << rang::fgB::red << dataTypeName(data_type) << rang::style::reset; throw NormalError(os.str()); } } @@ -53,7 +53,6 @@ DiscreteFunctionVectorInterpoler::interpolate() const throw NormalError("invalid discrete function type for vector interpolation"); } - std::shared_ptr<IDiscreteFunction> discrete_function; switch (m_mesh->dimension()) { case 1: { return this->_interpolate<1>(); @@ -64,8 +63,10 @@ DiscreteFunctionVectorInterpoler::interpolate() const case 3: { return this->_interpolate<3>(); } + // LCOV_EXCL_START default: { throw UnexpectedError("invalid dimension"); } + // LCOV_EXCL_STOP } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4bac187608a6a5bfd19f058b6f8a00683f852f05..25bcaa008af00dd141bfc4a0ba1fc65bb11a2003 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -44,6 +44,9 @@ add_executable (unit_tests test_BinaryExpressionProcessor_comparison.cpp test_BinaryExpressionProcessor_equality.cpp test_BinaryExpressionProcessor_logic.cpp + test_BinaryExpressionProcessor_raw.cpp + test_BinaryExpressionProcessor_shift.cpp + test_BinaryOperatorMangler.cpp test_BiCGStab.cpp test_BuildInfo.cpp test_BuiltinFunctionEmbedder.cpp @@ -63,9 +66,11 @@ add_executable (unit_tests test_DiscreteFunctionDescriptorP0.cpp test_DiscreteFunctionDescriptorP0Vector.cpp test_DiscreteFunctionType.cpp + test_DiscreteFunctionUtils.cpp test_DoWhileProcessor.cpp test_EigenvalueSolver.cpp test_EmbeddedData.cpp + test_EmbeddedIDiscreteFunctionUtils.cpp test_EscapedString.cpp test_Exceptions.cpp test_ExecutionPolicy.cpp @@ -93,6 +98,7 @@ add_executable (unit_tests test_PugsUtils.cpp test_RevisionInfo.cpp test_SmallArray.cpp + test_SmallVector.cpp test_SymbolTable.cpp test_Table.cpp test_Timer.cpp @@ -100,14 +106,19 @@ add_executable (unit_tests test_TinyVector.cpp test_TupleToVectorProcessor.cpp test_UnaryExpressionProcessor.cpp + test_UnaryOperatorMangler.cpp test_Vector.cpp test_WhileProcessor.cpp ) add_executable (mpi_unit_tests mpi_test_main.cpp + test_DiscreteFunctionInterpoler.cpp test_DiscreteFunctionP0.cpp test_DiscreteFunctionP0Vector.cpp + test_DiscreteFunctionVectorInterpoler.cpp + test_EmbeddedIDiscreteFunctionMathFunctions.cpp + test_EmbeddedIDiscreteFunctionOperators.cpp test_InterpolateItemArray.cpp test_InterpolateItemValue.cpp test_ItemArray.cpp diff --git a/tests/FixturesForBuiltinT.hpp b/tests/FixturesForBuiltinT.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d2ff0fcb3b0db1e37a9a5f00487eb8288f1aec3d --- /dev/null +++ b/tests/FixturesForBuiltinT.hpp @@ -0,0 +1,68 @@ +#ifndef FIXTURES_FOR_BUILTIN_T_HPP +#define FIXTURES_FOR_BUILTIN_T_HPP + +#include <language/utils/ASTNodeDataTypeTraits.hpp> + +#include <memory> +#include <stdexcept> + +template <> +inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const double>> = + ASTNodeDataType::build<ASTNodeDataType::type_id_t>("builtin_t"); +const auto builtin_data_type = ast_node_data_type_from<std::shared_ptr<const double>>; + +inline std::shared_ptr<const double> +operator-(const std::shared_ptr<const double>& p_a) +{ + return std::make_shared<double>(-*p_a); +} + +inline std::shared_ptr<const double> +operator+(const std::shared_ptr<const double>& p_a, const std::shared_ptr<const double>& p_b) +{ + return std::make_shared<double>(*p_a + *p_b); +} + +inline std::shared_ptr<const double> +operator+(std::shared_ptr<const double> p_a, double b) +{ + return std::make_shared<double>(*p_a + b); +} + +inline std::shared_ptr<const double> +operator+(double a, const std::shared_ptr<const double>& p_b) +{ + return std::make_shared<double>(a + *p_b); +} + +inline std::shared_ptr<const double> +operator/(const std::shared_ptr<const double>&, const std::shared_ptr<const double>&) +{ + throw std::runtime_error("runtime error both"); +} + +inline std::shared_ptr<const double> +operator/(const std::shared_ptr<const double>&, double) +{ + throw std::runtime_error("runtime error lhs"); +} + +inline std::shared_ptr<const double> +operator/(double, const std::shared_ptr<const double>&) +{ + throw std::runtime_error("runtime error rhs"); +} + +inline std::shared_ptr<const double> +operator<<(const std::shared_ptr<const double>& p_a, const std::shared_ptr<const double>& p_b) +{ + return std::make_shared<double>(static_cast<int>(*p_a) << static_cast<int>(*p_b)); +} + +inline std::shared_ptr<const double> +operator>>(const std::shared_ptr<const double>& p_a, const std::shared_ptr<const double>& p_b) +{ + return std::make_shared<double>(static_cast<int>(*p_a) >> static_cast<int>(*p_b)); +} + +#endif // FIXTURES_FOR_BUILTIN_T_HPP diff --git a/tests/test_ASTNodeBinaryOperatorExpressionBuilder.cpp b/tests/test_ASTNodeBinaryOperatorExpressionBuilder.cpp index b99aa045958dc356cb03f2b548e8c632d58b124d..4a9f2bb8d64b047f3355d3f16b81f1e1dada2143 100644 --- a/tests/test_ASTNodeBinaryOperatorExpressionBuilder.cpp +++ b/tests/test_ASTNodeBinaryOperatorExpressionBuilder.cpp @@ -1,6 +1,8 @@ #include <catch2/catch_test_macros.hpp> #include <catch2/matchers/catch_matchers_all.hpp> +#include <FixturesForBuiltinT.hpp> + #include <language/ast/ASTBuilder.hpp> #include <language/ast/ASTModulesImporter.hpp> #include <language/ast/ASTNodeBinaryOperatorExpressionBuilder.hpp> @@ -10,6 +12,11 @@ #include <language/ast/ASTNodeTypeCleaner.hpp> #include <language/ast/ASTSymbolTableBuilder.hpp> #include <language/utils/ASTPrinter.hpp> +#include <language/utils/BasicAffectationRegistrerFor.hpp> +#include <language/utils/BinaryOperatorProcessorBuilder.hpp> +#include <language/utils/DataHandler.hpp> +#include <language/utils/OperatorRepository.hpp> +#include <language/utils/TypeDescriptor.hpp> #include <utils/Demangle.hpp> #include <pegtl/string_input.hpp> @@ -1417,6 +1424,111 @@ x!=y; } } + SECTION("shift") + { +#define CHECK_AST_BUILTIN_SHIFT_EXPRESSION(data, expected_output) \ + { \ + TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; \ + auto ast = ASTBuilder::build(input); \ + \ + ASTModulesImporter{*ast}; \ + \ + BasicAffectationRegisterFor<EmbeddedData>{ASTNodeDataType::build<ASTNodeDataType::type_id_t>("builtin_t")}; \ + \ + OperatorRepository& repository = OperatorRepository::instance(); \ + \ + repository.addBinaryOperator<language::shift_left_op>( \ + std::make_shared< \ + BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const double>, \ + std::shared_ptr<const double>, std::shared_ptr<const double>>>()); \ + \ + repository.addBinaryOperator<language::shift_right_op>( \ + std::make_shared< \ + BinaryOperatorProcessorBuilder<language::shift_right_op, std::shared_ptr<const double>, \ + std::shared_ptr<const double>, std::shared_ptr<const double>>>()); \ + \ + SymbolTable& symbol_table = *ast->m_symbol_table; \ + auto [i_symbol, success] = symbol_table.add(builtin_data_type.nameOfTypeId(), ast->begin()); \ + if (not success) { \ + throw UnexpectedError("cannot add '" + builtin_data_type.nameOfTypeId() + "' type for testing"); \ + } \ + \ + i_symbol->attributes().setDataType(ASTNodeDataType::build<ASTNodeDataType::type_name_id_t>()); \ + i_symbol->attributes().setIsInitialized(); \ + i_symbol->attributes().value() = symbol_table.typeEmbedderTable().size(); \ + symbol_table.typeEmbedderTable().add(std::make_shared<TypeDescriptor>(builtin_data_type.nameOfTypeId())); \ + \ + auto [i_symbol_bt_a, success_bt_a] = symbol_table.add("bt_a", ast->begin()); \ + if (not success_bt_a) { \ + throw UnexpectedError("cannot add 'bt_a' of type builtin_t for testing"); \ + } \ + i_symbol_bt_a->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>); \ + i_symbol_bt_a->attributes().setIsInitialized(); \ + i_symbol_bt_a->attributes().value() = \ + EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(3.2))); \ + \ + auto [i_symbol_bt_b, success_bt_b] = symbol_table.add("bt_b", ast->begin()); \ + if (not success_bt_b) { \ + throw UnexpectedError("cannot add 'bt_b' of type builtin_t for testing"); \ + } \ + i_symbol_bt_b->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>); \ + i_symbol_bt_b->attributes().setIsInitialized(); \ + i_symbol_bt_b->attributes().value() = \ + EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(5.3))); \ + \ + ASTNodeTypeCleaner<language::import_instruction>{*ast}; \ + \ + ASTSymbolTableBuilder{*ast}; \ + ASTNodeDataTypeBuilder{*ast}; \ + \ + ASTNodeDeclarationToAffectationConverter{*ast}; \ + ASTNodeTypeCleaner<language::var_declaration>{*ast}; \ + \ + ASTNodeExpressionBuilder{*ast}; \ + \ + std::stringstream ast_output; \ + ast_output << '\n' << ASTPrinter{*ast, ASTPrinter::Format::raw, {ASTPrinter::Info::exec_type}}; \ + \ + REQUIRE(ast_output.str() == expected_output); \ + } + + SECTION("shift left (builtin)") + { + std::string_view data = R"( +let m : builtin_t; +let n : builtin_t; +n << m; +)"; + + std::string_view result = R"( +(root:ASTNodeListProcessor) + `-(language::shift_left_op:BinaryExpressionProcessor<language::shift_left_op, std::shared_ptr<double const>, std::shared_ptr<double const>, std::shared_ptr<double const> >) + +-(language::name:n:NameProcessor) + `-(language::name:m:NameProcessor) +)"; + + CHECK_AST_BUILTIN_SHIFT_EXPRESSION(data, result); + } + + SECTION("shift right (builtin)") + { + std::string_view data = R"( +let m : builtin_t; +let n : builtin_t; +n >> m; +)"; + + std::string_view result = R"( +(root:ASTNodeListProcessor) + `-(language::shift_right_op:BinaryExpressionProcessor<language::shift_right_op, std::shared_ptr<double const>, std::shared_ptr<double const>, std::shared_ptr<double const> >) + +-(language::name:n:NameProcessor) + `-(language::name:m:NameProcessor) +)"; + + CHECK_AST_BUILTIN_SHIFT_EXPRESSION(data, result); + } + } + SECTION("Errors") { SECTION("Invalid binary operator type") diff --git a/tests/test_BinaryExpressionProcessor_arithmetic.cpp b/tests/test_BinaryExpressionProcessor_arithmetic.cpp index 2c91674b2fd300bb59ef4faae100c4ea0423df33..6ded2c991a25508ae89ce90fca602aad71901547 100644 --- a/tests/test_BinaryExpressionProcessor_arithmetic.cpp +++ b/tests/test_BinaryExpressionProcessor_arithmetic.cpp @@ -1,10 +1,161 @@ #include <catch2/catch_test_macros.hpp> #include <catch2/matchers/catch_matchers_all.hpp> +#include <FixturesForBuiltinT.hpp> + +#include <language/utils/BasicAffectationRegistrerFor.hpp> +#include <language/utils/BinaryOperatorProcessorBuilder.hpp> +#include <language/utils/DataHandler.hpp> +#include <language/utils/OperatorRepository.hpp> +#include <language/utils/TypeDescriptor.hpp> + #include <test_BinaryExpressionProcessor_utils.hpp> // clazy:excludeall=non-pod-global-static +#define CHECK_BUILTIN_BINARY_EXPRESSION_RESULT(data, result) \ + { \ + TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; \ + auto ast = ASTBuilder::build(input); \ + \ + ASTModulesImporter{*ast}; \ + \ + BasicAffectationRegisterFor<EmbeddedData>{ASTNodeDataType::build<ASTNodeDataType::type_id_t>("builtin_t")}; \ + \ + OperatorRepository& repository = OperatorRepository::instance(); \ + \ + repository.addBinaryOperator<language::plus_op>( \ + std::make_shared< \ + BinaryOperatorProcessorBuilder<language::plus_op, std::shared_ptr<const double>, \ + std::shared_ptr<const double>, std::shared_ptr<const double>>>()); \ + \ + repository.addBinaryOperator<language::plus_op>( \ + std::make_shared<BinaryOperatorProcessorBuilder<language::plus_op, std::shared_ptr<const double>, \ + std::shared_ptr<const double>, double>>()); \ + \ + repository.addBinaryOperator<language::plus_op>( \ + std::make_shared<BinaryOperatorProcessorBuilder<language::plus_op, std::shared_ptr<const double>, double, \ + std::shared_ptr<const double>>>()); \ + \ + SymbolTable& symbol_table = *ast->m_symbol_table; \ + auto [i_symbol, success] = symbol_table.add(builtin_data_type.nameOfTypeId(), ast->begin()); \ + if (not success) { \ + throw UnexpectedError("cannot add '" + builtin_data_type.nameOfTypeId() + "' type for testing"); \ + } \ + \ + i_symbol->attributes().setDataType(ASTNodeDataType::build<ASTNodeDataType::type_name_id_t>()); \ + i_symbol->attributes().setIsInitialized(); \ + i_symbol->attributes().value() = symbol_table.typeEmbedderTable().size(); \ + symbol_table.typeEmbedderTable().add(std::make_shared<TypeDescriptor>(builtin_data_type.nameOfTypeId())); \ + \ + auto [i_symbol_bt_a, success_bt_a] = symbol_table.add("bt_a", ast->begin()); \ + if (not success_bt_a) { \ + throw UnexpectedError("cannot add 'bt_a' of type builtin_t for testing"); \ + } \ + i_symbol_bt_a->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>); \ + i_symbol_bt_a->attributes().setIsInitialized(); \ + i_symbol_bt_a->attributes().value() = \ + EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(3.2))); \ + \ + auto [i_symbol_bt_b, success_bt_b] = symbol_table.add("bt_b", ast->begin()); \ + if (not success_bt_b) { \ + throw UnexpectedError("cannot add 'bt_b' of type builtin_t for testing"); \ + } \ + i_symbol_bt_b->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>); \ + i_symbol_bt_b->attributes().setIsInitialized(); \ + i_symbol_bt_b->attributes().value() = \ + EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(5.3))); \ + \ + ASTNodeTypeCleaner<language::import_instruction>{*ast}; \ + \ + ASTSymbolTableBuilder{*ast}; \ + ASTNodeDataTypeBuilder{*ast}; \ + \ + ASTNodeDeclarationToAffectationConverter{*ast}; \ + ASTNodeTypeCleaner<language::var_declaration>{*ast}; \ + \ + ASTNodeExpressionBuilder{*ast}; \ + ExecutionPolicy exec_policy; \ + ast->execute(exec_policy); \ + \ + using namespace TAO_PEGTL_NAMESPACE; \ + position use_position{internal::iterator{"fixture"}, "fixture"}; \ + use_position.byte = 10000; \ + auto [symbol, found] = symbol_table.find("r", use_position); \ + \ + auto attributes = symbol->attributes(); \ + auto embedded_value = std::get<EmbeddedData>(attributes.value()); \ + \ + double value = *dynamic_cast<const DataHandler<const double>&>(embedded_value.get()).data_ptr(); \ + REQUIRE(value == expected); \ + } + +#define CHECK_BUILTIN_BINARY_EXPRESSION_ERROR(data, error_msg) \ + { \ + TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; \ + auto ast = ASTBuilder::build(input); \ + \ + ASTModulesImporter{*ast}; \ + \ + BasicAffectationRegisterFor<EmbeddedData>{ASTNodeDataType::build<ASTNodeDataType::type_id_t>("builtin_t")}; \ + \ + OperatorRepository& repository = OperatorRepository::instance(); \ + \ + repository.addBinaryOperator<language::divide_op>( \ + std::make_shared< \ + BinaryOperatorProcessorBuilder<language::divide_op, std::shared_ptr<const double>, \ + std::shared_ptr<const double>, std::shared_ptr<const double>>>()); \ + \ + repository.addBinaryOperator<language::divide_op>( \ + std::make_shared<BinaryOperatorProcessorBuilder<language::divide_op, std::shared_ptr<const double>, \ + std::shared_ptr<const double>, double>>()); \ + \ + repository.addBinaryOperator<language::divide_op>( \ + std::make_shared<BinaryOperatorProcessorBuilder<language::divide_op, std::shared_ptr<const double>, double, \ + std::shared_ptr<const double>>>()); \ + \ + SymbolTable& symbol_table = *ast->m_symbol_table; \ + auto [i_symbol, success] = symbol_table.add(builtin_data_type.nameOfTypeId(), ast->begin()); \ + if (not success) { \ + throw UnexpectedError("cannot add '" + builtin_data_type.nameOfTypeId() + "' type for testing"); \ + } \ + \ + i_symbol->attributes().setDataType(ASTNodeDataType::build<ASTNodeDataType::type_name_id_t>()); \ + i_symbol->attributes().setIsInitialized(); \ + i_symbol->attributes().value() = symbol_table.typeEmbedderTable().size(); \ + symbol_table.typeEmbedderTable().add(std::make_shared<TypeDescriptor>(builtin_data_type.nameOfTypeId())); \ + \ + auto [i_symbol_bt_a, success_bt_a] = symbol_table.add("bt_a", ast->begin()); \ + if (not success_bt_a) { \ + throw UnexpectedError("cannot add 'bt_a' of type builtin_t for testing"); \ + } \ + i_symbol_bt_a->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>); \ + i_symbol_bt_a->attributes().setIsInitialized(); \ + i_symbol_bt_a->attributes().value() = \ + EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(3.2))); \ + \ + auto [i_symbol_bt_b, success_bt_b] = symbol_table.add("bt_b", ast->begin()); \ + if (not success_bt_b) { \ + throw UnexpectedError("cannot add 'bt_b' of type builtin_t for testing"); \ + } \ + i_symbol_bt_b->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>); \ + i_symbol_bt_b->attributes().setIsInitialized(); \ + i_symbol_bt_b->attributes().value() = \ + EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(5.3))); \ + \ + ASTNodeTypeCleaner<language::import_instruction>{*ast}; \ + \ + ASTSymbolTableBuilder{*ast}; \ + ASTNodeDataTypeBuilder{*ast}; \ + \ + ASTNodeDeclarationToAffectationConverter{*ast}; \ + ASTNodeTypeCleaner<language::var_declaration>{*ast}; \ + \ + ASTNodeExpressionBuilder{*ast}; \ + ExecutionPolicy exec_policy; \ + REQUIRE_THROWS_WITH(ast->execute(exec_policy), error_msg); \ + } + TEST_CASE("BinaryExpressionProcessor arithmetic", "[language]") { SECTION("+") @@ -151,4 +302,43 @@ TEST_CASE("BinaryExpressionProcessor arithmetic", "[language]") CHECK_BINARY_EXPRESSION_RESULT(R"(let r:R, r = -1.2 / 2.3;)", "r", (-1.2 / 2.3)); } } + + SECTION("binary operator [builtin]") + { + SECTION("builtin both side") + { + std::string_view data = R"(let r:builtin_t, r = bt_a + bt_b;)"; + const double expected = double{3.2} + double{5.3}; + + CHECK_BUILTIN_BINARY_EXPRESSION_RESULT(data, expected); + } + + SECTION("builtin lhs") + { + std::string_view data = R"(let r:builtin_t, r = bt_a + 5.;)"; + const double expected = double{3.2} + double{5}; + + CHECK_BUILTIN_BINARY_EXPRESSION_RESULT(data, expected); + } + + SECTION("builtin rhs") + { + std::string_view data = R"(let r:builtin_t, r = 5. + bt_a;)"; + const double expected = double{3.2} + double{5}; + + CHECK_BUILTIN_BINARY_EXPRESSION_RESULT(data, expected); + } + + SECTION("runtime error") + { + std::string_view data_both = R"(let r:builtin_t, r = bt_a / bt_b;)"; + CHECK_BUILTIN_BINARY_EXPRESSION_ERROR(data_both, "runtime error both"); + + std::string_view data_lhs = R"(let r:builtin_t, r = bt_a / 2.3;)"; + CHECK_BUILTIN_BINARY_EXPRESSION_ERROR(data_lhs, "runtime error lhs"); + + std::string_view data_rhs = R"(let r:builtin_t, r = 2.3/ bt_a;)"; + CHECK_BUILTIN_BINARY_EXPRESSION_ERROR(data_rhs, "runtime error rhs"); + } + } } diff --git a/tests/test_BinaryExpressionProcessor_raw.cpp b/tests/test_BinaryExpressionProcessor_raw.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c115c67ca070c0610e5225c022fc527e4cff8a1a --- /dev/null +++ b/tests/test_BinaryExpressionProcessor_raw.cpp @@ -0,0 +1,86 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <language/node_processor/BinaryExpressionProcessor.hpp> +#include <language/utils/OFStream.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("BinaryExpressionProcessor raw operators", "[language]") +{ + REQUIRE(BinOp<language::and_op>{}.eval(true, true) == true); + REQUIRE(BinOp<language::and_op>{}.eval(true, false) == false); + REQUIRE(BinOp<language::and_op>{}.eval(false, true) == false); + REQUIRE(BinOp<language::and_op>{}.eval(false, false) == false); + + REQUIRE(BinOp<language::or_op>{}.eval(true, true) == true); + REQUIRE(BinOp<language::or_op>{}.eval(true, false) == true); + REQUIRE(BinOp<language::or_op>{}.eval(false, true) == true); + REQUIRE(BinOp<language::or_op>{}.eval(false, false) == false); + + REQUIRE(BinOp<language::xor_op>{}.eval(true, true) == false); + REQUIRE(BinOp<language::xor_op>{}.eval(true, false) == true); + REQUIRE(BinOp<language::xor_op>{}.eval(false, true) == true); + REQUIRE(BinOp<language::xor_op>{}.eval(false, false) == false); + + REQUIRE(BinOp<language::eqeq_op>{}.eval(3, 3) == true); + REQUIRE(BinOp<language::eqeq_op>{}.eval(2, 3) == false); + + REQUIRE(BinOp<language::not_eq_op>{}.eval(3, 3) == false); + REQUIRE(BinOp<language::not_eq_op>{}.eval(2, 3) == true); + + REQUIRE(BinOp<language::lesser_op>{}.eval(2.9, 3) == true); + REQUIRE(BinOp<language::lesser_op>{}.eval(3, 3) == false); + REQUIRE(BinOp<language::lesser_op>{}.eval(3.1, 3) == false); + + REQUIRE(BinOp<language::lesser_or_eq_op>{}.eval(2.9, 3) == true); + REQUIRE(BinOp<language::lesser_or_eq_op>{}.eval(3, 3) == true); + REQUIRE(BinOp<language::lesser_or_eq_op>{}.eval(3.1, 3) == false); + + REQUIRE(BinOp<language::greater_op>{}.eval(2.9, 3) == false); + REQUIRE(BinOp<language::greater_op>{}.eval(3, 3) == false); + REQUIRE(BinOp<language::greater_op>{}.eval(3.1, 3) == true); + + REQUIRE(BinOp<language::greater_or_eq_op>{}.eval(2.9, 3) == false); + REQUIRE(BinOp<language::greater_or_eq_op>{}.eval(3, 3) == true); + REQUIRE(BinOp<language::greater_or_eq_op>{}.eval(3.1, 3) == true); + + REQUIRE(BinOp<language::plus_op>{}.eval(2.9, 3) == (2.9 + 3)); + REQUIRE(BinOp<language::minus_op>{}.eval(2.9, 3) == (2.9 - 3)); + REQUIRE(BinOp<language::multiply_op>{}.eval(2.9, 3) == (2.9 * 3)); + REQUIRE(BinOp<language::divide_op>{}.eval(2.9, 3) == (2.9 / 3)); + + { + std::filesystem::path path = std::filesystem::temp_directory_path(); + path.append(std::string{"binary_expression_processor_shift_left_"} + std::to_string(getpid())); + + std::string filename = path.string(); + + std::shared_ptr<const OStream> p_fout = std::make_shared<OFStream>(filename); + BinOp<language::shift_left_op>{}.eval(p_fout, true); + BinOp<language::shift_left_op>{}.eval(p_fout, std::string{" bar\n"}); + p_fout.reset(); + + REQUIRE(std::filesystem::exists(filename)); + + { + std::string file_content; + std::ifstream fin(filename.c_str()); + + do { + char c = fin.get(); + if (c != EOF) { + file_content += c; + } + } while (fin); + + REQUIRE(file_content == "true bar\n"); + } + + std::filesystem::remove(filename); + REQUIRE(not std::filesystem::exists(filename)); + } + + REQUIRE(BinOp<language::shift_left_op>{}.eval(3, 2) == (3 << 2)); + REQUIRE(BinOp<language::shift_right_op>{}.eval(17, 2) == (17 >> 2)); +} diff --git a/tests/test_BinaryExpressionProcessor_shift.cpp b/tests/test_BinaryExpressionProcessor_shift.cpp new file mode 100644 index 0000000000000000000000000000000000000000..082d5fc5e829e11ac14e700e226f07278fca13c8 --- /dev/null +++ b/tests/test_BinaryExpressionProcessor_shift.cpp @@ -0,0 +1,62 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <test_BinaryExpressionProcessor_utils.hpp> + +#include <fstream> +#include <unistd.h> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("BinaryExpressionProcessor shift", "[language]") +{ + SECTION("<<") + { + std::filesystem::path path = std::filesystem::temp_directory_path(); + + path.append(std::string{"binary_expression_processor_"} + std::to_string(getpid())); + + std::string filename = path.string(); + + { + std::ostringstream data; + data << "let fout:ostream, fout = ofstream(\"" << path.string() << "\");\n"; + data << R"(fout << 2 << " " << true << " " << 2 + 3 << "\n";)"; + + TAO_PEGTL_NAMESPACE::string_input input{data.str(), "test.pgs"}; + auto ast = ASTBuilder::build(input); + + ASTModulesImporter{*ast}; + ASTNodeTypeCleaner<language::import_instruction>{*ast}; + + ASTSymbolTableBuilder{*ast}; + ASTNodeDataTypeBuilder{*ast}; + + ASTNodeDeclarationToAffectationConverter{*ast}; + ASTNodeTypeCleaner<language::var_declaration>{*ast}; + + ASTNodeExpressionBuilder{*ast}; + ExecutionPolicy exec_policy; + ast->execute(exec_policy); + } + + REQUIRE(std::filesystem::exists(filename)); + + { + std::string file_content; + std::ifstream fin(filename.c_str()); + + do { + char c = fin.get(); + if (c != EOF) { + file_content += c; + } + } while (fin); + + REQUIRE(file_content == "2 true 5\n"); + } + + std::filesystem::remove(filename); + REQUIRE(not std::filesystem::exists(filename)); + } +} diff --git a/tests/test_BinaryOperatorMangler.cpp b/tests/test_BinaryOperatorMangler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..329c59dcb9fd5fadfae97f39dcbb9e7a6ee3e582 --- /dev/null +++ b/tests/test_BinaryOperatorMangler.cpp @@ -0,0 +1,31 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <language/utils/BinaryOperatorMangler.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("BinaryOperatorMangler", "[language]") +{ + SECTION("binary operators") + { + const ASTNodeDataType R = ASTNodeDataType::build<ASTNodeDataType::double_t>(); + const ASTNodeDataType Z = ASTNodeDataType::build<ASTNodeDataType::int_t>(); + + REQUIRE(binaryOperatorMangler<language::multiply_op>(R, Z) == "R * Z"); + REQUIRE(binaryOperatorMangler<language::divide_op>(R, Z) == "R / Z"); + REQUIRE(binaryOperatorMangler<language::plus_op>(R, Z) == "R + Z"); + REQUIRE(binaryOperatorMangler<language::minus_op>(R, Z) == "R - Z"); + REQUIRE(binaryOperatorMangler<language::or_op>(R, Z) == "R or Z"); + REQUIRE(binaryOperatorMangler<language::and_op>(R, Z) == "R and Z"); + REQUIRE(binaryOperatorMangler<language::xor_op>(R, Z) == "R xor Z"); + REQUIRE(binaryOperatorMangler<language::greater_op>(R, Z) == "R > Z"); + REQUIRE(binaryOperatorMangler<language::greater_or_eq_op>(R, Z) == "R >= Z"); + REQUIRE(binaryOperatorMangler<language::lesser_op>(R, Z) == "R < Z"); + REQUIRE(binaryOperatorMangler<language::lesser_or_eq_op>(R, Z) == "R <= Z"); + REQUIRE(binaryOperatorMangler<language::eqeq_op>(R, Z) == "R == Z"); + REQUIRE(binaryOperatorMangler<language::not_eq_op>(R, Z) == "R != Z"); + REQUIRE(binaryOperatorMangler<language::shift_left_op>(R, Z) == "R << Z"); + REQUIRE(binaryOperatorMangler<language::shift_right_op>(R, Z) == "R >> Z"); + } +} diff --git a/tests/test_BuiltinFunctionProcessor.cpp b/tests/test_BuiltinFunctionProcessor.cpp index a1fb3fe82b982bc50d2b5247c1158941da24ce0d..bbc5ac52e1a6e8034b68b591adff83c1d8701ff5 100644 --- a/tests/test_BuiltinFunctionProcessor.cpp +++ b/tests/test_BuiltinFunctionProcessor.cpp @@ -278,6 +278,42 @@ let z:Z, z = round(-1.2); CHECK_BUILTIN_FUNCTION_EVALUATION_RESULT(data, "z", int64_t{-1}); } + { // min + tested_function_set.insert("min:R*R"); + std::string_view data = R"( +import math; +let x:R, x = min(-2,2.3); +)"; + CHECK_BUILTIN_FUNCTION_EVALUATION_RESULT(data, "x", double{-2}); + } + + { // min + tested_function_set.insert("min:Z*Z"); + std::string_view data = R"( +import math; +let z:Z, z = min(-1,2); +)"; + CHECK_BUILTIN_FUNCTION_EVALUATION_RESULT(data, "z", int64_t{-1}); + } + + { // max + tested_function_set.insert("max:R*R"); + std::string_view data = R"( +import math; +let x:R, x = max(-1,2.3); +)"; + CHECK_BUILTIN_FUNCTION_EVALUATION_RESULT(data, "x", double{2.3}); + } + + { // max + tested_function_set.insert("max:Z*Z"); + std::string_view data = R"( +import math; +let z:Z, z = max(-1,2); +)"; + CHECK_BUILTIN_FUNCTION_EVALUATION_RESULT(data, "z", int64_t{2}); + } + { // dot tested_function_set.insert("dot:R^1*R^1"); std::string_view data = R"( diff --git a/tests/test_CRSMatrixDescriptor.cpp b/tests/test_CRSMatrixDescriptor.cpp index 28276aaadb747f01933fda8ce302cb853af5cf6d..4d31a7397aee4fa031179a912f143b3cf3f32578 100644 --- a/tests/test_CRSMatrixDescriptor.cpp +++ b/tests/test_CRSMatrixDescriptor.cpp @@ -12,6 +12,34 @@ template class CRSMatrixDescriptor<int>; TEST_CASE("CRSMatrixDescriptor", "[algebra]") { + SECTION("sizes") + { + SECTION("rectangle") + { + const size_t nb_lines = 2; + const size_t nb_columns = 5; + + Array<int> non_zeros{nb_lines}; + non_zeros.fill(2); + CRSMatrixDescriptor<int> S(nb_lines, nb_columns, non_zeros); + + REQUIRE(S.numberOfRows() == 2); + REQUIRE(S.numberOfColumns() == 5); + } + + SECTION("square") + { + const size_t nb_lines = 3; + + Array<int> non_zeros{nb_lines}; + non_zeros.fill(2); + CRSMatrixDescriptor<int> S(nb_lines, non_zeros); + + REQUIRE(S.numberOfRows() == 3); + REQUIRE(S.numberOfColumns() == 3); + } + } + SECTION("has overflow / not filled") { const size_t nb_lines = 2; diff --git a/tests/test_DataVariant.cpp b/tests/test_DataVariant.cpp index 0db6af9b2fbe55081a8dc43d359a189cae4f5d5a..dab7d6436cc547b7163baccd369068f20103f7c5 100644 --- a/tests/test_DataVariant.cpp +++ b/tests/test_DataVariant.cpp @@ -11,11 +11,12 @@ TEST_CASE("DataVariant", "[language]") { SECTION("AggregateDataVariant") { - AggregateDataVariant aggregate{std::vector<DataVariant>{double{1.3}, int64_t{-3}, std::vector<double>{1, 2.7}}}; + AggregateDataVariant aggregate{ + std::vector<DataVariant>{double{1.3}, int64_t{-3}, std::vector<double>{1, 2.7}, bool{true}}}; SECTION("size") { - REQUIRE(aggregate.size() == 3); + REQUIRE(aggregate.size() == 4); } SECTION("output") @@ -24,7 +25,8 @@ TEST_CASE("DataVariant", "[language]") aggregate_output << aggregate; std::stringstream expected_output; - expected_output << '(' << double{1.3} << ", " << int64_t{-3} << ", (" << 1 << ", " << 2.7 << "))"; + expected_output << '(' << double{1.3} << ", " << int64_t{-3} << ", (" << 1 << ", " << 2.7 << "), " + << std::boolalpha << true << ")"; REQUIRE(aggregate_output.str() == expected_output.str()); } diff --git a/tests/test_DiscreteFunctionInterpoler.cpp b/tests/test_DiscreteFunctionInterpoler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d24bae5f065ba88afd225268c425b92f9211bcf5 --- /dev/null +++ b/tests/test_DiscreteFunctionInterpoler.cpp @@ -0,0 +1,916 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <language/ast/ASTBuilder.hpp> +#include <language/ast/ASTModulesImporter.hpp> +#include <language/ast/ASTNodeDataTypeBuilder.hpp> +#include <language/ast/ASTNodeExpressionBuilder.hpp> +#include <language/ast/ASTNodeFunctionEvaluationExpressionBuilder.hpp> +#include <language/ast/ASTNodeFunctionExpressionBuilder.hpp> +#include <language/ast/ASTNodeTypeCleaner.hpp> +#include <language/ast/ASTSymbolTableBuilder.hpp> +#include <language/utils/PugsFunctionAdapter.hpp> +#include <language/utils/SymbolTable.hpp> + +#include <MeshDataBaseForTests.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> + +#include <scheme/DiscreteFunctionDescriptorP0.hpp> +#include <scheme/DiscreteFunctionInterpoler.hpp> +#include <scheme/DiscreteFunctionP0.hpp> + +#include <pegtl/string_input.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("DiscreteFunctionInterpoler", "[scheme]") +{ + auto same_cell_value = [](auto f, auto g) -> bool { + using ItemIdType = typename decltype(f)::index_type; + for (ItemIdType item_id = 0; item_id < f.numberOfItems(); ++item_id) { + if (f[item_id] != g[item_id]) { + return false; + } + } + + return true; + }; + + SECTION("1D") + { + constexpr size_t Dimension = 1; + + const auto& mesh_1d = MeshDataBaseForTests::get().cartesianMesh1D(); + auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj(); + + std::string_view data = R"( +import math; +let B_scalar_non_linear_1d: R^1 -> B, x -> (exp(2 * x[0]) + 3 > 4); +let N_scalar_non_linear_1d: R^1 -> N, x -> floor(3 * x[0] * x[0] + 2); +let Z_scalar_non_linear_1d: R^1 -> Z, x -> floor(exp(2 * x[0]) - 1); +let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3; +let R1_non_linear_1d: R^1 -> R^1, x -> 2 * exp(x[0]); +let R2_non_linear_1d: R^1 -> R^2, x -> (2 * exp(x[0]), -3*x[0]); +let R3_non_linear_1d: R^1 -> R^3, x -> (2 * exp(x[0]) + 3, x[0] - 2, 3); +let R1x1_non_linear_1d: R^1 -> R^1x1, x -> (2 * exp(x[0]) * sin(x[0]) + 3); +let R2x2_non_linear_1d: R^1 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]); +let R3x3_non_linear_1d: R^1 -> R^3x3, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0], -4*x[0], 2*x[0]+1, 3, -6*x[0], exp(x[0])); +)"; + TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; + + auto ast = ASTBuilder::build(input); + + ASTModulesImporter{*ast}; + ASTNodeTypeCleaner<language::import_instruction>{*ast}; + + ASTSymbolTableBuilder{*ast}; + ASTNodeDataTypeBuilder{*ast}; + + ASTNodeTypeCleaner<language::var_declaration>{*ast}; + ASTNodeTypeCleaner<language::fct_declaration>{*ast}; + ASTNodeExpressionBuilder{*ast}; + + std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table; + + TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"}; + position.byte = data.size(); // ensure that variables are declared at this point + + SECTION("B_scalar_non_linear_1d") + { + auto [i_symbol, found] = symbol_table->find("B_scalar_non_linear_1d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<double> cell_value{mesh_1d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = std::exp(2 * x[0]) + 3 > 4; + }); + + DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function))); + } + + SECTION("N_scalar_non_linear_1d") + { + auto [i_symbol, found] = symbol_table->find("N_scalar_non_linear_1d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<double> cell_value{mesh_1d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = std::floor(3 * x[0] * x[0] + 2); + }); + + DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function))); + } + + SECTION("Z_scalar_non_linear_1d") + { + auto [i_symbol, found] = symbol_table->find("Z_scalar_non_linear_1d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<double> cell_value{mesh_1d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = std::floor(std::exp(2 * x[0]) - 1); + }); + + DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function))); + } + + SECTION("R_scalar_non_linear_1d") + { + auto [i_symbol, found] = symbol_table->find("R_scalar_non_linear_1d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<double> cell_value{mesh_1d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = 2 * std::exp(x[0]) + 3; + }); + + DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function))); + } + + SECTION("R1_non_linear_1d") + { + using DataType = TinyVector<1>; + + auto [i_symbol, found] = symbol_table->find("R1_non_linear_1d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<DataType> cell_value{mesh_1d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = DataType{2 * std::exp(x[0])}; + }); + + DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function))); + } + + SECTION("R2_non_linear_1d") + { + using DataType = TinyVector<2>; + + auto [i_symbol, found] = symbol_table->find("R2_non_linear_1d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<DataType> cell_value{mesh_1d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = DataType{2 * std::exp(x[0]), -3 * x[0]}; + }); + + DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function))); + } + + SECTION("R3_non_linear_1d") + { + using DataType = TinyVector<3>; + + auto [i_symbol, found] = symbol_table->find("R3_non_linear_1d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<DataType> cell_value{mesh_1d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = DataType{2 * std::exp(x[0]) + 3, x[0] - 2, 3}; + }); + + DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function))); + } + + SECTION("R1x1_non_linear_1d") + { + using DataType = TinyMatrix<1>; + + auto [i_symbol, found] = symbol_table->find("R1x1_non_linear_1d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<DataType> cell_value{mesh_1d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = DataType{2 * std::exp(x[0]) * std::sin(x[0]) + 3}; + }); + + DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function))); + } + + SECTION("R2x2_non_linear_1d") + { + using DataType = TinyMatrix<2>; + + auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_1d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<DataType> cell_value{mesh_1d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = + DataType{2 * std::exp(x[0]) * std::sin(x[0]) + 3, std::sin(x[0] - 2 * x[0]), 3, x[0] * x[0]}; + }); + + DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function))); + } + + SECTION("R3x3_non_linear_1d") + { + using DataType = TinyMatrix<3>; + + auto [i_symbol, found] = symbol_table->find("R3x3_non_linear_1d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<DataType> cell_value{mesh_1d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = DataType{2 * exp(x[0]) * std::sin(x[0]) + 3, + std::sin(x[0] - 2 * x[0]), + 3, + x[0] * x[0], + -4 * x[0], + 2 * x[0] + 1, + 3, + -6 * x[0], + std::exp(x[0])}; + }); + + DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function))); + } + } + + SECTION("2D") + { + constexpr size_t Dimension = 2; + + const auto& mesh_2d = MeshDataBaseForTests::get().cartesianMesh2D(); + auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj(); + + std::string_view data = R"( +import math; +let B_scalar_non_linear_2d: R^2 -> B, x -> (exp(2 * x[0])< 2*x[1]); +let N_scalar_non_linear_2d: R^2 -> N, x -> floor(3 * (x[0] + x[1]) * (x[0] + x[1]) + 2); +let Z_scalar_non_linear_2d: R^2 -> Z, x -> floor(exp(2 * x[0]) - 3 * x[1]); +let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0]) + 3 * x[1]; +let R1_non_linear_2d: R^2 -> R^1, x -> 2 * exp(x[0]); +let R2_non_linear_2d: R^2 -> R^2, x -> (2 * exp(x[0]), -3*x[1]); +let R3_non_linear_2d: R^2 -> R^3, x -> (2 * exp(x[0]) + 3, x[1] - 2, 3); +let R1x1_non_linear_2d: R^2 -> R^1x1, x -> (2 * exp(x[0]) * sin(x[1]) + 3); +let R2x2_non_linear_2d: R^2 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[1]) + 3, sin(x[1] - 2 * x[0]), 3, x[1] * x[0]); +let R3x3_non_linear_2d: R^2 -> R^3x3, x -> (2 * exp(x[0]) * sin(x[1]) + 3, sin(x[1] - 2 * x[0]), 3, x[1] * x[0], -4*x[1], 2*x[0]+1, 3, -6*x[0], exp(x[1])); +)"; + TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; + + auto ast = ASTBuilder::build(input); + + ASTModulesImporter{*ast}; + ASTNodeTypeCleaner<language::import_instruction>{*ast}; + + ASTSymbolTableBuilder{*ast}; + ASTNodeDataTypeBuilder{*ast}; + + ASTNodeTypeCleaner<language::var_declaration>{*ast}; + ASTNodeTypeCleaner<language::fct_declaration>{*ast}; + ASTNodeExpressionBuilder{*ast}; + + std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table; + + TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"}; + position.byte = data.size(); // ensure that variables are declared at this point + + SECTION("B_scalar_non_linear_2d") + { + auto [i_symbol, found] = symbol_table->find("B_scalar_non_linear_2d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<double> cell_value{mesh_2d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = std::exp(2 * x[0]) < 2 * x[1]; + }); + + DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function))); + } + + SECTION("N_scalar_non_linear_2d") + { + auto [i_symbol, found] = symbol_table->find("N_scalar_non_linear_2d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<double> cell_value{mesh_2d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = std::floor(3 * (x[0] + x[1]) * (x[0] + x[1]) + 2); + }); + + DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function))); + } + + SECTION("Z_scalar_non_linear_2d") + { + auto [i_symbol, found] = symbol_table->find("Z_scalar_non_linear_2d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<double> cell_value{mesh_2d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = std::floor(std::exp(2 * x[0]) - 3 * x[1]); + }); + + DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function))); + } + + SECTION("R_scalar_non_linear_2d") + { + auto [i_symbol, found] = symbol_table->find("R_scalar_non_linear_2d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<double> cell_value{mesh_2d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = 2 * std::exp(x[0]) + 3 * x[1]; + }); + + DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function))); + } + + SECTION("R1_non_linear_2d") + { + using DataType = TinyVector<1>; + + auto [i_symbol, found] = symbol_table->find("R1_non_linear_2d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<DataType> cell_value{mesh_2d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = DataType{2 * std::exp(x[0])}; + }); + + DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function))); + } + + SECTION("R2_non_linear_2d") + { + using DataType = TinyVector<2>; + + auto [i_symbol, found] = symbol_table->find("R2_non_linear_2d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<DataType> cell_value{mesh_2d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = DataType{2 * std::exp(x[0]), -3 * x[1]}; + }); + + DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function))); + } + + SECTION("R3_non_linear_2d") + { + using DataType = TinyVector<3>; + + auto [i_symbol, found] = symbol_table->find("R3_non_linear_2d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<DataType> cell_value{mesh_2d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = DataType{2 * std::exp(x[0]) + 3, x[1] - 2, 3}; + }); + + DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function))); + } + + SECTION("R1x1_non_linear_2d") + { + using DataType = TinyMatrix<1>; + + auto [i_symbol, found] = symbol_table->find("R1x1_non_linear_2d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<DataType> cell_value{mesh_2d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3}; + }); + + DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function))); + } + + SECTION("R2x2_non_linear_2d") + { + using DataType = TinyMatrix<2>; + + auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_2d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<DataType> cell_value{mesh_2d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = + DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3, std::sin(x[1] - 2 * x[0]), 3, x[1] * x[0]}; + }); + + DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function))); + } + + SECTION("R3x3_non_linear_2d") + { + using DataType = TinyMatrix<3>; + + auto [i_symbol, found] = symbol_table->find("R3x3_non_linear_2d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<DataType> cell_value{mesh_2d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + + cell_value[cell_id] = DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3, + std::sin(x[1] - 2 * x[0]), + 3, + x[1] * x[0], + -4 * x[1], + 2 * x[0] + 1, + 3, + -6 * x[0], + std::exp(x[1])}; + }); + + DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function))); + } + } + + SECTION("3D") + { + constexpr size_t Dimension = 3; + + const auto& mesh_3d = MeshDataBaseForTests::get().cartesianMesh3D(); + auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj(); + + std::string_view data = R"( +import math; +let B_scalar_non_linear_3d: R^3 -> B, x -> (exp(2 * x[0])< 2*x[1]+x[2]); +let N_scalar_non_linear_3d: R^3 -> N, x -> floor(3 * (x[0] + x[1]) * (x[0] + x[1]) + x[2] * x[2]); +let Z_scalar_non_linear_3d: R^3 -> Z, x -> floor(exp(2 * x[0]) - 3 * x[1] + x[2]); +let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0]+x[2]) + 3 * x[1]; +let R1_non_linear_3d: R^3 -> R^1, x -> 2 * exp(x[0])+sin(x[1] + x[2]); +let R2_non_linear_3d: R^3 -> R^2, x -> (2 * exp(x[0]), -3*x[1] * x[2]); +let R3_non_linear_3d: R^3 -> R^3, x -> (2 * exp(x[0]) + 3, x[1] - 2, 3 * x[2]); +let R1x1_non_linear_3d: R^3 -> R^1x1, x -> (2 * exp(x[0]) * sin(x[1]) + 3 * x[2]); +let R2x2_non_linear_3d: R^3 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[1]) + 3, sin(x[2] - 2 * x[0]), 3, x[1] * x[0] - x[2]); +let R3x3_non_linear_3d: R^3 -> R^3x3, x -> (2 * exp(x[0]) * sin(x[1]) + 3, sin(x[1] - 2 * x[2]), 3, x[1] * x[2], -4*x[1], 2*x[2]+1, 3, -6*x[2], exp(x[1] + x[2])); +)"; + TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; + + auto ast = ASTBuilder::build(input); + + ASTModulesImporter{*ast}; + ASTNodeTypeCleaner<language::import_instruction>{*ast}; + + ASTSymbolTableBuilder{*ast}; + ASTNodeDataTypeBuilder{*ast}; + + ASTNodeTypeCleaner<language::var_declaration>{*ast}; + ASTNodeTypeCleaner<language::fct_declaration>{*ast}; + ASTNodeExpressionBuilder{*ast}; + + std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table; + + TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"}; + position.byte = data.size(); // ensure that variables are declared at this point + + SECTION("B_scalar_non_linear_3d") + { + auto [i_symbol, found] = symbol_table->find("B_scalar_non_linear_3d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<double> cell_value{mesh_3d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = std::exp(2 * x[0]) < 2 * x[1] + x[2]; + }); + + DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function))); + } + + SECTION("N_scalar_non_linear_3d") + { + auto [i_symbol, found] = symbol_table->find("N_scalar_non_linear_3d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<double> cell_value{mesh_3d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = std::floor(3 * (x[0] + x[1]) * (x[0] + x[1]) + x[2] * x[2]); + }); + + DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function))); + } + + SECTION("Z_scalar_non_linear_3d") + { + auto [i_symbol, found] = symbol_table->find("Z_scalar_non_linear_3d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<double> cell_value{mesh_3d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = std::floor(std::exp(2 * x[0]) - 3 * x[1] + x[2]); + }); + + DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function))); + } + + SECTION("R_scalar_non_linear_3d") + { + auto [i_symbol, found] = symbol_table->find("R_scalar_non_linear_3d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<double> cell_value{mesh_3d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = 2 * std::exp(x[0] + x[2]) + 3 * x[1]; + }); + + DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function))); + } + + SECTION("R1_non_linear_3d") + { + using DataType = TinyVector<1>; + + auto [i_symbol, found] = symbol_table->find("R1_non_linear_3d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<DataType> cell_value{mesh_3d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = DataType{2 * std::exp(x[0]) + std::sin(x[1] + x[2])}; + }); + + DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function))); + } + + SECTION("R2_non_linear_3d") + { + using DataType = TinyVector<2>; + + auto [i_symbol, found] = symbol_table->find("R2_non_linear_3d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<DataType> cell_value{mesh_3d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = DataType{2 * std::exp(x[0]), -3 * x[1] * x[2]}; + }); + + DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function))); + } + + SECTION("R3_non_linear_3d") + { + using DataType = TinyVector<3>; + + auto [i_symbol, found] = symbol_table->find("R3_non_linear_3d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<DataType> cell_value{mesh_3d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = DataType{2 * std::exp(x[0]) + 3, x[1] - 2, 3 * x[2]}; + }); + + DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function))); + } + + SECTION("R1x1_non_linear_3d") + { + using DataType = TinyMatrix<1>; + + auto [i_symbol, found] = symbol_table->find("R1x1_non_linear_3d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<DataType> cell_value{mesh_3d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3 * x[2]}; + }); + + DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function))); + } + + SECTION("R2x2_non_linear_3d") + { + using DataType = TinyMatrix<2>; + + auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_3d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<DataType> cell_value{mesh_3d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = + DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3, std::sin(x[2] - 2 * x[0]), 3, x[1] * x[0] - x[2]}; + }); + + DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function))); + } + + SECTION("R3x3_non_linear_3d") + { + using DataType = TinyMatrix<3>; + + auto [i_symbol, found] = symbol_table->find("R3x3_non_linear_3d", position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + + CellValue<DataType> cell_value{mesh_3d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + + cell_value[cell_id] = DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3, + std::sin(x[1] - 2 * x[2]), + 3, + x[1] * x[2], + -4 * x[1], + 2 * x[2] + 1, + 3, + -6 * x[2], + std::exp(x[1] + x[2])}; + }); + + DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(), + function_symbol_id); + std::shared_ptr discrete_function = interpoler.interpolate(); + + REQUIRE( + same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function))); + } + } +} diff --git a/tests/test_DiscreteFunctionP0.cpp b/tests/test_DiscreteFunctionP0.cpp index 8b7e48ea8f999b7d3d166a2f0b6eefc2a363bc75..f8d7bb6ef78d027287cf7d721299f051ba7c7a98 100644 --- a/tests/test_DiscreteFunctionP0.cpp +++ b/tests/test_DiscreteFunctionP0.cpp @@ -2309,6 +2309,1073 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]") } } + SECTION("math functions") + { +#define CHECK_STD_MATH_FUNCTION(data_expression, FCT) \ + { \ + DiscreteFunctionP0 data = data_expression; \ + DiscreteFunctionP0 result = FCT(data); \ + bool is_same = true; \ + parallel_for(data.cellValues().numberOfItems(), [&](const CellId cell_id) { \ + if (result[cell_id] != std::FCT(data[cell_id])) { \ + is_same = false; \ + } \ + }); \ + REQUIRE(is_same); \ + } + +#define CHECK_STD_BINARY_MATH_FUNCTION(lhs_expression, rhs_expression, FCT) \ + { \ + DiscreteFunctionP0 lhs = lhs_expression; \ + DiscreteFunctionP0 rhs = rhs_expression; \ + DiscreteFunctionP0 result = FCT(lhs, rhs); \ + using namespace std; \ + bool is_same = true; \ + parallel_for(lhs.cellValues().numberOfItems(), [&](const CellId cell_id) { \ + if (result[cell_id] != FCT(lhs[cell_id], rhs[cell_id])) { \ + is_same = false; \ + } \ + }); \ + REQUIRE(is_same); \ + } + +#define CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(lhs, rhs_expression, FCT) \ + { \ + DiscreteFunctionP0 rhs = rhs_expression; \ + DiscreteFunctionP0 result = FCT(lhs, rhs); \ + bool is_same = true; \ + using namespace std; \ + parallel_for(rhs.cellValues().numberOfItems(), [&](const CellId cell_id) { \ + if (result[cell_id] != FCT(lhs, rhs[cell_id])) { \ + is_same = false; \ + } \ + }); \ + REQUIRE(is_same); \ + } + +#define CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(lhs_expression, rhs, FCT) \ + { \ + DiscreteFunctionP0 lhs = lhs_expression; \ + DiscreteFunctionP0 result = FCT(lhs, rhs); \ + bool is_same = true; \ + using namespace std; \ + parallel_for(lhs.cellValues().numberOfItems(), [&](const CellId cell_id) { \ + if (result[cell_id] != FCT(lhs[cell_id], rhs)) { \ + is_same = false; \ + } \ + }); \ + REQUIRE(is_same); \ + } + + SECTION("1D") + { + std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh1D(); + + constexpr size_t Dimension = 1; + + auto xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + + DiscreteFunctionP0<Dimension, double> positive_function{mesh}; + + parallel_for( + mesh->numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { positive_function[cell_id] = 1 + std::abs(xj[cell_id][0]); }); + + const double min_value = min(positive_function); + SECTION("min") + { + double local_min = std::numeric_limits<double>::max(); + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + local_min = std::min(local_min, positive_function[cell_id]); + } + REQUIRE(min_value == parallel::allReduceMin(local_min)); + } + + const double max_value = max(positive_function); + SECTION("max") + { + double local_max = -std::numeric_limits<double>::max(); + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + local_max = std::max(local_max, positive_function[cell_id]); + } + REQUIRE(max_value == parallel::allReduceMax(local_max)); + } + + REQUIRE(min_value < max_value); + + DiscreteFunctionP0 unsigned_function = positive_function - 0.5 * (min_value + max_value); + + SECTION("sqrt") + { + CHECK_STD_MATH_FUNCTION(positive_function, sqrt); + } + + SECTION("abs") + { + CHECK_STD_MATH_FUNCTION(positive_function, abs); + } + + SECTION("cos") + { + CHECK_STD_MATH_FUNCTION(positive_function, cos); + } + + SECTION("sin") + { + CHECK_STD_MATH_FUNCTION(positive_function, sin); + } + + SECTION("tan") + { + CHECK_STD_MATH_FUNCTION(positive_function, tan); + } + + DiscreteFunctionP0<Dimension, double> unit_function{mesh}; + + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + unit_function[cell_id] = (2 * (positive_function[cell_id] - min_value) / (max_value - min_value) - 1) * 0.95; + }); + + SECTION("acos") + { + CHECK_STD_MATH_FUNCTION(unit_function, acos); + } + + SECTION("asin") + { + CHECK_STD_MATH_FUNCTION(unit_function, asin); + } + + SECTION("atan") + { + CHECK_STD_MATH_FUNCTION(unit_function, atan); + } + + SECTION("cosh") + { + CHECK_STD_MATH_FUNCTION(positive_function, cosh); + } + + SECTION("sinh") + { + CHECK_STD_MATH_FUNCTION(positive_function, sinh); + } + + SECTION("tanh") + { + CHECK_STD_MATH_FUNCTION(positive_function, tanh); + } + + SECTION("acosh") + { + CHECK_STD_MATH_FUNCTION(positive_function, acosh); + } + + SECTION("asinh") + { + CHECK_STD_MATH_FUNCTION(positive_function, asinh); + } + + SECTION("atanh") + { + CHECK_STD_MATH_FUNCTION(unit_function, atanh); + } + + SECTION("exp") + { + CHECK_STD_MATH_FUNCTION(positive_function, exp); + } + + SECTION("log") + { + CHECK_STD_MATH_FUNCTION(positive_function, log); + } + + SECTION("max(uh,hv)") + { + CHECK_STD_BINARY_MATH_FUNCTION(cos(positive_function), sin(positive_function), max); + } + + SECTION("max(0.2,vh)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.2, sin(positive_function), max); + } + + SECTION("max(uh,0.2)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(cos(positive_function), 0.2, max); + } + + SECTION("atan2(uh,hv)") + { + CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 2 + positive_function, atan2); + } + + SECTION("atan2(0.5,uh)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, 2 + positive_function, atan2); + } + + SECTION("atan2(uh,0.2)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(2 + cos(positive_function), 0.2, atan2); + } + + SECTION("pow(uh,hv)") + { + CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 0.5 * positive_function, pow); + } + + SECTION("pow(uh,0.5)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, positive_function, pow); + } + + SECTION("pow(uh,0.2)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(positive_function, 1.3, pow); + } + + SECTION("min(uh,hv)") + { + CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), min); + } + + SECTION("min(uh,0.5)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, cos(positive_function), min); + } + + SECTION("min(uh,0.2)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.5, min); + } + + SECTION("max(uh,hv)") + { + CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), max); + } + + SECTION("min(uh,0.5)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.1, cos(positive_function), max); + } + + SECTION("min(uh,0.2)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.1, max); + } + + SECTION("dot(uh,hv)") + { + DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + uh[cell_id] = TinyVector<2>{x + 1, 2 * x - 3}; + }); + + DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + vh[cell_id] = TinyVector<2>{2.3 * x, 1 - x}; + }); + + CHECK_STD_BINARY_MATH_FUNCTION(uh, vh, dot); + } + + SECTION("dot(uh,v)") + { + DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + uh[cell_id] = TinyVector<2>{x + 1, 2 * x - 3}; + }); + + const TinyVector<2> v{1, 2}; + + CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(uh, v, dot); + } + + SECTION("dot(u,hv)") + { + const TinyVector<2> u{3, -2}; + + DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + vh[cell_id] = TinyVector<2>{2.3 * x, 1 - x}; + }); + + CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(u, vh, dot); + } + + SECTION("scalar sum") + { + const CellValue<const double> cell_value = positive_function.cellValues(); + + REQUIRE(sum(cell_value) == sum(positive_function)); + } + + SECTION("vector sum") + { + DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + uh[cell_id] = TinyVector<2>{x + 1, 2 * x - 3}; + }); + const CellValue<const TinyVector<2>> cell_value = uh.cellValues(); + + REQUIRE(sum(cell_value) == sum(uh)); + } + + SECTION("matrix sum") + { + DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + uh[cell_id] = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 3 * x - 1}; + }); + const CellValue<const TinyMatrix<2>> cell_value = uh.cellValues(); + + REQUIRE(sum(cell_value) == sum(uh)); + } + + SECTION("integrate scalar") + { + const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + CellValue<double> cell_value{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + cell_value[cell_id] = cell_volume[cell_id] * positive_function[cell_id]; + }); + + REQUIRE(integrate(positive_function) == Catch::Approx(sum(cell_value))); + } + + SECTION("integrate vector") + { + DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + uh[cell_id] = TinyVector<2>{x + 1, 2 * x - 3}; + }); + + const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + CellValue<TinyVector<2>> cell_value{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; }); + + REQUIRE(integrate(uh)[0] == Catch::Approx(sum(cell_value)[0])); + REQUIRE(integrate(uh)[1] == Catch::Approx(sum(cell_value)[1])); + } + + SECTION("integrate matrix") + { + DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + uh[cell_id] = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 1 - x}; + }); + + const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + CellValue<TinyMatrix<2>> cell_value{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; }); + + REQUIRE(integrate(uh)(0, 0) == Catch::Approx(sum(cell_value)(0, 0))); + REQUIRE(integrate(uh)(0, 1) == Catch::Approx(sum(cell_value)(0, 1))); + REQUIRE(integrate(uh)(1, 0) == Catch::Approx(sum(cell_value)(1, 0))); + REQUIRE(integrate(uh)(1, 1) == Catch::Approx(sum(cell_value)(1, 1))); + } + } + + SECTION("2D") + { + std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh2D(); + + constexpr size_t Dimension = 2; + + auto xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + + DiscreteFunctionP0<Dimension, double> positive_function{mesh}; + + parallel_for( + mesh->numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { positive_function[cell_id] = 1 + std::abs(xj[cell_id][0]); }); + + const double min_value = min(positive_function); + SECTION("min") + { + double local_min = std::numeric_limits<double>::max(); + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + local_min = std::min(local_min, positive_function[cell_id]); + } + REQUIRE(min_value == parallel::allReduceMin(local_min)); + } + + const double max_value = max(positive_function); + SECTION("max") + { + double local_max = -std::numeric_limits<double>::max(); + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + local_max = std::max(local_max, positive_function[cell_id]); + } + REQUIRE(max_value == parallel::allReduceMax(local_max)); + } + + REQUIRE(min_value < max_value); + + DiscreteFunctionP0 unsigned_function = positive_function - 0.5 * (min_value + max_value); + + SECTION("sqrt") + { + CHECK_STD_MATH_FUNCTION(positive_function, sqrt); + } + + SECTION("abs") + { + CHECK_STD_MATH_FUNCTION(positive_function, abs); + } + + SECTION("cos") + { + CHECK_STD_MATH_FUNCTION(positive_function, cos); + } + + SECTION("sin") + { + CHECK_STD_MATH_FUNCTION(positive_function, sin); + } + + SECTION("tan") + { + CHECK_STD_MATH_FUNCTION(positive_function, tan); + } + + DiscreteFunctionP0<Dimension, double> unit_function{mesh}; + + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + unit_function[cell_id] = (2 * (positive_function[cell_id] - min_value) / (max_value - min_value) - 1) * 0.95; + }); + + SECTION("acos") + { + CHECK_STD_MATH_FUNCTION(unit_function, acos); + } + + SECTION("asin") + { + CHECK_STD_MATH_FUNCTION(unit_function, asin); + } + + SECTION("atan") + { + CHECK_STD_MATH_FUNCTION(unit_function, atan); + } + + SECTION("cosh") + { + CHECK_STD_MATH_FUNCTION(positive_function, cosh); + } + + SECTION("sinh") + { + CHECK_STD_MATH_FUNCTION(positive_function, sinh); + } + + SECTION("tanh") + { + CHECK_STD_MATH_FUNCTION(positive_function, tanh); + } + + SECTION("acosh") + { + CHECK_STD_MATH_FUNCTION(positive_function, acosh); + } + + SECTION("asinh") + { + CHECK_STD_MATH_FUNCTION(positive_function, asinh); + } + + SECTION("atanh") + { + CHECK_STD_MATH_FUNCTION(unit_function, atanh); + } + + SECTION("exp") + { + CHECK_STD_MATH_FUNCTION(positive_function, exp); + } + + SECTION("log") + { + CHECK_STD_MATH_FUNCTION(positive_function, log); + } + + SECTION("max(uh,hv)") + { + CHECK_STD_BINARY_MATH_FUNCTION(cos(positive_function), sin(positive_function), max); + } + + SECTION("max(0.2,vh)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.2, sin(positive_function), max); + } + + SECTION("max(uh,0.2)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(cos(positive_function), 0.2, max); + } + + SECTION("atan2(uh,hv)") + { + CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 2 + positive_function, atan2); + } + + SECTION("atan2(0.5,uh)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, 2 + positive_function, atan2); + } + + SECTION("atan2(uh,0.2)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(2 + cos(positive_function), 0.2, atan2); + } + + SECTION("pow(uh,hv)") + { + CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 0.5 * positive_function, pow); + } + + SECTION("pow(uh,0.5)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, positive_function, pow); + } + + SECTION("pow(uh,0.2)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(positive_function, 1.3, pow); + } + + SECTION("min(uh,hv)") + { + CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), min); + } + + SECTION("min(uh,0.5)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, cos(positive_function), min); + } + + SECTION("min(uh,0.2)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.5, min); + } + + SECTION("max(uh,hv)") + { + CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), max); + } + + SECTION("min(uh,0.5)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.1, cos(positive_function), max); + } + + SECTION("min(uh,0.2)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.1, max); + } + + SECTION("dot(uh,hv)") + { + DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + uh[cell_id] = TinyVector<2>{x + 1, 2 * x - 3}; + }); + + DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + vh[cell_id] = TinyVector<2>{2.3 * x, 1 - x}; + }); + + CHECK_STD_BINARY_MATH_FUNCTION(uh, vh, dot); + } + + SECTION("dot(uh,v)") + { + DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + uh[cell_id] = TinyVector<2>{x + 1, 2 * x - 3}; + }); + + const TinyVector<2> v{1, 2}; + + CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(uh, v, dot); + } + + SECTION("dot(u,hv)") + { + const TinyVector<2> u{3, -2}; + + DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + vh[cell_id] = TinyVector<2>{2.3 * x, 1 - x}; + }); + + CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(u, vh, dot); + } + + SECTION("scalar sum") + { + const CellValue<const double> cell_value = positive_function.cellValues(); + + REQUIRE(sum(cell_value) == sum(positive_function)); + } + + SECTION("vector sum") + { + DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + uh[cell_id] = TinyVector<2>{x + 1, 2 * x - 3}; + }); + const CellValue<const TinyVector<2>> cell_value = uh.cellValues(); + + REQUIRE(sum(cell_value) == sum(uh)); + } + + SECTION("matrix sum") + { + DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + uh[cell_id] = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 3 * x - 1}; + }); + const CellValue<const TinyMatrix<2>> cell_value = uh.cellValues(); + + REQUIRE(sum(cell_value) == sum(uh)); + } + + SECTION("integrate scalar") + { + const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + CellValue<double> cell_value{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + cell_value[cell_id] = cell_volume[cell_id] * positive_function[cell_id]; + }); + + REQUIRE(integrate(positive_function) == Catch::Approx(sum(cell_value))); + } + + SECTION("integrate vector") + { + DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + uh[cell_id] = TinyVector<2>{x + 1, 2 * x - 3}; + }); + + const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + CellValue<TinyVector<2>> cell_value{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; }); + + REQUIRE(integrate(uh)[0] == Catch::Approx(sum(cell_value)[0])); + REQUIRE(integrate(uh)[1] == Catch::Approx(sum(cell_value)[1])); + } + + SECTION("integrate matrix") + { + DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + uh[cell_id] = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 1 - x}; + }); + + const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + CellValue<TinyMatrix<2>> cell_value{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; }); + + REQUIRE(integrate(uh)(0, 0) == Catch::Approx(sum(cell_value)(0, 0))); + REQUIRE(integrate(uh)(0, 1) == Catch::Approx(sum(cell_value)(0, 1))); + REQUIRE(integrate(uh)(1, 0) == Catch::Approx(sum(cell_value)(1, 0))); + REQUIRE(integrate(uh)(1, 1) == Catch::Approx(sum(cell_value)(1, 1))); + } + } + + SECTION("3D") + { + std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh3D(); + + constexpr size_t Dimension = 3; + + auto xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + + DiscreteFunctionP0<Dimension, double> positive_function{mesh}; + + parallel_for( + mesh->numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { positive_function[cell_id] = 1 + std::abs(xj[cell_id][0]); }); + + const double min_value = min(positive_function); + SECTION("min") + { + double local_min = std::numeric_limits<double>::max(); + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + local_min = std::min(local_min, positive_function[cell_id]); + } + REQUIRE(min_value == parallel::allReduceMin(local_min)); + } + + const double max_value = max(positive_function); + SECTION("max") + { + double local_max = -std::numeric_limits<double>::max(); + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + local_max = std::max(local_max, positive_function[cell_id]); + } + REQUIRE(max_value == parallel::allReduceMax(local_max)); + } + + REQUIRE(min_value < max_value); + + DiscreteFunctionP0 unsigned_function = positive_function - 0.5 * (min_value + max_value); + + SECTION("sqrt") + { + CHECK_STD_MATH_FUNCTION(positive_function, sqrt); + } + + SECTION("abs") + { + CHECK_STD_MATH_FUNCTION(positive_function, abs); + } + + SECTION("cos") + { + CHECK_STD_MATH_FUNCTION(positive_function, cos); + } + + SECTION("sin") + { + CHECK_STD_MATH_FUNCTION(positive_function, sin); + } + + SECTION("tan") + { + CHECK_STD_MATH_FUNCTION(positive_function, tan); + } + + DiscreteFunctionP0<Dimension, double> unit_function{mesh}; + + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + unit_function[cell_id] = (2 * (positive_function[cell_id] - min_value) / (max_value - min_value) - 1) * 0.95; + }); + + SECTION("acos") + { + CHECK_STD_MATH_FUNCTION(unit_function, acos); + } + + SECTION("asin") + { + CHECK_STD_MATH_FUNCTION(unit_function, asin); + } + + SECTION("atan") + { + CHECK_STD_MATH_FUNCTION(unit_function, atan); + } + + SECTION("cosh") + { + CHECK_STD_MATH_FUNCTION(positive_function, cosh); + } + + SECTION("sinh") + { + CHECK_STD_MATH_FUNCTION(positive_function, sinh); + } + + SECTION("tanh") + { + CHECK_STD_MATH_FUNCTION(positive_function, tanh); + } + + SECTION("acosh") + { + CHECK_STD_MATH_FUNCTION(positive_function, acosh); + } + + SECTION("asinh") + { + CHECK_STD_MATH_FUNCTION(positive_function, asinh); + } + + SECTION("atanh") + { + CHECK_STD_MATH_FUNCTION(unit_function, atanh); + } + + SECTION("exp") + { + CHECK_STD_MATH_FUNCTION(positive_function, exp); + } + + SECTION("log") + { + CHECK_STD_MATH_FUNCTION(positive_function, log); + } + + SECTION("max(uh,hv)") + { + CHECK_STD_BINARY_MATH_FUNCTION(cos(positive_function), sin(positive_function), max); + } + + SECTION("max(0.2,vh)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.2, sin(positive_function), max); + } + + SECTION("max(uh,0.2)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(cos(positive_function), 0.2, max); + } + + SECTION("atan2(uh,hv)") + { + CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 2 + positive_function, atan2); + } + + SECTION("atan2(0.5,uh)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, 2 + positive_function, atan2); + } + + SECTION("atan2(uh,0.2)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(2 + cos(positive_function), 0.2, atan2); + } + + SECTION("pow(uh,hv)") + { + CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 0.5 * positive_function, pow); + } + + SECTION("pow(uh,0.5)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, positive_function, pow); + } + + SECTION("pow(uh,0.2)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(positive_function, 1.3, pow); + } + + SECTION("min(uh,hv)") + { + CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), min); + } + + SECTION("min(uh,0.5)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, cos(positive_function), min); + } + + SECTION("min(uh,0.2)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.5, min); + } + + SECTION("max(uh,hv)") + { + CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), max); + } + + SECTION("min(uh,0.5)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.1, cos(positive_function), max); + } + + SECTION("min(uh,0.2)") + { + CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.1, max); + } + + SECTION("dot(uh,hv)") + { + DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + uh[cell_id] = TinyVector<2>{x + 1, 2 * x - 3}; + }); + + DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + vh[cell_id] = TinyVector<2>{2.3 * x, 1 - x}; + }); + + CHECK_STD_BINARY_MATH_FUNCTION(uh, vh, dot); + } + + SECTION("dot(uh,v)") + { + DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + uh[cell_id] = TinyVector<2>{x + 1, 2 * x - 3}; + }); + + const TinyVector<2> v{1, 2}; + + CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(uh, v, dot); + } + + SECTION("dot(u,hv)") + { + const TinyVector<2> u{3, -2}; + + DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + vh[cell_id] = TinyVector<2>{2.3 * x, 1 - x}; + }); + + CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(u, vh, dot); + } + + SECTION("scalar sum") + { + const CellValue<const double> cell_value = positive_function.cellValues(); + + REQUIRE(sum(cell_value) == sum(positive_function)); + } + + SECTION("vector sum") + { + DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + uh[cell_id] = TinyVector<2>{x + 1, 2 * x - 3}; + }); + const CellValue<const TinyVector<2>> cell_value = uh.cellValues(); + + REQUIRE(sum(cell_value) == sum(uh)); + } + + SECTION("matrix sum") + { + DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + uh[cell_id] = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 3 * x - 1}; + }); + const CellValue<const TinyMatrix<2>> cell_value = uh.cellValues(); + + REQUIRE(sum(cell_value) == sum(uh)); + } + + SECTION("integrate scalar") + { + const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + CellValue<double> cell_value{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + cell_value[cell_id] = cell_volume[cell_id] * positive_function[cell_id]; + }); + + REQUIRE(integrate(positive_function) == Catch::Approx(sum(cell_value))); + } + + SECTION("integrate vector") + { + DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + uh[cell_id] = TinyVector<2>{x + 1, 2 * x - 3}; + }); + + const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + CellValue<TinyVector<2>> cell_value{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; }); + + REQUIRE(integrate(uh)[0] == Catch::Approx(sum(cell_value)[0])); + REQUIRE(integrate(uh)[1] == Catch::Approx(sum(cell_value)[1])); + } + + SECTION("integrate matrix") + { + DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh}; + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { + const double x = xj[cell_id][0]; + uh[cell_id] = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 1 - x}; + }); + + const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj(); + + CellValue<TinyMatrix<2>> cell_value{mesh->connectivity()}; + parallel_for( + mesh->numberOfCells(), + PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; }); + + REQUIRE(integrate(uh)(0, 0) == Catch::Approx(sum(cell_value)(0, 0))); + REQUIRE(integrate(uh)(0, 1) == Catch::Approx(sum(cell_value)(0, 1))); + REQUIRE(integrate(uh)(1, 0) == Catch::Approx(sum(cell_value)(1, 0))); + REQUIRE(integrate(uh)(1, 1) == Catch::Approx(sum(cell_value)(1, 1))); + } + } + } + #ifndef NDEBUG SECTION("error") { diff --git a/tests/test_DiscreteFunctionUtils.cpp b/tests/test_DiscreteFunctionUtils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6587fcba0e201539552e78f4550523b9dbe277eb --- /dev/null +++ b/tests/test_DiscreteFunctionUtils.cpp @@ -0,0 +1,477 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> +#include <scheme/DiscreteFunctionP0.hpp> +#include <scheme/DiscreteFunctionP0Vector.hpp> +#include <scheme/DiscreteFunctionUtils.hpp> + +#include <mesh/CartesianMeshBuilder.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("DiscreteFunctionUtils", "[scheme]") +{ + SECTION("1D") + { + constexpr size_t Dimension = 1; + + std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh1D(); + std::shared_ptr mesh_copy = + std::make_shared<std::decay_t<decltype(*mesh)>>(mesh->shared_connectivity(), mesh->xr()); + + SECTION("common mesh") + { + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh); + std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh); + std::shared_ptr wh = std::make_shared<DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh); + + std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy); + + REQUIRE(getCommonMesh({uh, vh, wh}).get() == mesh.get()); + REQUIRE(getCommonMesh({uh, vh, wh, qh}).use_count() == 0); + } + + SECTION("check discretization type") + { + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh); + std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh); + + std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy); + + std::shared_ptr Uh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3); + std::shared_ptr Vh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3); + + REQUIRE(checkDiscretizationType({uh}, DiscreteFunctionType::P0)); + REQUIRE(checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0)); + REQUIRE(not checkDiscretizationType({uh}, DiscreteFunctionType::P0Vector)); + REQUIRE(not checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0Vector)); + REQUIRE(checkDiscretizationType({Uh}, DiscreteFunctionType::P0Vector)); + REQUIRE(checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0Vector)); + REQUIRE(not checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0)); + REQUIRE(not checkDiscretizationType({Uh}, DiscreteFunctionType::P0)); + } + + SECTION("scalar function shallow copy") + { + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*wh).cellValues()[CellId{0}])); + } + + SECTION("R^1 function shallow copy") + { + using DataType = TinyVector<1>; + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}])); + } + + SECTION("R^2 function shallow copy") + { + using DataType = TinyVector<2>; + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}])); + } + + SECTION("R^3 function shallow copy") + { + using DataType = TinyVector<3>; + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}])); + } + + SECTION("R^1x1 function shallow copy") + { + using DataType = TinyMatrix<1>; + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}])); + } + + SECTION("R^2x2 function shallow copy") + { + using DataType = TinyMatrix<2>; + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}])); + } + + SECTION("R^3x3 function shallow copy") + { + using DataType = TinyMatrix<3>; + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}])); + } + } + + SECTION("2D") + { + constexpr size_t Dimension = 2; + + std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh2D(); + std::shared_ptr mesh_copy = + std::make_shared<std::decay_t<decltype(*mesh)>>(mesh->shared_connectivity(), mesh->xr()); + + SECTION("common mesh") + { + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh); + std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh); + std::shared_ptr wh = std::make_shared<DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh); + + std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy); + + REQUIRE(getCommonMesh({uh, vh, wh}).get() == mesh.get()); + REQUIRE(getCommonMesh({uh, vh, wh, qh}).use_count() == 0); + } + + SECTION("check discretization type") + { + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh); + std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh); + + std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy); + + std::shared_ptr Uh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3); + std::shared_ptr Vh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3); + + REQUIRE(checkDiscretizationType({uh}, DiscreteFunctionType::P0)); + REQUIRE(checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0)); + REQUIRE(not checkDiscretizationType({uh}, DiscreteFunctionType::P0Vector)); + REQUIRE(not checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0Vector)); + REQUIRE(checkDiscretizationType({Uh}, DiscreteFunctionType::P0Vector)); + REQUIRE(checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0Vector)); + REQUIRE(not checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0)); + REQUIRE(not checkDiscretizationType({Uh}, DiscreteFunctionType::P0)); + } + + SECTION("scalar function shallow copy") + { + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*wh).cellValues()[CellId{0}])); + } + + SECTION("R^1 function shallow copy") + { + using DataType = TinyVector<1>; + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}])); + } + + SECTION("R^2 function shallow copy") + { + using DataType = TinyVector<2>; + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}])); + } + + SECTION("R^3 function shallow copy") + { + using DataType = TinyVector<3>; + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}])); + } + + SECTION("R^1x1 function shallow copy") + { + using DataType = TinyMatrix<1>; + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}])); + } + + SECTION("R^2x2 function shallow copy") + { + using DataType = TinyMatrix<2>; + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}])); + } + + SECTION("R^3x3 function shallow copy") + { + using DataType = TinyMatrix<3>; + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}])); + } + } + + SECTION("3D") + { + constexpr size_t Dimension = 3; + + std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh3D(); + std::shared_ptr mesh_copy = + std::make_shared<std::decay_t<decltype(*mesh)>>(mesh->shared_connectivity(), mesh->xr()); + + SECTION("common mesh") + { + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh); + std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh); + std::shared_ptr wh = std::make_shared<DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh); + + std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy); + + REQUIRE(getCommonMesh({uh, vh, wh}).get() == mesh.get()); + REQUIRE(getCommonMesh({uh, vh, wh, qh}).use_count() == 0); + } + + SECTION("check discretization type") + { + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh); + std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh); + + std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy); + + std::shared_ptr Uh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3); + std::shared_ptr Vh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3); + + REQUIRE(checkDiscretizationType({uh}, DiscreteFunctionType::P0)); + REQUIRE(checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0)); + REQUIRE(not checkDiscretizationType({uh}, DiscreteFunctionType::P0Vector)); + REQUIRE(not checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0Vector)); + REQUIRE(checkDiscretizationType({Uh}, DiscreteFunctionType::P0Vector)); + REQUIRE(checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0Vector)); + REQUIRE(not checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0)); + REQUIRE(not checkDiscretizationType({Uh}, DiscreteFunctionType::P0)); + } + + SECTION("scalar function shallow copy") + { + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*wh).cellValues()[CellId{0}])); + } + + SECTION("R^1 function shallow copy") + { + using DataType = TinyVector<1>; + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}])); + } + + SECTION("R^2 function shallow copy") + { + using DataType = TinyVector<2>; + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}])); + } + + SECTION("R^3 function shallow copy") + { + using DataType = TinyVector<3>; + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}])); + } + + SECTION("R^1x1 function shallow copy") + { + using DataType = TinyMatrix<1>; + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}])); + } + + SECTION("R^2x2 function shallow copy") + { + using DataType = TinyMatrix<2>; + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}])); + } + + SECTION("R^3x3 function shallow copy") + { + using DataType = TinyMatrix<3>; + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh); + std::shared_ptr vh = shallowCopy(mesh, uh); + + REQUIRE(uh == vh); + + std::shared_ptr wh = shallowCopy(mesh_copy, uh); + + REQUIRE(uh != wh); + REQUIRE(&(uh->cellValues()[CellId{0}]) == + &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}])); + } + } + + SECTION("errors") + { + SECTION("different connectivities") + { + constexpr size_t Dimension = 1; + + std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh1D(); + std::shared_ptr other_mesh = + CartesianMeshBuilder{TinyVector<1>{-1}, TinyVector<1>{3}, TinyVector<1, size_t>{19}}.mesh(); + + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh); + + REQUIRE_THROWS_WITH(shallowCopy(other_mesh, uh), "error: cannot shallow copy when connectivity changes"); + } + + SECTION("incompatible mesh dimension") + { + constexpr size_t Dimension = 1; + + std::shared_ptr mesh_1d = MeshDataBaseForTests::get().cartesianMesh1D(); + std::shared_ptr mesh_2d = MeshDataBaseForTests::get().cartesianMesh2D(); + + std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_1d); + + REQUIRE_THROWS_WITH(shallowCopy(mesh_2d, uh), "error: incompatible mesh dimensions"); + } + } +} diff --git a/tests/test_DiscreteFunctionVectorInterpoler.cpp b/tests/test_DiscreteFunctionVectorInterpoler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a1a5a94b55302aa36fbd85b7d6e7b1522a4ef6b --- /dev/null +++ b/tests/test_DiscreteFunctionVectorInterpoler.cpp @@ -0,0 +1,414 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <language/ast/ASTBuilder.hpp> +#include <language/ast/ASTModulesImporter.hpp> +#include <language/ast/ASTNodeDataTypeBuilder.hpp> +#include <language/ast/ASTNodeExpressionBuilder.hpp> +#include <language/ast/ASTNodeFunctionEvaluationExpressionBuilder.hpp> +#include <language/ast/ASTNodeFunctionExpressionBuilder.hpp> +#include <language/ast/ASTNodeTypeCleaner.hpp> +#include <language/ast/ASTSymbolTableBuilder.hpp> +#include <language/utils/PugsFunctionAdapter.hpp> +#include <language/utils/SymbolTable.hpp> + +#include <MeshDataBaseForTests.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> + +#include <scheme/DiscreteFunctionDescriptorP0Vector.hpp> +#include <scheme/DiscreteFunctionP0Vector.hpp> +#include <scheme/DiscreteFunctionVectorInterpoler.hpp> + +#include <pegtl/string_input.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("DiscreteFunctionVectorInterpoler", "[scheme]") +{ + auto same_cell_value = [](const CellValue<const double>& fi, const size_t i, const auto& f) -> bool { + for (CellId cell_id = 0; cell_id < fi.numberOfItems(); ++cell_id) { + if (fi[cell_id] != f[cell_id][i]) { + return false; + } + } + + return true; + }; + + auto register_function = [](const TAO_PEGTL_NAMESPACE::position& position, + const std::shared_ptr<SymbolTable>& symbol_table, const std::string& name, + std::vector<FunctionSymbolId>& function_id_list) { + auto [i_symbol, found] = symbol_table->find(name, position); + REQUIRE(found); + REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t); + + FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table); + function_id_list.push_back(function_symbol_id); + }; + + SECTION("1D") + { + constexpr size_t Dimension = 1; + + const auto& mesh_1d = MeshDataBaseForTests::get().cartesianMesh1D(); + auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj(); + + std::string_view data = R"( +import math; +let B_scalar_non_linear_1d: R^1 -> B, x -> (exp(2 * x[0]) + 3 > 4); +let N_scalar_non_linear_1d: R^1 -> N, x -> floor(3 * x[0] * x[0] + 2); +let Z_scalar_non_linear_1d: R^1 -> Z, x -> floor(exp(2 * x[0]) - 1); +let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3; +)"; + TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; + + auto ast = ASTBuilder::build(input); + + ASTModulesImporter{*ast}; + ASTNodeTypeCleaner<language::import_instruction>{*ast}; + + ASTSymbolTableBuilder{*ast}; + ASTNodeDataTypeBuilder{*ast}; + + ASTNodeTypeCleaner<language::var_declaration>{*ast}; + ASTNodeTypeCleaner<language::fct_declaration>{*ast}; + ASTNodeExpressionBuilder{*ast}; + + TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"}; + position.byte = data.size(); // ensure that variables are declared at this point + + std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table; + + std::vector<FunctionSymbolId> function_id_list; + register_function(position, symbol_table, "B_scalar_non_linear_1d", function_id_list); + register_function(position, symbol_table, "N_scalar_non_linear_1d", function_id_list); + register_function(position, symbol_table, "Z_scalar_non_linear_1d", function_id_list); + register_function(position, symbol_table, "R_scalar_non_linear_1d", function_id_list); + + DiscreteFunctionVectorInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(), + function_id_list); + std::shared_ptr discrete_function = interpoler.interpolate(); + + size_t i = 0; + + { + CellValue<double> cell_value{mesh_1d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = std::exp(2 * x[0]) + 3 > 4; + }); + + REQUIRE(same_cell_value(cell_value, i++, + dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function))); + } + + { + CellValue<double> cell_value{mesh_1d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = std::floor(3 * x[0] * x[0] + 2); + }); + + REQUIRE(same_cell_value(cell_value, i++, + dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function))); + } + + { + CellValue<double> cell_value{mesh_1d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = std::floor(std::exp(2 * x[0]) - 1); + }); + + REQUIRE(same_cell_value(cell_value, i++, + dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function))); + } + + { + CellValue<double> cell_value{mesh_1d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = 2 * std::exp(x[0]) + 3; + }); + + REQUIRE(same_cell_value(cell_value, i++, + dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function))); + } + + REQUIRE(i == function_id_list.size()); + } + + SECTION("2D") + { + constexpr size_t Dimension = 2; + + const auto& mesh_2d = MeshDataBaseForTests::get().cartesianMesh2D(); + auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj(); + + std::string_view data = R"( +import math; +let B_scalar_non_linear_2d: R^2 -> B, x -> (exp(2 * x[0]) + 3 > 4); +let N_scalar_non_linear_2d: R^2 -> N, x -> floor(3 * (x[0] * x[1]) * (x[0] * x[1]) + 2); +let Z_scalar_non_linear_2d: R^2 -> Z, x -> floor(exp(2 * x[1]) - 1); +let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3; +)"; + TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; + + auto ast = ASTBuilder::build(input); + + ASTModulesImporter{*ast}; + ASTNodeTypeCleaner<language::import_instruction>{*ast}; + + ASTSymbolTableBuilder{*ast}; + ASTNodeDataTypeBuilder{*ast}; + + ASTNodeTypeCleaner<language::var_declaration>{*ast}; + ASTNodeTypeCleaner<language::fct_declaration>{*ast}; + ASTNodeExpressionBuilder{*ast}; + + TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"}; + position.byte = data.size(); // ensure that variables are declared at this point + + std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table; + + std::vector<FunctionSymbolId> function_id_list; + register_function(position, symbol_table, "B_scalar_non_linear_2d", function_id_list); + register_function(position, symbol_table, "N_scalar_non_linear_2d", function_id_list); + register_function(position, symbol_table, "Z_scalar_non_linear_2d", function_id_list); + register_function(position, symbol_table, "R_scalar_non_linear_2d", function_id_list); + + DiscreteFunctionVectorInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(), + function_id_list); + std::shared_ptr discrete_function = interpoler.interpolate(); + + size_t i = 0; + + { + CellValue<double> cell_value{mesh_2d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = std::exp(2 * x[0]) + 3 > 4; + }); + + REQUIRE(same_cell_value(cell_value, i++, + dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function))); + } + + { + CellValue<double> cell_value{mesh_2d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = std::floor(3 * (x[0] * x[1]) * (x[0] * x[1]) + 2); + }); + + REQUIRE(same_cell_value(cell_value, i++, + dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function))); + } + + { + CellValue<double> cell_value{mesh_2d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = std::floor(std::exp(2 * x[1]) - 1); + }); + + REQUIRE(same_cell_value(cell_value, i++, + dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function))); + } + + { + CellValue<double> cell_value{mesh_2d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = 2 * std::exp(x[0] + x[1]) + 3; + }); + + REQUIRE(same_cell_value(cell_value, i++, + dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function))); + } + + REQUIRE(i == function_id_list.size()); + } + + SECTION("3D") + { + constexpr size_t Dimension = 3; + + const auto& mesh_3d = MeshDataBaseForTests::get().cartesianMesh3D(); + auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj(); + + std::string_view data = R"( +import math; +let B_scalar_non_linear_3d: R^3 -> B, x -> (exp(2 * x[0] + x[2]) + 3 > 4); +let N_scalar_non_linear_3d: R^3 -> N, x -> floor(3 * (x[0] * x[1]) * (x[0] * x[1]) + 2); +let Z_scalar_non_linear_3d: R^3 -> Z, x -> floor(exp(2 * x[1]) - x[2]); +let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2]; +)"; + TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; + + auto ast = ASTBuilder::build(input); + + ASTModulesImporter{*ast}; + ASTNodeTypeCleaner<language::import_instruction>{*ast}; + + ASTSymbolTableBuilder{*ast}; + ASTNodeDataTypeBuilder{*ast}; + + ASTNodeTypeCleaner<language::var_declaration>{*ast}; + ASTNodeTypeCleaner<language::fct_declaration>{*ast}; + ASTNodeExpressionBuilder{*ast}; + + TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"}; + position.byte = data.size(); // ensure that variables are declared at this point + + std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table; + + std::vector<FunctionSymbolId> function_id_list; + register_function(position, symbol_table, "B_scalar_non_linear_3d", function_id_list); + register_function(position, symbol_table, "N_scalar_non_linear_3d", function_id_list); + register_function(position, symbol_table, "Z_scalar_non_linear_3d", function_id_list); + register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list); + + DiscreteFunctionVectorInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(), + function_id_list); + std::shared_ptr discrete_function = interpoler.interpolate(); + + size_t i = 0; + + { + CellValue<double> cell_value{mesh_3d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = std::exp(2 * x[0] + x[2]) + 3 > 4; + }); + + REQUIRE(same_cell_value(cell_value, i++, + dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function))); + } + + { + CellValue<double> cell_value{mesh_3d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = std::floor(3 * (x[0] * x[1]) * (x[0] * x[1]) + 2); + }); + + REQUIRE(same_cell_value(cell_value, i++, + dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function))); + } + + { + CellValue<double> cell_value{mesh_3d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = std::floor(std::exp(2 * x[1]) - x[2]); + }); + + REQUIRE(same_cell_value(cell_value, i++, + dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function))); + } + + { + CellValue<double> cell_value{mesh_3d->connectivity()}; + parallel_for( + cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<Dimension>& x = xj[cell_id]; + cell_value[cell_id] = 2 * std::exp(x[0] + x[1]) + 3 * x[2]; + }); + + REQUIRE(same_cell_value(cell_value, i++, + dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function))); + } + + REQUIRE(i == function_id_list.size()); + } + + SECTION("errors") + { + const auto& mesh_3d = MeshDataBaseForTests::get().cartesianMesh3D(); + auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj(); + + std::string_view data = R"( +import math; +let B_scalar_non_linear_2d: R^2 -> B, x -> (exp(2 * x[0] + x[1]) + 3 > 4); +let N_scalar_non_linear_1d: R^1 -> N, x -> floor(3 * x[0] * x[0] + 2); +let Z_scalar_non_linear_3d: R^3 -> Z, x -> floor(exp(2 * x[1]) - x[2]); +let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2]; +let R2_scalar_non_linear_3d: R^3 -> R^2, x -> (2 * exp(x[0] + x[1]) + 3 * x[2], x[0] - x[1]); +)"; + TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; + + auto ast = ASTBuilder::build(input); + + ASTModulesImporter{*ast}; + ASTNodeTypeCleaner<language::import_instruction>{*ast}; + + ASTSymbolTableBuilder{*ast}; + ASTNodeDataTypeBuilder{*ast}; + + ASTNodeTypeCleaner<language::var_declaration>{*ast}; + ASTNodeTypeCleaner<language::fct_declaration>{*ast}; + ASTNodeExpressionBuilder{*ast}; + + TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"}; + position.byte = data.size(); // ensure that variables are declared at this point + + std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table; + + SECTION("invalid function type") + { + std::vector<FunctionSymbolId> function_id_list; + register_function(position, symbol_table, "B_scalar_non_linear_2d", function_id_list); + register_function(position, symbol_table, "N_scalar_non_linear_1d", function_id_list); + register_function(position, symbol_table, "Z_scalar_non_linear_3d", function_id_list); + register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list); + + DiscreteFunctionVectorInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(), + function_id_list); + + const std::string error_msg = R"(error: invalid function type +note: expecting R^3 -> R +note: provided function B_scalar_non_linear_2d: R^2 -> B)"; + + REQUIRE_THROWS_WITH(interpoler.interpolate(), error_msg); + } + + SECTION("invalid value type") + { + std::vector<FunctionSymbolId> function_id_list; + register_function(position, symbol_table, "Z_scalar_non_linear_3d", function_id_list); + register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list); + register_function(position, symbol_table, "R2_scalar_non_linear_3d", function_id_list); + + DiscreteFunctionVectorInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(), + function_id_list); + + const std::string error_msg = R"(error: vector functions require scalar value type. +Invalid interpolation value type: R^2)"; + + REQUIRE_THROWS_WITH(interpoler.interpolate(), error_msg); + } + + SECTION("invalid discrete function type") + { + const std::string error_msg = "error: invalid discrete function type for vector interpolation"; + + DiscreteFunctionVectorInterpoler interpoler{mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(), {}}; + REQUIRE_THROWS_WITH(interpoler.interpolate(), error_msg); + } + } +} diff --git a/tests/test_EmbeddedIDiscreteFunctionMathFunctions.cpp b/tests/test_EmbeddedIDiscreteFunctionMathFunctions.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d41be6566f5bcbc1205c5561abf474f02780b6af --- /dev/null +++ b/tests/test_EmbeddedIDiscreteFunctionMathFunctions.cpp @@ -0,0 +1,1596 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> + +#include <language/utils/EmbeddedIDiscreteFunctionMathFunctions.hpp> +#include <scheme/DiscreteFunctionP0.hpp> +#include <scheme/DiscreteFunctionP0Vector.hpp> + +// clazy:excludeall=non-pod-global-static + +#define CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(P_U, FCT) \ + { \ + using DiscreteFunctionType = const std::decay_t<decltype(*P_U)>; \ + std::shared_ptr p_fu = ::FCT(P_U); \ + \ + REQUIRE(p_fu.use_count() > 0); \ + REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fu)); \ + \ + const auto& fu = dynamic_cast<const DiscreteFunctionType&>(*p_fu); \ + \ + auto values = P_U->cellValues(); \ + bool is_same = true; \ + for (CellId cell_id = 0; cell_id < values.numberOfItems(); ++cell_id) { \ + if (fu[cell_id] != std::FCT(values[cell_id])) { \ + is_same = false; \ + break; \ + } \ + } \ + \ + REQUIRE(is_same); \ + } + +#define CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(P_LHS, P_RHS, FCT) \ + { \ + using DiscreteFunctionType = const std::decay_t<decltype(FCT(*P_LHS, *P_RHS))>; \ + std::shared_ptr p_fuv = ::FCT(P_LHS, P_RHS); \ + \ + REQUIRE(p_fuv.use_count() > 0); \ + REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fuv)); \ + \ + const auto& fuv = dynamic_cast<const DiscreteFunctionType&>(*p_fuv); \ + \ + auto lhs_values = P_LHS->cellValues(); \ + auto rhs_values = P_RHS->cellValues(); \ + bool is_same = true; \ + for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) { \ + using namespace std; \ + if (fuv[cell_id] != FCT(lhs_values[cell_id], rhs_values[cell_id])) { \ + is_same = false; \ + break; \ + } \ + } \ + \ + REQUIRE(is_same); \ + } + +#define CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(P_LHS, RHS, FCT) \ + { \ + using DiscreteFunctionType = const std::decay_t<decltype(FCT(*P_LHS, RHS))>; \ + std::shared_ptr p_fuv = ::FCT(P_LHS, RHS); \ + \ + REQUIRE(p_fuv.use_count() > 0); \ + REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fuv)); \ + \ + const auto& fuv = dynamic_cast<const DiscreteFunctionType&>(*p_fuv); \ + \ + auto lhs_values = P_LHS->cellValues(); \ + bool is_same = true; \ + for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) { \ + using namespace std; \ + if (fuv[cell_id] != FCT(lhs_values[cell_id], RHS)) { \ + is_same = false; \ + break; \ + } \ + } \ + \ + REQUIRE(is_same); \ + } + +#define CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(LHS, P_RHS, FCT) \ + { \ + using DiscreteFunctionType = const std::decay_t<decltype(FCT(LHS, *P_RHS))>; \ + std::shared_ptr p_fuv = ::FCT(LHS, P_RHS); \ + \ + REQUIRE(p_fuv.use_count() > 0); \ + REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fuv)); \ + \ + const auto& fuv = dynamic_cast<const DiscreteFunctionType&>(*p_fuv); \ + \ + auto rhs_values = P_RHS->cellValues(); \ + bool is_same = true; \ + for (CellId cell_id = 0; cell_id < rhs_values.numberOfItems(); ++cell_id) { \ + using namespace std; \ + if (fuv[cell_id] != FCT(LHS, rhs_values[cell_id])) { \ + is_same = false; \ + break; \ + } \ + } \ + \ + REQUIRE(is_same); \ + } + +TEST_CASE("EmbeddedIDiscreteFunctionMathFunctions", "[scheme]") +{ + SECTION("1D") + { + constexpr size_t Dimension = 1; + + using Rd = TinyVector<Dimension>; + + std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh1D(); + + std::shared_ptr other_mesh = + std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr()); + + CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + + CellValue<double> values = [=] { + CellValue<double> build_values{mesh->connectivity()}; + parallel_for( + build_values.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); }); + return build_values; + }(); + + CellValue<double> positive_values = [=] { + CellValue<double> build_values{mesh->connectivity()}; + parallel_for( + build_values.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 2 + std::sin(l2Norm(xj[cell_id])); }); + return build_values; + }(); + + CellValue<double> bounded_values = [=] { + CellValue<double> build_values{mesh->connectivity()}; + parallel_for( + build_values.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.9 * std::sin(l2Norm(xj[cell_id])); }); + return build_values; + }(); + + std::shared_ptr p_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, values); + std::shared_ptr p_other_mesh_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, values); + std::shared_ptr p_positive_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, positive_values); + std::shared_ptr p_bounded_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, bounded_values); + + std::shared_ptr p_R1_u = [=] { + CellValue<TinyVector<1>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj); + }(); + + std::shared_ptr p_R1_v = [=] { + CellValue<TinyVector<1>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj); + }(); + + std::shared_ptr p_other_mesh_R1_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues()); + + constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> { + if constexpr (Dimension == 1) { + return {x[0], 1 + x[0] * x[0]}; + } else if constexpr (Dimension == 2) { + return {x[0], x[1]}; + } else if constexpr (Dimension == 3) { + return {x[0], x[1] + x[2]}; + } + }; + + std::shared_ptr p_R2_u = [=] { + CellValue<TinyVector<2>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + uj[cell_id] = {2 * x[0] + 1, 1 - x[1]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj); + }(); + + std::shared_ptr p_R2_v = [=] { + CellValue<TinyVector<2>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + vj[cell_id] = {x[0] * x[1] + 1, 2 * x[1]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj); + }(); + + std::shared_ptr p_other_mesh_R2_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues()); + + constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> { + if constexpr (Dimension == 1) { + return {x[0], 1 + x[0] * x[0], 2 - x[0]}; + } else if constexpr (Dimension == 2) { + return {x[0], x[1], x[0] + x[1]}; + } else if constexpr (Dimension == 3) { + return {x[0], x[1], x[2]}; + } + }; + + std::shared_ptr p_R3_u = [=] { + CellValue<TinyVector<3>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + uj[cell_id] = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj); + }(); + + std::shared_ptr p_R3_v = [=] { + CellValue<TinyVector<3>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + vj[cell_id] = {x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj); + }(); + + std::shared_ptr p_other_mesh_R3_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues()); + + std::shared_ptr p_R1x1_u = [=] { + CellValue<TinyMatrix<1>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj); + }(); + + std::shared_ptr p_R2x2_u = [=] { + CellValue<TinyMatrix<2>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + + uj[cell_id] = {2 * x[0] + 1, 1 - x[1], // + 2 * x[1], -x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj); + }(); + + std::shared_ptr p_R3x3_u = [=] { + CellValue<TinyMatrix<3>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + + uj[cell_id] = {2 * x[0] + 1, 1 - x[1], 3, // + 2 * x[1], -x[0], x[0] - x[1], // + 3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj); + }(); + + std::shared_ptr p_Vector3_u = [=] { + CellArray<double> uj_vector{mesh->connectivity(), 3}; + parallel_for( + uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + uj_vector[cell_id][0] = 2 * x[0] + 1; + uj_vector[cell_id][1] = 1 - x[1] * x[2]; + uj_vector[cell_id][2] = x[0] + x[2]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector); + }(); + + std::shared_ptr p_Vector3_v = [=] { + CellArray<double> vj_vector{mesh->connectivity(), 3}; + parallel_for( + vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + vj_vector[cell_id][0] = x[0] * x[1] + 1; + vj_vector[cell_id][1] = 2 * x[1]; + vj_vector[cell_id][2] = x[2] * x[0]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector); + }(); + + std::shared_ptr p_Vector2_w = [=] { + CellArray<double> wj_vector{mesh->connectivity(), 2}; + parallel_for( + wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + wj_vector[cell_id][0] = x[0] + x[1] * 2; + wj_vector[cell_id][1] = x[0] * x[1]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector); + }(); + + SECTION("sqrt Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, sqrt); + REQUIRE_THROWS_WITH(sqrt(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("abs Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, abs); + REQUIRE_THROWS_WITH(abs(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("sin Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sin); + REQUIRE_THROWS_WITH(sin(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("cos Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cos); + REQUIRE_THROWS_WITH(cos(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("tan Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tan); + REQUIRE_THROWS_WITH(tan(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("asin Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, asin); + REQUIRE_THROWS_WITH(asin(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("acos Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, acos); + REQUIRE_THROWS_WITH(acos(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("atan Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atan); + REQUIRE_THROWS_WITH(atan(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("sinh Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sinh); + REQUIRE_THROWS_WITH(sinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("cosh Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cosh); + REQUIRE_THROWS_WITH(cosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("tanh Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tanh); + REQUIRE_THROWS_WITH(tanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("asinh Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, asinh); + REQUIRE_THROWS_WITH(asinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("acosh Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, acosh); + REQUIRE_THROWS_WITH(acosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("atanh Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atanh); + REQUIRE_THROWS_WITH(atanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("exp Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, exp); + REQUIRE_THROWS_WITH(exp(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("log Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, log); + REQUIRE_THROWS_WITH(log(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("atan2 Vh*Vh -> Vh") + { + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, atan2); + REQUIRE_THROWS_WITH(atan2(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(atan2(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)"); + REQUIRE_THROWS_WITH(atan2(p_u, p_other_mesh_u), "error: operands are defined on different meshes"); + } + + SECTION("atan2 Vh*R -> Vh") + { + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 3.6, atan2); + REQUIRE_THROWS_WITH(atan2(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R"); + } + + SECTION("atan2 R*Vh -> Vh") + { + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.4, p_u, atan2); + REQUIRE_THROWS_WITH(atan2(2.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)"); + } + + SECTION("min Vh*Vh -> Vh") + { + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, min); + REQUIRE_THROWS_WITH(::min(p_u, p_other_mesh_u), "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(::min(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(::min(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)"); + } + + SECTION("min Vh*R -> Vh") + { + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, min); + REQUIRE_THROWS_WITH(min(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R"); + } + + SECTION("min R*Vh -> Vh") + { + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, min); + REQUIRE_THROWS_WITH(min(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)"); + } + + SECTION("min Vh -> R") + { + REQUIRE(min(std::shared_ptr<const IDiscreteFunction>{p_u}) == min(p_u->cellValues())); + REQUIRE_THROWS_WITH(min(std::shared_ptr<const IDiscreteFunction>{p_R1_u}), + "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("max Vh*Vh -> Vh") + { + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, max); + REQUIRE_THROWS_WITH(::max(p_u, p_other_mesh_u), "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(::max(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(::max(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)"); + } + + SECTION("max Vh*R -> Vh") + { + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, max); + REQUIRE_THROWS_WITH(max(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R"); + } + + SECTION("max Vh -> R") + { + REQUIRE(max(std::shared_ptr<const IDiscreteFunction>{p_u}) == max(p_u->cellValues())); + REQUIRE_THROWS_WITH(max(std::shared_ptr<const IDiscreteFunction>{p_R1_u}), + "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("max R*Vh -> Vh") + { + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, max); + REQUIRE_THROWS_WITH(max(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)"); + } + + SECTION("pow Vh*Vh -> Vh") + { + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, pow); + REQUIRE_THROWS_WITH(pow(p_u, p_other_mesh_u), "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(pow(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(pow(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)"); + } + + SECTION("pow Vh*R -> Vh") + { + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_positive_u, 3.3, pow); + REQUIRE_THROWS_WITH(pow(p_R1_u, 3.1), "error: incompatible operand types Vh(P0:R^1) and R"); + } + + SECTION("pow R*Vh -> Vh") + { + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.1, p_u, pow); + REQUIRE_THROWS_WITH(pow(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)"); + } + + SECTION("dot Vh*Vh -> Vh") + { + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R1_u, p_R1_v, dot); + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R2_u, p_R2_v, dot); + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R3_u, p_R3_v, dot); + + { + auto p_UV = dot(p_Vector3_u, p_Vector3_v); + REQUIRE(p_UV.use_count() == 1); + + auto UV = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*p_UV); + auto direct_UV = dot(*p_Vector3_u, *p_Vector3_v); + + bool is_same = true; + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + if (UV[cell_id] != direct_UV[cell_id]) { + is_same = false; + break; + } + } + + REQUIRE(is_same); + } + + REQUIRE_THROWS_WITH(dot(p_R1_u, p_other_mesh_R1_u), "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(dot(p_R2_u, p_other_mesh_R2_u), "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(dot(p_R3_u, p_other_mesh_R3_u), "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(dot(p_R1_u, p_R3_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^3)"); + REQUIRE_THROWS_WITH(dot(p_Vector3_u, p_Vector2_w), "error: operands have different dimension"); + } + + SECTION("dot Vh*Rd -> Vh") + { + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R1_u, (TinyVector<1>{3}), dot); + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R2_u, (TinyVector<2>{-6, 2}), dot); + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R3_u, (TinyVector<3>{-1, 5, 2}), dot); + REQUIRE_THROWS_WITH(dot(p_R1_u, (TinyVector<2>{-6, 2})), "error: incompatible operand types Vh(P0:R^1) and R^2"); + REQUIRE_THROWS_WITH(dot(p_R2_u, (TinyVector<3>{-1, 5, 2})), + "error: incompatible operand types Vh(P0:R^2) and R^3"); + REQUIRE_THROWS_WITH(dot(p_R3_u, (TinyVector<1>{-1})), "error: incompatible operand types Vh(P0:R^3) and R^1"); + } + + SECTION("dot Rd*Vh -> Vh") + { + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<1>{3}), p_R1_u, dot); + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<2>{-6, 2}), p_R2_u, dot); + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<3>{-1, 5, 2}), p_R3_u, dot); + REQUIRE_THROWS_WITH(dot((TinyVector<2>{-6, 2}), p_R1_u), "error: incompatible operand types R^2 and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(dot((TinyVector<3>{-1, 5, 2}), p_R2_u), + "error: incompatible operand types R^3 and Vh(P0:R^2)"); + REQUIRE_THROWS_WITH(dot((TinyVector<1>{-1}), p_R3_u), "error: incompatible operand types R^1 and Vh(P0:R^3)"); + } + + SECTION("sum_of_R* Vh -> R*") + { + REQUIRE(sum_of<double>(p_u) == sum(p_u->cellValues())); + REQUIRE(sum_of<TinyVector<1>>(p_R1_u) == sum(p_R1_u->cellValues())); + REQUIRE(sum_of<TinyVector<2>>(p_R2_u) == sum(p_R2_u->cellValues())); + REQUIRE(sum_of<TinyVector<3>>(p_R3_u) == sum(p_R3_u->cellValues())); + REQUIRE(sum_of<TinyMatrix<1>>(p_R1x1_u) == sum(p_R1x1_u->cellValues())); + REQUIRE(sum_of<TinyMatrix<2>>(p_R2x2_u) == sum(p_R2x2_u->cellValues())); + REQUIRE(sum_of<TinyMatrix<3>>(p_R3x3_u) == sum(p_R3x3_u->cellValues())); + + REQUIRE_THROWS_WITH(sum_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)"); + REQUIRE_THROWS_WITH(sum_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(sum_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)"); + REQUIRE_THROWS_WITH(sum_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)"); + REQUIRE_THROWS_WITH(sum_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(sum_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(sum_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)"); + } + + SECTION("integral_of_R* Vh -> R*") + { + auto integrate_locally = [&](const auto& cell_values) { + const auto& Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + using DataType = decltype(double{} * cell_values[CellId{0}]); + CellValue<DataType> local_integral{mesh->connectivity()}; + parallel_for( + local_integral.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { local_integral[cell_id] = Vj[cell_id] * cell_values[cell_id]; }); + return local_integral; + }; + + REQUIRE(integral_of<double>(p_u) == sum(integrate_locally(p_u->cellValues()))); + REQUIRE(integral_of<TinyVector<1>>(p_R1_u) == sum(integrate_locally(p_R1_u->cellValues()))); + REQUIRE(integral_of<TinyVector<2>>(p_R2_u) == sum(integrate_locally(p_R2_u->cellValues()))); + REQUIRE(integral_of<TinyVector<3>>(p_R3_u) == sum(integrate_locally(p_R3_u->cellValues()))); + REQUIRE(integral_of<TinyMatrix<1>>(p_R1x1_u) == sum(integrate_locally(p_R1x1_u->cellValues()))); + REQUIRE(integral_of<TinyMatrix<2>>(p_R2x2_u) == sum(integrate_locally(p_R2x2_u->cellValues()))); + REQUIRE(integral_of<TinyMatrix<3>>(p_R3x3_u) == sum(integrate_locally(p_R3x3_u->cellValues()))); + + REQUIRE_THROWS_WITH(integral_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)"); + REQUIRE_THROWS_WITH(integral_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(integral_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)"); + REQUIRE_THROWS_WITH(integral_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)"); + REQUIRE_THROWS_WITH(integral_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(integral_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(integral_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)"); + } + } + + SECTION("2D") + { + constexpr size_t Dimension = 2; + + using Rd = TinyVector<Dimension>; + + std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh2D(); + + std::shared_ptr other_mesh = + std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr()); + + CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + + CellValue<double> values = [=] { + CellValue<double> build_values{mesh->connectivity()}; + parallel_for( + build_values.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); }); + return build_values; + }(); + + CellValue<double> positive_values = [=] { + CellValue<double> build_values{mesh->connectivity()}; + parallel_for( + build_values.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 2 + std::sin(l2Norm(xj[cell_id])); }); + return build_values; + }(); + + CellValue<double> bounded_values = [=] { + CellValue<double> build_values{mesh->connectivity()}; + parallel_for( + build_values.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.9 * std::sin(l2Norm(xj[cell_id])); }); + return build_values; + }(); + + std::shared_ptr p_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, values); + std::shared_ptr p_other_mesh_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, values); + std::shared_ptr p_positive_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, positive_values); + std::shared_ptr p_bounded_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, bounded_values); + + std::shared_ptr p_R1_u = [=] { + CellValue<TinyVector<1>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj); + }(); + + std::shared_ptr p_R1_v = [=] { + CellValue<TinyVector<1>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj); + }(); + + std::shared_ptr p_other_mesh_R1_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues()); + + constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> { + if constexpr (Dimension == 1) { + return {x[0], 1 + x[0] * x[0]}; + } else if constexpr (Dimension == 2) { + return {x[0], x[1]}; + } else if constexpr (Dimension == 3) { + return {x[0], x[1] + x[2]}; + } + }; + + std::shared_ptr p_R2_u = [=] { + CellValue<TinyVector<2>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + uj[cell_id] = {2 * x[0] + 1, 1 - x[1]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj); + }(); + + std::shared_ptr p_R2_v = [=] { + CellValue<TinyVector<2>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + vj[cell_id] = {x[0] * x[1] + 1, 2 * x[1]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj); + }(); + + std::shared_ptr p_other_mesh_R2_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues()); + + constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> { + if constexpr (Dimension == 1) { + return {x[0], 1 + x[0] * x[0], 2 - x[0]}; + } else if constexpr (Dimension == 2) { + return {x[0], x[1], x[0] + x[1]}; + } else if constexpr (Dimension == 3) { + return {x[0], x[1], x[2]}; + } + }; + + std::shared_ptr p_R3_u = [=] { + CellValue<TinyVector<3>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + uj[cell_id] = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj); + }(); + + std::shared_ptr p_R3_v = [=] { + CellValue<TinyVector<3>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + vj[cell_id] = {x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj); + }(); + + std::shared_ptr p_other_mesh_R3_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues()); + + std::shared_ptr p_R1x1_u = [=] { + CellValue<TinyMatrix<1>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj); + }(); + + std::shared_ptr p_R2x2_u = [=] { + CellValue<TinyMatrix<2>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + + uj[cell_id] = {2 * x[0] + 1, 1 - x[1], // + 2 * x[1], -x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj); + }(); + + std::shared_ptr p_R3x3_u = [=] { + CellValue<TinyMatrix<3>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + + uj[cell_id] = {2 * x[0] + 1, 1 - x[1], 3, // + 2 * x[1], -x[0], x[0] - x[1], // + 3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj); + }(); + + std::shared_ptr p_Vector3_u = [=] { + CellArray<double> uj_vector{mesh->connectivity(), 3}; + parallel_for( + uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + uj_vector[cell_id][0] = 2 * x[0] + 1; + uj_vector[cell_id][1] = 1 - x[1] * x[2]; + uj_vector[cell_id][2] = x[0] + x[2]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector); + }(); + + std::shared_ptr p_Vector3_v = [=] { + CellArray<double> vj_vector{mesh->connectivity(), 3}; + parallel_for( + vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + vj_vector[cell_id][0] = x[0] * x[1] + 1; + vj_vector[cell_id][1] = 2 * x[1]; + vj_vector[cell_id][2] = x[2] * x[0]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector); + }(); + + std::shared_ptr p_Vector2_w = [=] { + CellArray<double> wj_vector{mesh->connectivity(), 2}; + parallel_for( + wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + wj_vector[cell_id][0] = x[0] + x[1] * 2; + wj_vector[cell_id][1] = x[0] * x[1]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector); + }(); + + SECTION("sqrt Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, sqrt); + REQUIRE_THROWS_WITH(sqrt(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("abs Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, abs); + REQUIRE_THROWS_WITH(abs(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("sin Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sin); + REQUIRE_THROWS_WITH(sin(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("cos Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cos); + REQUIRE_THROWS_WITH(cos(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("tan Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tan); + REQUIRE_THROWS_WITH(tan(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("asin Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, asin); + REQUIRE_THROWS_WITH(asin(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("acos Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, acos); + REQUIRE_THROWS_WITH(acos(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("atan Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atan); + REQUIRE_THROWS_WITH(atan(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("sinh Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sinh); + REQUIRE_THROWS_WITH(sinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("cosh Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cosh); + REQUIRE_THROWS_WITH(cosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("tanh Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tanh); + REQUIRE_THROWS_WITH(tanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("asinh Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, asinh); + REQUIRE_THROWS_WITH(asinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("acosh Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, acosh); + REQUIRE_THROWS_WITH(acosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("atanh Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atanh); + REQUIRE_THROWS_WITH(atanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("exp Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, exp); + REQUIRE_THROWS_WITH(exp(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("log Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, log); + REQUIRE_THROWS_WITH(log(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("atan2 Vh*Vh -> Vh") + { + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, atan2); + REQUIRE_THROWS_WITH(atan2(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(atan2(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)"); + REQUIRE_THROWS_WITH(atan2(p_u, p_other_mesh_u), "error: operands are defined on different meshes"); + } + + SECTION("atan2 Vh*R -> Vh") + { + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 3.6, atan2); + REQUIRE_THROWS_WITH(atan2(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R"); + } + + SECTION("atan2 R*Vh -> Vh") + { + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.4, p_u, atan2); + REQUIRE_THROWS_WITH(atan2(2.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)"); + } + + SECTION("min Vh*Vh -> Vh") + { + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, min); + REQUIRE_THROWS_WITH(::min(p_u, p_other_mesh_u), "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(::min(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(::min(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)"); + } + + SECTION("min Vh*R -> Vh") + { + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, min); + REQUIRE_THROWS_WITH(min(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R"); + } + + SECTION("min R*Vh -> Vh") + { + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, min); + REQUIRE_THROWS_WITH(min(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)"); + } + + SECTION("min Vh -> R") + { + REQUIRE(min(std::shared_ptr<const IDiscreteFunction>{p_u}) == min(p_u->cellValues())); + REQUIRE_THROWS_WITH(min(std::shared_ptr<const IDiscreteFunction>{p_R1_u}), + "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("max Vh*Vh -> Vh") + { + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, max); + REQUIRE_THROWS_WITH(::max(p_u, p_other_mesh_u), "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(::max(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(::max(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)"); + } + + SECTION("max Vh*R -> Vh") + { + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, max); + REQUIRE_THROWS_WITH(max(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R"); + } + + SECTION("max Vh -> R") + { + REQUIRE(max(std::shared_ptr<const IDiscreteFunction>{p_u}) == max(p_u->cellValues())); + REQUIRE_THROWS_WITH(max(std::shared_ptr<const IDiscreteFunction>{p_R1_u}), + "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("max R*Vh -> Vh") + { + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, max); + REQUIRE_THROWS_WITH(max(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)"); + } + + SECTION("pow Vh*Vh -> Vh") + { + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, pow); + REQUIRE_THROWS_WITH(pow(p_u, p_other_mesh_u), "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(pow(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(pow(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)"); + } + + SECTION("pow Vh*R -> Vh") + { + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_positive_u, 3.3, pow); + REQUIRE_THROWS_WITH(pow(p_R1_u, 3.1), "error: incompatible operand types Vh(P0:R^1) and R"); + } + + SECTION("pow R*Vh -> Vh") + { + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.1, p_u, pow); + REQUIRE_THROWS_WITH(pow(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)"); + } + + SECTION("dot Vh*Vh -> Vh") + { + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R1_u, p_R1_v, dot); + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R2_u, p_R2_v, dot); + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R3_u, p_R3_v, dot); + + { + auto p_UV = dot(p_Vector3_u, p_Vector3_v); + REQUIRE(p_UV.use_count() == 1); + + auto UV = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*p_UV); + auto direct_UV = dot(*p_Vector3_u, *p_Vector3_v); + + bool is_same = true; + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + if (UV[cell_id] != direct_UV[cell_id]) { + is_same = false; + break; + } + } + + REQUIRE(is_same); + } + + REQUIRE_THROWS_WITH(dot(p_R1_u, p_other_mesh_R1_u), "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(dot(p_R2_u, p_other_mesh_R2_u), "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(dot(p_R3_u, p_other_mesh_R3_u), "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(dot(p_R1_u, p_R3_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^3)"); + REQUIRE_THROWS_WITH(dot(p_Vector3_u, p_Vector2_w), "error: operands have different dimension"); + } + + SECTION("dot Vh*Rd -> Vh") + { + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R1_u, (TinyVector<1>{3}), dot); + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R2_u, (TinyVector<2>{-6, 2}), dot); + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R3_u, (TinyVector<3>{-1, 5, 2}), dot); + REQUIRE_THROWS_WITH(dot(p_R1_u, (TinyVector<2>{-6, 2})), "error: incompatible operand types Vh(P0:R^1) and R^2"); + REQUIRE_THROWS_WITH(dot(p_R2_u, (TinyVector<3>{-1, 5, 2})), + "error: incompatible operand types Vh(P0:R^2) and R^3"); + REQUIRE_THROWS_WITH(dot(p_R3_u, (TinyVector<1>{-1})), "error: incompatible operand types Vh(P0:R^3) and R^1"); + } + + SECTION("dot Rd*Vh -> Vh") + { + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<1>{3}), p_R1_u, dot); + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<2>{-6, 2}), p_R2_u, dot); + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<3>{-1, 5, 2}), p_R3_u, dot); + REQUIRE_THROWS_WITH(dot((TinyVector<2>{-6, 2}), p_R1_u), "error: incompatible operand types R^2 and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(dot((TinyVector<3>{-1, 5, 2}), p_R2_u), + "error: incompatible operand types R^3 and Vh(P0:R^2)"); + REQUIRE_THROWS_WITH(dot((TinyVector<1>{-1}), p_R3_u), "error: incompatible operand types R^1 and Vh(P0:R^3)"); + } + + SECTION("sum_of_R* Vh -> R*") + { + REQUIRE(sum_of<double>(p_u) == sum(p_u->cellValues())); + REQUIRE(sum_of<TinyVector<1>>(p_R1_u) == sum(p_R1_u->cellValues())); + REQUIRE(sum_of<TinyVector<2>>(p_R2_u) == sum(p_R2_u->cellValues())); + REQUIRE(sum_of<TinyVector<3>>(p_R3_u) == sum(p_R3_u->cellValues())); + REQUIRE(sum_of<TinyMatrix<1>>(p_R1x1_u) == sum(p_R1x1_u->cellValues())); + REQUIRE(sum_of<TinyMatrix<2>>(p_R2x2_u) == sum(p_R2x2_u->cellValues())); + REQUIRE(sum_of<TinyMatrix<3>>(p_R3x3_u) == sum(p_R3x3_u->cellValues())); + + REQUIRE_THROWS_WITH(sum_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)"); + REQUIRE_THROWS_WITH(sum_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(sum_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)"); + REQUIRE_THROWS_WITH(sum_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)"); + REQUIRE_THROWS_WITH(sum_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(sum_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(sum_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)"); + } + + SECTION("integral_of_R* Vh -> R*") + { + auto integrate_locally = [&](const auto& cell_values) { + const auto& Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + using DataType = decltype(double{} * cell_values[CellId{0}]); + CellValue<DataType> local_integral{mesh->connectivity()}; + parallel_for( + local_integral.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { local_integral[cell_id] = Vj[cell_id] * cell_values[cell_id]; }); + return local_integral; + }; + + REQUIRE(integral_of<double>(p_u) == sum(integrate_locally(p_u->cellValues()))); + REQUIRE(integral_of<TinyVector<1>>(p_R1_u) == sum(integrate_locally(p_R1_u->cellValues()))); + REQUIRE(integral_of<TinyVector<2>>(p_R2_u) == sum(integrate_locally(p_R2_u->cellValues()))); + REQUIRE(integral_of<TinyVector<3>>(p_R3_u) == sum(integrate_locally(p_R3_u->cellValues()))); + REQUIRE(integral_of<TinyMatrix<1>>(p_R1x1_u) == sum(integrate_locally(p_R1x1_u->cellValues()))); + REQUIRE(integral_of<TinyMatrix<2>>(p_R2x2_u) == sum(integrate_locally(p_R2x2_u->cellValues()))); + REQUIRE(integral_of<TinyMatrix<3>>(p_R3x3_u) == sum(integrate_locally(p_R3x3_u->cellValues()))); + + REQUIRE_THROWS_WITH(integral_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)"); + REQUIRE_THROWS_WITH(integral_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(integral_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)"); + REQUIRE_THROWS_WITH(integral_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)"); + REQUIRE_THROWS_WITH(integral_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(integral_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(integral_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)"); + } + } + + SECTION("3D") + { + constexpr size_t Dimension = 3; + + using Rd = TinyVector<Dimension>; + + std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh3D(); + + std::shared_ptr other_mesh = + std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr()); + + CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + + CellValue<double> values = [=] { + CellValue<double> build_values{mesh->connectivity()}; + parallel_for( + build_values.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); }); + return build_values; + }(); + + CellValue<double> positive_values = [=] { + CellValue<double> build_values{mesh->connectivity()}; + parallel_for( + build_values.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 2 + std::sin(l2Norm(xj[cell_id])); }); + return build_values; + }(); + + CellValue<double> bounded_values = [=] { + CellValue<double> build_values{mesh->connectivity()}; + parallel_for( + build_values.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.9 * std::sin(l2Norm(xj[cell_id])); }); + return build_values; + }(); + + std::shared_ptr p_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, values); + std::shared_ptr p_other_mesh_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, values); + std::shared_ptr p_positive_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, positive_values); + std::shared_ptr p_bounded_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, bounded_values); + + std::shared_ptr p_R1_u = [=] { + CellValue<TinyVector<1>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj); + }(); + + std::shared_ptr p_R1_v = [=] { + CellValue<TinyVector<1>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj); + }(); + + std::shared_ptr p_other_mesh_R1_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues()); + + constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> { + if constexpr (Dimension == 1) { + return {x[0], 1 + x[0] * x[0]}; + } else if constexpr (Dimension == 2) { + return {x[0], x[1]}; + } else if constexpr (Dimension == 3) { + return {x[0], x[1] + x[2]}; + } + }; + + std::shared_ptr p_R2_u = [=] { + CellValue<TinyVector<2>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + uj[cell_id] = {2 * x[0] + 1, 1 - x[1]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj); + }(); + + std::shared_ptr p_R2_v = [=] { + CellValue<TinyVector<2>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + vj[cell_id] = {x[0] * x[1] + 1, 2 * x[1]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj); + }(); + + std::shared_ptr p_other_mesh_R2_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues()); + + constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> { + if constexpr (Dimension == 1) { + return {x[0], 1 + x[0] * x[0], 2 - x[0]}; + } else if constexpr (Dimension == 2) { + return {x[0], x[1], x[0] + x[1]}; + } else if constexpr (Dimension == 3) { + return {x[0], x[1], x[2]}; + } + }; + + std::shared_ptr p_R3_u = [=] { + CellValue<TinyVector<3>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + uj[cell_id] = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj); + }(); + + std::shared_ptr p_R3_v = [=] { + CellValue<TinyVector<3>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + vj[cell_id] = {x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj); + }(); + + std::shared_ptr p_other_mesh_R3_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues()); + + std::shared_ptr p_R1x1_u = [=] { + CellValue<TinyMatrix<1>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj); + }(); + + std::shared_ptr p_R2x2_u = [=] { + CellValue<TinyMatrix<2>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + + uj[cell_id] = {2 * x[0] + 1, 1 - x[1], // + 2 * x[1], -x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj); + }(); + + std::shared_ptr p_R3x3_u = [=] { + CellValue<TinyMatrix<3>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + + uj[cell_id] = {2 * x[0] + 1, 1 - x[1], 3, // + 2 * x[1], -x[0], x[0] - x[1], // + 3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj); + }(); + + std::shared_ptr p_Vector3_u = [=] { + CellArray<double> uj_vector{mesh->connectivity(), 3}; + parallel_for( + uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + uj_vector[cell_id][0] = 2 * x[0] + 1; + uj_vector[cell_id][1] = 1 - x[1] * x[2]; + uj_vector[cell_id][2] = x[0] + x[2]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector); + }(); + + std::shared_ptr p_Vector3_v = [=] { + CellArray<double> vj_vector{mesh->connectivity(), 3}; + parallel_for( + vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + vj_vector[cell_id][0] = x[0] * x[1] + 1; + vj_vector[cell_id][1] = 2 * x[1]; + vj_vector[cell_id][2] = x[2] * x[0]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector); + }(); + + std::shared_ptr p_Vector2_w = [=] { + CellArray<double> wj_vector{mesh->connectivity(), 2}; + parallel_for( + wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + wj_vector[cell_id][0] = x[0] + x[1] * 2; + wj_vector[cell_id][1] = x[0] * x[1]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector); + }(); + + SECTION("sqrt Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, sqrt); + REQUIRE_THROWS_WITH(sqrt(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("abs Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, abs); + REQUIRE_THROWS_WITH(abs(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("sin Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sin); + REQUIRE_THROWS_WITH(sin(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("cos Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cos); + REQUIRE_THROWS_WITH(cos(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("tan Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tan); + REQUIRE_THROWS_WITH(tan(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("asin Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, asin); + REQUIRE_THROWS_WITH(asin(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("acos Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, acos); + REQUIRE_THROWS_WITH(acos(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("atan Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atan); + REQUIRE_THROWS_WITH(atan(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("sinh Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sinh); + REQUIRE_THROWS_WITH(sinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("cosh Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cosh); + REQUIRE_THROWS_WITH(cosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("tanh Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tanh); + REQUIRE_THROWS_WITH(tanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("asinh Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, asinh); + REQUIRE_THROWS_WITH(asinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("acosh Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, acosh); + REQUIRE_THROWS_WITH(acosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("atanh Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atanh); + REQUIRE_THROWS_WITH(atanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("exp Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, exp); + REQUIRE_THROWS_WITH(exp(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("log Vh -> Vh") + { + CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, log); + REQUIRE_THROWS_WITH(log(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("atan2 Vh*Vh -> Vh") + { + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, atan2); + REQUIRE_THROWS_WITH(atan2(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(atan2(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)"); + REQUIRE_THROWS_WITH(atan2(p_u, p_other_mesh_u), "error: operands are defined on different meshes"); + } + + SECTION("atan2 Vh*R -> Vh") + { + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 3.6, atan2); + REQUIRE_THROWS_WITH(atan2(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R"); + } + + SECTION("atan2 R*Vh -> Vh") + { + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.4, p_u, atan2); + REQUIRE_THROWS_WITH(atan2(2.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)"); + } + + SECTION("min Vh*Vh -> Vh") + { + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, min); + REQUIRE_THROWS_WITH(::min(p_u, p_other_mesh_u), "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(::min(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(::min(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)"); + } + + SECTION("min Vh*R -> Vh") + { + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, min); + REQUIRE_THROWS_WITH(min(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R"); + } + + SECTION("min R*Vh -> Vh") + { + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, min); + REQUIRE_THROWS_WITH(min(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)"); + } + + SECTION("min Vh -> R") + { + REQUIRE(min(std::shared_ptr<const IDiscreteFunction>{p_u}) == min(p_u->cellValues())); + REQUIRE_THROWS_WITH(min(std::shared_ptr<const IDiscreteFunction>{p_R1_u}), + "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("max Vh*Vh -> Vh") + { + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, max); + REQUIRE_THROWS_WITH(::max(p_u, p_other_mesh_u), "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(::max(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(::max(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)"); + } + + SECTION("max Vh*R -> Vh") + { + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, max); + REQUIRE_THROWS_WITH(max(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R"); + } + + SECTION("max Vh -> R") + { + REQUIRE(max(std::shared_ptr<const IDiscreteFunction>{p_u}) == max(p_u->cellValues())); + REQUIRE_THROWS_WITH(max(std::shared_ptr<const IDiscreteFunction>{p_R1_u}), + "error: invalid operand type Vh(P0:R^1)"); + } + + SECTION("max R*Vh -> Vh") + { + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, max); + REQUIRE_THROWS_WITH(max(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)"); + } + + SECTION("pow Vh*Vh -> Vh") + { + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, pow); + REQUIRE_THROWS_WITH(pow(p_u, p_other_mesh_u), "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(pow(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(pow(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)"); + } + + SECTION("pow Vh*R -> Vh") + { + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_positive_u, 3.3, pow); + REQUIRE_THROWS_WITH(pow(p_R1_u, 3.1), "error: incompatible operand types Vh(P0:R^1) and R"); + } + + SECTION("pow R*Vh -> Vh") + { + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.1, p_u, pow); + REQUIRE_THROWS_WITH(pow(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)"); + } + + SECTION("dot Vh*Vh -> Vh") + { + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R1_u, p_R1_v, dot); + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R2_u, p_R2_v, dot); + CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R3_u, p_R3_v, dot); + + { + auto p_UV = dot(p_Vector3_u, p_Vector3_v); + REQUIRE(p_UV.use_count() == 1); + + auto UV = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*p_UV); + auto direct_UV = dot(*p_Vector3_u, *p_Vector3_v); + + bool is_same = true; + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + if (UV[cell_id] != direct_UV[cell_id]) { + is_same = false; + break; + } + } + + REQUIRE(is_same); + } + + REQUIRE_THROWS_WITH(dot(p_R1_u, p_other_mesh_R1_u), "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(dot(p_R2_u, p_other_mesh_R2_u), "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(dot(p_R3_u, p_other_mesh_R3_u), "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(dot(p_R1_u, p_R3_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^3)"); + REQUIRE_THROWS_WITH(dot(p_Vector3_u, p_Vector2_w), "error: operands have different dimension"); + } + + SECTION("dot Vh*Rd -> Vh") + { + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R1_u, (TinyVector<1>{3}), dot); + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R2_u, (TinyVector<2>{-6, 2}), dot); + CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R3_u, (TinyVector<3>{-1, 5, 2}), dot); + REQUIRE_THROWS_WITH(dot(p_R1_u, (TinyVector<2>{-6, 2})), "error: incompatible operand types Vh(P0:R^1) and R^2"); + REQUIRE_THROWS_WITH(dot(p_R2_u, (TinyVector<3>{-1, 5, 2})), + "error: incompatible operand types Vh(P0:R^2) and R^3"); + REQUIRE_THROWS_WITH(dot(p_R3_u, (TinyVector<1>{-1})), "error: incompatible operand types Vh(P0:R^3) and R^1"); + } + + SECTION("dot Rd*Vh -> Vh") + { + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<1>{3}), p_R1_u, dot); + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<2>{-6, 2}), p_R2_u, dot); + CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<3>{-1, 5, 2}), p_R3_u, dot); + REQUIRE_THROWS_WITH(dot((TinyVector<2>{-6, 2}), p_R1_u), "error: incompatible operand types R^2 and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(dot((TinyVector<3>{-1, 5, 2}), p_R2_u), + "error: incompatible operand types R^3 and Vh(P0:R^2)"); + REQUIRE_THROWS_WITH(dot((TinyVector<1>{-1}), p_R3_u), "error: incompatible operand types R^1 and Vh(P0:R^3)"); + } + + SECTION("sum_of_R* Vh -> R*") + { + REQUIRE(sum_of<double>(p_u) == sum(p_u->cellValues())); + REQUIRE(sum_of<TinyVector<1>>(p_R1_u) == sum(p_R1_u->cellValues())); + REQUIRE(sum_of<TinyVector<2>>(p_R2_u) == sum(p_R2_u->cellValues())); + REQUIRE(sum_of<TinyVector<3>>(p_R3_u) == sum(p_R3_u->cellValues())); + REQUIRE(sum_of<TinyMatrix<1>>(p_R1x1_u) == sum(p_R1x1_u->cellValues())); + REQUIRE(sum_of<TinyMatrix<2>>(p_R2x2_u) == sum(p_R2x2_u->cellValues())); + REQUIRE(sum_of<TinyMatrix<3>>(p_R3x3_u) == sum(p_R3x3_u->cellValues())); + + REQUIRE_THROWS_WITH(sum_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)"); + REQUIRE_THROWS_WITH(sum_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(sum_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)"); + REQUIRE_THROWS_WITH(sum_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)"); + REQUIRE_THROWS_WITH(sum_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(sum_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(sum_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)"); + } + + SECTION("integral_of_R* Vh -> R*") + { + auto integrate_locally = [&](const auto& cell_values) { + const auto& Vj = MeshDataManager::instance().getMeshData(*mesh).Vj(); + using DataType = decltype(double{} * cell_values[CellId{0}]); + CellValue<DataType> local_integral{mesh->connectivity()}; + parallel_for( + local_integral.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { local_integral[cell_id] = Vj[cell_id] * cell_values[cell_id]; }); + return local_integral; + }; + + REQUIRE(integral_of<double>(p_u) == sum(integrate_locally(p_u->cellValues()))); + REQUIRE(integral_of<TinyVector<1>>(p_R1_u) == sum(integrate_locally(p_R1_u->cellValues()))); + REQUIRE(integral_of<TinyVector<2>>(p_R2_u) == sum(integrate_locally(p_R2_u->cellValues()))); + REQUIRE(integral_of<TinyVector<3>>(p_R3_u) == sum(integrate_locally(p_R3_u->cellValues()))); + REQUIRE(integral_of<TinyMatrix<1>>(p_R1x1_u) == sum(integrate_locally(p_R1x1_u->cellValues()))); + REQUIRE(integral_of<TinyMatrix<2>>(p_R2x2_u) == sum(integrate_locally(p_R2x2_u->cellValues()))); + REQUIRE(integral_of<TinyMatrix<3>>(p_R3x3_u) == sum(integrate_locally(p_R3x3_u->cellValues()))); + + REQUIRE_THROWS_WITH(integral_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)"); + REQUIRE_THROWS_WITH(integral_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(integral_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)"); + REQUIRE_THROWS_WITH(integral_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)"); + REQUIRE_THROWS_WITH(integral_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(integral_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(integral_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)"); + } + } +} diff --git a/tests/test_EmbeddedIDiscreteFunctionOperators.cpp b/tests/test_EmbeddedIDiscreteFunctionOperators.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5232e8fe8d05aca29d5d56440071c0718a206627 --- /dev/null +++ b/tests/test_EmbeddedIDiscreteFunctionOperators.cpp @@ -0,0 +1,2622 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <MeshDataBaseForTests.hpp> + +#include <language/utils/EmbeddedIDiscreteFunctionOperators.hpp> +#include <scheme/DiscreteFunctionP0.hpp> +#include <scheme/DiscreteFunctionP0Vector.hpp> + +// clazy:excludeall=non-pod-global-static + +#define CHECK_SCALAR_VH2_TO_VH(P_LHS, OPERATOR, P_RHS) \ + { \ + using DiscreteFunctionType = const std::decay_t<decltype(*P_LHS OPERATOR * P_RHS)>; \ + \ + std::shared_ptr p_fuv = P_LHS OPERATOR P_RHS; \ + \ + REQUIRE(p_fuv.use_count() > 0); \ + REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fuv)); \ + \ + const auto& fuv = dynamic_cast<const DiscreteFunctionType&>(*p_fuv); \ + \ + auto lhs_values = P_LHS->cellValues(); \ + auto rhs_values = P_RHS->cellValues(); \ + bool is_same = true; \ + for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) { \ + if (fuv[cell_id] != (lhs_values[cell_id] OPERATOR rhs_values[cell_id])) { \ + is_same = false; \ + break; \ + } \ + } \ + \ + REQUIRE(is_same); \ + } + +#define CHECK_SCALAR_VHxX_TO_VH(P_LHS, OPERATOR, RHS) \ + { \ + using DiscreteFunctionType = const std::decay_t<decltype(*P_LHS OPERATOR RHS)>; \ + \ + std::shared_ptr p_fuv = P_LHS OPERATOR RHS; \ + \ + REQUIRE(p_fuv.use_count() > 0); \ + REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fuv)); \ + \ + const auto& fuv = dynamic_cast<const DiscreteFunctionType&>(*p_fuv); \ + \ + auto lhs_values = P_LHS->cellValues(); \ + bool is_same = true; \ + for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) { \ + if (fuv[cell_id] != (lhs_values[cell_id] OPERATOR RHS)) { \ + is_same = false; \ + break; \ + } \ + } \ + \ + REQUIRE(is_same); \ + } + +#define CHECK_SCALAR_XxVH_TO_VH(LHS, OPERATOR, P_RHS) \ + { \ + using DiscreteFunctionType = const std::decay_t<decltype(LHS OPERATOR * P_RHS)>; \ + \ + std::shared_ptr p_fuv = LHS OPERATOR P_RHS; \ + \ + REQUIRE(p_fuv.use_count() > 0); \ + REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fuv)); \ + \ + const auto& fuv = dynamic_cast<const DiscreteFunctionType&>(*p_fuv); \ + \ + auto rhs_values = P_RHS->cellValues(); \ + bool is_same = true; \ + for (CellId cell_id = 0; cell_id < rhs_values.numberOfItems(); ++cell_id) { \ + if (fuv[cell_id] != (LHS OPERATOR rhs_values[cell_id])) { \ + is_same = false; \ + break; \ + } \ + } \ + \ + REQUIRE(is_same); \ + } + +#define CHECK_VECTOR_VH2_TO_VH(P_LHS, OPERATOR, P_RHS) \ + { \ + using DiscreteFunctionType = const std::decay_t<decltype(*P_LHS OPERATOR * P_RHS)>; \ + \ + std::shared_ptr p_fuv = P_LHS OPERATOR P_RHS; \ + \ + REQUIRE(p_fuv.use_count() > 0); \ + REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fuv)); \ + \ + const auto& fuv = dynamic_cast<const DiscreteFunctionType&>(*p_fuv); \ + \ + auto lhs_arrays = P_LHS->cellArrays(); \ + auto rhs_arrays = P_RHS->cellArrays(); \ + bool is_same = true; \ + REQUIRE(rhs_arrays.sizeOfArrays() > 0); \ + REQUIRE(lhs_arrays.sizeOfArrays() == rhs_arrays.sizeOfArrays()); \ + REQUIRE(lhs_arrays.sizeOfArrays() == fuv.size()); \ + for (CellId cell_id = 0; cell_id < lhs_arrays.numberOfItems(); ++cell_id) { \ + for (size_t i = 0; i < fuv.size(); ++i) { \ + if (fuv[cell_id][i] != (lhs_arrays[cell_id][i] OPERATOR rhs_arrays[cell_id][i])) { \ + is_same = false; \ + break; \ + } \ + } \ + } \ + \ + REQUIRE(is_same); \ + } + +#define CHECK_VECTOR_XxVH_TO_VH(LHS, OPERATOR, P_RHS) \ + { \ + using DiscreteFunctionType = const std::decay_t<decltype(LHS OPERATOR * P_RHS)>; \ + \ + std::shared_ptr p_fuv = LHS OPERATOR P_RHS; \ + \ + REQUIRE(p_fuv.use_count() > 0); \ + REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fuv)); \ + \ + const auto& fuv = dynamic_cast<const DiscreteFunctionType&>(*p_fuv); \ + \ + auto rhs_arrays = P_RHS->cellArrays(); \ + bool is_same = true; \ + REQUIRE(rhs_arrays.sizeOfArrays() > 0); \ + REQUIRE(fuv.size() == rhs_arrays.sizeOfArrays()); \ + for (CellId cell_id = 0; cell_id < rhs_arrays.numberOfItems(); ++cell_id) { \ + for (size_t i = 0; i < fuv.size(); ++i) { \ + if (fuv[cell_id][i] != (LHS OPERATOR rhs_arrays[cell_id][i])) { \ + is_same = false; \ + break; \ + } \ + } \ + } \ + \ + REQUIRE(is_same); \ + } + +#define CHECK_SCALAR_VH_TO_VH(OPERATOR, P_RHS) \ + { \ + using DiscreteFunctionType = const std::decay_t<decltype(OPERATOR * P_RHS)>; \ + \ + std::shared_ptr p_fu = OPERATOR P_RHS; \ + \ + REQUIRE(p_fu.use_count() > 0); \ + REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fu)); \ + \ + const auto& fu = dynamic_cast<const DiscreteFunctionType&>(*p_fu); \ + \ + auto rhs_values = P_RHS->cellValues(); \ + bool is_same = true; \ + for (CellId cell_id = 0; cell_id < rhs_values.numberOfItems(); ++cell_id) { \ + if (fu[cell_id] != (OPERATOR rhs_values[cell_id])) { \ + is_same = false; \ + break; \ + } \ + } \ + \ + REQUIRE(is_same); \ + } + +#define CHECK_VECTOR_VH_TO_VH(OPERATOR, P_RHS) \ + { \ + using DiscreteFunctionType = const std::decay_t<decltype(OPERATOR * P_RHS)>; \ + \ + std::shared_ptr p_fu = OPERATOR P_RHS; \ + \ + REQUIRE(p_fu.use_count() > 0); \ + REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fu)); \ + \ + const auto& fu = dynamic_cast<const DiscreteFunctionType&>(*p_fu); \ + \ + auto rhs_arrays = P_RHS->cellArrays(); \ + REQUIRE(rhs_arrays.sizeOfArrays() > 0); \ + REQUIRE(rhs_arrays.sizeOfArrays() == fu.size()); \ + bool is_same = true; \ + for (CellId cell_id = 0; cell_id < rhs_arrays.numberOfItems(); ++cell_id) { \ + for (size_t i = 0; i < rhs_arrays.sizeOfArrays(); ++i) { \ + if (fu[cell_id][i] != (OPERATOR rhs_arrays[cell_id][i])) { \ + is_same = false; \ + break; \ + } \ + } \ + } \ + \ + REQUIRE(is_same); \ + } + +TEST_CASE("EmbeddedIDiscreteFunctionOperators", "[scheme]") +{ + SECTION("binary operators") + { + SECTION("1D") + { + constexpr size_t Dimension = 1; + + using Rd = TinyVector<Dimension>; + + std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh1D(); + + std::shared_ptr other_mesh = + std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr()); + + CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + + CellValue<double> u_R_values = [=] { + CellValue<double> build_values{mesh->connectivity()}; + parallel_for( + build_values.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); }); + return build_values; + }(); + + CellValue<double> v_R_values = [=] { + CellValue<double> build_values{mesh->connectivity()}; + parallel_for( + build_values.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.6 + std::sin(l2Norm(xj[cell_id])); }); + return build_values; + }(); + + std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values); + std::shared_ptr p_other_mesh_R_u = + std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, u_R_values); + std::shared_ptr p_R_v = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, v_R_values); + + std::shared_ptr p_R1_u = [=] { + CellValue<TinyVector<1>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj); + }(); + + std::shared_ptr p_R1_v = [=] { + CellValue<TinyVector<1>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj); + }(); + + std::shared_ptr p_other_mesh_R1_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues()); + + constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> { + if constexpr (Dimension == 1) { + return {x[0], 1 + x[0] * x[0]}; + } else if constexpr (Dimension == 2) { + return {x[0], x[1]}; + } else if constexpr (Dimension == 3) { + return {x[0], x[1] + x[2]}; + } + }; + + std::shared_ptr p_R2_u = [=] { + CellValue<TinyVector<2>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + uj[cell_id] = {2 * x[0] + 1, 1 - x[1]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj); + }(); + + std::shared_ptr p_R2_v = [=] { + CellValue<TinyVector<2>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + vj[cell_id] = {x[0] * x[1] + 1, 2 * x[1]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj); + }(); + + std::shared_ptr p_other_mesh_R2_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues()); + + constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> { + if constexpr (Dimension == 1) { + return {x[0], 1 + x[0] * x[0], 2 - x[0]}; + } else if constexpr (Dimension == 2) { + return {x[0], x[1], x[0] + x[1]}; + } else if constexpr (Dimension == 3) { + return {x[0], x[1], x[2]}; + } + }; + + std::shared_ptr p_R3_u = [=] { + CellValue<TinyVector<3>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + uj[cell_id] = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj); + }(); + + std::shared_ptr p_R3_v = [=] { + CellValue<TinyVector<3>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + vj[cell_id] = {x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj); + }(); + + std::shared_ptr p_other_mesh_R3_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues()); + + std::shared_ptr p_R1x1_u = [=] { + CellValue<TinyMatrix<1>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj); + }(); + + std::shared_ptr p_other_mesh_R1x1_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(other_mesh, p_R1x1_u->cellValues()); + + std::shared_ptr p_R1x1_v = [=] { + CellValue<TinyMatrix<1>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { vj[cell_id] = {0.3 - xj[cell_id][0]}; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, vj); + }(); + + std::shared_ptr p_R2x2_u = [=] { + CellValue<TinyMatrix<2>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + + uj[cell_id] = {2 * x[0] + 1, 1 - x[1], // + 2 * x[1], -x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj); + }(); + + std::shared_ptr p_other_mesh_R2x2_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(other_mesh, p_R2x2_u->cellValues()); + + std::shared_ptr p_R2x2_v = [=] { + CellValue<TinyMatrix<2>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + + vj[cell_id] = {x[0] + 0.3, 1 - x[1] - x[0], // + 2 * x[1] + x[0], x[1] - x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, vj); + }(); + + std::shared_ptr p_R3x3_u = [=] { + CellValue<TinyMatrix<3>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + + uj[cell_id] = {2 * x[0] + 1, 1 - x[1], 3, // + 2 * x[1], -x[0], x[0] - x[1], // + 3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj); + }(); + + std::shared_ptr p_other_mesh_R3x3_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(other_mesh, p_R3x3_u->cellValues()); + + std::shared_ptr p_R3x3_v = [=] { + CellValue<TinyMatrix<3>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + + vj[cell_id] = {0.2 * x[0] + 1, 2 + x[1], 3 - x[2], // + 2.3 * x[2], x[1] - x[0], x[2] - x[1], // + 2 * x[2] + x[0], x[1] + 0.2 * x[2], x[2] - 2 * x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, vj); + }(); + + std::shared_ptr p_Vector3_u = [=] { + CellArray<double> uj_vector{mesh->connectivity(), 3}; + parallel_for( + uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + uj_vector[cell_id][0] = 2 * x[0] + 1; + uj_vector[cell_id][1] = 1 - x[1] * x[2]; + uj_vector[cell_id][2] = x[0] + x[2]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector); + }(); + + std::shared_ptr p_other_mesh_Vector3_u = + std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(other_mesh, p_Vector3_u->cellArrays()); + + std::shared_ptr p_Vector3_v = [=] { + CellArray<double> vj_vector{mesh->connectivity(), 3}; + parallel_for( + vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + vj_vector[cell_id][0] = x[0] * x[1] + 1; + vj_vector[cell_id][1] = 2 * x[1]; + vj_vector[cell_id][2] = x[2] * x[0]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector); + }(); + + std::shared_ptr p_Vector2_w = [=] { + CellArray<double> wj_vector{mesh->connectivity(), 2}; + parallel_for( + wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + wj_vector[cell_id][0] = x[0] + x[1] * 2; + wj_vector[cell_id][1] = x[0] * x[1]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector); + }(); + + SECTION("sum") + { + SECTION("Vh + Vh -> Vh") + { + CHECK_SCALAR_VH2_TO_VH(p_R_u, +, p_R_v); + + CHECK_SCALAR_VH2_TO_VH(p_R1_u, +, p_R1_v); + CHECK_SCALAR_VH2_TO_VH(p_R2_u, +, p_R2_v); + CHECK_SCALAR_VH2_TO_VH(p_R3_u, +, p_R3_v); + + CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, +, p_R1x1_v); + CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, +, p_R2x2_v); + CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, +, p_R3x3_v); + + CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, +, p_Vector3_v); + + REQUIRE_THROWS_WITH(p_R_u + p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R2_u + p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R3_u + p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(p_Vector3_u + p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)"); + REQUIRE_THROWS_WITH(p_Vector3_u + p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes"); + + REQUIRE_THROWS_WITH(p_R_u + p_other_mesh_R_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R1_u + p_other_mesh_R1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R2_u + p_other_mesh_R2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R3_u + p_other_mesh_R3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R1x1_u + p_other_mesh_R1x1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R2x2_u + p_other_mesh_R2x2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R3x3_u + p_other_mesh_R3x3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_Vector3_u + p_other_mesh_Vector3_u, "error: operands are defined on different meshes"); + } + + SECTION("Vh + X -> Vh") + { + CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, bool{true}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, uint64_t{1}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, int64_t{2}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, double{1.3}); + + CHECK_SCALAR_VHxX_TO_VH(p_R1_u, +, (TinyVector<1>{1.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R2_u, +, (TinyVector<2>{1.2, 2.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R3_u, +, (TinyVector<3>{3.2, 7.1, 5.2})); + + CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, +, (TinyMatrix<1>{1.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, +, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1})); + CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, +, + (TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8})); + + REQUIRE_THROWS_WITH(p_R_u + (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1"); + REQUIRE_THROWS_WITH(p_R_u + (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2"); + REQUIRE_THROWS_WITH(p_R_u + (TinyVector<3>{2, 3, 2}), "error: incompatible operand types Vh(P0:R) and R^3"); + REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1"); + REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<2>{2, 3, 1, 4}), + "error: incompatible operand types Vh(P0:R) and R^2x2"); + REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}), + "error: incompatible operand types Vh(P0:R) and R^3x3"); + + REQUIRE_THROWS_WITH(p_Vector3_u + (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R"); + REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<1>{1}), + "error: incompatible operand types Vh(P0Vector:R) and R^1"); + REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<2>{1, 2}), + "error: incompatible operand types Vh(P0Vector:R) and R^2"); + } + + SECTION("X + Vh -> Vh") + { + CHECK_SCALAR_XxVH_TO_VH(bool{true}, +, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, +, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, +, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, +, p_R_u); + + CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), +, p_R1_u); + CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), +, p_R2_u); + CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), +, p_R3_u); + + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), +, p_R1x1_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), +, p_R2x2_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8}), + +, p_R3x3_u); + + REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) + p_R_u, "error: incompatible operand types R^3 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) + p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) + p_R_u, + "error: incompatible operand types R^2x2 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) + p_R_u, + "error: incompatible operand types R^3x3 and Vh(P0:R)"); + + REQUIRE_THROWS_WITH((double{1}) + p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_Vector3_u, + "error: incompatible operand types R^1 and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_Vector3_u, + "error: incompatible operand types R^2 and Vh(P0Vector:R)"); + } + } + + SECTION("difference") + { + SECTION("Vh - Vh -> Vh") + { + CHECK_SCALAR_VH2_TO_VH(p_R_u, -, p_R_v); + + CHECK_SCALAR_VH2_TO_VH(p_R1_u, -, p_R1_v); + CHECK_SCALAR_VH2_TO_VH(p_R2_u, -, p_R2_v); + CHECK_SCALAR_VH2_TO_VH(p_R3_u, -, p_R3_v); + + CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, -, p_R1x1_v); + CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, -, p_R2x2_v); + CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, -, p_R3x3_v); + + CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, -, p_Vector3_v); + + REQUIRE_THROWS_WITH(p_R_u - p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R2_u - p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R3_u - p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(p_R_u - p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(p_Vector3_u - p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)"); + REQUIRE_THROWS_WITH(p_Vector3_u - p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes"); + + REQUIRE_THROWS_WITH(p_R_u - p_other_mesh_R_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R1_u - p_other_mesh_R1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R2_u - p_other_mesh_R2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R3_u - p_other_mesh_R3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R1x1_u - p_other_mesh_R1x1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R2x2_u - p_other_mesh_R2x2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R3x3_u - p_other_mesh_R3x3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_Vector3_u - p_other_mesh_Vector3_u, "error: operands are defined on different meshes"); + } + + SECTION("Vh - X -> Vh") + { + CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, bool{true}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, uint64_t{1}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, int64_t{2}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, double{1.3}); + + CHECK_SCALAR_VHxX_TO_VH(p_R1_u, -, (TinyVector<1>{1.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R2_u, -, (TinyVector<2>{1.2, 2.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R3_u, -, (TinyVector<3>{3.2, 7.1, 5.2})); + + CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, -, (TinyMatrix<1>{1.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, -, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1})); + CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, -, + (TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8})); + + REQUIRE_THROWS_WITH(p_R_u - (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1"); + REQUIRE_THROWS_WITH(p_R_u - (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2"); + REQUIRE_THROWS_WITH(p_R_u - (TinyVector<3>{2, 3, 2}), "error: incompatible operand types Vh(P0:R) and R^3"); + REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1"); + REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<2>{2, 3, 1, 4}), + "error: incompatible operand types Vh(P0:R) and R^2x2"); + REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}), + "error: incompatible operand types Vh(P0:R) and R^3x3"); + + REQUIRE_THROWS_WITH(p_Vector3_u - (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R"); + REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<1>{1}), + "error: incompatible operand types Vh(P0Vector:R) and R^1"); + REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<2>{1, 2}), + "error: incompatible operand types Vh(P0Vector:R) and R^2"); + } + + SECTION("X - Vh -> Vh") + { + CHECK_SCALAR_XxVH_TO_VH(bool{true}, -, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, -, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, -, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, -, p_R_u); + + CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), -, p_R1_u); + CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), -, p_R2_u); + CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), -, p_R3_u); + + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), -, p_R1x1_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), -, p_R2x2_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8}), + -, p_R3x3_u); + + REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) - p_R_u, "error: incompatible operand types R^3 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) - p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) - p_R_u, + "error: incompatible operand types R^2x2 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) - p_R_u, + "error: incompatible operand types R^3x3 and Vh(P0:R)"); + + REQUIRE_THROWS_WITH((double{1}) - p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_Vector3_u, + "error: incompatible operand types R^1 and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_Vector3_u, + "error: incompatible operand types R^2 and Vh(P0Vector:R)"); + } + } + + SECTION("product") + { + SECTION("Vh * Vh -> Vh") + { + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R_v); + + CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1x1_v); + CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2x2_v); + CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3x3_v); + + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1_v); + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2_v); + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3_v); + + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1x1_v); + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2x2_v); + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3x3_v); + + CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1_v); + CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2_v); + CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3_v); + + { + std::shared_ptr p_fuv = p_R_u * p_Vector3_v; + + REQUIRE(p_fuv.use_count() > 0); + REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv)); + + const auto& fuv = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv); + + auto lhs_values = p_R_u->cellValues(); + auto rhs_arrays = p_Vector3_v->cellArrays(); + bool is_same = true; + for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) { + for (size_t i = 0; i < fuv.size(); ++i) { + if (fuv[cell_id][i] != (lhs_values[cell_id] * rhs_arrays[cell_id][i])) { + is_same = false; + break; + } + } + } + + REQUIRE(is_same); + } + + REQUIRE_THROWS_WITH(p_R1_u * p_R1_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R2_u * p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R3_u * p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(p_R1_u * p_R2x2_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^2x2)"); + + REQUIRE_THROWS_WITH(p_R1x1_u * p_R2x2_v, "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(p_R2x2_u * p_R3x3_v, "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3x3)"); + REQUIRE_THROWS_WITH(p_R3x3_u * p_R1x1_v, "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1x1)"); + + REQUIRE_THROWS_WITH(p_R1x1_u * p_R2_v, "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2)"); + REQUIRE_THROWS_WITH(p_R2x2_u * p_R3_v, "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3)"); + REQUIRE_THROWS_WITH(p_R3x3_u * p_R1_v, "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1)"); + + REQUIRE_THROWS_WITH(p_R1_u * p_Vector3_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH(p_R2_u * p_Vector3_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH(p_R3_u * p_Vector3_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH(p_R1x1_u * p_Vector3_v, + "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH(p_R2x2_u * p_Vector3_v, + "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH(p_R3x3_u * p_Vector3_v, + "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH(p_Vector3_u * p_Vector3_v, + "error: incompatible operand types Vh(P0Vector:R) and Vh(P0Vector:R)"); + + REQUIRE_THROWS_WITH(p_Vector3_v * p_R_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)"); + REQUIRE_THROWS_WITH(p_Vector3_v * p_R1_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_Vector3_v * p_R2_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2)"); + REQUIRE_THROWS_WITH(p_Vector3_v * p_R3_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3)"); + REQUIRE_THROWS_WITH(p_Vector3_v * p_R1x1_u, + "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(p_Vector3_v * p_R2x2_u, + "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(p_Vector3_v * p_R3x3_u, + "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3x3)"); + + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1x1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2x2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3x3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R1x1_u * p_other_mesh_R1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R2x2_u * p_other_mesh_R2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R3x3_u * p_other_mesh_R3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_Vector3_u, "error: operands are defined on different meshes"); + } + + SECTION("Vh * X -> Vh") + { + CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, bool{true}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, uint64_t{1}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, int64_t{2}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, double{1.3}); + + CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, *, (TinyMatrix<1>{1.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, *, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1})); + CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, *, + (TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8})); + + REQUIRE_THROWS_WITH(p_R1_u * (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R^1) and R^1"); + REQUIRE_THROWS_WITH(p_R2_u * (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R^2) and R^2"); + REQUIRE_THROWS_WITH(p_R3_u * (TinyVector<3>{2, 3, 2}), + "error: incompatible operand types Vh(P0:R^3) and R^3"); + REQUIRE_THROWS_WITH(p_R1_u * (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R^1) and R^1x1"); + REQUIRE_THROWS_WITH(p_R2_u * (TinyMatrix<2>{2, 3, 1, 4}), + "error: incompatible operand types Vh(P0:R^2) and R^2x2"); + REQUIRE_THROWS_WITH(p_R3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}), + "error: incompatible operand types Vh(P0:R^3) and R^3x3"); + REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<1>{2}), + "error: incompatible operand types Vh(P0:R^2x2) and R^1x1"); + REQUIRE_THROWS_WITH(p_R1x1_u * (TinyMatrix<2>{2, 3, 1, 4}), + "error: incompatible operand types Vh(P0:R^1x1) and R^2x2"); + REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}), + "error: incompatible operand types Vh(P0:R^2x2) and R^3x3"); + + REQUIRE_THROWS_WITH(p_Vector3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}), + "error: incompatible operand types Vh(P0Vector:R) and R^3x3"); + REQUIRE_THROWS_WITH(p_Vector3_u * (double{2}), "error: incompatible operand types Vh(P0Vector:R) and R"); + } + + SECTION("X * Vh -> Vh") + { + CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R_u); + + CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R1x1_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R1x1_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R1x1_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R1x1_u); + + CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R2x2_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R2x2_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R2x2_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R2x2_u); + + CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R3x3_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R3x3_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R3x3_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R3x3_u); + + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8}), + *, p_R3_u); + + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1x1_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2x2_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8}), + *, p_R3x3_u); + + CHECK_VECTOR_XxVH_TO_VH(bool{true}, *, p_Vector3_u); + CHECK_VECTOR_XxVH_TO_VH(uint64_t{1}, *, p_Vector3_u); + CHECK_VECTOR_XxVH_TO_VH(int64_t{2}, *, p_Vector3_u); + CHECK_VECTOR_XxVH_TO_VH(double{1.3}, *, p_Vector3_u); + + REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R_u, + "error: incompatible operand types R^2x2 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R_u, + "error: incompatible operand types R^3x3 and Vh(P0:R)"); + + REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2_u, "error: incompatible operand types R^1x1 and Vh(P0:R^2)"); + REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3_u, + "error: incompatible operand types R^2x2 and Vh(P0:R^3)"); + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2_u, + "error: incompatible operand types R^3x3 and Vh(P0:R^2)"); + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R1_u, + "error: incompatible operand types R^3x3 and Vh(P0:R^1)"); + + REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2x2_u, + "error: incompatible operand types R^1x1 and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3x3_u, + "error: incompatible operand types R^2x2 and Vh(P0:R^3x3)"); + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2x2_u, + "error: incompatible operand types R^3x3 and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R1x1_u, + "error: incompatible operand types R^2x2 and Vh(P0:R^1x1)"); + + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_Vector3_u, + "error: incompatible operand types R^3x3 and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_Vector3_u, + "error: incompatible operand types R^1x1 and Vh(P0Vector:R)"); + } + } + + SECTION("ratio") + { + SECTION("Vh / Vh -> Vh") + { + CHECK_SCALAR_VH2_TO_VH(p_R_u, /, p_R_v); + + REQUIRE_THROWS_WITH(p_R_u / p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R2_u / p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R3_u / p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(p_R_u / p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)"); + + REQUIRE_THROWS_WITH(p_R_u / p_other_mesh_R_u, "error: operands are defined on different meshes"); + } + + SECTION("X / Vh -> Vh") + { + CHECK_SCALAR_XxVH_TO_VH(bool{true}, /, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, /, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, /, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, /, p_R_u); + } + } + } + + SECTION("2D") + { + constexpr size_t Dimension = 2; + + using Rd = TinyVector<Dimension>; + + std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh2D(); + + std::shared_ptr other_mesh = + std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr()); + + CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + + CellValue<double> u_R_values = [=] { + CellValue<double> build_values{mesh->connectivity()}; + parallel_for( + build_values.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); }); + return build_values; + }(); + + CellValue<double> v_R_values = [=] { + CellValue<double> build_values{mesh->connectivity()}; + parallel_for( + build_values.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.6 + std::sin(l2Norm(xj[cell_id])); }); + return build_values; + }(); + + std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values); + std::shared_ptr p_other_mesh_R_u = + std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, u_R_values); + std::shared_ptr p_R_v = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, v_R_values); + + std::shared_ptr p_R1_u = [=] { + CellValue<TinyVector<1>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj); + }(); + + std::shared_ptr p_R1_v = [=] { + CellValue<TinyVector<1>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj); + }(); + + std::shared_ptr p_other_mesh_R1_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues()); + + constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> { + if constexpr (Dimension == 1) { + return {x[0], 1 + x[0] * x[0]}; + } else if constexpr (Dimension == 2) { + return {x[0], x[1]}; + } else if constexpr (Dimension == 3) { + return {x[0], x[1] + x[2]}; + } + }; + + std::shared_ptr p_R2_u = [=] { + CellValue<TinyVector<2>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + uj[cell_id] = {2 * x[0] + 1, 1 - x[1]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj); + }(); + + std::shared_ptr p_R2_v = [=] { + CellValue<TinyVector<2>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + vj[cell_id] = {x[0] * x[1] + 1, 2 * x[1]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj); + }(); + + std::shared_ptr p_other_mesh_R2_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues()); + + constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> { + if constexpr (Dimension == 1) { + return {x[0], 1 + x[0] * x[0], 2 - x[0]}; + } else if constexpr (Dimension == 2) { + return {x[0], x[1], x[0] + x[1]}; + } else if constexpr (Dimension == 3) { + return {x[0], x[1], x[2]}; + } + }; + + std::shared_ptr p_R3_u = [=] { + CellValue<TinyVector<3>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + uj[cell_id] = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj); + }(); + + std::shared_ptr p_R3_v = [=] { + CellValue<TinyVector<3>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + vj[cell_id] = {x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj); + }(); + + std::shared_ptr p_other_mesh_R3_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues()); + + std::shared_ptr p_R1x1_u = [=] { + CellValue<TinyMatrix<1>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj); + }(); + + std::shared_ptr p_other_mesh_R1x1_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(other_mesh, p_R1x1_u->cellValues()); + + std::shared_ptr p_R1x1_v = [=] { + CellValue<TinyMatrix<1>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { vj[cell_id] = {0.3 - xj[cell_id][0]}; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, vj); + }(); + + std::shared_ptr p_R2x2_u = [=] { + CellValue<TinyMatrix<2>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + + uj[cell_id] = {2 * x[0] + 1, 1 - x[1], // + 2 * x[1], -x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj); + }(); + + std::shared_ptr p_other_mesh_R2x2_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(other_mesh, p_R2x2_u->cellValues()); + + std::shared_ptr p_R2x2_v = [=] { + CellValue<TinyMatrix<2>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + + vj[cell_id] = {x[0] + 0.3, 1 - x[1] - x[0], // + 2 * x[1] + x[0], x[1] - x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, vj); + }(); + + std::shared_ptr p_R3x3_u = [=] { + CellValue<TinyMatrix<3>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + + uj[cell_id] = {2 * x[0] + 1, 1 - x[1], 3, // + 2 * x[1], -x[0], x[0] - x[1], // + 3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj); + }(); + + std::shared_ptr p_other_mesh_R3x3_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(other_mesh, p_R3x3_u->cellValues()); + + std::shared_ptr p_R3x3_v = [=] { + CellValue<TinyMatrix<3>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + + vj[cell_id] = {0.2 * x[0] + 1, 2 + x[1], 3 - x[2], // + 2.3 * x[2], x[1] - x[0], x[2] - x[1], // + 2 * x[2] + x[0], x[1] + 0.2 * x[2], x[2] - 2 * x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, vj); + }(); + + std::shared_ptr p_Vector3_u = [=] { + CellArray<double> uj_vector{mesh->connectivity(), 3}; + parallel_for( + uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + uj_vector[cell_id][0] = 2 * x[0] + 1; + uj_vector[cell_id][1] = 1 - x[1] * x[2]; + uj_vector[cell_id][2] = x[0] + x[2]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector); + }(); + + std::shared_ptr p_other_mesh_Vector3_u = + std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(other_mesh, p_Vector3_u->cellArrays()); + + std::shared_ptr p_Vector3_v = [=] { + CellArray<double> vj_vector{mesh->connectivity(), 3}; + parallel_for( + vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + vj_vector[cell_id][0] = x[0] * x[1] + 1; + vj_vector[cell_id][1] = 2 * x[1]; + vj_vector[cell_id][2] = x[2] * x[0]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector); + }(); + + std::shared_ptr p_Vector2_w = [=] { + CellArray<double> wj_vector{mesh->connectivity(), 2}; + parallel_for( + wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + wj_vector[cell_id][0] = x[0] + x[1] * 2; + wj_vector[cell_id][1] = x[0] * x[1]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector); + }(); + + SECTION("sum") + { + SECTION("Vh + Vh -> Vh") + { + CHECK_SCALAR_VH2_TO_VH(p_R_u, +, p_R_v); + + CHECK_SCALAR_VH2_TO_VH(p_R1_u, +, p_R1_v); + CHECK_SCALAR_VH2_TO_VH(p_R2_u, +, p_R2_v); + CHECK_SCALAR_VH2_TO_VH(p_R3_u, +, p_R3_v); + + CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, +, p_R1x1_v); + CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, +, p_R2x2_v); + CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, +, p_R3x3_v); + + CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, +, p_Vector3_v); + + REQUIRE_THROWS_WITH(p_R_u + p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R2_u + p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R3_u + p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(p_Vector3_u + p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)"); + REQUIRE_THROWS_WITH(p_Vector3_u + p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes"); + + REQUIRE_THROWS_WITH(p_R_u + p_other_mesh_R_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R1_u + p_other_mesh_R1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R2_u + p_other_mesh_R2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R3_u + p_other_mesh_R3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R1x1_u + p_other_mesh_R1x1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R2x2_u + p_other_mesh_R2x2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R3x3_u + p_other_mesh_R3x3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_Vector3_u + p_other_mesh_Vector3_u, "error: operands are defined on different meshes"); + } + + SECTION("Vh + X -> Vh") + { + CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, bool{true}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, uint64_t{1}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, int64_t{2}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, double{1.3}); + + CHECK_SCALAR_VHxX_TO_VH(p_R1_u, +, (TinyVector<1>{1.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R2_u, +, (TinyVector<2>{1.2, 2.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R3_u, +, (TinyVector<3>{3.2, 7.1, 5.2})); + + CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, +, (TinyMatrix<1>{1.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, +, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1})); + CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, +, + (TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8})); + + REQUIRE_THROWS_WITH(p_R_u + (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1"); + REQUIRE_THROWS_WITH(p_R_u + (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2"); + REQUIRE_THROWS_WITH(p_R_u + (TinyVector<3>{2, 3, 2}), "error: incompatible operand types Vh(P0:R) and R^3"); + REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1"); + REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<2>{2, 3, 1, 4}), + "error: incompatible operand types Vh(P0:R) and R^2x2"); + REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}), + "error: incompatible operand types Vh(P0:R) and R^3x3"); + + REQUIRE_THROWS_WITH(p_Vector3_u + (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R"); + REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<1>{1}), + "error: incompatible operand types Vh(P0Vector:R) and R^1"); + REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<2>{1, 2}), + "error: incompatible operand types Vh(P0Vector:R) and R^2"); + } + + SECTION("X + Vh -> Vh") + { + CHECK_SCALAR_XxVH_TO_VH(bool{true}, +, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, +, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, +, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, +, p_R_u); + + CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), +, p_R1_u); + CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), +, p_R2_u); + CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), +, p_R3_u); + + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), +, p_R1x1_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), +, p_R2x2_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8}), + +, p_R3x3_u); + + REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) + p_R_u, "error: incompatible operand types R^3 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) + p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) + p_R_u, + "error: incompatible operand types R^2x2 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) + p_R_u, + "error: incompatible operand types R^3x3 and Vh(P0:R)"); + + REQUIRE_THROWS_WITH((double{1}) + p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_Vector3_u, + "error: incompatible operand types R^1 and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_Vector3_u, + "error: incompatible operand types R^2 and Vh(P0Vector:R)"); + } + } + + SECTION("difference") + { + SECTION("Vh - Vh -> Vh") + { + CHECK_SCALAR_VH2_TO_VH(p_R_u, -, p_R_v); + + CHECK_SCALAR_VH2_TO_VH(p_R1_u, -, p_R1_v); + CHECK_SCALAR_VH2_TO_VH(p_R2_u, -, p_R2_v); + CHECK_SCALAR_VH2_TO_VH(p_R3_u, -, p_R3_v); + + CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, -, p_R1x1_v); + CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, -, p_R2x2_v); + CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, -, p_R3x3_v); + + CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, -, p_Vector3_v); + + REQUIRE_THROWS_WITH(p_R_u - p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R2_u - p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R3_u - p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(p_R_u - p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(p_Vector3_u - p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)"); + REQUIRE_THROWS_WITH(p_Vector3_u - p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes"); + + REQUIRE_THROWS_WITH(p_R_u - p_other_mesh_R_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R1_u - p_other_mesh_R1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R2_u - p_other_mesh_R2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R3_u - p_other_mesh_R3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R1x1_u - p_other_mesh_R1x1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R2x2_u - p_other_mesh_R2x2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R3x3_u - p_other_mesh_R3x3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_Vector3_u - p_other_mesh_Vector3_u, "error: operands are defined on different meshes"); + } + + SECTION("Vh - X -> Vh") + { + CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, bool{true}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, uint64_t{1}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, int64_t{2}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, double{1.3}); + + CHECK_SCALAR_VHxX_TO_VH(p_R1_u, -, (TinyVector<1>{1.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R2_u, -, (TinyVector<2>{1.2, 2.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R3_u, -, (TinyVector<3>{3.2, 7.1, 5.2})); + + CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, -, (TinyMatrix<1>{1.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, -, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1})); + CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, -, + (TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8})); + + REQUIRE_THROWS_WITH(p_R_u - (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1"); + REQUIRE_THROWS_WITH(p_R_u - (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2"); + REQUIRE_THROWS_WITH(p_R_u - (TinyVector<3>{2, 3, 2}), "error: incompatible operand types Vh(P0:R) and R^3"); + REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1"); + REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<2>{2, 3, 1, 4}), + "error: incompatible operand types Vh(P0:R) and R^2x2"); + REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}), + "error: incompatible operand types Vh(P0:R) and R^3x3"); + + REQUIRE_THROWS_WITH(p_Vector3_u - (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R"); + REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<1>{1}), + "error: incompatible operand types Vh(P0Vector:R) and R^1"); + REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<2>{1, 2}), + "error: incompatible operand types Vh(P0Vector:R) and R^2"); + } + + SECTION("X - Vh -> Vh") + { + CHECK_SCALAR_XxVH_TO_VH(bool{true}, -, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, -, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, -, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, -, p_R_u); + + CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), -, p_R1_u); + CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), -, p_R2_u); + CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), -, p_R3_u); + + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), -, p_R1x1_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), -, p_R2x2_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8}), + -, p_R3x3_u); + + REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) - p_R_u, "error: incompatible operand types R^3 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) - p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) - p_R_u, + "error: incompatible operand types R^2x2 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) - p_R_u, + "error: incompatible operand types R^3x3 and Vh(P0:R)"); + + REQUIRE_THROWS_WITH((double{1}) - p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_Vector3_u, + "error: incompatible operand types R^1 and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_Vector3_u, + "error: incompatible operand types R^2 and Vh(P0Vector:R)"); + } + } + + SECTION("product") + { + SECTION("Vh * Vh -> Vh") + { + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R_v); + + CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1x1_v); + CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2x2_v); + CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3x3_v); + + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1_v); + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2_v); + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3_v); + + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1x1_v); + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2x2_v); + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3x3_v); + + CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1_v); + CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2_v); + CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3_v); + + { + std::shared_ptr p_fuv = p_R_u * p_Vector3_v; + + REQUIRE(p_fuv.use_count() > 0); + REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv)); + + const auto& fuv = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv); + + auto lhs_values = p_R_u->cellValues(); + auto rhs_arrays = p_Vector3_v->cellArrays(); + bool is_same = true; + for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) { + for (size_t i = 0; i < fuv.size(); ++i) { + if (fuv[cell_id][i] != (lhs_values[cell_id] * rhs_arrays[cell_id][i])) { + is_same = false; + break; + } + } + } + + REQUIRE(is_same); + } + + REQUIRE_THROWS_WITH(p_R1_u * p_R1_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R2_u * p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R3_u * p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(p_R1_u * p_R2x2_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^2x2)"); + + REQUIRE_THROWS_WITH(p_R1x1_u * p_R2x2_v, "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(p_R2x2_u * p_R3x3_v, "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3x3)"); + REQUIRE_THROWS_WITH(p_R3x3_u * p_R1x1_v, "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1x1)"); + + REQUIRE_THROWS_WITH(p_R1x1_u * p_R2_v, "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2)"); + REQUIRE_THROWS_WITH(p_R2x2_u * p_R3_v, "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3)"); + REQUIRE_THROWS_WITH(p_R3x3_u * p_R1_v, "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1)"); + + REQUIRE_THROWS_WITH(p_R1_u * p_Vector3_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH(p_R2_u * p_Vector3_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH(p_R3_u * p_Vector3_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH(p_R1x1_u * p_Vector3_v, + "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH(p_R2x2_u * p_Vector3_v, + "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH(p_R3x3_u * p_Vector3_v, + "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH(p_Vector3_u * p_Vector3_v, + "error: incompatible operand types Vh(P0Vector:R) and Vh(P0Vector:R)"); + + REQUIRE_THROWS_WITH(p_Vector3_v * p_R_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)"); + REQUIRE_THROWS_WITH(p_Vector3_v * p_R1_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_Vector3_v * p_R2_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2)"); + REQUIRE_THROWS_WITH(p_Vector3_v * p_R3_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3)"); + REQUIRE_THROWS_WITH(p_Vector3_v * p_R1x1_u, + "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(p_Vector3_v * p_R2x2_u, + "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(p_Vector3_v * p_R3x3_u, + "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3x3)"); + + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1x1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2x2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3x3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R1x1_u * p_other_mesh_R1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R2x2_u * p_other_mesh_R2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R3x3_u * p_other_mesh_R3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_Vector3_u, "error: operands are defined on different meshes"); + } + + SECTION("Vh * X -> Vh") + { + CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, bool{true}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, uint64_t{1}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, int64_t{2}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, double{1.3}); + + CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, *, (TinyMatrix<1>{1.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, *, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1})); + CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, *, + (TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8})); + + REQUIRE_THROWS_WITH(p_R1_u * (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R^1) and R^1"); + REQUIRE_THROWS_WITH(p_R2_u * (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R^2) and R^2"); + REQUIRE_THROWS_WITH(p_R3_u * (TinyVector<3>{2, 3, 2}), + "error: incompatible operand types Vh(P0:R^3) and R^3"); + REQUIRE_THROWS_WITH(p_R1_u * (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R^1) and R^1x1"); + REQUIRE_THROWS_WITH(p_R2_u * (TinyMatrix<2>{2, 3, 1, 4}), + "error: incompatible operand types Vh(P0:R^2) and R^2x2"); + REQUIRE_THROWS_WITH(p_R3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}), + "error: incompatible operand types Vh(P0:R^3) and R^3x3"); + REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<1>{2}), + "error: incompatible operand types Vh(P0:R^2x2) and R^1x1"); + REQUIRE_THROWS_WITH(p_R1x1_u * (TinyMatrix<2>{2, 3, 1, 4}), + "error: incompatible operand types Vh(P0:R^1x1) and R^2x2"); + REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}), + "error: incompatible operand types Vh(P0:R^2x2) and R^3x3"); + + REQUIRE_THROWS_WITH(p_Vector3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}), + "error: incompatible operand types Vh(P0Vector:R) and R^3x3"); + REQUIRE_THROWS_WITH(p_Vector3_u * (double{2}), "error: incompatible operand types Vh(P0Vector:R) and R"); + } + + SECTION("X * Vh -> Vh") + { + CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R_u); + + CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R1x1_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R1x1_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R1x1_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R1x1_u); + + CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R2x2_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R2x2_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R2x2_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R2x2_u); + + CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R3x3_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R3x3_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R3x3_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R3x3_u); + + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8}), + *, p_R3_u); + + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1x1_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2x2_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8}), + *, p_R3x3_u); + + CHECK_VECTOR_XxVH_TO_VH(bool{true}, *, p_Vector3_u); + CHECK_VECTOR_XxVH_TO_VH(uint64_t{1}, *, p_Vector3_u); + CHECK_VECTOR_XxVH_TO_VH(int64_t{2}, *, p_Vector3_u); + CHECK_VECTOR_XxVH_TO_VH(double{1.3}, *, p_Vector3_u); + + REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R_u, + "error: incompatible operand types R^2x2 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R_u, + "error: incompatible operand types R^3x3 and Vh(P0:R)"); + + REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2_u, "error: incompatible operand types R^1x1 and Vh(P0:R^2)"); + REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3_u, + "error: incompatible operand types R^2x2 and Vh(P0:R^3)"); + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2_u, + "error: incompatible operand types R^3x3 and Vh(P0:R^2)"); + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R1_u, + "error: incompatible operand types R^3x3 and Vh(P0:R^1)"); + + REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2x2_u, + "error: incompatible operand types R^1x1 and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3x3_u, + "error: incompatible operand types R^2x2 and Vh(P0:R^3x3)"); + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2x2_u, + "error: incompatible operand types R^3x3 and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R1x1_u, + "error: incompatible operand types R^2x2 and Vh(P0:R^1x1)"); + + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_Vector3_u, + "error: incompatible operand types R^3x3 and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_Vector3_u, + "error: incompatible operand types R^1x1 and Vh(P0Vector:R)"); + } + } + + SECTION("ratio") + { + SECTION("Vh / Vh -> Vh") + { + CHECK_SCALAR_VH2_TO_VH(p_R_u, /, p_R_v); + + REQUIRE_THROWS_WITH(p_R_u / p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R2_u / p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R3_u / p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(p_R_u / p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)"); + + REQUIRE_THROWS_WITH(p_R_u / p_other_mesh_R_u, "error: operands are defined on different meshes"); + } + + SECTION("X / Vh -> Vh") + { + CHECK_SCALAR_XxVH_TO_VH(bool{true}, /, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, /, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, /, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, /, p_R_u); + } + } + } + + SECTION("3D") + { + constexpr size_t Dimension = 3; + + using Rd = TinyVector<Dimension>; + + std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh3D(); + + std::shared_ptr other_mesh = + std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr()); + + CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + + CellValue<double> u_R_values = [=] { + CellValue<double> build_values{mesh->connectivity()}; + parallel_for( + build_values.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); }); + return build_values; + }(); + + CellValue<double> v_R_values = [=] { + CellValue<double> build_values{mesh->connectivity()}; + parallel_for( + build_values.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.6 + std::sin(l2Norm(xj[cell_id])); }); + return build_values; + }(); + + std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values); + std::shared_ptr p_other_mesh_R_u = + std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, u_R_values); + std::shared_ptr p_R_v = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, v_R_values); + + std::shared_ptr p_R1_u = [=] { + CellValue<TinyVector<1>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj); + }(); + + std::shared_ptr p_R1_v = [=] { + CellValue<TinyVector<1>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj); + }(); + + std::shared_ptr p_other_mesh_R1_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues()); + + constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> { + if constexpr (Dimension == 1) { + return {x[0], 1 + x[0] * x[0]}; + } else if constexpr (Dimension == 2) { + return {x[0], x[1]}; + } else if constexpr (Dimension == 3) { + return {x[0], x[1] + x[2]}; + } + }; + + std::shared_ptr p_R2_u = [=] { + CellValue<TinyVector<2>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + uj[cell_id] = {2 * x[0] + 1, 1 - x[1]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj); + }(); + + std::shared_ptr p_R2_v = [=] { + CellValue<TinyVector<2>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + vj[cell_id] = {x[0] * x[1] + 1, 2 * x[1]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj); + }(); + + std::shared_ptr p_other_mesh_R2_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues()); + + constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> { + if constexpr (Dimension == 1) { + return {x[0], 1 + x[0] * x[0], 2 - x[0]}; + } else if constexpr (Dimension == 2) { + return {x[0], x[1], x[0] + x[1]}; + } else if constexpr (Dimension == 3) { + return {x[0], x[1], x[2]}; + } + }; + + std::shared_ptr p_R3_u = [=] { + CellValue<TinyVector<3>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + uj[cell_id] = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj); + }(); + + std::shared_ptr p_R3_v = [=] { + CellValue<TinyVector<3>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + vj[cell_id] = {x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj); + }(); + + std::shared_ptr p_other_mesh_R3_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues()); + + std::shared_ptr p_R1x1_u = [=] { + CellValue<TinyMatrix<1>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj); + }(); + + std::shared_ptr p_other_mesh_R1x1_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(other_mesh, p_R1x1_u->cellValues()); + + std::shared_ptr p_R1x1_v = [=] { + CellValue<TinyMatrix<1>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { vj[cell_id] = {0.3 - xj[cell_id][0]}; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, vj); + }(); + + std::shared_ptr p_R2x2_u = [=] { + CellValue<TinyMatrix<2>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + + uj[cell_id] = {2 * x[0] + 1, 1 - x[1], // + 2 * x[1], -x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj); + }(); + + std::shared_ptr p_other_mesh_R2x2_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(other_mesh, p_R2x2_u->cellValues()); + + std::shared_ptr p_R2x2_v = [=] { + CellValue<TinyMatrix<2>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + + vj[cell_id] = {x[0] + 0.3, 1 - x[1] - x[0], // + 2 * x[1] + x[0], x[1] - x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, vj); + }(); + + std::shared_ptr p_R3x3_u = [=] { + CellValue<TinyMatrix<3>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + + uj[cell_id] = {2 * x[0] + 1, 1 - x[1], 3, // + 2 * x[1], -x[0], x[0] - x[1], // + 3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj); + }(); + + std::shared_ptr p_other_mesh_R3x3_u = + std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(other_mesh, p_R3x3_u->cellValues()); + + std::shared_ptr p_R3x3_v = [=] { + CellValue<TinyMatrix<3>> vj{mesh->connectivity()}; + parallel_for( + vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + + vj[cell_id] = {0.2 * x[0] + 1, 2 + x[1], 3 - x[2], // + 2.3 * x[2], x[1] - x[0], x[2] - x[1], // + 2 * x[2] + x[0], x[1] + 0.2 * x[2], x[2] - 2 * x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, vj); + }(); + + std::shared_ptr p_Vector3_u = [=] { + CellArray<double> uj_vector{mesh->connectivity(), 3}; + parallel_for( + uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + uj_vector[cell_id][0] = 2 * x[0] + 1; + uj_vector[cell_id][1] = 1 - x[1] * x[2]; + uj_vector[cell_id][2] = x[0] + x[2]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector); + }(); + + std::shared_ptr p_other_mesh_Vector3_u = + std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(other_mesh, p_Vector3_u->cellArrays()); + + std::shared_ptr p_Vector3_v = [=] { + CellArray<double> vj_vector{mesh->connectivity(), 3}; + parallel_for( + vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + vj_vector[cell_id][0] = x[0] * x[1] + 1; + vj_vector[cell_id][1] = 2 * x[1]; + vj_vector[cell_id][2] = x[2] * x[0]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector); + }(); + + std::shared_ptr p_Vector2_w = [=] { + CellArray<double> wj_vector{mesh->connectivity(), 2}; + parallel_for( + wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + wj_vector[cell_id][0] = x[0] + x[1] * 2; + wj_vector[cell_id][1] = x[0] * x[1]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector); + }(); + + SECTION("sum") + { + SECTION("Vh + Vh -> Vh") + { + CHECK_SCALAR_VH2_TO_VH(p_R_u, +, p_R_v); + + CHECK_SCALAR_VH2_TO_VH(p_R1_u, +, p_R1_v); + CHECK_SCALAR_VH2_TO_VH(p_R2_u, +, p_R2_v); + CHECK_SCALAR_VH2_TO_VH(p_R3_u, +, p_R3_v); + + CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, +, p_R1x1_v); + CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, +, p_R2x2_v); + CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, +, p_R3x3_v); + + CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, +, p_Vector3_v); + + REQUIRE_THROWS_WITH(p_R_u + p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R2_u + p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R3_u + p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(p_Vector3_u + p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)"); + REQUIRE_THROWS_WITH(p_Vector3_u + p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes"); + + REQUIRE_THROWS_WITH(p_R_u + p_other_mesh_R_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R1_u + p_other_mesh_R1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R2_u + p_other_mesh_R2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R3_u + p_other_mesh_R3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R1x1_u + p_other_mesh_R1x1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R2x2_u + p_other_mesh_R2x2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R3x3_u + p_other_mesh_R3x3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_Vector3_u + p_other_mesh_Vector3_u, "error: operands are defined on different meshes"); + } + + SECTION("Vh + X -> Vh") + { + CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, bool{true}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, uint64_t{1}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, int64_t{2}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, double{1.3}); + + CHECK_SCALAR_VHxX_TO_VH(p_R1_u, +, (TinyVector<1>{1.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R2_u, +, (TinyVector<2>{1.2, 2.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R3_u, +, (TinyVector<3>{3.2, 7.1, 5.2})); + + CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, +, (TinyMatrix<1>{1.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, +, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1})); + CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, +, + (TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8})); + + REQUIRE_THROWS_WITH(p_R_u + (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1"); + REQUIRE_THROWS_WITH(p_R_u + (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2"); + REQUIRE_THROWS_WITH(p_R_u + (TinyVector<3>{2, 3, 2}), "error: incompatible operand types Vh(P0:R) and R^3"); + REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1"); + REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<2>{2, 3, 1, 4}), + "error: incompatible operand types Vh(P0:R) and R^2x2"); + REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}), + "error: incompatible operand types Vh(P0:R) and R^3x3"); + + REQUIRE_THROWS_WITH(p_Vector3_u + (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R"); + REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<1>{1}), + "error: incompatible operand types Vh(P0Vector:R) and R^1"); + REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<2>{1, 2}), + "error: incompatible operand types Vh(P0Vector:R) and R^2"); + } + + SECTION("X + Vh -> Vh") + { + CHECK_SCALAR_XxVH_TO_VH(bool{true}, +, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, +, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, +, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, +, p_R_u); + + CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), +, p_R1_u); + CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), +, p_R2_u); + CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), +, p_R3_u); + + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), +, p_R1x1_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), +, p_R2x2_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8}), + +, p_R3x3_u); + + REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) + p_R_u, "error: incompatible operand types R^3 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) + p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) + p_R_u, + "error: incompatible operand types R^2x2 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) + p_R_u, + "error: incompatible operand types R^3x3 and Vh(P0:R)"); + + REQUIRE_THROWS_WITH((double{1}) + p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_Vector3_u, + "error: incompatible operand types R^1 and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_Vector3_u, + "error: incompatible operand types R^2 and Vh(P0Vector:R)"); + } + } + + SECTION("difference") + { + SECTION("Vh - Vh -> Vh") + { + CHECK_SCALAR_VH2_TO_VH(p_R_u, -, p_R_v); + + CHECK_SCALAR_VH2_TO_VH(p_R1_u, -, p_R1_v); + CHECK_SCALAR_VH2_TO_VH(p_R2_u, -, p_R2_v); + CHECK_SCALAR_VH2_TO_VH(p_R3_u, -, p_R3_v); + + CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, -, p_R1x1_v); + CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, -, p_R2x2_v); + CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, -, p_R3x3_v); + + CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, -, p_Vector3_v); + + REQUIRE_THROWS_WITH(p_R_u - p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R2_u - p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R3_u - p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(p_R_u - p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(p_Vector3_u - p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)"); + REQUIRE_THROWS_WITH(p_Vector3_u - p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes"); + + REQUIRE_THROWS_WITH(p_R_u - p_other_mesh_R_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R1_u - p_other_mesh_R1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R2_u - p_other_mesh_R2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R3_u - p_other_mesh_R3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R1x1_u - p_other_mesh_R1x1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R2x2_u - p_other_mesh_R2x2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R3x3_u - p_other_mesh_R3x3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_Vector3_u - p_other_mesh_Vector3_u, "error: operands are defined on different meshes"); + } + + SECTION("Vh - X -> Vh") + { + CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, bool{true}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, uint64_t{1}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, int64_t{2}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, double{1.3}); + + CHECK_SCALAR_VHxX_TO_VH(p_R1_u, -, (TinyVector<1>{1.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R2_u, -, (TinyVector<2>{1.2, 2.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R3_u, -, (TinyVector<3>{3.2, 7.1, 5.2})); + + CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, -, (TinyMatrix<1>{1.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, -, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1})); + CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, -, + (TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8})); + + REQUIRE_THROWS_WITH(p_R_u - (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1"); + REQUIRE_THROWS_WITH(p_R_u - (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2"); + REQUIRE_THROWS_WITH(p_R_u - (TinyVector<3>{2, 3, 2}), "error: incompatible operand types Vh(P0:R) and R^3"); + REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1"); + REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<2>{2, 3, 1, 4}), + "error: incompatible operand types Vh(P0:R) and R^2x2"); + REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}), + "error: incompatible operand types Vh(P0:R) and R^3x3"); + + REQUIRE_THROWS_WITH(p_Vector3_u - (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R"); + REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<1>{1}), + "error: incompatible operand types Vh(P0Vector:R) and R^1"); + REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<2>{1, 2}), + "error: incompatible operand types Vh(P0Vector:R) and R^2"); + } + + SECTION("X - Vh -> Vh") + { + CHECK_SCALAR_XxVH_TO_VH(bool{true}, -, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, -, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, -, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, -, p_R_u); + + CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), -, p_R1_u); + CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), -, p_R2_u); + CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), -, p_R3_u); + + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), -, p_R1x1_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), -, p_R2x2_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8}), + -, p_R3x3_u); + + REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) - p_R_u, "error: incompatible operand types R^3 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) - p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) - p_R_u, + "error: incompatible operand types R^2x2 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) - p_R_u, + "error: incompatible operand types R^3x3 and Vh(P0:R)"); + + REQUIRE_THROWS_WITH((double{1}) - p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_Vector3_u, + "error: incompatible operand types R^1 and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_Vector3_u, + "error: incompatible operand types R^2 and Vh(P0Vector:R)"); + } + } + + SECTION("product") + { + SECTION("Vh * Vh -> Vh") + { + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R_v); + + CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1x1_v); + CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2x2_v); + CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3x3_v); + + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1_v); + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2_v); + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3_v); + + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1x1_v); + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2x2_v); + CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3x3_v); + + CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1_v); + CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2_v); + CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3_v); + + { + std::shared_ptr p_fuv = p_R_u * p_Vector3_v; + + REQUIRE(p_fuv.use_count() > 0); + REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv)); + + const auto& fuv = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv); + + auto lhs_values = p_R_u->cellValues(); + auto rhs_arrays = p_Vector3_v->cellArrays(); + bool is_same = true; + for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) { + for (size_t i = 0; i < fuv.size(); ++i) { + if (fuv[cell_id][i] != (lhs_values[cell_id] * rhs_arrays[cell_id][i])) { + is_same = false; + break; + } + } + } + + REQUIRE(is_same); + } + + REQUIRE_THROWS_WITH(p_R1_u * p_R1_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R2_u * p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R3_u * p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(p_R1_u * p_R2x2_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^2x2)"); + + REQUIRE_THROWS_WITH(p_R1x1_u * p_R2x2_v, "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(p_R2x2_u * p_R3x3_v, "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3x3)"); + REQUIRE_THROWS_WITH(p_R3x3_u * p_R1x1_v, "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1x1)"); + + REQUIRE_THROWS_WITH(p_R1x1_u * p_R2_v, "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2)"); + REQUIRE_THROWS_WITH(p_R2x2_u * p_R3_v, "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3)"); + REQUIRE_THROWS_WITH(p_R3x3_u * p_R1_v, "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1)"); + + REQUIRE_THROWS_WITH(p_R1_u * p_Vector3_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH(p_R2_u * p_Vector3_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH(p_R3_u * p_Vector3_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH(p_R1x1_u * p_Vector3_v, + "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH(p_R2x2_u * p_Vector3_v, + "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH(p_R3x3_u * p_Vector3_v, + "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH(p_Vector3_u * p_Vector3_v, + "error: incompatible operand types Vh(P0Vector:R) and Vh(P0Vector:R)"); + + REQUIRE_THROWS_WITH(p_Vector3_v * p_R_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)"); + REQUIRE_THROWS_WITH(p_Vector3_v * p_R1_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_Vector3_v * p_R2_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2)"); + REQUIRE_THROWS_WITH(p_Vector3_v * p_R3_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3)"); + REQUIRE_THROWS_WITH(p_Vector3_v * p_R1x1_u, + "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(p_Vector3_v * p_R2x2_u, + "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH(p_Vector3_v * p_R3x3_u, + "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3x3)"); + + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1x1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2x2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3x3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R1x1_u * p_other_mesh_R1_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R2x2_u * p_other_mesh_R2_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R3x3_u * p_other_mesh_R3_u, "error: operands are defined on different meshes"); + REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_Vector3_u, "error: operands are defined on different meshes"); + } + + SECTION("Vh * X -> Vh") + { + CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, bool{true}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, uint64_t{1}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, int64_t{2}); + CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, double{1.3}); + + CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, *, (TinyMatrix<1>{1.3})); + CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, *, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1})); + CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, *, + (TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8})); + + REQUIRE_THROWS_WITH(p_R1_u * (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R^1) and R^1"); + REQUIRE_THROWS_WITH(p_R2_u * (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R^2) and R^2"); + REQUIRE_THROWS_WITH(p_R3_u * (TinyVector<3>{2, 3, 2}), + "error: incompatible operand types Vh(P0:R^3) and R^3"); + REQUIRE_THROWS_WITH(p_R1_u * (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R^1) and R^1x1"); + REQUIRE_THROWS_WITH(p_R2_u * (TinyMatrix<2>{2, 3, 1, 4}), + "error: incompatible operand types Vh(P0:R^2) and R^2x2"); + REQUIRE_THROWS_WITH(p_R3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}), + "error: incompatible operand types Vh(P0:R^3) and R^3x3"); + REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<1>{2}), + "error: incompatible operand types Vh(P0:R^2x2) and R^1x1"); + REQUIRE_THROWS_WITH(p_R1x1_u * (TinyMatrix<2>{2, 3, 1, 4}), + "error: incompatible operand types Vh(P0:R^1x1) and R^2x2"); + REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}), + "error: incompatible operand types Vh(P0:R^2x2) and R^3x3"); + + REQUIRE_THROWS_WITH(p_Vector3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}), + "error: incompatible operand types Vh(P0Vector:R) and R^3x3"); + REQUIRE_THROWS_WITH(p_Vector3_u * (double{2}), "error: incompatible operand types Vh(P0Vector:R) and R"); + } + + SECTION("X * Vh -> Vh") + { + CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R_u); + + CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R1x1_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R1x1_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R1x1_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R1x1_u); + + CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R2x2_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R2x2_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R2x2_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R2x2_u); + + CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R3x3_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R3x3_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R3x3_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R3x3_u); + + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8}), + *, p_R3_u); + + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1x1_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2x2_u); + CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2, // + 4.7, 2.3, 7.1, // + 9.7, 3.2, 6.8}), + *, p_R3x3_u); + + CHECK_VECTOR_XxVH_TO_VH(bool{true}, *, p_Vector3_u); + CHECK_VECTOR_XxVH_TO_VH(uint64_t{1}, *, p_Vector3_u); + CHECK_VECTOR_XxVH_TO_VH(int64_t{2}, *, p_Vector3_u); + CHECK_VECTOR_XxVH_TO_VH(double{1.3}, *, p_Vector3_u); + + REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R_u, + "error: incompatible operand types R^2x2 and Vh(P0:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R_u, + "error: incompatible operand types R^3x3 and Vh(P0:R)"); + + REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2_u, "error: incompatible operand types R^1x1 and Vh(P0:R^2)"); + REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3_u, + "error: incompatible operand types R^2x2 and Vh(P0:R^3)"); + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2_u, + "error: incompatible operand types R^3x3 and Vh(P0:R^2)"); + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R1_u, + "error: incompatible operand types R^3x3 and Vh(P0:R^1)"); + + REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2x2_u, + "error: incompatible operand types R^1x1 and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3x3_u, + "error: incompatible operand types R^2x2 and Vh(P0:R^3x3)"); + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2x2_u, + "error: incompatible operand types R^3x3 and Vh(P0:R^2x2)"); + REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R1x1_u, + "error: incompatible operand types R^2x2 and Vh(P0:R^1x1)"); + + REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_Vector3_u, + "error: incompatible operand types R^3x3 and Vh(P0Vector:R)"); + REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_Vector3_u, + "error: incompatible operand types R^1x1 and Vh(P0Vector:R)"); + } + } + + SECTION("ratio") + { + SECTION("Vh / Vh -> Vh") + { + CHECK_SCALAR_VH2_TO_VH(p_R_u, /, p_R_v); + + REQUIRE_THROWS_WITH(p_R_u / p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R2_u / p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)"); + REQUIRE_THROWS_WITH(p_R3_u / p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)"); + REQUIRE_THROWS_WITH(p_R_u / p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)"); + + REQUIRE_THROWS_WITH(p_R_u / p_other_mesh_R_u, "error: operands are defined on different meshes"); + } + + SECTION("X / Vh -> Vh") + { + CHECK_SCALAR_XxVH_TO_VH(bool{true}, /, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, /, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, /, p_R_u); + CHECK_SCALAR_XxVH_TO_VH(double{1.3}, /, p_R_u); + } + } + } + } + + SECTION("unary operators") + { + SECTION("1D") + { + constexpr size_t Dimension = 1; + + using Rd = TinyVector<Dimension>; + + std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh1D(); + + CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + + CellValue<double> u_R_values = [=] { + CellValue<double> build_values{mesh->connectivity()}; + parallel_for( + build_values.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); }); + return build_values; + }(); + + std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values); + + std::shared_ptr p_R1_u = [=] { + CellValue<TinyVector<1>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj); + }(); + + constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> { + if constexpr (Dimension == 1) { + return {x[0], 1 + x[0] * x[0]}; + } else if constexpr (Dimension == 2) { + return {x[0], x[1]}; + } else if constexpr (Dimension == 3) { + return {x[0], x[1] + x[2]}; + } + }; + + std::shared_ptr p_R2_u = [=] { + CellValue<TinyVector<2>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + uj[cell_id] = {2 * x[0] + 1, 1 - x[1]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj); + }(); + + constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> { + if constexpr (Dimension == 1) { + return {x[0], 1 + x[0] * x[0], 2 - x[0]}; + } else if constexpr (Dimension == 2) { + return {x[0], x[1], x[0] + x[1]}; + } else if constexpr (Dimension == 3) { + return {x[0], x[1], x[2]}; + } + }; + + std::shared_ptr p_R3_u = [=] { + CellValue<TinyVector<3>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + uj[cell_id] = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj); + }(); + + std::shared_ptr p_R1x1_u = [=] { + CellValue<TinyMatrix<1>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj); + }(); + + std::shared_ptr p_R2x2_u = [=] { + CellValue<TinyMatrix<2>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + + uj[cell_id] = {2 * x[0] + 1, 1 - x[1], // + 2 * x[1], -x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj); + }(); + + std::shared_ptr p_R3x3_u = [=] { + CellValue<TinyMatrix<3>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + + uj[cell_id] = {2 * x[0] + 1, 1 - x[1], 3, // + 2 * x[1], -x[0], x[0] - x[1], // + 3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj); + }(); + + std::shared_ptr p_Vector3_u = [=] { + CellArray<double> uj_vector{mesh->connectivity(), 3}; + parallel_for( + uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + uj_vector[cell_id][0] = 2 * x[0] + 1; + uj_vector[cell_id][1] = 1 - x[1] * x[2]; + uj_vector[cell_id][2] = x[0] + x[2]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector); + }(); + + SECTION("unary minus") + { + SECTION("- Vh -> Vh") + { + CHECK_SCALAR_VH_TO_VH(-, p_R_u); + + CHECK_SCALAR_VH_TO_VH(-, p_R1_u); + CHECK_SCALAR_VH_TO_VH(-, p_R2_u); + CHECK_SCALAR_VH_TO_VH(-, p_R3_u); + + CHECK_SCALAR_VH_TO_VH(-, p_R1x1_u); + CHECK_SCALAR_VH_TO_VH(-, p_R2x2_u); + CHECK_SCALAR_VH_TO_VH(-, p_R3x3_u); + + CHECK_VECTOR_VH_TO_VH(-, p_Vector3_u); + } + } + } + + SECTION("2D") + { + constexpr size_t Dimension = 2; + + using Rd = TinyVector<Dimension>; + + std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh2D(); + + CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + + CellValue<double> u_R_values = [=] { + CellValue<double> build_values{mesh->connectivity()}; + parallel_for( + build_values.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); }); + return build_values; + }(); + + std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values); + + std::shared_ptr p_R1_u = [=] { + CellValue<TinyVector<1>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj); + }(); + + constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> { + if constexpr (Dimension == 1) { + return {x[0], 1 + x[0] * x[0]}; + } else if constexpr (Dimension == 2) { + return {x[0], x[1]}; + } else if constexpr (Dimension == 3) { + return {x[0], x[1] + x[2]}; + } + }; + + std::shared_ptr p_R2_u = [=] { + CellValue<TinyVector<2>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + uj[cell_id] = {2 * x[0] + 1, 1 - x[1]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj); + }(); + + constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> { + if constexpr (Dimension == 1) { + return {x[0], 1 + x[0] * x[0], 2 - x[0]}; + } else if constexpr (Dimension == 2) { + return {x[0], x[1], x[0] + x[1]}; + } else if constexpr (Dimension == 3) { + return {x[0], x[1], x[2]}; + } + }; + + std::shared_ptr p_R3_u = [=] { + CellValue<TinyVector<3>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + uj[cell_id] = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj); + }(); + + std::shared_ptr p_R1x1_u = [=] { + CellValue<TinyMatrix<1>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj); + }(); + + std::shared_ptr p_R2x2_u = [=] { + CellValue<TinyMatrix<2>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + + uj[cell_id] = {2 * x[0] + 1, 1 - x[1], // + 2 * x[1], -x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj); + }(); + + std::shared_ptr p_R3x3_u = [=] { + CellValue<TinyMatrix<3>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + + uj[cell_id] = {2 * x[0] + 1, 1 - x[1], 3, // + 2 * x[1], -x[0], x[0] - x[1], // + 3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj); + }(); + + std::shared_ptr p_Vector3_u = [=] { + CellArray<double> uj_vector{mesh->connectivity(), 3}; + parallel_for( + uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + uj_vector[cell_id][0] = 2 * x[0] + 1; + uj_vector[cell_id][1] = 1 - x[1] * x[2]; + uj_vector[cell_id][2] = x[0] + x[2]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector); + }(); + + SECTION("unary minus") + { + SECTION("- Vh -> Vh") + { + CHECK_SCALAR_VH_TO_VH(-, p_R_u); + + CHECK_SCALAR_VH_TO_VH(-, p_R1_u); + CHECK_SCALAR_VH_TO_VH(-, p_R2_u); + CHECK_SCALAR_VH_TO_VH(-, p_R3_u); + + CHECK_SCALAR_VH_TO_VH(-, p_R1x1_u); + CHECK_SCALAR_VH_TO_VH(-, p_R2x2_u); + CHECK_SCALAR_VH_TO_VH(-, p_R3x3_u); + + CHECK_VECTOR_VH_TO_VH(-, p_Vector3_u); + } + } + } + + SECTION("3D") + { + constexpr size_t Dimension = 3; + + using Rd = TinyVector<Dimension>; + + std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh3D(); + + CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj(); + + CellValue<double> u_R_values = [=] { + CellValue<double> build_values{mesh->connectivity()}; + parallel_for( + build_values.numberOfItems(), + PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); }); + return build_values; + }(); + + std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values); + + std::shared_ptr p_R1_u = [=] { + CellValue<TinyVector<1>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj); + }(); + + constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> { + if constexpr (Dimension == 1) { + return {x[0], 1 + x[0] * x[0]}; + } else if constexpr (Dimension == 2) { + return {x[0], x[1]}; + } else if constexpr (Dimension == 3) { + return {x[0], x[1] + x[2]}; + } + }; + + std::shared_ptr p_R2_u = [=] { + CellValue<TinyVector<2>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + uj[cell_id] = {2 * x[0] + 1, 1 - x[1]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj); + }(); + + constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> { + if constexpr (Dimension == 1) { + return {x[0], 1 + x[0] * x[0], 2 - x[0]}; + } else if constexpr (Dimension == 2) { + return {x[0], x[1], x[0] + x[1]}; + } else if constexpr (Dimension == 3) { + return {x[0], x[1], x[2]}; + } + }; + + std::shared_ptr p_R3_u = [=] { + CellValue<TinyVector<3>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + uj[cell_id] = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj); + }(); + + std::shared_ptr p_R1x1_u = [=] { + CellValue<TinyMatrix<1>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj); + }(); + + std::shared_ptr p_R2x2_u = [=] { + CellValue<TinyMatrix<2>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<2> x = to_2d(xj[cell_id]); + + uj[cell_id] = {2 * x[0] + 1, 1 - x[1], // + 2 * x[1], -x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj); + }(); + + std::shared_ptr p_R3x3_u = [=] { + CellValue<TinyMatrix<3>> uj{mesh->connectivity()}; + parallel_for( + uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + + uj[cell_id] = {2 * x[0] + 1, 1 - x[1], 3, // + 2 * x[1], -x[0], x[0] - x[1], // + 3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]}; + }); + + return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj); + }(); + + std::shared_ptr p_Vector3_u = [=] { + CellArray<double> uj_vector{mesh->connectivity(), 3}; + parallel_for( + uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { + const TinyVector<3> x = to_3d(xj[cell_id]); + uj_vector[cell_id][0] = 2 * x[0] + 1; + uj_vector[cell_id][1] = 1 - x[1] * x[2]; + uj_vector[cell_id][2] = x[0] + x[2]; + }); + + return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector); + }(); + + SECTION("unary minus") + { + SECTION("- Vh -> Vh") + { + CHECK_SCALAR_VH_TO_VH(-, p_R_u); + + CHECK_SCALAR_VH_TO_VH(-, p_R1_u); + CHECK_SCALAR_VH_TO_VH(-, p_R2_u); + CHECK_SCALAR_VH_TO_VH(-, p_R3_u); + + CHECK_SCALAR_VH_TO_VH(-, p_R1x1_u); + CHECK_SCALAR_VH_TO_VH(-, p_R2x2_u); + CHECK_SCALAR_VH_TO_VH(-, p_R3x3_u); + + CHECK_VECTOR_VH_TO_VH(-, p_Vector3_u); + } + } + } + } +} diff --git a/tests/test_EmbeddedIDiscreteFunctionUtils.cpp b/tests/test_EmbeddedIDiscreteFunctionUtils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0ded74fd6b5764bfb67917a48a1a24ab8b1db873 --- /dev/null +++ b/tests/test_EmbeddedIDiscreteFunctionUtils.cpp @@ -0,0 +1,125 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <language/utils/EmbeddedIDiscreteFunctionUtils.hpp> +#include <scheme/DiscreteFunctionP0.hpp> +#include <scheme/DiscreteFunctionP0Vector.hpp> + +#include <MeshDataBaseForTests.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("EmbeddedIDiscreteFunctionUtils", "[language]") +{ + using R1 = TinyVector<1, double>; + using R2 = TinyVector<2, double>; + using R3 = TinyVector<3, double>; + + using R1x1 = TinyMatrix<1, 1, double>; + using R2x2 = TinyMatrix<2, 2, double>; + using R3x3 = TinyMatrix<3, 3, double>; + + SECTION("operand type name") + { + SECTION("basic types") + { + REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(double{1}) == "R"); + REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(std::make_shared<double>(1)) == "R"); + } + + SECTION("discrete P0 function") + { + std::shared_ptr mesh_1d = MeshDataBaseForTests::get().cartesianMesh1D(); + + REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, double>{mesh_1d}) == "Vh(P0:R)"); + + REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R1>{mesh_1d}) == "Vh(P0:R^1)"); + REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R2>{mesh_1d}) == "Vh(P0:R^2)"); + REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R3>{mesh_1d}) == "Vh(P0:R^3)"); + + REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R1x1>{mesh_1d}) == + "Vh(P0:R^1x1)"); + REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R2x2>{mesh_1d}) == + "Vh(P0:R^2x2)"); + REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R3x3>{mesh_1d}) == + "Vh(P0:R^3x3)"); + } + + SECTION("discrete P0Vector function") + { + std::shared_ptr mesh_1d = MeshDataBaseForTests::get().cartesianMesh1D(); + + REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0Vector<1, double>{mesh_1d, 2}) == + "Vh(P0Vector:R)"); + } + } + + SECTION("check if is same discretization") + { + SECTION("from shared_ptr") + { + std::shared_ptr mesh_1d = MeshDataBaseForTests::get().cartesianMesh1D(); + + REQUIRE( + EmbeddedIDiscreteFunctionUtils::isSameDiscretization(std::make_shared<DiscreteFunctionP0<1, double>>(mesh_1d), + std::make_shared<DiscreteFunctionP0<1, double>>(mesh_1d))); + + REQUIRE(not EmbeddedIDiscreteFunctionUtils:: + isSameDiscretization(std::make_shared<DiscreteFunctionP0<1, double>>(mesh_1d), + std::make_shared<DiscreteFunctionP0Vector<1, double>>(mesh_1d, 1))); + } + + SECTION("from value") + { + std::shared_ptr mesh_1d = MeshDataBaseForTests::get().cartesianMesh1D(); + + REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, double>{mesh_1d}, + DiscreteFunctionP0<1, double>{mesh_1d})); + + REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R1>{mesh_1d}, + DiscreteFunctionP0<1, R1>{mesh_1d})); + + REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R2>{mesh_1d}, + DiscreteFunctionP0<1, R2>{mesh_1d})); + + REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R3>{mesh_1d}, + DiscreteFunctionP0<1, R3>{mesh_1d})); + + REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R1x1>{mesh_1d}, + DiscreteFunctionP0<1, R1x1>{mesh_1d})); + + REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R2x2>{mesh_1d}, + DiscreteFunctionP0<1, R2x2>{mesh_1d})); + + REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R3x3>{mesh_1d}, + DiscreteFunctionP0<1, R3x3>{mesh_1d})); + + REQUIRE(not EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, double>{mesh_1d}, + DiscreteFunctionP0<1, R1>{mesh_1d})); + + REQUIRE(not EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R2>{mesh_1d}, + DiscreteFunctionP0<1, R2x2>{mesh_1d})); + + REQUIRE(not EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R3x3>{mesh_1d}, + DiscreteFunctionP0<1, R2x2>{mesh_1d})); + } + + SECTION("invalid data type") + { + std::shared_ptr mesh_1d = MeshDataBaseForTests::get().cartesianMesh1D(); + + REQUIRE_THROWS_WITH(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, int64_t>{mesh_1d}, + DiscreteFunctionP0<1, int64_t>{mesh_1d}), + "unexpected error: invalid data type Vh(P0:Z)"); + } + } + +#ifndef NDEBUG + SECTION("errors") + { + REQUIRE_THROWS_WITH(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(std::shared_ptr<double>()), + "dangling shared_ptr"); + } + +#endif // NDEBUG +} diff --git a/tests/test_FunctionArgumentConverter.cpp b/tests/test_FunctionArgumentConverter.cpp index 7befebe52d93fc7ba0e17b940dba88acaecfb306..643fbba0b092d337743ea5aeaedb25eb86a5b445 100644 --- a/tests/test_FunctionArgumentConverter.cpp +++ b/tests/test_FunctionArgumentConverter.cpp @@ -172,6 +172,18 @@ TEST_CASE("FunctionArgumentConverter", "[language]") std::vector<TinyVector<2>>{TinyVector<2>{1, 3.2}, TinyVector<2>{-1, 0.2}}); REQUIRE(std::get<std::vector<TinyVector<2>>>(execution_policy.currentContext()[2]) == std::vector<TinyVector<2>>{TinyVector<2>{-3, 12.2}, TinyVector<2>{2, 1.2}}); + + std::shared_ptr symbol_table = std::make_shared<SymbolTable>(); + AggregateDataVariant v_fid{std::vector<DataVariant>{uint64_t{3}, uint64_t{2}, uint64_t{7}}}; + + FunctionListArgumentConverter<FunctionSymbolId, FunctionSymbolId> converterFid{0, symbol_table}; + converterFid.convert(execution_policy, v_fid); + + auto&& fid_tuple = std::get<std::vector<FunctionSymbolId>>(execution_policy.currentContext()[0]); + + REQUIRE(fid_tuple[0].id() == 3); + REQUIRE(fid_tuple[1].id() == 2); + REQUIRE(fid_tuple[2].id() == 7); } SECTION("FunctionArgumentToFunctionSymbolIdConverter") @@ -184,4 +196,16 @@ TEST_CASE("FunctionArgumentConverter", "[language]") REQUIRE(std::get<FunctionSymbolId>(execution_policy.currentContext()[0]).id() == f_id); } + + SECTION("FunctionArgumentToTupleFunctionSymbolIdConverter") + { + std::shared_ptr symbol_table = std::make_shared<SymbolTable>(); + + const uint64_t f_id = 3; + FunctionArgumentToTupleFunctionSymbolIdConverter converter0{0, symbol_table}; + converter0.convert(execution_policy, f_id); + auto&& tuple = std::get<std::vector<FunctionSymbolId>>(execution_policy.currentContext()[0]); + REQUIRE(tuple.size() == 1); + REQUIRE(tuple[0].id() == f_id); + } } diff --git a/tests/test_MathModule.cpp b/tests/test_MathModule.cpp index 7c2a9bc5438367b7f3cf70d14d3b8acc58a29ae7..4d71927f13d3229d450c941250bb4202d071b109 100644 --- a/tests/test_MathModule.cpp +++ b/tests/test_MathModule.cpp @@ -15,7 +15,7 @@ TEST_CASE("MathModule", "[language]") MathModule math_module; const auto& name_builtin_function = math_module.getNameBuiltinFunctionMap(); - REQUIRE(name_builtin_function.size() == 25); + REQUIRE(name_builtin_function.size() == 29); SECTION("double -> double") { @@ -323,6 +323,63 @@ TEST_CASE("MathModule", "[language]") auto result = std::pow(arg0, arg1); REQUIRE(std::get<decltype(result)>(result_variant) == Catch::Approx(result)); } + + SECTION("min") + { + auto i_function = name_builtin_function.find("min:R*R"); + REQUIRE(i_function != name_builtin_function.end()); + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({arg0_variant, arg1_variant}); + + auto result = std::min(arg0, arg1); + REQUIRE(std::get<decltype(result)>(result_variant) == Catch::Approx(result)); + } + + SECTION("max") + { + auto i_function = name_builtin_function.find("max:R*R"); + REQUIRE(i_function != name_builtin_function.end()); + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({arg0_variant, arg1_variant}); + + auto result = std::max(arg0, arg1); + REQUIRE(std::get<decltype(result)>(result_variant) == Catch::Approx(result)); + } + } + + SECTION("(uint64_t, uint64_t) -> uint64_t") + { + int64_t arg0 = 3; + int64_t arg1 = -2; + + DataVariant arg0_variant = arg0; + DataVariant arg1_variant = arg1; + + SECTION("min") + { + auto i_function = name_builtin_function.find("min:Z*Z"); + REQUIRE(i_function != name_builtin_function.end()); + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({arg0_variant, arg1_variant}); + + auto result = std::min(arg0, arg1); + REQUIRE(std::get<decltype(result)>(result_variant) == Catch::Approx(result)); + } + + SECTION("max") + { + auto i_function = name_builtin_function.find("max:Z*Z"); + REQUIRE(i_function != name_builtin_function.end()); + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({arg0_variant, arg1_variant}); + + auto result = std::max(arg0, arg1); + REQUIRE(std::get<decltype(result)>(result_variant) == Catch::Approx(result)); + } } SECTION("(R^d, R^d) -> double") diff --git a/tests/test_OFStream.cpp b/tests/test_OFStream.cpp index 32b4ff21b8e725dcaff822b586f9810d617d3a4d..5ca9c38fd4b8f01f6ed28a263d9199905481edf5 100644 --- a/tests/test_OFStream.cpp +++ b/tests/test_OFStream.cpp @@ -12,7 +12,7 @@ TEST_CASE("OFStream", "[language]") { SECTION("ofstream") { - const std::string basename = "ofstream_"; + const std::string basename = std::filesystem::temp_directory_path().append("ofstream_"); const std::string filename = basename + std::to_string(parallel::rank()); // Ensures that the file is closed after this line diff --git a/tests/test_SmallVector.cpp b/tests/test_SmallVector.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3b499c3ab3e984247b7f20f96b626f8ee413763a --- /dev/null +++ b/tests/test_SmallVector.cpp @@ -0,0 +1,472 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <utils/PugsAssert.hpp> + +#include <algebra/SmallVector.hpp> +#include <algebra/TinyVector.hpp> + +// Instantiate to ensure full coverage is performed +template class SmallVector<int>; + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("SmallVector", "[algebra]") +{ + SECTION("size") + { + SmallVector<int> x{5}; + REQUIRE(x.size() == 5); + } + + SECTION("write access") + { + SmallVector<int> x{5}; + x[0] = 0; + x[1] = 1; + x[2] = 2; + x[3] = 3; + x[4] = 4; + + REQUIRE(x[0] == 0); + REQUIRE(x[1] == 1); + REQUIRE(x[2] == 2); + REQUIRE(x[3] == 3); + REQUIRE(x[4] == 4); + } + + SECTION("fill") + { + SmallVector<int> x{5}; + x.fill(2); + + REQUIRE(x[0] == 2); + REQUIRE(x[1] == 2); + REQUIRE(x[2] == 2); + REQUIRE(x[3] == 2); + REQUIRE(x[4] == 2); + } + + SECTION("copy constructor (shallow)") + { + SmallVector<int> x{5}; + x[0] = 0; + x[1] = 1; + x[2] = 2; + x[3] = 3; + x[4] = 4; + + const SmallVector<int> y = x; + REQUIRE(y[0] == 0); + REQUIRE(y[1] == 1); + REQUIRE(y[2] == 2); + REQUIRE(y[3] == 3); + REQUIRE(y[4] == 4); + } + + SECTION("copy constructor (move)") + { + SmallVector<int> x{5}; + x[0] = 0; + x[1] = 1; + x[2] = 2; + x[3] = 3; + x[4] = 4; + + const SmallVector<int> y = std::move(x); + REQUIRE(y[0] == 0); + REQUIRE(y[1] == 1); + REQUIRE(y[2] == 2); + REQUIRE(y[3] == 3); + REQUIRE(y[4] == 4); + } + + SECTION("copy (shallow)") + { + SmallVector<int> x{5}; + x[0] = 0; + x[1] = 1; + x[2] = 2; + x[3] = 3; + x[4] = 4; + + SmallVector<int> y{5}; + y = x; + REQUIRE(y[0] == 0); + REQUIRE(y[1] == 1); + REQUIRE(y[2] == 2); + REQUIRE(y[3] == 3); + REQUIRE(y[4] == 4); + + x[0] = 14; + x[1] = 13; + x[2] = 12; + x[3] = 11; + x[4] = 10; + + REQUIRE(x[0] == 14); + REQUIRE(x[1] == 13); + REQUIRE(x[2] == 12); + REQUIRE(x[3] == 11); + REQUIRE(x[4] == 10); + + REQUIRE(y[0] == 14); + REQUIRE(y[1] == 13); + REQUIRE(y[2] == 12); + REQUIRE(y[3] == 11); + REQUIRE(y[4] == 10); + } + + SECTION("copy (deep)") + { + SmallVector<int> x{5}; + x[0] = 0; + x[1] = 1; + x[2] = 2; + x[3] = 3; + x[4] = 4; + + SmallVector<int> y{5}; + y = copy(x); + + x[0] = 14; + x[1] = 13; + x[2] = 12; + x[3] = 11; + x[4] = 10; + + REQUIRE(y[0] == 0); + REQUIRE(y[1] == 1); + REQUIRE(y[2] == 2); + REQUIRE(y[3] == 3); + REQUIRE(y[4] == 4); + + REQUIRE(x[0] == 14); + REQUIRE(x[1] == 13); + REQUIRE(x[2] == 12); + REQUIRE(x[3] == 11); + REQUIRE(x[4] == 10); + } + + SECTION("copy to const (shallow)") + { + SmallVector<int> x{5}; + x[0] = 0; + x[1] = 1; + x[2] = 2; + x[3] = 3; + x[4] = 4; + + SmallVector<const int> y; + y = x; + REQUIRE(y[0] == 0); + REQUIRE(y[1] == 1); + REQUIRE(y[2] == 2); + REQUIRE(y[3] == 3); + REQUIRE(y[4] == 4); + + SmallVector<int> z{5}; + z[0] = 14; + z[1] = 13; + z[2] = 12; + z[3] = 11; + z[4] = 10; + + y = z; + REQUIRE(z[0] == 14); + REQUIRE(z[1] == 13); + REQUIRE(z[2] == 12); + REQUIRE(z[3] == 11); + REQUIRE(z[4] == 10); + + REQUIRE(y[0] == 14); + REQUIRE(y[1] == 13); + REQUIRE(y[2] == 12); + REQUIRE(y[3] == 11); + REQUIRE(y[4] == 10); + } + + SECTION("self scalar multiplication") + { + SmallVector<int> x{5}; + x[0] = 0; + x[1] = 1; + x[2] = 2; + x[3] = 3; + x[4] = 4; + + x *= 2; + + REQUIRE(x[0] == 0); + REQUIRE(x[1] == 2); + REQUIRE(x[2] == 4); + REQUIRE(x[3] == 6); + REQUIRE(x[4] == 8); + } + + SECTION("left scalar multiplication") + { + SmallVector<int> x{5}; + x[0] = 0; + x[1] = 1; + x[2] = 2; + x[3] = 3; + x[4] = 4; + + const SmallVector<int> y = 2 * x; + + REQUIRE(y[0] == 0); + REQUIRE(y[1] == 2); + REQUIRE(y[2] == 4); + REQUIRE(y[3] == 6); + REQUIRE(y[4] == 8); + } + + SECTION("dot product") + { + SmallVector<int> x{5}; + x[0] = 0; + x[1] = 1; + x[2] = 2; + x[3] = 3; + x[4] = 4; + + SmallVector<int> y{5}; + y[0] = 7; + y[1] = 3; + y[2] = 4; + y[3] = 2; + y[4] = 8; + + const int s = dot(x, y); + REQUIRE(s == 49); + } + + SECTION("self scalar division") + { + SmallVector<int> x{5}; + x[0] = 2; + x[1] = 3; + x[2] = 5; + x[3] = 7; + x[4] = 8; + + x /= 2; + + REQUIRE(x[0] == 1); + REQUIRE(x[1] == 1); + REQUIRE(x[2] == 2); + REQUIRE(x[3] == 3); + REQUIRE(x[4] == 4); + } + + SECTION("self minus") + { + SmallVector<int> x{5}; + x[0] = 2; + x[1] = 3; + x[2] = 5; + x[3] = 7; + x[4] = 8; + + SmallVector<int> y{5}; + y[0] = 3; + y[1] = 8; + y[2] = 6; + y[3] = 2; + y[4] = 4; + + x -= y; + + REQUIRE(x[0] == -1); + REQUIRE(x[1] == -5); + REQUIRE(x[2] == -1); + REQUIRE(x[3] == 5); + REQUIRE(x[4] == 4); + } + + SECTION("self sum") + { + SmallVector<int> x{5}; + x[0] = 2; + x[1] = 3; + x[2] = 5; + x[3] = 7; + x[4] = 8; + + SmallVector<int> y{5}; + y[0] = 3; + y[1] = 8; + y[2] = 6; + y[3] = 2; + y[4] = 4; + + x += y; + + REQUIRE(x[0] == 5); + REQUIRE(x[1] == 11); + REQUIRE(x[2] == 11); + REQUIRE(x[3] == 9); + REQUIRE(x[4] == 12); + } + + SECTION("sum") + { + SmallVector<int> x{5}; + x[0] = 2; + x[1] = 3; + x[2] = 5; + x[3] = 7; + x[4] = 8; + + SmallVector<int> y{5}; + y[0] = 3; + y[1] = 8; + y[2] = 6; + y[3] = 2; + y[4] = 4; + + SmallVector z = x + y; + + REQUIRE(z[0] == 5); + REQUIRE(z[1] == 11); + REQUIRE(z[2] == 11); + REQUIRE(z[3] == 9); + REQUIRE(z[4] == 12); + } + + SECTION("difference") + { + SmallVector<int> x{5}; + x[0] = 2; + x[1] = 3; + x[2] = 5; + x[3] = 7; + x[4] = 8; + + SmallVector<int> y{5}; + y[0] = 3; + y[1] = 8; + y[2] = 6; + y[3] = 2; + y[4] = 4; + + SmallVector z = x - y; + + REQUIRE(z[0] == -1); + REQUIRE(z[1] == -5); + REQUIRE(z[2] == -1); + REQUIRE(z[3] == 5); + REQUIRE(z[4] == 4); + } + + SECTION("output") + { + SmallVector<int> x{5}; + x[0] = 3; + x[1] = 7; + x[2] = 2; + x[3] = 1; + x[4] = -4; + + std::ostringstream vector_ost; + vector_ost << x; + std::ostringstream ref_ost; + ref_ost << 0 << ':' << x[0]; + for (size_t i = 1; i < x.size(); ++i) { + ref_ost << ' ' << i << ':' << x[i]; + } + REQUIRE(vector_ost.str() == ref_ost.str()); + } + + SECTION("SmallVector from TinyVector") + { + TinyVector<5> tiny_vector{1, 3, 5, 7, 9}; + + SmallVector vector{tiny_vector}; + + REQUIRE(vector[0] == 1); + REQUIRE(vector[1] == 3); + REQUIRE(vector[2] == 5); + REQUIRE(vector[3] == 7); + REQUIRE(vector[4] == 9); + + SECTION("ensures deep copy") + { + tiny_vector = zero; + + REQUIRE(tiny_vector[0] == 0); + REQUIRE(tiny_vector[1] == 0); + REQUIRE(tiny_vector[2] == 0); + REQUIRE(tiny_vector[3] == 0); + REQUIRE(tiny_vector[4] == 0); + + REQUIRE(vector[0] == 1); + REQUIRE(vector[1] == 3); + REQUIRE(vector[2] == 5); + REQUIRE(vector[3] == 7); + REQUIRE(vector[4] == 9); + } + } + +#ifndef NDEBUG + + SECTION("output with signaling NaN") + { + SmallVector<double> x{5}; + x[0] = 3; + x[2] = 2; + + std::ostringstream vector_ost; + vector_ost << x; + std::ostringstream ref_ost; + ref_ost << 0 << ':' << 3 << ' '; + ref_ost << 1 << ":nan "; + ref_ost << 2 << ':' << 2 << ' '; + ref_ost << 3 << ":nan "; + ref_ost << 4 << ":nan"; + REQUIRE(vector_ost.str() == ref_ost.str()); + } + + SECTION("invalid dot product") + { + SmallVector<int> x{5}; + SmallVector<int> y{4}; + + REQUIRE_THROWS_WITH(dot(x, y), "cannot compute dot product: incompatible vector sizes"); + } + + SECTION("invalid substract") + { + SmallVector<int> x{5}; + SmallVector<int> y{4}; + + REQUIRE_THROWS_WITH(x -= y, "cannot substract vector: incompatible sizes"); + } + + SECTION("invalid add") + { + SmallVector<int> x{5}; + SmallVector<int> y{4}; + + REQUIRE_THROWS_WITH(x += y, "cannot add vector: incompatible sizes"); + } + + SECTION("invalid difference") + { + SmallVector<int> x{5}; + SmallVector<int> y{4}; + + REQUIRE_THROWS_WITH(x - y, "cannot compute vector difference: incompatible sizes"); + } + + SECTION("invalid sum") + { + SmallVector<int> x{5}; + SmallVector<int> y{4}; + + REQUIRE_THROWS_WITH(x + y, "cannot compute vector sum: incompatible sizes"); + } + +#endif // NDEBUG +} diff --git a/tests/test_TinyMatrix.cpp b/tests/test_TinyMatrix.cpp index c9b72d5de9ecb6b047ae8c16ad832f8b29b38157..81c187dc2f5c7663fb7491cfa5c68fc2fc2d7eb6 100644 --- a/tests/test_TinyMatrix.cpp +++ b/tests/test_TinyMatrix.cpp @@ -21,6 +21,16 @@ template class TinyMatrix<4, 4, double>; TEST_CASE("TinyMatrix", "[algebra]") { + REQUIRE(TinyMatrix<1, 1, int>::Dimension == 1); + REQUIRE(TinyMatrix<1, 1, int>::NumberOfRows == 1); + REQUIRE(TinyMatrix<1, 1, int>::NumberOfColumns == 1); + REQUIRE(TinyMatrix<2, 3, int>::Dimension == 6); + REQUIRE(TinyMatrix<2, 3, int>::NumberOfRows == 2); + REQUIRE(TinyMatrix<2, 3, int>::NumberOfColumns == 3); + REQUIRE(TinyMatrix<5, 4, int>::Dimension == 20); + REQUIRE(TinyMatrix<5, 4, int>::NumberOfRows == 5); + REQUIRE(TinyMatrix<5, 4, int>::NumberOfColumns == 4); + TinyMatrix<3, 4, int> A(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); REQUIRE(((A(0, 0) == 1) and (A(0, 1) == 2) and (A(0, 2) == 3) and (A(0, 3) == 4) and // (A(1, 0) == 5) and (A(1, 1) == 6) and (A(1, 2) == 7) and (A(1, 3) == 8) and // diff --git a/tests/test_TinyVector.cpp b/tests/test_TinyVector.cpp index cf77a22325ee19137367ab68eff4f2f5a817ef6a..0613e721cbce46386e6b1c86c3bc3e1123fdd1aa 100644 --- a/tests/test_TinyVector.cpp +++ b/tests/test_TinyVector.cpp @@ -15,6 +15,11 @@ template class TinyVector<3, int>; TEST_CASE("TinyVector", "[algebra]") { + REQUIRE(TinyVector<1, int>::Dimension == 1); + REQUIRE(TinyVector<2, int>::Dimension == 2); + REQUIRE(TinyVector<3, int>::Dimension == 3); + REQUIRE(TinyVector<4, int>::Dimension == 4); + TinyVector<3, int> v(1, 2, 3); REQUIRE(((v[0] == 1) and (v[1] == 2) and (v[2] == 3))); REQUIRE(-v == TinyVector<3, int>(-1, -2, -3)); diff --git a/tests/test_UnaryExpressionProcessor.cpp b/tests/test_UnaryExpressionProcessor.cpp index f63e45d1fa9912f1d007f83a2759bfc5b7316b42..2c3f6acb8eb7f8280e8717cdf495abd341dec5d0 100644 --- a/tests/test_UnaryExpressionProcessor.cpp +++ b/tests/test_UnaryExpressionProcessor.cpp @@ -1,6 +1,8 @@ #include <catch2/catch_test_macros.hpp> #include <catch2/matchers/catch_matchers_all.hpp> +#include <FixturesForBuiltinT.hpp> + #include <language/ast/ASTBuilder.hpp> #include <language/ast/ASTModulesImporter.hpp> #include <language/ast/ASTNodeDataTypeBuilder.hpp> @@ -8,6 +10,14 @@ #include <language/ast/ASTNodeExpressionBuilder.hpp> #include <language/ast/ASTNodeTypeCleaner.hpp> #include <language/ast/ASTSymbolTableBuilder.hpp> +#include <language/node_processor/UnaryExpressionProcessor.hpp> +#include <language/utils/ASTNodeDataTypeTraits.hpp> +#include <language/utils/AffectationProcessorBuilder.hpp> +#include <language/utils/BasicAffectationRegistrerFor.hpp> +#include <language/utils/DataHandler.hpp> +#include <language/utils/OperatorRepository.hpp> +#include <language/utils/TypeDescriptor.hpp> +#include <language/utils/UnaryOperatorProcessorBuilder.hpp> #include <utils/Demangle.hpp> #include <pegtl/string_input.hpp> @@ -66,37 +76,98 @@ TEST_CASE("UnaryExpressionProcessor", "[language]") CHECK_UNARY_EXPRESSION_RESULT(R"(let r:R, r = 2; r = -r;)", "r", -2.); } + SECTION("unary minus [builtin]") + { + std::string_view data = R"(let r:builtin_t, r = -bt;)"; + + TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; + auto ast = ASTBuilder::build(input); + + ASTModulesImporter{*ast}; + + BasicAffectationRegisterFor<EmbeddedData>{ASTNodeDataType::build<ASTNodeDataType::type_id_t>("builtin_t")}; + + OperatorRepository& repository = OperatorRepository::instance(); + + repository.addUnaryOperator<language::unary_minus>( + std::make_shared<UnaryOperatorProcessorBuilder<language::unary_minus, std::shared_ptr<const double>, + std::shared_ptr<const double>>>()); + + SymbolTable& symbol_table = *ast->m_symbol_table; + auto [i_symbol, success] = symbol_table.add(builtin_data_type.nameOfTypeId(), ast->begin()); + if (not success) { + throw UnexpectedError("cannot add '" + builtin_data_type.nameOfTypeId() + "' type for testing"); + } + + i_symbol->attributes().setDataType(ASTNodeDataType::build<ASTNodeDataType::type_name_id_t>()); + i_symbol->attributes().setIsInitialized(); + i_symbol->attributes().value() = symbol_table.typeEmbedderTable().size(); + symbol_table.typeEmbedderTable().add(std::make_shared<TypeDescriptor>(builtin_data_type.nameOfTypeId())); + + auto [i_symbol_bt, success_bt] = symbol_table.add("bt", ast->begin()); + if (not success_bt) { + throw UnexpectedError("cannot add 'bt' of type builtin_t for testing"); + } + i_symbol_bt->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>); + i_symbol_bt->attributes().setIsInitialized(); + i_symbol_bt->attributes().value() = + EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(3.2))); + + ASTNodeTypeCleaner<language::import_instruction>{*ast}; + + ASTSymbolTableBuilder{*ast}; + ASTNodeDataTypeBuilder{*ast}; + + ASTNodeDeclarationToAffectationConverter{*ast}; + ASTNodeTypeCleaner<language::var_declaration>{*ast}; + + ASTNodeExpressionBuilder{*ast}; + ExecutionPolicy exec_policy; + ast->execute(exec_policy); + + using namespace TAO_PEGTL_NAMESPACE; + position use_position{internal::iterator{"fixture"}, "fixture"}; + use_position.byte = 10000; + auto [symbol, found] = symbol_table.find("r", use_position); + + auto attributes = symbol->attributes(); + auto embedded_value = std::get<EmbeddedData>(attributes.value()); + + double value = *dynamic_cast<const DataHandler<const double>&>(embedded_value.get()).data_ptr(); + REQUIRE(value == double{-3.2}); + } + SECTION("unary not") { CHECK_UNARY_EXPRESSION_RESULT(R"(let b:B, b = false; b = not b;)", "b", true); CHECK_UNARY_EXPRESSION_RESULT(R"(let b:B, b = true; b = not b;)", "b", false); + } - SECTION("errors") + SECTION("errors") + { + SECTION("undefined not operator") { - SECTION("undefined not operator") - { - auto error_message = [](std::string type_name) { - return std::string{R"(undefined unary operator + auto error_message = [](std::string type_name) { + return std::string{R"(undefined unary operator note: unexpected operand type )"} + - type_name; - }; - - CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(let n:N, n = 0; not n;)", error_message("N")); - CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not 1;)", error_message("Z")); - CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not 1.3;)", error_message("R")); - CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not "foo";)", error_message("string")); - } - - SECTION("undefined unary minus operator") - { - auto error_message = [](std::string type_name) { - return std::string{R"(undefined unary operator + type_name; + }; + + CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(let n:N, n = 0; not n;)", error_message("N")); + CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not 1;)", error_message("Z")); + CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not 1.3;)", error_message("R")); + CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not "foo";)", error_message("string")); + } + + SECTION("undefined unary minus operator") + { + auto error_message = [](std::string type_name) { + return std::string{R"(undefined unary operator note: unexpected operand type )"} + - type_name; - }; + type_name; + }; - CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(-"foo";)", error_message("string")); - } + CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(-"foo";)", error_message("string")); } } } diff --git a/tests/test_UnaryOperatorMangler.cpp b/tests/test_UnaryOperatorMangler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c533e882a445818923e0b7bd3719051eb5db8867 --- /dev/null +++ b/tests/test_UnaryOperatorMangler.cpp @@ -0,0 +1,17 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <language/utils/UnaryOperatorMangler.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("UnaryOperatorMangler", "[language]") +{ + SECTION("unary operators") + { + const ASTNodeDataType Z = ASTNodeDataType::build<ASTNodeDataType::int_t>(); + + REQUIRE(unaryOperatorMangler<language::unary_minus>(Z) == "- Z"); + REQUIRE(unaryOperatorMangler<language::unary_not>(Z) == "not Z"); + } +}