From f38c571382d093ae1078c4fa9f9e731d0f5ff84b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com>
Date: Thu, 2 Sep 2021 12:03:41 +0200
Subject: [PATCH] Add tests for DiscreteFunctionVectorInterpoler

---
 .../DiscreteFunctionVectorInterpoler.cpp      |   5 +-
 tests/CMakeLists.txt                          |   1 +
 .../test_DiscreteFunctionVectorInterpoler.cpp | 414 ++++++++++++++++++
 3 files changed, 418 insertions(+), 2 deletions(-)
 create mode 100644 tests/test_DiscreteFunctionVectorInterpoler.cpp

diff --git a/src/scheme/DiscreteFunctionVectorInterpoler.cpp b/src/scheme/DiscreteFunctionVectorInterpoler.cpp
index 4827345a5..98d6513c9 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 b52aec0b9..379526adf 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -111,6 +111,7 @@ add_executable (mpi_unit_tests
   test_DiscreteFunctionInterpoler.cpp
   test_DiscreteFunctionP0.cpp
   test_DiscreteFunctionP0Vector.cpp
+  test_DiscreteFunctionVectorInterpoler.cpp
   test_InterpolateItemArray.cpp
   test_InterpolateItemValue.cpp
   test_ItemArray.cpp
diff --git a/tests/test_DiscreteFunctionVectorInterpoler.cpp b/tests/test_DiscreteFunctionVectorInterpoler.cpp
new file mode 100644
index 000000000..6a1a5a94b
--- /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);
+    }
+  }
+}
-- 
GitLab