diff --git a/.gitignore b/.gitignore index 3bb6d6cbcf2906a99d3302595285d50145395fe5..96214f7a18cd5cd618c43f91b02e940897be0c42 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ GPATH GRTAGS GTAGS /.clangd/ +/.cache/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 45c69b7a0d8744a1fde62b53170e7f3f8ef303c2..114ceecc5d49685a513708dced3dcffea83bff29 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -528,7 +528,9 @@ target_link_libraries( PugsLanguageAlgorithms PugsMesh PugsAlgebra + PugsScheme PugsUtils + PugsOutput PugsLanguageUtils kokkos ${PETSC_LIBRARIES} @@ -551,7 +553,8 @@ if(${CMAKE_VERSION} VERSION_LESS "3.13.0") LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) else() - install(TARGETS pugs PugsMesh PugsUtils PugsLanguage + install(TARGETS pugs PugsAlgebra PugsUtils PugsLanguage PugsLanguageAST PugsLanguageModules PugsLanguageAlgorithms PugsLanguageUtils PugsMesh PugsScheme PugsOutput + RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1b21f79a7545c442be5448792132acbdf840947a..16904a7be0cddc9aa54206638ab4b5c759cd0de8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,5 +15,8 @@ add_subdirectory(algebra) # Pugs mesh add_subdirectory(mesh) +# Pugs mesh +add_subdirectory(scheme) + # Pugs output -#add_subdirectory(output) +add_subdirectory(output) diff --git a/src/language/algorithms/AcousticSolverAlgorithm.cpp b/src/language/algorithms/AcousticSolverAlgorithm.cpp index 9811c90f0b0f2c52d5f72369efea4a4398f007e3..edce9e025f0feb4cd39acaadcb537451c9251065 100644 --- a/src/language/algorithms/AcousticSolverAlgorithm.cpp +++ b/src/language/algorithms/AcousticSolverAlgorithm.cpp @@ -1,12 +1,13 @@ #include <language/algorithms/AcousticSolverAlgorithm.hpp> #include <language/utils/InterpolateItemValue.hpp> -#include <output/VTKWriter.hpp> #include <scheme/AcousticSolver.hpp> #include <scheme/DirichletBoundaryConditionDescriptor.hpp> #include <scheme/IBoundaryDescriptor.hpp> #include <scheme/SymmetryBoundaryConditionDescriptor.hpp> +#include <iomanip> + template <size_t Dimension> AcousticSolverAlgorithm<Dimension>::AcousticSolverAlgorithm( const AcousticSolverType& solver_type, @@ -176,18 +177,9 @@ AcousticSolverAlgorithm<Dimension>::AcousticSolverAlgorithm( mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { inv_mj[j] = 1. / mj[j]; }); } - VTKWriter vtk_writer("mesh_" + std::to_string(Dimension), 0.01); - while ((t < tmax) and (iteration < itermax)) { MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); - vtk_writer.write(mesh, - {NamedItemValue{"density", rhoj}, NamedItemValue{"velocity", unknowns.uj()}, - NamedItemValue{"coords", mesh->xr()}, NamedItemValue{"xj", mesh_data.xj()}, - NamedItemValue{"cell_owner", mesh->connectivity().cellOwner()}, - NamedItemValue{"node_owner", mesh->connectivity().nodeOwner()}}, - t); - double dt = 0.4 * acoustic_solver.acoustic_dt(mesh_data.Vj(), cj); if (t + dt > tmax) { dt = tmax - t; @@ -208,16 +200,6 @@ AcousticSolverAlgorithm<Dimension>::AcousticSolverAlgorithm( std::cout << rang::style::bold << "Final time=" << rang::fgB::green << t << rang::style::reset << " reached after " << rang::fgB::cyan << iteration << rang::style::reset << rang::style::bold << " iterations" << rang::style::reset << '\n'; - { - MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); - - vtk_writer.write(mesh, - {NamedItemValue{"density", rhoj}, NamedItemValue{"velocity", unknowns.uj()}, - NamedItemValue{"coords", mesh->xr()}, NamedItemValue{"xj", mesh_data.xj()}, - NamedItemValue{"cell_owner", mesh->connectivity().cellOwner()}, - NamedItemValue{"node_owner", mesh->connectivity().nodeOwner()}}, - t, true); // forces last output - } } template AcousticSolverAlgorithm<1>::AcousticSolverAlgorithm( diff --git a/src/language/ast/ASTNodeAffectationExpressionBuilder.cpp b/src/language/ast/ASTNodeAffectationExpressionBuilder.cpp index 884711c081ded349d32663f80180c06c4911bfba..a97c13443bd628a2226de54623c673d464916a0c 100644 --- a/src/language/ast/ASTNodeAffectationExpressionBuilder.cpp +++ b/src/language/ast/ASTNodeAffectationExpressionBuilder.cpp @@ -9,24 +9,24 @@ #include <language/utils/ParseError.hpp> #include <utils/Exceptions.hpp> -ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode& n) +ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode& node) { - const ASTNodeDataType& target_data_type = n.children[0]->m_data_type; - const ASTNodeDataType& source_data_type = n.children[1]->m_data_type; + const ASTNodeDataType& target_data_type = node.children[0]->m_data_type; + const ASTNodeDataType& source_data_type = node.children[1]->m_data_type; const std::string affectation_name = [&] { - if (n.is_type<language::eq_op>()) { + if (node.is_type<language::eq_op>()) { return affectationMangler<language::eq_op>(target_data_type, source_data_type); - } else if (n.is_type<language::multiplyeq_op>()) { + } else if (node.is_type<language::multiplyeq_op>()) { return affectationMangler<language::multiplyeq_op>(target_data_type, source_data_type); - } else if (n.is_type<language::divideeq_op>()) { + } else if (node.is_type<language::divideeq_op>()) { return affectationMangler<language::divideeq_op>(target_data_type, source_data_type); - } else if (n.is_type<language::pluseq_op>()) { + } else if (node.is_type<language::pluseq_op>()) { return affectationMangler<language::pluseq_op>(target_data_type, source_data_type); - } else if (n.is_type<language::minuseq_op>()) { + } else if (node.is_type<language::minuseq_op>()) { return affectationMangler<language::minuseq_op>(target_data_type, source_data_type); } else { - throw ParseError("unexpected error: undefined affectation operator", std::vector{n.begin()}); + throw ParseError("unexpected error: undefined affectation operator", std::vector{node.begin()}); } }(); @@ -34,12 +34,15 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode OperatorRepository::instance().getAffectationProcessorBuilder(affectation_name); if (optional_processor_builder.has_value()) { - n.m_node_processor = optional_processor_builder.value()->getNodeProcessor(n); + ASTNode& lhs_node = *node.children[0]; + ASTNode& rhs_node = *node.children[1]; + + node.m_node_processor = optional_processor_builder.value()->getNodeProcessor(lhs_node, rhs_node); } else { std::ostringstream error_message; error_message << "undefined affectation type: "; error_message << rang::fgB::red << affectation_name << rang::fg::reset; - throw ParseError(error_message.str(), std::vector{n.children[0]->begin()}); + throw ParseError(error_message.str(), std::vector{node.begin()}); } } diff --git a/src/language/ast/ASTNodeDataTypeBuilder.cpp b/src/language/ast/ASTNodeDataTypeBuilder.cpp index 018bc94f74160bfcd680df08ac9b2a08c54ba492..b5f65437ce1b45b4a403aaf079317de8f04c0de5 100644 --- a/src/language/ast/ASTNodeDataTypeBuilder.cpp +++ b/src/language/ast/ASTNodeDataTypeBuilder.cpp @@ -333,6 +333,22 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const value_type = getMatrixDataType(image_node); } else if (image_node.is_type<language::string_type>()) { value_type = ASTNodeDataType::build<ASTNodeDataType::string_t>(); + } else if (image_node.is_type<language::type_name_id>()) { + const std::string& type_name_id = image_node.string(); + + auto& symbol_table = *image_node.m_symbol_table; + + const auto [i_type_symbol, found] = symbol_table.find(type_name_id, image_node.begin()); + if (not found) { + throw ParseError("undefined type identifier", std::vector{image_node.begin()}); + } else if (i_type_symbol->attributes().dataType() != ASTNodeDataType::type_name_id_t) { + std::ostringstream os; + os << "invalid type identifier, '" << type_name_id << "' was previously defined as a '" + << dataTypeName(i_type_symbol->attributes().dataType()) << '\''; + throw ParseError(os.str(), std::vector{image_node.begin()}); + } + + value_type = ASTNodeDataType::build<ASTNodeDataType::type_id_t>(type_name_id); } // LCOV_EXCL_START @@ -348,7 +364,6 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const } n.m_data_type = ASTNodeDataType::build<ASTNodeDataType::typename_t>( ASTNodeDataType::build<ASTNodeDataType::list_t>(sub_data_type_list)); - } else if (n.is_type<language::for_post>() or n.is_type<language::for_init>() or n.is_type<language::for_statement_block>()) { n.m_data_type = ASTNodeDataType::build<ASTNodeDataType::void_t>(); @@ -361,13 +376,11 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const const ASTNode& test_node = *n.children[0]; ASTNodeNaturalConversionChecker{test_node, ASTNodeDataType::build<ASTNodeDataType::bool_t>()}; - } else if (n.is_type<language::do_while_statement>()) { n.m_data_type = ASTNodeDataType::build<ASTNodeDataType::void_t>(); const ASTNode& test_node = *n.children[1]; ASTNodeNaturalConversionChecker{test_node, ASTNodeDataType::build<ASTNodeDataType::bool_t>()}; - } else if (n.is_type<language::unary_not>() or n.is_type<language::unary_minus>()) { auto& operator_repository = OperatorRepository::instance(); @@ -394,7 +407,6 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const << rang::style::reset; throw ParseError(message.str(), n.begin()); } - } else if (n.is_type<language::unary_plusplus>() or n.is_type<language::unary_minusminus>() or n.is_type<language::post_plusplus>() or n.is_type<language::post_minusminus>()) { auto& operator_repository = OperatorRepository::instance(); @@ -428,7 +440,6 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const << rang::style::reset; throw ParseError(message.str(), n.begin()); } - } else if (n.is_type<language::plus_op>() or n.is_type<language::minus_op>() or n.is_type<language::multiply_op>() or n.is_type<language::divide_op>() or n.is_type<language::lesser_op>() or n.is_type<language::lesser_or_eq_op>() or @@ -496,7 +507,6 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const << "note: incompatible operand types " << dataTypeName(type_0) << " and " << dataTypeName(type_1); throw ParseError(message.str(), n.begin()); } - } else if (n.is_type<language::function_evaluation>()) { if (n.children[0]->m_data_type == ASTNodeDataType::function_t) { const std::string& function_name = n.children[0]->string(); diff --git a/src/language/ast/ASTNodeDataTypeFlattener.cpp b/src/language/ast/ASTNodeDataTypeFlattener.cpp index 51d055836393b1ee680b7d983756bee39020d992..6a06c72bb66926af546ff0a130315eacc8b54c9a 100644 --- a/src/language/ast/ASTNodeDataTypeFlattener.cpp +++ b/src/language/ast/ASTNodeDataTypeFlattener.cpp @@ -1,6 +1,7 @@ #include <language/ast/ASTNodeDataTypeFlattener.hpp> #include <language/PEGGrammar.hpp> +#include <language/utils/BuiltinFunctionEmbedder.hpp> #include <language/utils/FunctionTable.hpp> #include <language/utils/SymbolTable.hpp> @@ -33,9 +34,20 @@ ASTNodeDataTypeFlattener::ASTNodeDataTypeFlattener(ASTNode& node, FlattenedDataT } break; } + case ASTNodeDataType::builtin_function_t: { + uint64_t builtin_function_id = std::get<uint64_t>(i_function_symbol->attributes().value()); + auto builtin_function_embedder = node.m_symbol_table->builtinFunctionEmbedderTable()[builtin_function_id]; + + const auto& compound_data_type = builtin_function_embedder->getReturnDataType(); + for (auto data_type : compound_data_type.contentTypeList()) { + flattened_datatype_list.push_back({*data_type, node}); + } + + break; + } // LCOV_EXCL_START default: { - throw ParseError("unexpected function type", node.begin()); + throw ParseError{"unexpected function type", node.begin()}; } // LCOV_EXCL_STOP } diff --git a/src/language/ast/ASTNodeListAffectationExpressionBuilder.cpp b/src/language/ast/ASTNodeListAffectationExpressionBuilder.cpp index b135bbcd3f67127c23c044880d96ad28ee6527c5..6b53db72a1a257f74196a56f6e2c83694531fd5f 100644 --- a/src/language/ast/ASTNodeListAffectationExpressionBuilder.cpp +++ b/src/language/ast/ASTNodeListAffectationExpressionBuilder.cpp @@ -6,6 +6,9 @@ #include <language/utils/ASTNodeNaturalConversionChecker.hpp> #include <language/utils/ParseError.hpp> +#include <language/utils/AffectationMangler.hpp> +#include <language/utils/OperatorRepository.hpp> + template <typename OperatorT> void ASTNodeListAffectationExpressionBuilder::_buildAffectationProcessor( @@ -115,6 +118,25 @@ ASTNodeListAffectationExpressionBuilder::_buildAffectationProcessor( } }; + auto add_affectation_processor_for_embedded_data = [&](const ASTNodeSubDataType& node_sub_data_type) { + if constexpr (std::is_same_v<OperatorT, language::eq_op>) { + switch (node_sub_data_type.m_data_type) { + case ASTNodeDataType::type_id_t: { + list_affectation_processor->template add<EmbeddedData, EmbeddedData>(value_node); + break; + } + // LCOV_EXCL_START + default: { + throw ParseError("unexpected error:invalid operand type for embedded data affectation", + std::vector{node_sub_data_type.m_parent_node.begin()}); + } + // LCOV_EXCL_STOP + } + } else { + throw ParseError("unexpected error: undefined operator type for string affectation", std::vector{m_node.begin()}); + } + }; + auto add_affectation_processor_for_string_data = [&](const ASTNodeSubDataType& node_sub_data_type) { if constexpr (std::is_same_v<OperatorT, language::eq_op>) { switch (node_sub_data_type.m_data_type) { @@ -192,6 +214,10 @@ ASTNodeListAffectationExpressionBuilder::_buildAffectationProcessor( add_affectation_processor_for_data(double{}, node_sub_data_type); break; } + case ASTNodeDataType::type_id_t: { + add_affectation_processor_for_embedded_data(node_sub_data_type); + break; + } case ASTNodeDataType::vector_t: { switch (value_type.dimension()) { case 1: { @@ -251,7 +277,21 @@ ASTNodeListAffectationExpressionBuilder::_buildAffectationProcessor( ASTNodeNaturalConversionChecker<AllowRToR1Conversion>(rhs_node_sub_data_type, value_node.m_data_type); - add_affectation_processor_for_value(value_node.m_data_type, rhs_node_sub_data_type); + const std::string affectation_name = + affectationMangler<language::eq_op>(value_node.m_data_type, rhs_node_sub_data_type.m_data_type); + + const auto& optional_processor_builder = + OperatorRepository::instance().getAffectationProcessorBuilder(affectation_name); + + if (optional_processor_builder.has_value()) { + add_affectation_processor_for_value(value_node.m_data_type, rhs_node_sub_data_type); + } else { + std::ostringstream error_message; + error_message << "undefined affectation type: "; + error_message << rang::fgB::red << affectation_name << rang::fg::reset; + + throw ParseError(error_message.str(), std::vector{m_node.children[0]->begin()}); + } } template <typename OperatorT> diff --git a/src/language/modules/CMakeLists.txt b/src/language/modules/CMakeLists.txt index 020727478c5e64bac9ec0a780a0cfb80541fb28a..dfc777168d454337ffbd38ef084f37f50924b117 100644 --- a/src/language/modules/CMakeLists.txt +++ b/src/language/modules/CMakeLists.txt @@ -9,7 +9,7 @@ add_library(PugsLanguageModules ModuleRepository.cpp SchemeModule.cpp UtilsModule.cpp - VTKModule.cpp + WriterModule.cpp ) diff --git a/src/language/modules/ModuleRepository.cpp b/src/language/modules/ModuleRepository.cpp index 65bfcafd1f1c85f077a97f6e932fa97da0079537..44a437d04dc177cbfc6274420999c94fafaa22c9 100644 --- a/src/language/modules/ModuleRepository.cpp +++ b/src/language/modules/ModuleRepository.cpp @@ -7,7 +7,7 @@ #include <language/modules/MeshModule.hpp> #include <language/modules/SchemeModule.hpp> #include <language/modules/UtilsModule.hpp> -#include <language/modules/VTKModule.hpp> +#include <language/modules/WriterModule.hpp> #include <language/utils/BasicAffectationRegistrerFor.hpp> #include <language/utils/BuiltinFunctionEmbedder.hpp> #include <language/utils/ParseError.hpp> @@ -32,7 +32,7 @@ ModuleRepository::ModuleRepository() this->_subscribe(std::make_unique<MeshModule>()); this->_subscribe(std::make_unique<SchemeModule>()); this->_subscribe(std::make_unique<UtilsModule>()); - this->_subscribe(std::make_unique<VTKModule>()); + this->_subscribe(std::make_unique<WriterModule>()); } template <typename NameEmbedderMapT, typename EmbedderTableT> diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp index 14f798eb6bac43b77fd98367a009fa19d5dcfcbd..8277ff9fc0ed6c2035520966bcdf304b794f4aeb 100644 --- a/src/language/modules/SchemeModule.cpp +++ b/src/language/modules/SchemeModule.cpp @@ -6,10 +6,14 @@ #include <language/utils/TypeDescriptor.hpp> #include <mesh/Mesh.hpp> #include <scheme/DirichletBoundaryConditionDescriptor.hpp> +#include <scheme/DiscreteFunctionDescriptorP0.hpp> +#include <scheme/DiscreteFunctionInterpoler.hpp> #include <scheme/FourierBoundaryConditionDescriptor.hpp> #include <scheme/FreeBoundaryConditionDescriptor.hpp> #include <scheme/IBoundaryConditionDescriptor.hpp> #include <scheme/IBoundaryDescriptor.hpp> +#include <scheme/IDiscreteFunction.hpp> +#include <scheme/IDiscreteFunctionDescriptor.hpp> #include <scheme/NamedBoundaryDescriptor.hpp> #include <scheme/NeumannBoundaryConditionDescriptor.hpp> #include <scheme/NumberedBoundaryDescriptor.hpp> @@ -19,9 +23,33 @@ SchemeModule::SchemeModule() { + this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const IDiscreteFunction>>); + this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const IDiscreteFunctionDescriptor>>); + this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const IBoundaryDescriptor>>); this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const IBoundaryConditionDescriptor>>); + this->_addBuiltinFunction("P0", std::make_shared< + BuiltinFunctionEmbedder<std::shared_ptr<const IDiscreteFunctionDescriptor>()>>( + []() -> std::shared_ptr<const IDiscreteFunctionDescriptor> { + return std::make_shared<DiscreteFunctionDescriptorP0>(); + } + + )); + + this->_addBuiltinFunction( + "interpolate", + std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr< + const IDiscreteFunction>(std::shared_ptr<const IMesh>, std::shared_ptr<const IDiscreteFunctionDescriptor>, + const FunctionSymbolId&)>>( + [](std::shared_ptr<const IMesh> mesh, + std::shared_ptr<const IDiscreteFunctionDescriptor> discrete_function_descriptor, + const FunctionSymbolId& function_id) -> std::shared_ptr<const IDiscreteFunction> { + return DiscreteFunctionInterpoler{mesh, discrete_function_descriptor, function_id}.interpolate(); + } + + )); + this->_addBuiltinFunction("boundaryName", std::make_shared< BuiltinFunctionEmbedder<std::shared_ptr<const IBoundaryDescriptor>(const std::string&)>>( diff --git a/src/language/modules/SchemeModule.hpp b/src/language/modules/SchemeModule.hpp index aa0f1322618956da9b3a4bc095c87f95ebf719fa..c49d1d4e8bc7fdf19430dac36d8bde4709eeda1f 100644 --- a/src/language/modules/SchemeModule.hpp +++ b/src/language/modules/SchemeModule.hpp @@ -20,6 +20,16 @@ template <> inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const BasisType>> = ASTNodeDataType::build<ASTNodeDataType::type_id_t>("basis_type"); +class IDiscreteFunction; +template <> +inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const IDiscreteFunction>> = + ASTNodeDataType::build<ASTNodeDataType::type_id_t>("Vh"); + +class IDiscreteFunctionDescriptor; +template <> +inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const IDiscreteFunctionDescriptor>> = + ASTNodeDataType::build<ASTNodeDataType::type_id_t>("Vh_type"); + class SchemeModule : public BuiltinModule { public: diff --git a/src/language/modules/UtilsModule.cpp b/src/language/modules/UtilsModule.cpp index 8b2271158c1548d05b39c379782abb3cc4754d66..b74ddc54eaab21ebf44dd53dbb585f4d0111c3fe 100644 --- a/src/language/modules/UtilsModule.cpp +++ b/src/language/modules/UtilsModule.cpp @@ -52,9 +52,7 @@ UtilsModule::UtilsModule() std::make_shared<BuiltinFunctionEmbedder<std::string(const FunctionSymbolId&)>>( [](const FunctionSymbolId& function_symbol_id) -> std::string { - auto& function_table = function_symbol_id.symbolTable().functionTable(); - - const auto& function_descriptor = function_table[function_symbol_id.id()]; + const auto& function_descriptor = function_symbol_id.descriptor(); std::ostringstream os; os << function_descriptor.name() << ": domain mapping\n"; diff --git a/src/language/modules/VTKModule.cpp b/src/language/modules/VTKModule.cpp deleted file mode 100644 index ce84141ee866f7ac6fdbb8448db78d0d1b64de88..0000000000000000000000000000000000000000 --- a/src/language/modules/VTKModule.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include <language/modules/VTKModule.hpp> - -#include <language/utils/BuiltinFunctionEmbedder.hpp> -#include <language/utils/TypeDescriptor.hpp> -#include <mesh/Connectivity.hpp> -#include <mesh/GmshReader.hpp> -#include <mesh/Mesh.hpp> -#include <output/VTKWriter.hpp> - -VTKModule::VTKModule() -{ - this->_addBuiltinFunction("writeVTK", - std::make_shared< - BuiltinFunctionEmbedder<void(std::shared_ptr<const IMesh>, const std::string&)>>( - - [](std::shared_ptr<const IMesh> p_mesh, const std::string& filename) -> void { - VTKWriter writer(filename, 0.1); - - static double time = 0; - - switch (p_mesh->dimension()) { - case 1: { - using MeshType = Mesh<Connectivity<1>>; - const std::shared_ptr<const MeshType> mesh = - std::dynamic_pointer_cast<const MeshType>(p_mesh); - - writer.write(mesh, - OutputNamedItemValueSet{ - NamedItemValue{"cell_number", mesh->connectivity().cellNumber()}}, - time, true); - break; - } - case 2: { - using MeshType = Mesh<Connectivity<2>>; - const std::shared_ptr<const MeshType> mesh = - std::dynamic_pointer_cast<const MeshType>(p_mesh); - - writer.write(mesh, - OutputNamedItemValueSet{ - NamedItemValue{"cell_number", mesh->connectivity().cellNumber()}}, - time, true); - break; - } - case 3: { - using MeshType = Mesh<Connectivity<3>>; - const std::shared_ptr<const MeshType> mesh = - std::dynamic_pointer_cast<const MeshType>(p_mesh); - - writer.write(mesh, - OutputNamedItemValueSet{ - NamedItemValue{"cell_number", mesh->connectivity().cellNumber()}}, - time, true); - break; - } - } - - time++; - } - - )); -} diff --git a/src/language/modules/VTKModule.hpp b/src/language/modules/VTKModule.hpp deleted file mode 100644 index 1578fad811b7dae1181053317e891dbb865ad035..0000000000000000000000000000000000000000 --- a/src/language/modules/VTKModule.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef VTK_MODULE_HPP -#define VTK_MODULE_HPP - -#include <language/modules/BuiltinModule.hpp> -#include <utils/PugsMacros.hpp> - -class VTKModule : public BuiltinModule -{ - public: - std::string_view - name() const final - { - return "vtk"; - } - - VTKModule(); - - ~VTKModule() = default; -}; - -#endif // VTK_MODULE_HPP diff --git a/src/language/modules/WriterModule.cpp b/src/language/modules/WriterModule.cpp new file mode 100644 index 0000000000000000000000000000000000000000..611952939906e0afce5ea4682428ec1eefd36eaa --- /dev/null +++ b/src/language/modules/WriterModule.cpp @@ -0,0 +1,91 @@ +#include <language/modules/WriterModule.hpp> + +#include <language/utils/BuiltinFunctionEmbedder.hpp> +#include <language/utils/TypeDescriptor.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/GmshReader.hpp> +#include <mesh/Mesh.hpp> +#include <output/GnuplotWriter.hpp> +#include <output/IWriter.hpp> +#include <output/NamedDiscreteFunction.hpp> +#include <output/VTKWriter.hpp> +#include <scheme/DiscreteFunctionP0.hpp> +#include <scheme/IDiscreteFunction.hpp> +#include <scheme/IDiscreteFunctionDescriptor.hpp> + +WriterModule::WriterModule() +{ + this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const NamedDiscreteFunction>>); + + this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const IWriter>>); + + this->_addBuiltinFunction("vtk_writer", + std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<const IWriter>(const std::string&, + const double&)>>( + + [](const std::string& filename, const double& period) { + return std::make_shared<VTKWriter>(filename, period); + } + + )); + + this->_addBuiltinFunction("gnuplot_writer", + std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<const IWriter>(const std::string&, + const double&)>>( + + [](const std::string& filename, const double& period) { + return std::make_shared<GnuplotWriter>(filename, period); + } + + )); + + this->_addBuiltinFunction("name_output", + std::make_shared<BuiltinFunctionEmbedder< + std::shared_ptr<const NamedDiscreteFunction>(std::shared_ptr<const IDiscreteFunction>, + const std::string&)>>( + + [](std::shared_ptr<const IDiscreteFunction> discrete_function, + const std::string& name) -> std::shared_ptr<const NamedDiscreteFunction> { + return std::make_shared<const NamedDiscreteFunction>(discrete_function, name); + } + + )); + + this->_addBuiltinFunction("write_mesh", + std::make_shared<BuiltinFunctionEmbedder<void(std::shared_ptr<const IWriter>, + std::shared_ptr<const IMesh>)>>( + + [](std::shared_ptr<const IWriter> writer, std::shared_ptr<const IMesh> p_mesh) -> void { + writer->writeMesh(p_mesh); + } + + )); + + this->_addBuiltinFunction("write", + std::make_shared<BuiltinFunctionEmbedder< + void(std::shared_ptr<const IWriter>, + const std::vector<std::shared_ptr<const NamedDiscreteFunction>>&, const double&)>>( + + [](std::shared_ptr<const IWriter> writer, + const std::vector<std::shared_ptr<const NamedDiscreteFunction>>& + named_discrete_function_list, + const double& time) -> void { + writer->writeIfNeeded(named_discrete_function_list, time); + } + + )); + + this->_addBuiltinFunction("force_write", + std::make_shared<BuiltinFunctionEmbedder< + void(std::shared_ptr<const IWriter>, + const std::vector<std::shared_ptr<const NamedDiscreteFunction>>&, const double&)>>( + + [](std::shared_ptr<const IWriter> writer, + const std::vector<std::shared_ptr<const NamedDiscreteFunction>>& + named_discrete_function_list, + const double& time) -> void { + writer->writeForced(named_discrete_function_list, time); + } + + )); +} diff --git a/src/language/modules/WriterModule.hpp b/src/language/modules/WriterModule.hpp new file mode 100644 index 0000000000000000000000000000000000000000..14a961d7a812b34aac1c5010ddda674749fdbfb8 --- /dev/null +++ b/src/language/modules/WriterModule.hpp @@ -0,0 +1,37 @@ +#ifndef WRITER_MODULE_HPP +#define WRITER_MODULE_HPP + +#include <language/modules/BuiltinModule.hpp> +#include <language/utils/ASTNodeDataTypeTraits.hpp> +#include <utils/PugsMacros.hpp> + +class OutputNamedItemValueSet; +class NamedDiscreteFunction; +class IDiscreteFunction; + +#include <string> + +template <> +inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const NamedDiscreteFunction>> = + ASTNodeDataType::build<ASTNodeDataType::type_id_t>("output"); + +class IWriter; +template <> +inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const IWriter>> = + ASTNodeDataType::build<ASTNodeDataType::type_id_t>("writer"); + +class WriterModule : public BuiltinModule +{ + public: + std::string_view + name() const final + { + return "writer"; + } + + WriterModule(); + + ~WriterModule() = default; +}; + +#endif // WRITER_MODULE_HPP diff --git a/src/language/node_processor/AffectationProcessor.hpp b/src/language/node_processor/AffectationProcessor.hpp index 959578619b023bc28236b1ae52141b018aad8c69..07a52de44e09cba57cf5b60aba65e844ff7faa94 100644 --- a/src/language/node_processor/AffectationProcessor.hpp +++ b/src/language/node_processor/AffectationProcessor.hpp @@ -121,7 +121,7 @@ class AffectationExecutor final : public IAffectationExecutor } } else { if constexpr (std::is_same_v<OperatorT, language::eq_op>) { - if constexpr (std::is_convertible_v<ValueT, DataT>) { + if constexpr (std::is_convertible_v<DataT, ValueT>) { m_lhs = std::get<DataT>(rhs); } else if constexpr (std::is_same_v<DataT, AggregateDataVariant>) { const AggregateDataVariant& v = std::get<AggregateDataVariant>(rhs); @@ -222,7 +222,7 @@ class MatrixComponentAffectationExecutor final : public IAffectationExecutor }()}; public: - MatrixComponentAffectationExecutor(ASTNode& node, + MatrixComponentAffectationExecutor(ASTNode& lhs_node, ArrayT& lhs_array, ASTNode& index0_expression, ASTNode& index1_expression) @@ -230,7 +230,7 @@ class MatrixComponentAffectationExecutor final : public IAffectationExecutor { // LCOV_EXCL_START if constexpr (not m_is_defined) { - throw ParseError("unexpected error: invalid operands to affectation expression", std::vector{node.begin()}); + throw ParseError("unexpected error: invalid operands to affectation expression", std::vector{lhs_node.begin()}); } // LCOV_EXCL_STOP } @@ -275,11 +275,8 @@ class MatrixComponentAffectationExecutor final : public IAffectationExecutor } } else { if constexpr (std::is_same_v<OperatorT, language::eq_op>) { - if constexpr (std::is_same_v<ValueT, DataT>) { - m_lhs_array(index0_value, index1_value) = std::get<DataT>(rhs); - } else { - m_lhs_array(index0_value, index1_value) = static_cast<ValueT>(std::get<DataT>(rhs)); - } + static_assert(std::is_convertible_v<DataT, ValueT>, "unexpected types"); + m_lhs_array(index0_value, index1_value) = static_cast<ValueT>(std::get<DataT>(rhs)); } else { AffOp<OperatorT>().eval(m_lhs_array(index0_value, index1_value), std::get<DataT>(rhs)); } @@ -354,11 +351,8 @@ class VectorComponentAffectationExecutor final : public IAffectationExecutor } } else { if constexpr (std::is_same_v<OperatorT, language::eq_op>) { - if constexpr (std::is_same_v<ValueT, DataT>) { - m_lhs_array[index_value] = std::get<DataT>(rhs); - } else { - m_lhs_array[index_value] = static_cast<ValueT>(std::get<DataT>(rhs)); - } + static_assert(std::is_convertible_v<DataT, ValueT>, "incompatible data and value types"); + m_lhs_array[index_value] = static_cast<ValueT>(std::get<DataT>(rhs)); } else { AffOp<OperatorT>().eval(m_lhs_array[index_value], std::get<DataT>(rhs)); } @@ -371,24 +365,16 @@ template <typename OperatorT, typename ValueT, typename DataT> class AffectationProcessor final : public INodeProcessor { private: - ASTNode& m_node; + ASTNode& m_rhs_node; std::unique_ptr<IAffectationExecutor> m_affectation_executor; - public: - DataVariant - execute(ExecutionPolicy& exec_policy) + std::unique_ptr<IAffectationExecutor> + _buildAffectationExecutor(ASTNode& lhs_node) { - m_affectation_executor->affect(exec_policy, m_node.children[1]->execute(exec_policy)); - - return {}; - } - - AffectationProcessor(ASTNode& node) : m_node{node} - { - if (node.children[0]->is_type<language::name>()) { - const std::string& symbol = m_node.children[0]->string(); - auto [i_symbol, found] = m_node.m_symbol_table->find(symbol, m_node.children[0]->begin()); + if (lhs_node.is_type<language::name>()) { + const std::string& symbol = lhs_node.string(); + auto [i_symbol, found] = lhs_node.m_symbol_table->find(symbol, lhs_node.begin()); Assert(found); DataVariant& value = i_symbol->attributes().value(); @@ -397,23 +383,21 @@ class AffectationProcessor final : public INodeProcessor } using AffectationExecutorT = AffectationExecutor<OperatorT, ValueT, DataT>; - m_affectation_executor = std::make_unique<AffectationExecutorT>(m_node, std::get<ValueT>(value)); - } else if (node.children[0]->is_type<language::subscript_expression>()) { - auto& array_subscript_expression = *node.children[0]; - - auto& array_expression = *array_subscript_expression.children[0]; + return std::make_unique<AffectationExecutorT>(lhs_node, std::get<ValueT>(value)); + } else if (lhs_node.is_type<language::subscript_expression>()) { + auto& array_expression = *lhs_node.children[0]; Assert(array_expression.is_type<language::name>()); const std::string& symbol = array_expression.string(); - auto [i_symbol, found] = m_node.m_symbol_table->find(symbol, array_subscript_expression.begin()); + auto [i_symbol, found] = lhs_node.m_symbol_table->find(symbol, lhs_node.begin()); Assert(found); DataVariant& value = i_symbol->attributes().value(); if (array_expression.m_data_type == ASTNodeDataType::vector_t) { - Assert(array_subscript_expression.children.size() == 2); + Assert(lhs_node.children.size() == 2); - auto& index_expression = *array_subscript_expression.children[1]; + auto& index_expression = *lhs_node.children[1]; switch (array_expression.m_data_type.dimension()) { case 1: { @@ -422,9 +406,8 @@ class AffectationProcessor final : public INodeProcessor value = ArrayTypeT{}; } using AffectationExecutorT = VectorComponentAffectationExecutor<OperatorT, ArrayTypeT, ValueT, DataT>; - m_affectation_executor = - std::make_unique<AffectationExecutorT>(node, std::get<ArrayTypeT>(value), index_expression); - break; + + return std::make_unique<AffectationExecutorT>(lhs_node, std::get<ArrayTypeT>(value), index_expression); } case 2: { using ArrayTypeT = TinyVector<2>; @@ -432,9 +415,8 @@ class AffectationProcessor final : public INodeProcessor value = ArrayTypeT{}; } using AffectationExecutorT = VectorComponentAffectationExecutor<OperatorT, ArrayTypeT, ValueT, DataT>; - m_affectation_executor = - std::make_unique<AffectationExecutorT>(node, std::get<ArrayTypeT>(value), index_expression); - break; + + return std::make_unique<AffectationExecutorT>(lhs_node, std::get<ArrayTypeT>(value), index_expression); } case 3: { using ArrayTypeT = TinyVector<3>; @@ -442,23 +424,21 @@ class AffectationProcessor final : public INodeProcessor value = ArrayTypeT{}; } using AffectationExecutorT = VectorComponentAffectationExecutor<OperatorT, ArrayTypeT, ValueT, DataT>; - m_affectation_executor = - std::make_unique<AffectationExecutorT>(node, std::get<ArrayTypeT>(value), index_expression); - break; + + return std::make_unique<AffectationExecutorT>(lhs_node, std::get<ArrayTypeT>(value), index_expression); } // LCOV_EXCL_START default: { - throw ParseError("unexpected error: invalid vector dimension", - std::vector{array_subscript_expression.begin()}); + throw ParseError("unexpected error: invalid vector dimension", std::vector{lhs_node.begin()}); } // LCOV_EXCL_STOP } } else if (array_expression.m_data_type == ASTNodeDataType::matrix_t) { - Assert(array_subscript_expression.children.size() == 3); + Assert(lhs_node.children.size() == 3); Assert(array_expression.m_data_type.nbRows() == array_expression.m_data_type.nbColumns()); - auto& index0_expression = *array_subscript_expression.children[1]; - auto& index1_expression = *array_subscript_expression.children[2]; + auto& index0_expression = *lhs_node.children[1]; + auto& index1_expression = *lhs_node.children[2]; switch (array_expression.m_data_type.nbRows()) { case 1: { @@ -467,9 +447,9 @@ class AffectationProcessor final : public INodeProcessor value = ArrayTypeT{}; } using AffectationExecutorT = MatrixComponentAffectationExecutor<OperatorT, ArrayTypeT, ValueT, DataT>; - m_affectation_executor = std::make_unique<AffectationExecutorT>(node, std::get<ArrayTypeT>(value), - index0_expression, index1_expression); - break; + + return std::make_unique<AffectationExecutorT>(lhs_node, std::get<ArrayTypeT>(value), index0_expression, + index1_expression); } case 2: { using ArrayTypeT = TinyMatrix<2>; @@ -477,9 +457,9 @@ class AffectationProcessor final : public INodeProcessor value = ArrayTypeT{}; } using AffectationExecutorT = MatrixComponentAffectationExecutor<OperatorT, ArrayTypeT, ValueT, DataT>; - m_affectation_executor = std::make_unique<AffectationExecutorT>(node, std::get<ArrayTypeT>(value), - index0_expression, index1_expression); - break; + + return std::make_unique<AffectationExecutorT>(lhs_node, std::get<ArrayTypeT>(value), index0_expression, + index1_expression); } case 3: { using ArrayTypeT = TinyMatrix<3>; @@ -487,14 +467,13 @@ class AffectationProcessor final : public INodeProcessor value = ArrayTypeT{}; } using AffectationExecutorT = MatrixComponentAffectationExecutor<OperatorT, ArrayTypeT, ValueT, DataT>; - m_affectation_executor = std::make_unique<AffectationExecutorT>(node, std::get<ArrayTypeT>(value), - index0_expression, index1_expression); - break; + + return std::make_unique<AffectationExecutorT>(lhs_node, std::get<ArrayTypeT>(value), index0_expression, + index1_expression); } // LCOV_EXCL_START default: { - throw ParseError("unexpected error: invalid vector dimension", - std::vector{array_subscript_expression.begin()}); + throw ParseError("unexpected error: invalid vector dimension", std::vector{lhs_node.begin()}); } // LCOV_EXCL_STOP } @@ -505,25 +484,54 @@ class AffectationProcessor final : public INodeProcessor } } else { // LCOV_EXCL_START - throw ParseError("unexpected error: invalid lhs", std::vector{node.children[0]->begin()}); + throw ParseError("unexpected error: invalid lhs", std::vector{lhs_node.begin()}); // LCOV_EXCL_STOP } } + + public: + DataVariant + execute(ExecutionPolicy& exec_policy) + { + m_affectation_executor->affect(exec_policy, m_rhs_node.execute(exec_policy)); + + return {}; + } + + AffectationProcessor(ASTNode& lhs_node, ASTNode& rhs_node) + : m_rhs_node{rhs_node}, m_affectation_executor{this->_buildAffectationExecutor(lhs_node)} + {} +}; + +class AffectationToDataVariantProcessorBase : public INodeProcessor +{ + protected: + DataVariant* m_lhs; + + public: + AffectationToDataVariantProcessorBase(ASTNode& lhs_node) + { + const std::string& symbol = lhs_node.string(); + auto [i_symbol, found] = lhs_node.m_symbol_table->find(symbol, lhs_node.begin()); + Assert(found); + + m_lhs = &i_symbol->attributes().value(); + } + + virtual ~AffectationToDataVariantProcessorBase() = default; }; template <typename OperatorT, typename ValueT> -class AffectationToTinyVectorFromListProcessor final : public INodeProcessor +class AffectationToTinyVectorFromListProcessor final : public AffectationToDataVariantProcessorBase { private: - ASTNode& m_node; - - DataVariant* m_lhs; + ASTNode& m_rhs_node; public: DataVariant execute(ExecutionPolicy& exec_policy) { - AggregateDataVariant children_values = std::get<AggregateDataVariant>(m_node.children[1]->execute(exec_policy)); + AggregateDataVariant children_values = std::get<AggregateDataVariant>(m_rhs_node.execute(exec_policy)); static_assert(std::is_same_v<OperatorT, language::eq_op>, "forbidden affection operator for list to vectors"); @@ -537,7 +545,7 @@ class AffectationToTinyVectorFromListProcessor final : public INodeProcessor v[i] = child_value; } else { // LCOV_EXCL_START - throw ParseError("unexpected error: unexpected right hand side type in affectation", m_node.begin()); + throw ParseError("unexpected error: unexpected right hand side type in affectation", m_rhs_node.begin()); // LCOV_EXCL_STOP } }, @@ -548,29 +556,22 @@ class AffectationToTinyVectorFromListProcessor final : public INodeProcessor return {}; } - AffectationToTinyVectorFromListProcessor(ASTNode& node) : m_node{node} - { - const std::string& symbol = m_node.children[0]->string(); - auto [i_symbol, found] = m_node.m_symbol_table->find(symbol, m_node.children[0]->begin()); - Assert(found); - - m_lhs = &i_symbol->attributes().value(); - } + AffectationToTinyVectorFromListProcessor(ASTNode& lhs_node, ASTNode& rhs_node) + : AffectationToDataVariantProcessorBase(lhs_node), m_rhs_node{rhs_node} + {} }; template <typename OperatorT, typename ValueT> -class AffectationToTinyMatrixFromListProcessor final : public INodeProcessor +class AffectationToTinyMatrixFromListProcessor final : public AffectationToDataVariantProcessorBase { private: - ASTNode& m_node; - - DataVariant* m_lhs; + ASTNode& m_rhs_node; public: DataVariant execute(ExecutionPolicy& exec_policy) { - AggregateDataVariant children_values = std::get<AggregateDataVariant>(m_node.children[1]->execute(exec_policy)); + AggregateDataVariant children_values = std::get<AggregateDataVariant>(m_rhs_node.execute(exec_policy)); static_assert(std::is_same_v<OperatorT, language::eq_op>, "forbidden affection operator for list to vectors"); @@ -585,7 +586,7 @@ class AffectationToTinyMatrixFromListProcessor final : public INodeProcessor v(i, j) = child_value; } else { // LCOV_EXCL_START - throw ParseError("unexpected error: unexpected right hand side type in affectation", m_node.begin()); + throw ParseError("unexpected error: unexpected right hand side type in affectation", m_rhs_node.begin()); // LCOV_EXCL_STOP } }, @@ -597,29 +598,22 @@ class AffectationToTinyMatrixFromListProcessor final : public INodeProcessor return {}; } - AffectationToTinyMatrixFromListProcessor(ASTNode& node) : m_node{node} - { - const std::string& symbol = m_node.children[0]->string(); - auto [i_symbol, found] = m_node.m_symbol_table->find(symbol, m_node.children[0]->begin()); - Assert(found); - - m_lhs = &i_symbol->attributes().value(); - } + AffectationToTinyMatrixFromListProcessor(ASTNode& lhs_node, ASTNode& rhs_node) + : AffectationToDataVariantProcessorBase(lhs_node), m_rhs_node{rhs_node} + {} }; template <typename ValueT> -class AffectationToTupleProcessor final : public INodeProcessor +class AffectationToTupleProcessor final : public AffectationToDataVariantProcessorBase { private: - ASTNode& m_node; - - DataVariant* m_lhs; + ASTNode& m_rhs_node; public: DataVariant execute(ExecutionPolicy& exec_policy) { - DataVariant value = m_node.children[1]->execute(exec_policy); + DataVariant value = m_rhs_node.execute(exec_policy); std::visit( [&](auto&& v) { @@ -646,12 +640,12 @@ class AffectationToTupleProcessor final : public INodeProcessor *m_lhs = std::vector<ValueT>{ValueT{zero}}; } else { // LCOV_EXCL_START - throw ParseError("unexpected error: unexpected right hand side type in affectation", m_node.begin()); + throw ParseError("unexpected error: unexpected right hand side type in affectation", m_rhs_node.begin()); // LCOV_EXCL_STOP } } else { // LCOV_EXCL_START - throw ParseError("unexpected error: unexpected right hand side type in affectation", m_node.begin()); + throw ParseError("unexpected error: unexpected right hand side type in affectation", m_rhs_node.begin()); // LCOV_EXCL_STOP } }, @@ -660,23 +654,16 @@ class AffectationToTupleProcessor final : public INodeProcessor return {}; } - AffectationToTupleProcessor(ASTNode& node) : m_node{node} - { - const std::string& symbol = m_node.children[0]->string(); - auto [i_symbol, found] = m_node.m_symbol_table->find(symbol, m_node.children[0]->begin()); - Assert(found); - - m_lhs = &i_symbol->attributes().value(); - } + AffectationToTupleProcessor(ASTNode& lhs_node, ASTNode& rhs_node) + : AffectationToDataVariantProcessorBase(lhs_node), m_rhs_node{rhs_node} + {} }; template <typename ValueT> -class AffectationToTupleFromListProcessor final : public INodeProcessor +class AffectationToTupleFromListProcessor final : public AffectationToDataVariantProcessorBase { private: - ASTNode& m_node; - - DataVariant* m_lhs; + ASTNode& m_rhs_node; void _copyAggregateDataVariant(const AggregateDataVariant& children_values) @@ -711,7 +698,7 @@ class AffectationToTupleFromListProcessor final : public INodeProcessor } else { // LCOV_EXCL_START throw ParseError("unexpected error: unexpected right hand side type in affectation", - m_node.children[1]->children[i]->begin()); + m_rhs_node.children[i]->begin()); // LCOV_EXCL_STOP } }, @@ -728,7 +715,7 @@ class AffectationToTupleFromListProcessor final : public INodeProcessor } else { // LCOV_EXCL_START throw ParseError("unexpected error: unexpected right hand side type in affectation", - m_node.children[1]->children[i]->begin()); + m_rhs_node.children[i]->begin()); // LCOV_EXCL_STOP } } else if constexpr (is_tiny_matrix_v<ValueT>) { @@ -745,7 +732,7 @@ class AffectationToTupleFromListProcessor final : public INodeProcessor } else { // LCOV_EXCL_START throw ParseError("unexpected error: unexpected right hand side type in affectation", - m_node.children[1]->children[i]->begin()); + m_rhs_node.children[i]->begin()); // LCOV_EXCL_STOP } }, @@ -763,13 +750,13 @@ class AffectationToTupleFromListProcessor final : public INodeProcessor } else { // LCOV_EXCL_START throw ParseError("unexpected error: unexpected right hand side type in affectation", - m_node.children[1]->children[i]->begin()); + m_rhs_node.children[i]->begin()); // LCOV_EXCL_STOP } } else { // LCOV_EXCL_START throw ParseError("unexpected error: unexpected right hand side type in affectation", - m_node.children[1]->children[i]->begin()); + m_rhs_node.children[i]->begin()); // LCOV_EXCL_STOP } }, @@ -805,8 +792,7 @@ class AffectationToTupleFromListProcessor final : public INodeProcessor } } else { // LCOV_EXCL_START - throw ParseError("unexpected error: unexpected right hand side type in tuple affectation", - m_node.children[1]->begin()); + throw ParseError("unexpected error: unexpected right hand side type in tuple affectation", m_rhs_node.begin()); // LCOV_EXCL_STOP } @@ -826,34 +812,23 @@ class AffectationToTupleFromListProcessor final : public INodeProcessor this->_copyVector(value_list); } else { // LCOV_EXCL_START - throw ParseError("unexpected error: invalid lhs (expecting list or tuple)", - std::vector{m_node.children[1]->begin()}); + throw ParseError("unexpected error: invalid lhs (expecting list or tuple)", std::vector{m_rhs_node.begin()}); // LCOV_EXCL_STOP } }, - m_node.children[1]->execute(exec_policy)); + m_rhs_node.execute(exec_policy)); return {}; } - AffectationToTupleFromListProcessor(ASTNode& node) : m_node{node} - { - const std::string& symbol = m_node.children[0]->string(); - auto [i_symbol, found] = m_node.m_symbol_table->find(symbol, m_node.children[0]->begin()); - Assert(found); - - m_lhs = &i_symbol->attributes().value(); - } + AffectationToTupleFromListProcessor(ASTNode& lhs_node, ASTNode& rhs_node) + : AffectationToDataVariantProcessorBase(lhs_node), m_rhs_node{rhs_node} + {} }; template <typename ValueT> -class AffectationFromZeroProcessor final : public INodeProcessor +class AffectationFromZeroProcessor final : public AffectationToDataVariantProcessorBase { - private: - ASTNode& m_node; - - DataVariant* m_lhs; - public: DataVariant execute(ExecutionPolicy&) @@ -862,14 +837,7 @@ class AffectationFromZeroProcessor final : public INodeProcessor return {}; } - AffectationFromZeroProcessor(ASTNode& node) : m_node{node} - { - const std::string& symbol = m_node.children[0]->string(); - auto [i_symbol, found] = m_node.m_symbol_table->find(symbol, m_node.children[0]->begin()); - Assert(found); - - m_lhs = &i_symbol->attributes().value(); - } + AffectationFromZeroProcessor(ASTNode& lhs_node) : AffectationToDataVariantProcessorBase(lhs_node) {} }; template <typename OperatorT> diff --git a/src/language/utils/AffectationProcessorBuilder.hpp b/src/language/utils/AffectationProcessorBuilder.hpp index 8d11599a01c67566e0f88fcb34cad581bc76de45..0e4b7ace65654b650989e817dbe2e4a545ae320f 100644 --- a/src/language/utils/AffectationProcessorBuilder.hpp +++ b/src/language/utils/AffectationProcessorBuilder.hpp @@ -15,18 +15,18 @@ class AffectationProcessorBuilder final : public IAffectationProcessorBuilder public: AffectationProcessorBuilder() = default; std::unique_ptr<INodeProcessor> - getNodeProcessor(ASTNode& node) const + getNodeProcessor(ASTNode& lhs_node, ASTNode& rhs_node) const final { if constexpr (std::is_same_v<ValueT, TinyVector<1>> and std::is_same_v<DataT, int64_t> and std::is_same_v<OperatorT, language::eq_op>) { // Special treatment for the case 0 -> R^1 - if ((node.children[1]->is_type<language::integer>()) and (std::stoi(node.children[1]->string()) == 0)) { - return std::make_unique<AffectationFromZeroProcessor<ValueT>>(node); + if ((rhs_node.is_type<language::integer>()) and (std::stoi(rhs_node.string()) == 0)) { + return std::make_unique<AffectationFromZeroProcessor<ValueT>>(lhs_node); } else { - return std::make_unique<AffectationProcessor<OperatorT, ValueT, DataT>>(node); + return std::make_unique<AffectationProcessor<OperatorT, ValueT, DataT>>(lhs_node, rhs_node); } } else { - return std::make_unique<AffectationProcessor<OperatorT, ValueT, DataT>>(node); + return std::make_unique<AffectationProcessor<OperatorT, ValueT, DataT>>(lhs_node, rhs_node); } } }; @@ -37,9 +37,9 @@ class AffectationToTupleProcessorBuilder final : public IAffectationProcessorBui public: AffectationToTupleProcessorBuilder() = default; std::unique_ptr<INodeProcessor> - getNodeProcessor(ASTNode& node) const + getNodeProcessor(ASTNode& lhs_node, ASTNode& rhs_node) const final { - return std::make_unique<AffectationToTupleProcessor<ValueT>>(node); + return std::make_unique<AffectationToTupleProcessor<ValueT>>(lhs_node, rhs_node); } }; @@ -49,10 +49,10 @@ class AffectationToTupleFromListProcessorBuilder final : public IAffectationProc public: AffectationToTupleFromListProcessorBuilder() = default; std::unique_ptr<INodeProcessor> - getNodeProcessor(ASTNode& node) const + getNodeProcessor(ASTNode& lhs_node, ASTNode& rhs_node) const final { - ASTNodeNaturalConversionChecker(*node.children[1], node.children[0]->m_data_type); - return std::make_unique<AffectationToTupleFromListProcessor<ValueT>>(node); + ASTNodeNaturalConversionChecker(rhs_node, lhs_node.m_data_type); + return std::make_unique<AffectationToTupleFromListProcessor<ValueT>>(lhs_node, rhs_node); } }; @@ -62,9 +62,9 @@ class AffectationToTinyVectorFromListProcessorBuilder final : public IAffectatio public: AffectationToTinyVectorFromListProcessorBuilder() = default; std::unique_ptr<INodeProcessor> - getNodeProcessor(ASTNode& node) const + getNodeProcessor(ASTNode& lhs_node, ASTNode& rhs_node) const final { - return std::make_unique<AffectationToTinyVectorFromListProcessor<OperatorT, ValueT>>(node); + return std::make_unique<AffectationToTinyVectorFromListProcessor<OperatorT, ValueT>>(lhs_node, rhs_node); } }; @@ -74,9 +74,9 @@ class AffectationToTinyMatrixFromListProcessorBuilder final : public IAffectatio public: AffectationToTinyMatrixFromListProcessorBuilder() = default; std::unique_ptr<INodeProcessor> - getNodeProcessor(ASTNode& node) const + getNodeProcessor(ASTNode& lhs_node, ASTNode& rhs_node) const final { - return std::make_unique<AffectationToTinyMatrixFromListProcessor<OperatorT, ValueT>>(node); + return std::make_unique<AffectationToTinyMatrixFromListProcessor<OperatorT, ValueT>>(lhs_node, rhs_node); } }; @@ -86,12 +86,12 @@ class AffectationFromZeroProcessorBuilder final : public IAffectationProcessorBu public: AffectationFromZeroProcessorBuilder() = default; std::unique_ptr<INodeProcessor> - getNodeProcessor(ASTNode& node) const + getNodeProcessor(ASTNode& lhs_node, ASTNode& rhs_node) const final { - if (std::stoi(node.children[1]->string()) == 0) { - return std::make_unique<AffectationFromZeroProcessor<ValueT>>(node); + if (std::stoi(rhs_node.string()) == 0) { + return std::make_unique<AffectationFromZeroProcessor<ValueT>>(lhs_node); } else { - throw ParseError("invalid integral value (0 is the solely valid value)", std::vector{node.children[1]->begin()}); + throw ParseError("invalid integral value (0 is the solely valid value)", std::vector{lhs_node.begin()}); } } }; diff --git a/src/language/utils/BuiltinFunctionEmbedder.hpp b/src/language/utils/BuiltinFunctionEmbedder.hpp index afb991acadcb36236bcd55a34c7840ca399ab6f0..265379a368ef3b7f847cd8de2b763a70b525f8a4 100644 --- a/src/language/utils/BuiltinFunctionEmbedder.hpp +++ b/src/language/utils/BuiltinFunctionEmbedder.hpp @@ -130,23 +130,30 @@ class BuiltinFunctionEmbedder<FX(Args...)> : public IBuiltinFunctionEmbedder return ast_node_data_type_from<T>; } - template <size_t I> + template <typename TupleT, size_t I> PUGS_INLINE ASTNodeDataType - _getOneParameterDataType(ArgsTuple& t) const + _getOneParameterDataType() const { - using ArgN_T = std::decay_t<decltype(std::get<I>(t))>; + using ArgN_T = std::decay_t<decltype(std::get<I>(TupleT{}))>; return _getDataType<ArgN_T>(); } template <size_t... I> - PUGS_INLINE std::vector<ASTNodeDataType> - _getParameterDataTypes(ArgsTuple t, std::index_sequence<I...>) const + PUGS_INLINE std::vector<ASTNodeDataType> _getParameterDataTypes(std::index_sequence<I...>) const { std::vector<ASTNodeDataType> parameter_type_list; - (parameter_type_list.push_back(this->_getOneParameterDataType<I>(t)), ...); + (parameter_type_list.push_back(this->_getOneParameterDataType<ArgsTuple, I>()), ...); return parameter_type_list; } + template <size_t... I> + PUGS_INLINE std::vector<std::shared_ptr<const ASTNodeDataType>> _getCompoundDataTypes(std::index_sequence<I...>) const + { + std::vector<std::shared_ptr<const ASTNodeDataType>> compound_type_list; + (compound_type_list.push_back(std::make_shared<ASTNodeDataType>(this->_getOneParameterDataType<FX, I>())), ...); + return compound_type_list; + } + template <typename T> PUGS_INLINE std::shared_ptr<IDataHandler> _createHandler(std::shared_ptr<T> data) const @@ -175,21 +182,52 @@ class BuiltinFunctionEmbedder<FX(Args...)> : public IBuiltinFunctionEmbedder (_check_arg<I>(), ...); } + template <typename ResultT> + PUGS_INLINE DataVariant + _resultToDataVariant(ResultT&& result) const + { + if constexpr (is_data_variant_v<std::decay_t<ResultT>>) { + return std::move(result); + } else { + return EmbeddedData(_createHandler(std::move(result))); + } + } + + PUGS_INLINE + AggregateDataVariant + _applyToAggregate(const ArgsTuple& t) const + { + auto tuple_result = std::apply(m_f, t); + std::vector<DataVariant> vector_result; + vector_result.reserve(std::tuple_size_v<decltype(tuple_result)>); + + std::apply([&](auto&&... result) { ((vector_result.emplace_back(_resultToDataVariant(result))), ...); }, + tuple_result); + + return vector_result; + } + public: PUGS_INLINE ASTNodeDataType getReturnDataType() const final { - return _getDataType<FX>(); + if constexpr (is_std_tuple_v<FX>) { + constexpr size_t N = std::tuple_size_v<FX>; + using IndexSequence = std::make_index_sequence<N>; + + return ASTNodeDataType::build<ASTNodeDataType::list_t>(this->_getCompoundDataTypes(IndexSequence{})); + } else { + return this->_getDataType<FX>(); + } } PUGS_INLINE std::vector<ASTNodeDataType> getParameterDataTypes() const final { - constexpr size_t N = std::tuple_size_v<ArgsTuple>; - ArgsTuple t; + constexpr size_t N = std::tuple_size_v<ArgsTuple>; using IndexSequence = std::make_index_sequence<N>; - return this->_getParameterDataTypes(t, IndexSequence{}); + return this->_getParameterDataTypes(IndexSequence{}); } PUGS_INLINE size_t @@ -198,7 +236,6 @@ class BuiltinFunctionEmbedder<FX(Args...)> : public IBuiltinFunctionEmbedder return sizeof...(Args); } - public: PUGS_INLINE DataVariant apply(const std::vector<DataVariant>& x) const final @@ -210,6 +247,8 @@ class BuiltinFunctionEmbedder<FX(Args...)> : public IBuiltinFunctionEmbedder this->_copyFromVector(t, x, IndexSequence{}); if constexpr (is_data_variant_v<FX>) { return {std::apply(m_f, t)}; + } else if constexpr (is_std_tuple_v<FX>) { + return this->_applyToAggregate(t); } else if constexpr (std::is_same_v<FX, void>) { std::apply(m_f, t); return {}; @@ -245,6 +284,22 @@ class BuiltinFunctionEmbedder<FX(void)> : public IBuiltinFunctionEmbedder private: std::function<FX(void)> m_f; + template <typename TupleT, size_t I> + PUGS_INLINE ASTNodeDataType + _getOneParameterDataType() const + { + using ArgN_T = std::decay_t<decltype(std::get<I>(TupleT{}))>; + return _getDataType<ArgN_T>(); + } + + template <size_t... I> + PUGS_INLINE std::vector<std::shared_ptr<const ASTNodeDataType>> _getCompoundDataTypes(std::index_sequence<I...>) const + { + std::vector<std::shared_ptr<const ASTNodeDataType>> compound_type_list; + (compound_type_list.push_back(std::make_shared<ASTNodeDataType>(this->_getOneParameterDataType<FX, I>())), ...); + return compound_type_list; + } + template <typename T> PUGS_INLINE std::shared_ptr<IDataHandler> _createHandler(std::shared_ptr<T> data) const @@ -260,11 +315,42 @@ class BuiltinFunctionEmbedder<FX(void)> : public IBuiltinFunctionEmbedder return ast_node_data_type_from<T>; } + template <typename ResultT> + PUGS_INLINE DataVariant + _resultToDataVariant(ResultT&& result) const + { + if constexpr (is_data_variant_v<std::decay_t<ResultT>>) { + return std::move(result); + } else { + return EmbeddedData(_createHandler(std::move(result))); + } + } + + PUGS_INLINE + AggregateDataVariant + _applyToAggregate() const + { + auto tuple_result = m_f(); + std::vector<DataVariant> vector_result; + vector_result.reserve(std::tuple_size_v<decltype(tuple_result)>); + + std::apply([&](auto&&... result) { ((vector_result.emplace_back(_resultToDataVariant(result))), ...); }, + tuple_result); + + return vector_result; + } + public: PUGS_INLINE ASTNodeDataType getReturnDataType() const final { - return this->_getDataType<FX>(); + if constexpr (is_std_tuple_v<FX>) { + constexpr size_t N = std::tuple_size_v<FX>; + using IndexSequence = std::make_index_sequence<N>; + return ASTNodeDataType::build<ASTNodeDataType::list_t>(this->_getCompoundDataTypes(IndexSequence{})); + } else { + return this->_getDataType<FX>(); + } } PUGS_INLINE std::vector<ASTNodeDataType> @@ -285,6 +371,8 @@ class BuiltinFunctionEmbedder<FX(void)> : public IBuiltinFunctionEmbedder { if constexpr (is_data_variant_v<FX>) { return {m_f()}; + } else if constexpr (is_std_tuple_v<FX>) { + return this->_applyToAggregate(); } else if constexpr (std::is_same_v<FX, void>) { m_f(); return {}; diff --git a/src/language/utils/CMakeLists.txt b/src/language/utils/CMakeLists.txt index 22c0ae52f184a7f18d852057cbe9b0616f1905e3..32a75c66f0dd73cc10c09ca2c6aecf640962c45c 100644 --- a/src/language/utils/CMakeLists.txt +++ b/src/language/utils/CMakeLists.txt @@ -22,6 +22,7 @@ add_library(PugsLanguageUtils BinaryOperatorRegisterForZ.cpp DataVariant.cpp EmbeddedData.cpp + FunctionSymbolId.cpp IncDecOperatorRegisterForN.cpp IncDecOperatorRegisterForR.cpp IncDecOperatorRegisterForZ.cpp diff --git a/src/language/utils/FunctionSymbolId.cpp b/src/language/utils/FunctionSymbolId.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d45c35c2d3e71311128026c8779094e370eee22d --- /dev/null +++ b/src/language/utils/FunctionSymbolId.cpp @@ -0,0 +1,8 @@ +#include <language/utils/FunctionSymbolId.hpp> +#include <language/utils/SymbolTable.hpp> + +const FunctionDescriptor& +FunctionSymbolId::descriptor() const +{ + return m_symbol_table->functionTable()[m_function_id]; +} diff --git a/src/language/utils/FunctionSymbolId.hpp b/src/language/utils/FunctionSymbolId.hpp index f96b783fa5bf3853507f4c650e02972faa5d6ebb..21496cb0e56e49f9c5a5d58d407a927f3d32cfd3 100644 --- a/src/language/utils/FunctionSymbolId.hpp +++ b/src/language/utils/FunctionSymbolId.hpp @@ -8,7 +8,9 @@ #include <iostream> #include <memory> +class FunctionDescriptor; class SymbolTable; + class FunctionSymbolId { private: @@ -29,6 +31,8 @@ class FunctionSymbolId return *m_symbol_table; } + [[nodiscard]] const FunctionDescriptor& descriptor() const; + friend std::ostream& operator<<(std::ostream& os, const FunctionSymbolId& function_symbol_id) { diff --git a/src/language/utils/IAffectationProcessorBuilder.hpp b/src/language/utils/IAffectationProcessorBuilder.hpp index 6d5f8540df9435bba4b86e6bee22f980b5727d7b..cc0ca85f67ece81b6c1313a13c429dfeb9718cd7 100644 --- a/src/language/utils/IAffectationProcessorBuilder.hpp +++ b/src/language/utils/IAffectationProcessorBuilder.hpp @@ -9,7 +9,7 @@ class INodeProcessor; class IAffectationProcessorBuilder { public: - virtual std::unique_ptr<INodeProcessor> getNodeProcessor(ASTNode& node) const = 0; + virtual std::unique_ptr<INodeProcessor> getNodeProcessor(ASTNode& lhs_node, ASTNode& rhs_node) const = 0; virtual ~IAffectationProcessorBuilder() = default; }; diff --git a/src/language/utils/PugsFunctionAdapter.hpp b/src/language/utils/PugsFunctionAdapter.hpp index 9b57a908e6341291d14e5e8f9c80aa8a87275dcc..b6d3a259dbb676d5f1259c058e0ea1f36fb925fd 100644 --- a/src/language/utils/PugsFunctionAdapter.hpp +++ b/src/language/utils/PugsFunctionAdapter.hpp @@ -119,10 +119,10 @@ class PugsFunctionAdapter<OutputType(InputType...)> [[nodiscard]] PUGS_INLINE static auto& getFunctionExpression(const FunctionSymbolId& function_symbol_id) { - auto& function = function_symbol_id.symbolTable().functionTable()[function_symbol_id.id()]; - _checkFunction(function); + auto& function_descriptor = function_symbol_id.descriptor(); + _checkFunction(function_descriptor); - return *function.definitionNode().children[1]; + return *function_descriptor.definitionNode().children[1]; } [[nodiscard]] PUGS_INLINE static auto @@ -209,7 +209,7 @@ class PugsFunctionAdapter<OutputType(InputType...)> }; } else { // If this point is reached must be a 0 vector - return [](DataVariant &&) -> OutputType { return OutputType{ZeroType{}}; }; + return [](DataVariant&&) -> OutputType { return OutputType{ZeroType{}}; }; } } case ASTNodeDataType::double_t: { @@ -236,8 +236,8 @@ class PugsFunctionAdapter<OutputType(InputType...)> AggregateDataVariant& v = std::get<AggregateDataVariant>(result); OutputType x; - for (size_t i = 0, l = 0; i < x.dimension(); ++i) { - for (size_t j = 0; j < x.dimension(); ++j, ++l) { + for (size_t i = 0, l = 0; i < x.nbRows(); ++i) { + for (size_t j = 0; j < x.nbColumns(); ++j, ++l) { std::visit( [&](auto&& Aij) { using Aij_T = std::decay_t<decltype(Aij)>; @@ -288,7 +288,7 @@ class PugsFunctionAdapter<OutputType(InputType...)> }; } else { // If this point is reached must be a 0 matrix - return [](DataVariant &&) -> OutputType { return OutputType{ZeroType{}}; }; + return [](DataVariant&&) -> OutputType { return OutputType{ZeroType{}}; }; } } case ASTNodeDataType::double_t: { diff --git a/src/language/utils/SymbolTable.hpp b/src/language/utils/SymbolTable.hpp index 6aa089bd8de7aa5a3a6a6cf2a8115e77de25dbe8..772ac6564859dd402ba2086d4fed7dc0b4c64059 100644 --- a/src/language/utils/SymbolTable.hpp +++ b/src/language/utils/SymbolTable.hpp @@ -267,12 +267,15 @@ class SymbolTable clearValues() { for (auto& symbol : m_symbol_list) { - std::visit( - [](auto&& value) { - using T = std::decay_t<decltype(value)>; - value = T{}; - }, - symbol.attributes().value()); + // local functions must kept their values (id) + if (symbol.attributes().dataType() != ASTNodeDataType::function_t) { + std::visit( + [](auto&& value) { + using T = std::decay_t<decltype(value)>; + value = T{}; + }, + symbol.attributes().value()); + } } } diff --git a/src/output/CMakeLists.txt b/src/output/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..a3e6799657d2ed422fee5ab7018939ea080cf4c2 --- /dev/null +++ b/src/output/CMakeLists.txt @@ -0,0 +1,16 @@ +# ------------------- Source files -------------------- + +add_library( + PugsOutput + GnuplotWriter.cpp + VTKWriter.cpp + WriterBase.cpp) + +# ------------------- Installation -------------------- +# temporary version workaround +if(${CMAKE_VERSION} VERSION_LESS "3.13.0") + install(TARGETS PugsMesh + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib) +endif() diff --git a/src/output/GnuplotWriter.cpp b/src/output/GnuplotWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..436ae7ffa22d7f3aaa20cbd44e2331393958823c --- /dev/null +++ b/src/output/GnuplotWriter.cpp @@ -0,0 +1,253 @@ +#include <output/GnuplotWriter.hpp> + +#include <mesh/Connectivity.hpp> +#include <mesh/ItemValue.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> +#include <utils/Messenger.hpp> +#include <utils/PugsTraits.hpp> +#include <utils/RevisionInfo.hpp> + +#include <utils/Demangle.hpp> + +#include <fstream> +#include <iomanip> + +std::string +GnuplotWriter::_getDateAndVersionComment() const +{ + std::ostringstream os; + + std::time_t now = std::time(nullptr); + os << "# Generated by pugs: " << std::ctime(&now); + os << "# version: " << RevisionInfo::version() << '\n'; + os << "# tag: " << RevisionInfo::gitTag() << '\n'; + os << "# HEAD: " << RevisionInfo::gitHead() << '\n'; + os << "# hash: " << RevisionInfo::gitHash() << " (" << ((RevisionInfo::gitIsClean()) ? "clean" : "dirty") << ")\n"; + os << '\n'; + + return os.str(); +} + +std::string +GnuplotWriter::_getFilename() const +{ + std::ostringstream sout; + sout << m_base_filename; + sout << '.' << std::setfill('0') << std::setw(4) << m_saved_times.size() << ".gnu"; + return sout.str(); +} + +template <size_t Dimension> +void +GnuplotWriter::_writePreamble(const OutputNamedItemValueSet& output_named_item_value_set, std::ostream& fout) const +{ + fout << "# list of data\n"; + fout << "# 1:x"; + if constexpr (Dimension > 1) { + fout << " 2:y"; + } + uint64_t i = Dimension + 1; + for (const auto& i_named_item_value : output_named_item_value_set) { + const std::string name = i_named_item_value.first; + const auto& item_value_variant = i_named_item_value.second; + std::visit( + [&](auto&& item_value) { + using ItemValueType = std::decay_t<decltype(item_value)>; + using DataType = std::decay_t<typename ItemValueType::data_type>; + if constexpr (std::is_arithmetic_v<DataType>) { + fout << ' ' << i++ << ':' << name; + } else if constexpr (is_tiny_vector_v<DataType>) { + for (size_t j = 0; j < DataType{}.dimension(); ++j) { + fout << ' ' << i++ << ':' << name << '[' << j << ']'; + } + } else if constexpr (is_tiny_matrix_v<DataType>) { + for (size_t j = 0; j < DataType{}.nbRows(); ++j) { + for (size_t k = 0; k < DataType{}.nbColumns(); ++k) { + fout << ' ' << i++ << ':' << name << '(' << j << ',' << k << ')'; + } + } + } else { + throw UnexpectedError("invalid data type"); + } + }, + item_value_variant); + } + fout << "\n\n"; +} + +template <typename DataType> +void +GnuplotWriter::_writeCellValue(const CellValue<DataType>& cell_value, CellId cell_id, std::ostream& fout) const +{ + const auto& value = cell_value[cell_id]; + if constexpr (std::is_arithmetic_v<DataType>) { + fout << ' ' << value; + } else if constexpr (is_tiny_vector_v<std::decay_t<DataType>>) { + for (size_t i = 0; i < value.dimension(); ++i) { + fout << ' ' << value[i]; + } + } else if constexpr (is_tiny_matrix_v<std::decay_t<DataType>>) { + for (size_t i = 0; i < value.nbRows(); ++i) { + for (size_t j = 0; j < value.nbColumns(); ++j) { + fout << ' ' << value(i, j); + } + } + } else { + throw UnexpectedError("invalid data type for cell value output: " + demangle<DataType>()); + } +} + +template <typename DataType, ItemType item_type> +void +GnuplotWriter::_writeValue(const ItemValue<DataType, item_type>& item_value, + CellId cell_id, + NodeId node_id, + std::ostream& fout) const +{ + if constexpr (item_type == ItemType::cell) { + this->_writeCellValue(item_value, cell_id, fout); + } else if constexpr (item_type == ItemType::node) { + this->_writeNodeValue(item_value, node_id, fout); + } else { + throw UnexpectedError{"invalid item type"}; + } +} + +template <typename MeshType> +void +GnuplotWriter::_writeValues(const std::shared_ptr<const MeshType>& mesh, + const OutputNamedItemValueSet& output_named_item_value_set, + std::ostream& fout) const +{ + if constexpr (MeshType::Dimension <= 2) { + auto cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix(); + auto cell_is_owned = mesh->connectivity().cellIsOwned(); + + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + if (cell_is_owned[cell_id]) { + const auto& cell_nodes = cell_to_node_matrix[cell_id]; + for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { + const NodeId& node_id = cell_nodes[i_node]; + const TinyVector<MeshType::Dimension>& xr = mesh->xr()[node_id]; + fout << xr[0]; + if (MeshType::Dimension == 2) { + fout << ' ' << xr[1]; + } + for (auto [name, item_value] : output_named_item_value_set) { + std::visit([&](auto&& cell_value) { _writeValue(cell_value, cell_id, node_id, fout); }, item_value); + } + + fout << '\n'; + } + fout << '\n'; + } + } + } else { + throw UnexpectedError("invalid mesh dimension"); + } +} + +template <typename ValueType> +void +GnuplotWriter::_writeNodeValue(const NodeValue<ValueType>& node_value, NodeId node_id, std::ostream& fout) const +{ + const auto& value = node_value[node_id]; + if constexpr (std::is_arithmetic_v<ValueType>) { + fout << ' ' << value; + } else if constexpr (is_tiny_vector_v<std::decay_t<ValueType>>) { + for (size_t i = 0; i < value.dimension(); ++i) { + fout << ' ' << value[i]; + } + } else if constexpr (is_tiny_matrix_v<std::decay_t<ValueType>>) { + for (size_t i = 0; i < value.nbRows(); ++i) { + for (size_t j = 0; j < value.nbColumns(); ++j) { + fout << ' ' << value(i, j); + } + } + } else { + throw UnexpectedError("invalid data type for cell value output: " + demangle<ValueType>()); + } +} + +template <typename MeshType> +void +GnuplotWriter::_write(const std::shared_ptr<const MeshType>& mesh, + const OutputNamedItemValueSet& output_named_item_value_set, + double time) const +{ + if (parallel::rank() == 0) { + std::ofstream fout{_getFilename()}; + fout.precision(15); + fout.setf(std::ios_base::scientific); + + fout << this->_getDateAndVersionComment(); + fout << "\n# time = " << time << "\n\n"; + + this->_writePreamble<MeshType::Dimension>(output_named_item_value_set, fout); + } + + for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { + if (i_rank == parallel::rank()) { + std::ofstream fout{_getFilename(), std::ios_base::app}; + fout.precision(15); + fout.setf(std::ios_base::scientific); + + this->_writeValues(mesh, output_named_item_value_set, fout); + } + parallel::barrier(); + } +} + +void +GnuplotWriter::writeMesh(const std::shared_ptr<const IMesh>& p_mesh) const +{ + OutputNamedItemValueSet output_named_item_value_set{}; + + std::ofstream fout(_getFilename()); + fout.precision(15); + fout.setf(std::ios_base::scientific); + fout << _getDateAndVersionComment(); + + switch (p_mesh->dimension()) { + case 1: { + this->_writePreamble<1>(output_named_item_value_set, fout); + this->_writeValues(std::dynamic_pointer_cast<const Mesh<Connectivity<1>>>(p_mesh), output_named_item_value_set, + fout); + break; + } + case 2: { + this->_writePreamble<2>(output_named_item_value_set, fout); + this->_writeValues(std::dynamic_pointer_cast<const Mesh<Connectivity<2>>>(p_mesh), output_named_item_value_set, + fout); + break; + } + default: { + throw NormalError("gnuplot format is not available in dimension " + std::to_string(p_mesh->dimension())); + } + } +} + +void +GnuplotWriter::write(const std::vector<std::shared_ptr<const NamedDiscreteFunction>>& named_discrete_function_list, + double time) const +{ + std::shared_ptr mesh = this->_getMesh(named_discrete_function_list); + + OutputNamedItemValueSet output_named_item_value_set = this->_getOutputNamedItemValueSet(named_discrete_function_list); + + switch (mesh->dimension()) { + case 1: { + this->_write(std::dynamic_pointer_cast<const Mesh<Connectivity<1>>>(mesh), output_named_item_value_set, time); + break; + } + case 2: { + this->_write(std::dynamic_pointer_cast<const Mesh<Connectivity<2>>>(mesh), output_named_item_value_set, time); + break; + } + default: { + throw NormalError("gnuplot format is not available in dimension " + std::to_string(mesh->dimension())); + } + } +} diff --git a/src/output/GnuplotWriter.hpp b/src/output/GnuplotWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..bf6487c998c29d8a5c12177a574eea587a1fb6ea --- /dev/null +++ b/src/output/GnuplotWriter.hpp @@ -0,0 +1,57 @@ +#ifndef GNUPLOT_WRITER_HPP +#define GNUPLOT_WRITER_HPP + +#include <output/WriterBase.hpp> + +#include <algebra/TinyMatrix.hpp> +#include <algebra/TinyVector.hpp> +#include <output/OutputNamedItemValueSet.hpp> + +class IMesh; + +#include <string> + +class GnuplotWriter : public WriterBase +{ + private: + std::string _getDateAndVersionComment() const; + + std::string _getFilename() const; + + template <typename DataType> + void _writeCellValue(const CellValue<DataType>& cell_value, CellId cell_id, std::ostream& fout) const; + + template <typename DataType> + void _writeNodeValue(const NodeValue<DataType>& node_value, NodeId cell_id, std::ostream& fout) const; + + template <size_t Dimension> + void _writePreamble(const OutputNamedItemValueSet& output_named_item_value_set, std::ostream& fout) const; + + template <typename DataType, ItemType item_type> + void _writeValue(const ItemValue<DataType, item_type>& item_value, + CellId cell_id, + NodeId node_id, + std::ostream& fout) const; + + template <typename MeshType> + void _writeValues(const std::shared_ptr<const MeshType>& mesh, + const OutputNamedItemValueSet& output_named_item_value_set, + std::ostream& fout) const; + + template <typename MeshType> + void _write(const std::shared_ptr<const MeshType>& mesh, + const OutputNamedItemValueSet& output_named_item_value_set, + double time) const; + + public: + void writeMesh(const std::shared_ptr<const IMesh>& mesh) const final; + + void write(const std::vector<std::shared_ptr<const NamedDiscreteFunction>>& named_discrete_function_list, + double time) const final; + + GnuplotWriter(const std::string& base_filename, const double time_period) : WriterBase(base_filename, time_period) {} + + ~GnuplotWriter() = default; +}; + +#endif // GNUPLOT_WRITER_HPP diff --git a/src/output/IWriter.hpp b/src/output/IWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..02317ad98b055567133f9b969476fedbd2a5c0e1 --- /dev/null +++ b/src/output/IWriter.hpp @@ -0,0 +1,28 @@ +#ifndef I_WRITER_HPP +#define I_WRITER_HPP + +#include <output/NamedDiscreteFunction.hpp> + +#include <memory> +#include <vector> + +class IMesh; + +class IWriter +{ + public: + virtual void writeMesh(const std::shared_ptr<const IMesh>& mesh) const = 0; + + virtual void writeIfNeeded( + const std::vector<std::shared_ptr<const NamedDiscreteFunction>>& named_discrete_function_list, + double time) const = 0; + + virtual void writeForced( + const std::vector<std::shared_ptr<const NamedDiscreteFunction>>& named_discrete_function_list, + double time) const = 0; + + IWriter() = default; + virtual ~IWriter() = default; +}; + +#endif // I_WRITER_HPP diff --git a/src/output/NamedDiscreteFunction.hpp b/src/output/NamedDiscreteFunction.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1b0850040a1fd944c5e3d3ba361f7d764bece3dd --- /dev/null +++ b/src/output/NamedDiscreteFunction.hpp @@ -0,0 +1,39 @@ +#ifndef NAMED_DISCRETE_FUNCTION_HPP +#define NAMED_DISCRETE_FUNCTION_HPP + +#include <memory> +#include <string> + +class IDiscreteFunction; + +class NamedDiscreteFunction +{ + private: + std::shared_ptr<const IDiscreteFunction> m_discrete_function; + std::string m_name; + + public: + const std::string& + name() const + { + return m_name; + } + + const std::shared_ptr<const IDiscreteFunction> + discreteFunction() const + { + return m_discrete_function; + } + + NamedDiscreteFunction(const std::shared_ptr<const IDiscreteFunction>& discrete_function, const std::string& name) + : m_discrete_function{discrete_function}, m_name{name} + {} + + NamedDiscreteFunction(const NamedDiscreteFunction&) = default; + NamedDiscreteFunction(NamedDiscreteFunction&&) = default; + + NamedDiscreteFunction() = default; + ~NamedDiscreteFunction() = default; +}; + +#endif // NAMED_DISCRETE_FUNCTION_HPP diff --git a/src/output/OutputNamedItemValueSet.hpp b/src/output/OutputNamedItemValueSet.hpp index 7dcc23e3d0aadaa95cb3a4333e38b73d1edad9a5..c7464135e3531c36ba3b1d3a801088cba002fbd7 100644 --- a/src/output/OutputNamedItemValueSet.hpp +++ b/src/output/OutputNamedItemValueSet.hpp @@ -54,19 +54,29 @@ class NamedItemValue class OutputNamedItemValueSet { public: - using ItemValueVariant = std::variant<NodeValue<const int>, + using ItemValueVariant = std::variant<NodeValue<const bool>, + NodeValue<const int>, NodeValue<const long int>, + NodeValue<const unsigned long int>, NodeValue<const double>, NodeValue<const TinyVector<1, double>>, NodeValue<const TinyVector<2, double>>, NodeValue<const TinyVector<3, double>>, + NodeValue<const TinyMatrix<1, double>>, + NodeValue<const TinyMatrix<2, double>>, + NodeValue<const TinyMatrix<3, double>>, + CellValue<const bool>, CellValue<const int>, CellValue<const long int>, + CellValue<const unsigned long int>, CellValue<const double>, CellValue<const TinyVector<1, double>>, CellValue<const TinyVector<2, double>>, - CellValue<const TinyVector<3, double>>>; + CellValue<const TinyVector<3, double>>, + CellValue<const TinyMatrix<1, double>>, + CellValue<const TinyMatrix<2, double>>, + CellValue<const TinyMatrix<3, double>>>; private: std::map<std::string, ItemValueVariant> m_name_itemvariant_map; @@ -103,6 +113,13 @@ class OutputNamedItemValueSet return m_name_itemvariant_map.end(); } + template <typename DataType, ItemType item_type> + void + add(const NamedItemValue<DataType, item_type>& named_itemvalue) + { + _doInsert(named_itemvalue); + } + template <typename... DataType, ItemType... item_type> OutputNamedItemValueSet(NamedItemValue<DataType, item_type>... named_itemvalue) { diff --git a/src/output/VTKWriter.cpp b/src/output/VTKWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..48f81dd376bd96c36820a535b2ff2fa20a4e831b --- /dev/null +++ b/src/output/VTKWriter.cpp @@ -0,0 +1,585 @@ +#include <output/VTKWriter.hpp> + +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <utils/Messenger.hpp> +#include <utils/RevisionInfo.hpp> + +#include <ctime> +#include <fstream> +#include <iomanip> +#include <sstream> +#include <unordered_map> + +std::string +VTKWriter::_getFilenamePVTU() const +{ + std::ostringstream sout; + sout << m_base_filename; + sout << '.' << std::setfill('0') << std::setw(4) << m_saved_times.size() << ".pvtu"; + return sout.str(); +} + +std::string +VTKWriter::_getDateAndVersionComment() const +{ + std::ostringstream os; + + std::time_t now = std::time(nullptr); + os << "<!--\n"; + os << " Generated by pugs: " << std::ctime(&now); + os << " version: " << RevisionInfo::version() << '\n'; + os << " tag: " << RevisionInfo::gitTag() << '\n'; + os << " HEAD: " << RevisionInfo::gitHead() << '\n'; + os << " hash: " << RevisionInfo::gitHash() << " (" << ((RevisionInfo::gitIsClean()) ? "clean" : "dirty") << ")\n"; + os << "-->\n"; + return os.str(); +} + +std::string +VTKWriter::_getFilenameVTU(int rank_number) const +{ + std::ostringstream sout; + sout << m_base_filename; + if (parallel::size() > 1) { + sout << '-' << std::setfill('0') << std::setw(4) << rank_number; + } + sout << '.' << std::setfill('0') << std::setw(4) << m_saved_times.size() << ".vtu"; + return sout.str(); +} + +template <typename DataType> +void +VTKWriter::_write_node_pvtu(std::ofstream& os, const std::string& name, const NodeValue<const DataType>&) const +{ + os << "<PDataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\"/>\n"; +} + +template <size_t N, typename DataType> +void +VTKWriter::_write_node_pvtu(std::ofstream& os, + const std::string& name, + const NodeValue<const TinyVector<N, DataType>>&) const +{ + os << "<PDataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\" NumberOfComponents=\"" << N + << "\"/>\n"; +} + +template <size_t N, typename DataType> +void +VTKWriter::_write_node_pvtu(std::ofstream& os, + const std::string& name, + const NodeValue<const TinyMatrix<N, DataType>>&) const +{ + os << "<PDataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\" NumberOfComponents=\"" << N * N + << "\"/>\n"; +} + +template <typename DataType> +void +VTKWriter::_write_node_pvtu(std::ofstream&, const std::string&, const CellValue<const DataType>&) const +{} + +template <typename DataType> +void +VTKWriter::_write_cell_pvtu(std::ofstream& os, const std::string& name, const CellValue<const DataType>&) const +{ + os << "<PDataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\"/>\n"; +} + +template <size_t N, typename DataType> +void +VTKWriter::_write_cell_pvtu(std::ofstream& os, + const std::string& name, + const CellValue<const TinyVector<N, DataType>>&) const +{ + os << "<PDataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\" NumberOfComponents=\"" << N + << "\"/>\n"; +} + +template <size_t N, typename DataType> +void +VTKWriter::_write_cell_pvtu(std::ofstream& os, + const std::string& name, + const CellValue<const TinyMatrix<N, DataType>>&) const +{ + os << "<PDataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\" NumberOfComponents=\"" << N * N + << "\"/>\n"; +} + +template <typename DataType> +void +VTKWriter::_write_cell_pvtu(std::ofstream&, const std::string&, const NodeValue<const DataType>&) const +{} + +template <typename DataType> +struct VTKWriter::VTKType +{ + inline const static std::string name = [] { + static_assert(std::is_arithmetic_v<DataType>, "invalid data type"); + + if constexpr (std::is_integral_v<DataType>) { + if constexpr (std::is_unsigned_v<DataType>) { + return "UInt" + std::to_string(sizeof(DataType) * 8); + } else { + return "UInt" + std::to_string(sizeof(DataType) * 8); + } + } else if constexpr (std::is_floating_point_v<DataType>) { + return "Float" + std::to_string(sizeof(DataType) * 8); + } + }(); +}; + +template <typename DataType> +void +VTKWriter::_write_array(std::ofstream& os, const std::string& name, const Array<DataType>& item_value) const +{ + os << "<DataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\">\n"; + for (typename Array<DataType>::index_type i = 0; i < item_value.size(); ++i) { + // The following '+' enforces integer output for char types + os << +item_value[i] << ' '; + } + os << "\n</DataArray>\n"; +} + +template <size_t N, typename DataType> +void +VTKWriter::_write_array(std::ofstream& os, + const std::string& name, + const Array<TinyVector<N, DataType>>& item_value) const +{ + os << "<DataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\" NumberOfComponents=\"" << N + << "\">\n"; + for (typename Array<DataType>::index_type i = 0; i < item_value.size(); ++i) { + for (size_t j = 0; j < N; ++j) { + // The following '+' enforces integer output for char types + os << +item_value[i][j] << ' '; + } + } + os << "\n</DataArray>\n"; +} + +template <typename DataType> +void +VTKWriter::_write_node_value(std::ofstream& os, + const std::string& name, + const NodeValue<const DataType>& item_value) const +{ + os << "<DataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\">\n"; + for (NodeId i = 0; i < item_value.size(); ++i) { + // The following '+' enforces integer output for char types + os << +item_value[i] << ' '; + } + os << "\n</DataArray>\n"; +} + +template <size_t N, typename DataType> +void +VTKWriter::_write_node_value(std::ofstream& os, + const std::string& name, + const NodeValue<const TinyVector<N, DataType>>& item_value) const +{ + os << "<DataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\" NumberOfComponents=\"" << N + << "\">\n"; + for (NodeId i = 0; i < item_value.size(); ++i) { + for (size_t j = 0; j < N; ++j) { + // The following '+' enforces integer output for char types + os << +item_value[i][j] << ' '; + } + } + os << "\n</DataArray>\n"; +} + +template <size_t N, typename DataType> +void +VTKWriter::_write_node_value(std::ofstream& os, + const std::string& name, + const NodeValue<const TinyMatrix<N, DataType>>& item_value) const +{ + os << "<DataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\" NumberOfComponents=\"" << N * N + << "\">\n"; + for (NodeId i = 0; i < item_value.size(); ++i) { + for (size_t j = 0; j < N; ++j) { + for (size_t k = 0; k < N; ++k) { + // The following '+' enforces integer output for char types + os << +item_value[i](j, k) << ' '; + } + } + } + os << "\n</DataArray>\n"; +} + +template <typename DataType> +void +VTKWriter::_write_node_value(std::ofstream&, const std::string&, const CellValue<const DataType>&) const +{} + +template <typename DataType> +void +VTKWriter::_write_cell_value(std::ofstream& os, + const std::string& name, + const CellValue<const DataType>& item_value) const +{ + os << "<DataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\">\n"; + for (CellId i = 0; i < item_value.size(); ++i) { + // The following '+' enforces integer output for char types + os << +item_value[i] << ' '; + } + os << "\n</DataArray>\n"; +} + +template <size_t N, typename DataType> +void +VTKWriter::_write_cell_value(std::ofstream& os, + const std::string& name, + const CellValue<const TinyVector<N, DataType>>& item_value) const +{ + os << "<DataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\" NumberOfComponents=\"" << N + << "\">\n"; + for (CellId i = 0; i < item_value.size(); ++i) { + for (size_t j = 0; j < N; ++j) { + // The following '+' enforces integer output for char types + os << +item_value[i][j] << ' '; + } + } + os << "\n</DataArray>\n"; +} + +template <size_t N, typename DataType> +void +VTKWriter::_write_cell_value(std::ofstream& os, + const std::string& name, + const CellValue<const TinyMatrix<N, DataType>>& item_value) const +{ + os << "<DataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\" NumberOfComponents=\"" << N * N + << "\">\n"; + for (CellId i = 0; i < item_value.size(); ++i) { + for (size_t j = 0; j < N; ++j) { + for (size_t k = 0; k < N; ++k) { + // The following '+' enforces integer output for char types + os << +item_value[i](j, k) << ' '; + } + } + } + os << "\n</DataArray>\n"; +} + +template <typename DataType> +void +VTKWriter::_write_cell_value(std::ofstream&, const std::string&, const NodeValue<const DataType>&) const +{} + +template <typename MeshType> +void +VTKWriter::_write(const std::shared_ptr<const MeshType>& mesh, + const OutputNamedItemValueSet& given_output_named_item_value_set, + double time) const +{ + OutputNamedItemValueSet output_named_item_value_set{given_output_named_item_value_set}; + // Adding basic mesh information + output_named_item_value_set.add(NamedItemValue{"cell_number", mesh->connectivity().cellNumber()}); + output_named_item_value_set.add(NamedItemValue{"node_number", mesh->connectivity().nodeNumber()}); + + if (parallel::rank() == 0) { // write PVTK file + std::ofstream fout(_getFilenamePVTU()); + fout << "<?xml version=\"1.0\"?>\n"; + fout << _getDateAndVersionComment(); + fout << "<VTKFile type=\"PUnstructuredGrid\">\n"; + fout << "<PUnstructuredGrid GhostLevel=\"0\">\n"; + + fout << "<PPoints>\n"; + fout << "<PDataArray Name=\"Positions\" NumberOfComponents=\"3\" " + "type=\"Float64\"/>\n"; + fout << "</PPoints>\n"; + + fout << "<PCells>\n"; + fout << "<PDataArray type=\"Int32\" Name=\"connectivity\" " + "NumberOfComponents=\"1\"/>\n"; + fout << "<PDataArray type=\"UInt32\" Name=\"offsets\" " + "NumberOfComponents=\"1\"/>\n"; + fout << "<PDataArray type=\"Int8\" Name=\"types\" " + "NumberOfComponents=\"1\"/>\n"; + for (const auto& [name, item_value_variant] : output_named_item_value_set) { + std::visit([&, name = name](auto&& item_value) { return this->_write_cell_pvtu(fout, name, item_value); }, + item_value_variant); + } + if constexpr (MeshType::Dimension == 3) { + fout << "<PDataArray type=\"Int64\" IdType=\"1\" Name=\"faces\"/>\n"; + fout << "<PDataArray type=\"Int64\" IdType=\"1\" Name=\"faceoffsets\"/>\n"; + } + fout << "</PCells>\n"; + + fout << "<PPointData>\n"; + for (const auto& [name, item_value_variant] : output_named_item_value_set) { + std::visit([&, name = name](auto&& item_value) { return this->_write_node_pvtu(fout, name, item_value); }, + item_value_variant); + } + fout << "</PPointData>\n"; + + fout << "<PCellData>\n"; + for (const auto& [name, item_value_variant] : output_named_item_value_set) { + std::visit([&, name = name](auto&& item_value) { return this->_write_cell_pvtu(fout, name, item_value); }, + item_value_variant); + } + fout << "</PCellData>\n"; + + for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { + fout << "<Piece Source=\"" << _getFilenameVTU(i_rank) << "\"/>\n"; + } + fout << "</PUnstructuredGrid>\n"; + fout << "</VTKFile>\n"; + } + + { // write VTK files + std::ofstream fout(_getFilenameVTU(parallel::rank())); + fout << "<?xml version=\"1.0\"?>\n"; + fout << _getDateAndVersionComment(); + fout << "<VTKFile type=\"UnstructuredGrid\">\n"; + fout << "<UnstructuredGrid>\n"; + fout << "<Piece NumberOfPoints=\"" << mesh->numberOfNodes() << "\" NumberOfCells=\"" << mesh->numberOfCells() + << "\">\n"; + fout << "<CellData>\n"; + for (const auto& [name, item_value_variant] : output_named_item_value_set) { + std::visit([&, name = name](auto&& item_value) { return this->_write_cell_value(fout, name, item_value); }, + item_value_variant); + } + fout << "</CellData>\n"; + fout << "<PointData>\n"; + for (const auto& [name, item_value_variant] : output_named_item_value_set) { + std::visit([&, name = name](auto&& item_value) { return this->_write_node_value(fout, name, item_value); }, + item_value_variant); + } + fout << "</PointData>\n"; + fout << "<Points>\n"; + { + using Rd = TinyVector<MeshType::Dimension>; + const NodeValue<const Rd>& xr = mesh->xr(); + Array<TinyVector<3>> positions(mesh->numberOfNodes()); + parallel_for( + mesh->numberOfNodes(), PUGS_LAMBDA(NodeId r) { + for (unsigned short i = 0; i < MeshType::Dimension; ++i) { + positions[r][i] = xr[r][i]; + } + for (unsigned short i = MeshType::Dimension; i < 3; ++i) { + positions[r][i] = 0; + } + }); + _write_array(fout, "Positions", positions); + } + fout << "</Points>\n"; + + fout << "<Cells>\n"; + { + const auto& cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix(); + + _write_array(fout, "connectivity", cell_to_node_matrix.entries()); + } + + { + const auto& cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix(); + Array<unsigned int> offsets(mesh->numberOfCells()); + unsigned int offset = 0; + for (CellId j = 0; j < mesh->numberOfCells(); ++j) { + const auto& cell_nodes = cell_to_node_matrix[j]; + offset += cell_nodes.size(); + offsets[j] = offset; + } + _write_array(fout, "offsets", offsets); + } + + { + Array<int8_t> types(mesh->numberOfCells()); + const auto& cell_type = mesh->connectivity().cellType(); + const auto& cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix(); + + parallel_for( + mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { + switch (cell_type[j]) { + case CellType::Line: { + types[j] = 3; + break; + } + case CellType::Triangle: { + types[j] = 5; + break; + } + case CellType::Quadrangle: { + types[j] = 9; + break; + } + case CellType::Tetrahedron: { + types[j] = 10; + break; + } + case CellType::Pyramid: { + if (cell_to_node_matrix[j].size() == 5) { + types[j] = 14; // quadrangle basis + } else { + types[j] = 41; // polygonal basis + } + break; + } + case CellType::Prism: { + types[j] = 13; + break; + } + case CellType::Hexahedron: { + types[j] = 12; + break; + } + case CellType::Diamond: { + types[j] = 41; + break; + } + default: { + std::ostringstream os; + os << __FILE__ << ':' << __LINE__ << ": unknown cell type"; + throw UnexpectedError(os.str()); + } + } + }); + _write_array(fout, "types", types); + if constexpr (MeshType::Dimension == 3) { + const bool has_general_polyhedron = [&] { + for (size_t i = 0; i < types.size(); ++i) { + if (types[i] == 41) + return true; + } + return false; + }(); + if (has_general_polyhedron) { + const auto& cell_to_face_matrix = mesh->connectivity().cellToFaceMatrix(); + const auto& face_to_node_matrix = mesh->connectivity().faceToNodeMatrix(); + const auto& cell_face_is_reversed = mesh->connectivity().cellFaceIsReversed(); + + Array<size_t> faces_offsets(mesh->numberOfCells()); + size_t next_offset = 0; + fout << "<DataArray type=\"Int64\" IdType=\"1\" Name=\"faces\">\n"; + for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { + const auto& cell_nodes = cell_to_node_matrix[cell_id]; + std::unordered_map<size_t, size_t> node_id_to_local_number_in_cell; + for (size_t i_cell_node = 0; i_cell_node < cell_nodes.size(); ++i_cell_node) { + node_id_to_local_number_in_cell[cell_nodes[i_cell_node]] = i_cell_node; + } + + const auto& cell_faces = cell_to_face_matrix[cell_id]; + fout << cell_faces.size() << '\n'; + next_offset++; + for (size_t i_cell_face = 0; i_cell_face < cell_faces.size(); ++i_cell_face) { + const FaceId& face_id = cell_faces[i_cell_face]; + const auto& face_nodes = face_to_node_matrix[face_id]; + fout << face_nodes.size(); + next_offset++; + Array<size_t> face_node_in_cell(face_nodes.size()); + + for (size_t i_face_node = 0; i_face_node < face_nodes.size(); ++i_face_node) { + const NodeId& node_id = face_nodes[i_face_node]; + auto i_node_id_to_local_number_in_cell = node_id_to_local_number_in_cell.find(node_id); + Assert(i_node_id_to_local_number_in_cell != node_id_to_local_number_in_cell.end()); + face_node_in_cell[i_face_node] = i_node_id_to_local_number_in_cell->second; + } + + if (cell_face_is_reversed(cell_id, i_cell_face)) { + for (size_t i = 0; i < face_node_in_cell.size(); ++i) { + fout << ' ' << face_node_in_cell[face_node_in_cell.size() - 1 - i]; + } + } else { + for (size_t i = 0; i < face_node_in_cell.size(); ++i) { + fout << ' ' << face_node_in_cell[i]; + } + } + + next_offset += face_nodes.size(); + + fout << '\n'; + } + faces_offsets[cell_id] = next_offset; + } + fout << "</DataArray>\n"; + fout << "<DataArray type=\"Int64\" IdType=\"1\" Name=\"faceoffsets\">\n"; + for (size_t i_face_offsets = 0; i_face_offsets < faces_offsets.size(); ++i_face_offsets) { + fout << faces_offsets[i_face_offsets] << '\n'; + } + fout << "</DataArray>\n"; + } + } + } + + fout << "</Cells>\n"; + fout << "</Piece>\n"; + fout << "</UnstructuredGrid>\n"; + fout << "</VTKFile>\n"; + } + + if (parallel::rank() == 0) { // write PVD file + std::ofstream fout(m_base_filename + ".pvd"); + + fout << "<?xml version=\"1.0\"?>\n"; + fout << "<VTKFile type=\"Collection\" version=\"0.1\">\n"; + + fout << "<Collection>\n"; + for (size_t i_time = 0; i_time < m_saved_times.size(); ++i_time) { + std::ostringstream sout; + sout << m_base_filename; + sout << '.' << std::setfill('0') << std::setw(4) << i_time << ".pvtu"; + + fout << "<DataSet timestep=\"" << m_saved_times[i_time] << "\" " + << "file=\"" << sout.str() << "\"/>\n"; + } + fout << "<DataSet timestep=\"" << time << "\" group=\"\" part=\"0\" file=\"" << _getFilenamePVTU() << "\"/>\n"; + + fout << "</Collection>\n"; + fout << "</VTKFile>\n"; + } +} + +void +VTKWriter::writeMesh(const std::shared_ptr<const IMesh>& mesh) const +{ + OutputNamedItemValueSet output_named_item_value_set; + + switch (mesh->dimension()) { + case 1: { + this->_write(std::dynamic_pointer_cast<const Mesh<Connectivity<1>>>(mesh), output_named_item_value_set, 0); + break; + } + case 2: { + this->_write(std::dynamic_pointer_cast<const Mesh<Connectivity<2>>>(mesh), output_named_item_value_set, 0); + break; + } + case 3: { + this->_write(std::dynamic_pointer_cast<const Mesh<Connectivity<3>>>(mesh), output_named_item_value_set, 0); + break; + } + default: { + throw UnexpectedError("invalid mesh dimension"); + } + } +} + +void +VTKWriter::write(const std::vector<std::shared_ptr<const NamedDiscreteFunction>>& named_discrete_function_list, + double time) const +{ + std::shared_ptr mesh = this->_getMesh(named_discrete_function_list); + + OutputNamedItemValueSet output_named_item_value_set = this->_getOutputNamedItemValueSet(named_discrete_function_list); + + switch (mesh->dimension()) { + case 1: { + this->_write(std::dynamic_pointer_cast<const Mesh<Connectivity<1>>>(mesh), output_named_item_value_set, time); + break; + } + case 2: { + this->_write(std::dynamic_pointer_cast<const Mesh<Connectivity<2>>>(mesh), output_named_item_value_set, time); + break; + } + case 3: { + this->_write(std::dynamic_pointer_cast<const Mesh<Connectivity<3>>>(mesh), output_named_item_value_set, time); + break; + } + default: { + throw UnexpectedError("invalid mesh dimension"); + } + } +} diff --git a/src/output/VTKWriter.hpp b/src/output/VTKWriter.hpp index f3c202ffc51633d291d36150d662fe950023ef58..704ba63f990ead2b6b8e8f64cafdda627c2be64b 100644 --- a/src/output/VTKWriter.hpp +++ b/src/output/VTKWriter.hpp @@ -1,456 +1,110 @@ #ifndef VTK_WRITER_HPP #define VTK_WRITER_HPP +#include <output/WriterBase.hpp> + +#include <algebra/TinyMatrix.hpp> #include <algebra/TinyVector.hpp> -#include <mesh/CellType.hpp> -#include <mesh/IConnectivity.hpp> -#include <mesh/ItemValue.hpp> #include <output/OutputNamedItemValueSet.hpp> -#include <utils/Exceptions.hpp> -#include <utils/Messenger.hpp> -#include <utils/PugsAssert.hpp> - -#include <fstream> -#include <iomanip> -#include <iostream> -#include <sstream> + +class IMesh; + #include <string> -#include <unordered_map> -class VTKWriter +class VTKWriter : public WriterBase { private: - const std::string m_base_filename; - unsigned int m_file_number; - double m_last_time; - const double m_time_period; - - std::string - _getFilenamePVTU() - { - std::ostringstream sout; - sout << m_base_filename; - sout << '.' << std::setfill('0') << std::setw(4) << m_file_number << ".pvtu"; - return sout.str(); - } - std::string - _getFilenameVTU(int rank_number) const - { - std::ostringstream sout; - sout << m_base_filename; - if (parallel::size() > 1) { - sout << '-' << std::setfill('0') << std::setw(4) << rank_number; - } - sout << '.' << std::setfill('0') << std::setw(4) << m_file_number << ".vtu"; - return sout.str(); - } + std::string _getDateAndVersionComment() const; + + std::string _getFilenamePVTU() const; + + std::string _getFilenameVTU(int rank_number) const; template <typename DataType> - void - _write_node_pvtu(std::ofstream& os, const std::string& name, const NodeValue<const DataType>&) - { - os << "<PDataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\"/>\n"; - } + void _write_node_pvtu(std::ofstream& os, const std::string& name, const NodeValue<const DataType>&) const; template <size_t N, typename DataType> - void - _write_node_pvtu(std::ofstream& os, const std::string& name, const NodeValue<const TinyVector<N, DataType>>&) - { - os << "<PDataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\" NumberOfComponents=\"" << N - << "\"/>\n"; - } + void _write_node_pvtu(std::ofstream& os, + const std::string& name, + const NodeValue<const TinyVector<N, DataType>>&) const; + + template <size_t N, typename DataType> + void _write_node_pvtu(std::ofstream& os, + const std::string& name, + const NodeValue<const TinyMatrix<N, DataType>>&) const; template <typename DataType> - void - _write_node_pvtu(std::ofstream&, const std::string&, const CellValue<const DataType>&) - {} + void _write_node_pvtu(std::ofstream&, const std::string&, const CellValue<const DataType>&) const; template <typename DataType> - void - _write_cell_pvtu(std::ofstream& os, const std::string& name, const CellValue<const DataType>&) - { - os << "<PDataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\"/>\n"; - } + void _write_cell_pvtu(std::ofstream& os, const std::string& name, const CellValue<const DataType>&) const; + + template <size_t N, typename DataType> + void _write_cell_pvtu(std::ofstream& os, + const std::string& name, + const CellValue<const TinyVector<N, DataType>>&) const; template <size_t N, typename DataType> - void - _write_cell_pvtu(std::ofstream& os, const std::string& name, const CellValue<const TinyVector<N, DataType>>&) - { - os << "<PDataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\" NumberOfComponents=\"" << N - << "\"/>\n"; - } + void _write_cell_pvtu(std::ofstream& os, + const std::string& name, + const CellValue<const TinyMatrix<N, DataType>>&) const; template <typename DataType> - void - _write_cell_pvtu(std::ofstream&, const std::string&, const NodeValue<const DataType>&) - {} + void _write_cell_pvtu(std::ofstream&, const std::string&, const NodeValue<const DataType>&) const; template <typename DataType> - struct VTKType - { - inline const static std::string name = [] { - static_assert(std::is_arithmetic_v<DataType>, "invalid data type"); - - if constexpr (std::is_integral_v<DataType>) { - if constexpr (std::is_unsigned_v<DataType>) { - return "UInt" + std::to_string(sizeof(DataType) * 8); - } else { - return "UInt" + std::to_string(sizeof(DataType) * 8); - } - } else if constexpr (std::is_floating_point_v<DataType>) { - return "Float" + std::to_string(sizeof(DataType) * 8); - } - }(); - }; + struct VTKType; template <typename DataType> - void - _write_array(std::ofstream& os, const std::string& name, const Array<DataType>& item_value) - { - os << "<DataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\">\n"; - for (typename Array<DataType>::index_type i = 0; i < item_value.size(); ++i) { - // The following '+' enforces integer output for char types - os << +item_value[i] << ' '; - } - os << "\n</DataArray>\n"; - } + void _write_array(std::ofstream& os, const std::string& name, const Array<DataType>& item_value) const; template <size_t N, typename DataType> - void - _write_array(std::ofstream& os, const std::string& name, const Array<TinyVector<N, DataType>>& item_value) - { - os << "<DataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\" NumberOfComponents=\"" << N - << "\">\n"; - for (typename Array<DataType>::index_type i = 0; i < item_value.size(); ++i) { - for (size_t j = 0; j < N; ++j) { - // The following '+' enforces integer output for char types - os << +item_value[i][j] << ' '; - } - } - os << "\n</DataArray>\n"; - } + void _write_array(std::ofstream& os, const std::string& name, const Array<TinyVector<N, DataType>>& item_value) const; template <typename DataType> - void - _write_node_value(std::ofstream& os, const std::string& name, const NodeValue<const DataType>& item_value) - { - os << "<DataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\">\n"; - for (NodeId i = 0; i < item_value.size(); ++i) { - // The following '+' enforces integer output for char types - os << +item_value[i] << ' '; - } - os << "\n</DataArray>\n"; - } + void _write_node_value(std::ofstream& os, const std::string& name, const NodeValue<const DataType>& item_value) const; template <size_t N, typename DataType> - void - _write_node_value(std::ofstream& os, - const std::string& name, - const NodeValue<const TinyVector<N, DataType>>& item_value) - { - os << "<DataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\" NumberOfComponents=\"" << N - << "\">\n"; - for (NodeId i = 0; i < item_value.size(); ++i) { - for (size_t j = 0; j < N; ++j) { - // The following '+' enforces integer output for char types - os << +item_value[i][j] << ' '; - } - } - os << "\n</DataArray>\n"; - } + void _write_node_value(std::ofstream& os, + const std::string& name, + const NodeValue<const TinyVector<N, DataType>>& item_value) const; + + template <size_t N, typename DataType> + void _write_node_value(std::ofstream& os, + const std::string& name, + const NodeValue<const TinyMatrix<N, DataType>>& item_value) const; template <typename DataType> - void - _write_node_value(std::ofstream&, const std::string&, const CellValue<const DataType>&) - {} + void _write_node_value(std::ofstream&, const std::string&, const CellValue<const DataType>&) const; template <typename DataType> - void - _write_cell_value(std::ofstream& os, const std::string& name, const CellValue<const DataType>& item_value) - { - os << "<DataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\">\n"; - for (CellId i = 0; i < item_value.size(); ++i) { - // The following '+' enforces integer output for char types - os << +item_value[i] << ' '; - } - os << "\n</DataArray>\n"; - } + void _write_cell_value(std::ofstream& os, const std::string& name, const CellValue<const DataType>& item_value) const; template <size_t N, typename DataType> - void - _write_cell_value(std::ofstream& os, - const std::string& name, - const CellValue<const TinyVector<N, DataType>>& item_value) - { - os << "<DataArray type=\"" << VTKType<DataType>::name << "\" Name=\"" << name << "\" NumberOfComponents=\"" << N - << "\">\n"; - for (CellId i = 0; i < item_value.size(); ++i) { - for (size_t j = 0; j < N; ++j) { - // The following '+' enforces integer output for char types - os << +item_value[i][j] << ' '; - } - } - os << "\n</DataArray>\n"; - } + void _write_cell_value(std::ofstream& os, + const std::string& name, + const CellValue<const TinyVector<N, DataType>>& item_value) const; + + template <size_t N, typename DataType> + void _write_cell_value(std::ofstream& os, + const std::string& name, + const CellValue<const TinyMatrix<N, DataType>>& item_value) const; template <typename DataType> - void - _write_cell_value(std::ofstream&, const std::string&, const NodeValue<const DataType>&) - {} + void _write_cell_value(std::ofstream&, const std::string&, const NodeValue<const DataType>&) const; - public: template <typename MeshType> - void - write(const std::shared_ptr<const MeshType>& mesh, - const OutputNamedItemValueSet& output_named_item_value_set, - double time, - bool forced_output = false) - { - if (time == m_last_time) - return; // output already performed - if ((time - m_last_time >= m_time_period) or forced_output) { - m_last_time = time; - } else { - return; - } - - if (parallel::rank() == 0) { // write PVTK file - std::ofstream fout(_getFilenamePVTU()); - fout << "<?xml version=\"1.0\"?>\n"; - fout << "<VTKFile type=\"PUnstructuredGrid\">\n"; - fout << "<PUnstructuredGrid GhostLevel=\"0\">\n"; - - fout << "<PPoints>\n"; - fout << "<PDataArray Name=\"Positions\" NumberOfComponents=\"3\" " - "type=\"Float64\"/>\n"; - fout << "</PPoints>\n"; - - fout << "<PCells>\n"; - fout << "<PDataArray type=\"Int32\" Name=\"connectivity\" " - "NumberOfComponents=\"1\"/>\n"; - fout << "<PDataArray type=\"UInt32\" Name=\"offsets\" " - "NumberOfComponents=\"1\"/>\n"; - fout << "<PDataArray type=\"Int8\" Name=\"types\" " - "NumberOfComponents=\"1\"/>\n"; - for (const auto& [name, item_value_variant] : output_named_item_value_set) { - std::visit([&, name = name](auto&& item_value) { return this->_write_cell_pvtu(fout, name, item_value); }, - item_value_variant); - } - if constexpr (MeshType::Dimension == 3) { - fout << "<PDataArray type=\"Int64\" IdType=\"1\" Name=\"faces\"/>\n"; - fout << "<PDataArray type=\"Int64\" IdType=\"1\" Name=\"faceoffsets\"/>\n"; - } - fout << "</PCells>\n"; - - fout << "<PPointData>\n"; - for (const auto& [name, item_value_variant] : output_named_item_value_set) { - std::visit([&, name = name](auto&& item_value) { return this->_write_node_pvtu(fout, name, item_value); }, - item_value_variant); - } - fout << "</PPointData>\n"; - - fout << "<PCellData>\n"; - for (const auto& [name, item_value_variant] : output_named_item_value_set) { - std::visit([&, name = name](auto&& item_value) { return this->_write_cell_pvtu(fout, name, item_value); }, - item_value_variant); - } - fout << "</PCellData>\n"; - - for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { - fout << "<Piece Source=\"" << _getFilenameVTU(i_rank) << "\"/>\n"; - } - fout << "</PUnstructuredGrid>\n"; - fout << "</VTKFile>\n"; - } - - { // write VTK files - std::ofstream fout(_getFilenameVTU(parallel::rank())); - fout << "<?xml version=\"1.0\"?>\n"; - fout << "<VTKFile type=\"UnstructuredGrid\">\n"; - fout << "<UnstructuredGrid>\n"; - fout << "<Piece NumberOfPoints=\"" << mesh->numberOfNodes() << "\" NumberOfCells=\"" << mesh->numberOfCells() - << "\">\n"; - fout << "<CellData>\n"; - for (const auto& [name, item_value_variant] : output_named_item_value_set) { - std::visit([&, name = name](auto&& item_value) { return this->_write_cell_value(fout, name, item_value); }, - item_value_variant); - } - fout << "</CellData>\n"; - fout << "<PointData>\n"; - for (const auto& [name, item_value_variant] : output_named_item_value_set) { - std::visit([&, name = name](auto&& item_value) { return this->_write_node_value(fout, name, item_value); }, - item_value_variant); - } - fout << "</PointData>\n"; - fout << "<Points>\n"; - { - using Rd = TinyVector<MeshType::Dimension>; - const NodeValue<const Rd>& xr = mesh->xr(); - Array<TinyVector<3>> positions(mesh->numberOfNodes()); - parallel_for( - mesh->numberOfNodes(), PUGS_LAMBDA(NodeId r) { - for (unsigned short i = 0; i < MeshType::Dimension; ++i) { - positions[r][i] = xr[r][i]; - } - for (unsigned short i = MeshType::Dimension; i < 3; ++i) { - positions[r][i] = 0; - } - }); - _write_array(fout, "Positions", positions); - } - fout << "</Points>\n"; - - fout << "<Cells>\n"; - { - const auto& cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix(); - - _write_array(fout, "connectivity", cell_to_node_matrix.entries()); - } - - { - const auto& cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix(); - Array<unsigned int> offsets(mesh->numberOfCells()); - unsigned int offset = 0; - for (CellId j = 0; j < mesh->numberOfCells(); ++j) { - const auto& cell_nodes = cell_to_node_matrix[j]; - offset += cell_nodes.size(); - offsets[j] = offset; - } - _write_array(fout, "offsets", offsets); - } - - { - Array<int8_t> types(mesh->numberOfCells()); - const auto& cell_type = mesh->connectivity().cellType(); - const auto& cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix(); - - parallel_for( - mesh->numberOfCells(), PUGS_LAMBDA(CellId j) { - switch (cell_type[j]) { - case CellType::Line: { - types[j] = 3; - break; - } - case CellType::Triangle: { - types[j] = 5; - break; - } - case CellType::Quadrangle: { - types[j] = 9; - break; - } - case CellType::Tetrahedron: { - types[j] = 10; - break; - } - case CellType::Pyramid: { - if (cell_to_node_matrix[j].size() == 5) { - types[j] = 14; // quadrangle basis - } else { - types[j] = 41; // polygonal basis - } - break; - } - case CellType::Prism: { - types[j] = 13; - break; - } - case CellType::Hexahedron: { - types[j] = 12; - break; - } - case CellType::Diamond: { - types[j] = 41; - break; - } - default: { - std::ostringstream os; - os << __FILE__ << ':' << __LINE__ << ": unknown cell type"; - throw UnexpectedError(os.str()); - } - } - }); - _write_array(fout, "types", types); - if constexpr (MeshType::Dimension == 3) { - const bool has_general_polyhedron = [&] { - for (size_t i = 0; i < types.size(); ++i) { - if (types[i] == 41) - return true; - } - return false; - }(); - if (has_general_polyhedron) { - const auto& cell_to_face_matrix = mesh->connectivity().cellToFaceMatrix(); - const auto& face_to_node_matrix = mesh->connectivity().faceToNodeMatrix(); - const auto& cell_face_is_reversed = mesh->connectivity().cellFaceIsReversed(); - - Array<size_t> faces_offsets(mesh->numberOfCells()); - size_t next_offset = 0; - fout << "<DataArray type=\"Int64\" IdType=\"1\" Name=\"faces\">\n"; - for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { - const auto& cell_nodes = cell_to_node_matrix[cell_id]; - std::unordered_map<size_t, size_t> node_id_to_local_number_in_cell; - for (size_t i_cell_node = 0; i_cell_node < cell_nodes.size(); ++i_cell_node) { - node_id_to_local_number_in_cell[cell_nodes[i_cell_node]] = i_cell_node; - } - - const auto& cell_faces = cell_to_face_matrix[cell_id]; - fout << cell_faces.size() << '\n'; - next_offset++; - for (size_t i_cell_face = 0; i_cell_face < cell_faces.size(); ++i_cell_face) { - const FaceId& face_id = cell_faces[i_cell_face]; - const auto& face_nodes = face_to_node_matrix[face_id]; - fout << face_nodes.size(); - next_offset++; - Array<size_t> face_node_in_cell(face_nodes.size()); - - for (size_t i_face_node = 0; i_face_node < face_nodes.size(); ++i_face_node) { - const NodeId& node_id = face_nodes[i_face_node]; - auto i_node_id_to_local_number_in_cell = node_id_to_local_number_in_cell.find(node_id); - Assert(i_node_id_to_local_number_in_cell != node_id_to_local_number_in_cell.end()); - face_node_in_cell[i_face_node] = i_node_id_to_local_number_in_cell->second; - } - - if (cell_face_is_reversed(cell_id, i_cell_face)) { - for (size_t i = 0; i < face_node_in_cell.size(); ++i) { - fout << ' ' << face_node_in_cell[face_node_in_cell.size() - 1 - i]; - } - } else { - for (size_t i = 0; i < face_node_in_cell.size(); ++i) { - fout << ' ' << face_node_in_cell[i]; - } - } - - next_offset += face_nodes.size(); - - fout << '\n'; - } - faces_offsets[cell_id] = next_offset; - } - fout << "</DataArray>\n"; - fout << "<DataArray type=\"Int64\" IdType=\"1\" Name=\"faceoffsets\">\n"; - for (size_t i_face_offsets = 0; i_face_offsets < faces_offsets.size(); ++i_face_offsets) { - fout << faces_offsets[i_face_offsets] << '\n'; - } - fout << "</DataArray>\n"; - } - } - } - - fout << "</Cells>\n"; - fout << "</Piece>\n"; - fout << "</UnstructuredGrid>\n"; - fout << "</VTKFile>\n"; - } - m_file_number++; - } - - VTKWriter(const std::string& base_filename, const double time_period) - : m_base_filename(base_filename), - m_file_number(0), - m_last_time(-std::numeric_limits<double>::max()), - m_time_period(time_period) - {} + void _write(const std::shared_ptr<const MeshType>& mesh, + const OutputNamedItemValueSet& output_named_item_value_set, + double time) const; + + public: + void writeMesh(const std::shared_ptr<const IMesh>& mesh) const final; + + void write(const std::vector<std::shared_ptr<const NamedDiscreteFunction>>& named_discrete_function_list, + double time) const final; + + VTKWriter(const std::string& base_filename, const double time_period) : WriterBase(base_filename, time_period) {} ~VTKWriter() = default; }; diff --git a/src/output/WriterBase.cpp b/src/output/WriterBase.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0359cf7b3d33c8aa9fb33f8b62c671425e5958ba --- /dev/null +++ b/src/output/WriterBase.cpp @@ -0,0 +1,201 @@ +#include <output/WriterBase.hpp> + +#include <mesh/IMesh.hpp> +#include <output/OutputNamedItemValueSet.hpp> +#include <scheme/DiscreteFunctionP0.hpp> +#include <scheme/IDiscreteFunction.hpp> +#include <scheme/IDiscreteFunctionDescriptor.hpp> +#include <utils/Exceptions.hpp> + +template <size_t Dimension, typename DataType> +void +WriterBase::registerDiscreteFunctionP0(const std::string& name, + const IDiscreteFunction& i_discrete_function, + OutputNamedItemValueSet& named_item_value_set) +{ + const DiscreteFunctionP0<Dimension, DataType>& discrete_function = + dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(i_discrete_function); + named_item_value_set.add(NamedItemValue{name, discrete_function.cellValues()}); +} + +template <size_t Dimension> +void +WriterBase::registerDiscreteFunctionP0(const std::string& name, + const IDiscreteFunction& i_discrete_function, + OutputNamedItemValueSet& named_item_value_set) +{ + const ASTNodeDataType& data_type = i_discrete_function.dataType(); + switch (data_type) { + case ASTNodeDataType::bool_t: { + registerDiscreteFunctionP0<Dimension, bool>(name, i_discrete_function, named_item_value_set); + break; + } + case ASTNodeDataType::unsigned_int_t: { + registerDiscreteFunctionP0<Dimension, uint64_t>(name, i_discrete_function, named_item_value_set); + break; + } + case ASTNodeDataType::int_t: { + registerDiscreteFunctionP0<Dimension, int64_t>(name, i_discrete_function, named_item_value_set); + break; + } + case ASTNodeDataType::double_t: { + registerDiscreteFunctionP0<Dimension, double>(name, i_discrete_function, named_item_value_set); + break; + } + case ASTNodeDataType::vector_t: { + switch (data_type.dimension()) { + case 1: { + registerDiscreteFunctionP0<Dimension, TinyVector<1, double>>(name, i_discrete_function, named_item_value_set); + break; + } + case 2: { + registerDiscreteFunctionP0<Dimension, TinyVector<2, double>>(name, i_discrete_function, named_item_value_set); + break; + } + case 3: { + registerDiscreteFunctionP0<Dimension, TinyVector<3, double>>(name, i_discrete_function, named_item_value_set); + break; + } + default: { + throw UnexpectedError("invalid vector dimension"); + } + } + break; + } + case ASTNodeDataType::matrix_t: { + Assert(data_type.nbRows() == data_type.nbColumns(), "invalid matrix dimensions"); + switch (data_type.nbRows()) { + case 1: { + registerDiscreteFunctionP0<Dimension, TinyMatrix<1, double>>(name, i_discrete_function, named_item_value_set); + break; + } + case 2: { + registerDiscreteFunctionP0<Dimension, TinyMatrix<2, double>>(name, i_discrete_function, named_item_value_set); + break; + } + case 3: { + registerDiscreteFunctionP0<Dimension, TinyMatrix<3, double>>(name, i_discrete_function, named_item_value_set); + break; + } + default: { + throw UnexpectedError("invalid matrix dimension"); + } + } + break; + } + default: { + throw UnexpectedError("invalid data type " + dataTypeName(data_type)); + } + } +} + +void +WriterBase::registerDiscreteFunctionP0(const NamedDiscreteFunction& named_discrete_function, + OutputNamedItemValueSet& named_item_value_set) +{ + const IDiscreteFunction& i_discrete_function = *named_discrete_function.discreteFunction(); + const std::string& name = named_discrete_function.name(); + switch (i_discrete_function.mesh()->dimension()) { + case 1: { + registerDiscreteFunctionP0<1>(name, i_discrete_function, named_item_value_set); + break; + } + case 2: { + registerDiscreteFunctionP0<2>(name, i_discrete_function, named_item_value_set); + break; + } + case 3: { + registerDiscreteFunctionP0<3>(name, i_discrete_function, named_item_value_set); + break; + } + default: { + throw UnexpectedError("invalid mesh dimension"); + } + } +} + +std::shared_ptr<const IMesh> +WriterBase::_getMesh( + const std::vector<std::shared_ptr<const NamedDiscreteFunction>>& named_discrete_function_list) const +{ + Assert(named_discrete_function_list.size() > 0); + + std::shared_ptr mesh = named_discrete_function_list[0]->discreteFunction()->mesh(); + for (size_t i = 1; i < named_discrete_function_list.size(); ++i) { + if (mesh != named_discrete_function_list[i]->discreteFunction()->mesh()) { + std::ostringstream error_msg; + error_msg << "discrete functions must be defined on the same mesh!\n" + << rang::fgB::yellow << "note:" << rang::style::reset << " cannot write " << rang::fgB::blue + << named_discrete_function_list[0]->name() << rang::style::reset << " and " << rang::fgB::blue + << named_discrete_function_list[i]->name() << rang::style::reset << " in the same file."; + throw NormalError(error_msg.str()); + } + } + + return mesh; +} + +OutputNamedItemValueSet +WriterBase::_getOutputNamedItemValueSet( + const std::vector<std::shared_ptr<const NamedDiscreteFunction>>& named_discrete_function_list) const +{ + OutputNamedItemValueSet named_item_value_set; + + for (auto& named_discrete_function : named_discrete_function_list) { + const IDiscreteFunction& i_discrete_function = *named_discrete_function->discreteFunction(); + + switch (i_discrete_function.descriptor().type()) { + case DiscreteFunctionType::P0: { + WriterBase::registerDiscreteFunctionP0(*named_discrete_function, named_item_value_set); + break; + } + default: { + std::ostringstream error_msg; + error_msg << "the type of discrete function of " << rang::fgB::blue << named_discrete_function->name() + << rang::style::reset << " is not supported"; + throw NormalError(error_msg.str()); + } + } + } + + return named_item_value_set; +} + +double +WriterBase::getLastTime() const +{ + if (m_saved_times.size() > 0) { + return m_saved_times[m_saved_times.size() - 1]; + } else { + return -std::numeric_limits<double>::max(); + } +} + +WriterBase::WriterBase(const std::string& base_filename, const double& time_period) + : m_base_filename{base_filename}, m_time_period{time_period} +{} + +void +WriterBase::writeIfNeeded(const std::vector<std::shared_ptr<const NamedDiscreteFunction>>& named_discrete_function_list, + double time) const +{ + const double last_time = getLastTime(); + if (time == last_time) + return; // output already performed + + if (time >= last_time + m_time_period) { + this->write(named_discrete_function_list, time); + m_saved_times.push_back(time); + } +} + +void +WriterBase::writeForced(const std::vector<std::shared_ptr<const NamedDiscreteFunction>>& named_discrete_function_list, + double time) const +{ + if (time == getLastTime()) + return; // output already performed + + this->write(named_discrete_function_list, time); + m_saved_times.push_back(time); +} diff --git a/src/output/WriterBase.hpp b/src/output/WriterBase.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6e9a08ad123361178327ddb984e5a3da5bdfa909 --- /dev/null +++ b/src/output/WriterBase.hpp @@ -0,0 +1,54 @@ +#ifndef WRITER_BASE_HPP +#define WRITER_BASE_HPP + +#include <output/IWriter.hpp> + +#include <string> + +class IMesh; +class OutputNamedItemValueSet; + +class WriterBase : public IWriter +{ + protected: + const std::string m_base_filename; + const double m_time_period; + + mutable std::vector<double> m_saved_times; + + private: + template <size_t Dimension, typename DataType> + static void registerDiscreteFunctionP0(const std::string& name, const IDiscreteFunction&, OutputNamedItemValueSet&); + + template <size_t Dimension> + static void registerDiscreteFunctionP0(const std::string& name, const IDiscreteFunction&, OutputNamedItemValueSet&); + + static void registerDiscreteFunctionP0(const NamedDiscreteFunction&, OutputNamedItemValueSet&); + + protected: + std::shared_ptr<const IMesh> _getMesh( + const std::vector<std::shared_ptr<const NamedDiscreteFunction>>& named_discrete_function_list) const; + + OutputNamedItemValueSet _getOutputNamedItemValueSet( + const std::vector<std::shared_ptr<const NamedDiscreteFunction>>& named_discrete_function_list) const; + + virtual void write(const std::vector<std::shared_ptr<const NamedDiscreteFunction>>& named_discrete_function_list, + double time) const = 0; + + public: + double getLastTime() const; + + void writeIfNeeded(const std::vector<std::shared_ptr<const NamedDiscreteFunction>>& named_discrete_function_list, + double time) const final; + + void writeForced(const std::vector<std::shared_ptr<const NamedDiscreteFunction>>& named_discrete_function_list, + double time) const final; + + WriterBase() = delete; + + WriterBase(const std::string& base_filename, const double& time_period); + + virtual ~WriterBase() = default; +}; + +#endif // WRITER_BASE_HPP diff --git a/src/scheme/CMakeLists.txt b/src/scheme/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..1467393f2cbcbd9e31dd495e2905e9be478a1d78 --- /dev/null +++ b/src/scheme/CMakeLists.txt @@ -0,0 +1,5 @@ +# ------------------- Source files -------------------- + +add_library( + PugsScheme + DiscreteFunctionInterpoler.cpp) diff --git a/src/scheme/DiscreteFunctionDescriptorP0.hpp b/src/scheme/DiscreteFunctionDescriptorP0.hpp new file mode 100644 index 0000000000000000000000000000000000000000..97bfbbf729d4224dbb7af41bf61d489c5a3080cb --- /dev/null +++ b/src/scheme/DiscreteFunctionDescriptorP0.hpp @@ -0,0 +1,24 @@ +#ifndef DISCRETE_FUNCTION_DESCRIPTOR_P_0HPP +#define DISCRETE_FUNCTION_DESCRIPTOR_P_0HPP + +#include <scheme/IDiscreteFunctionDescriptor.hpp> + +class DiscreteFunctionDescriptorP0 : public IDiscreteFunctionDescriptor +{ + public: + DiscreteFunctionType + type() const final + { + return DiscreteFunctionType::P0; + } + + DiscreteFunctionDescriptorP0() noexcept = default; + + DiscreteFunctionDescriptorP0(const DiscreteFunctionDescriptorP0&) = default; + + DiscreteFunctionDescriptorP0(DiscreteFunctionDescriptorP0&&) noexcept = default; + + ~DiscreteFunctionDescriptorP0() noexcept = default; +}; + +#endif // DISCRETE_FUNCTION_DESCRIPTOR_P_0HPP diff --git a/src/scheme/DiscreteFunctionInterpoler.cpp b/src/scheme/DiscreteFunctionInterpoler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fda64fdcb82d1eb36b8925e9cdc438989cd92306 --- /dev/null +++ b/src/scheme/DiscreteFunctionInterpoler.cpp @@ -0,0 +1,103 @@ +#include <scheme/DiscreteFunctionInterpoler.hpp> + +#include <scheme/DiscreteFunctionP0.hpp> +#include <utils/Exceptions.hpp> + +template <size_t Dimension, typename DataType> +std::shared_ptr<IDiscreteFunction> +DiscreteFunctionInterpoler::_interpolate() const +{ + std::shared_ptr mesh = std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(m_mesh); + return std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh, m_function_id); +} + +template <size_t Dimension> +std::shared_ptr<IDiscreteFunction> +DiscreteFunctionInterpoler::_interpolate() const +{ + const auto& function_descriptor = m_function_id.descriptor(); + Assert(function_descriptor.domainMappingNode().children[1]->m_data_type == ASTNodeDataType::typename_t); + + const ASTNodeDataType& data_type = function_descriptor.domainMappingNode().children[1]->m_data_type.contentType(); + + switch (data_type) { + case ASTNodeDataType::bool_t: { + return this->_interpolate<Dimension, bool>(); + } + case ASTNodeDataType::unsigned_int_t: { + return this->_interpolate<Dimension, uint64_t>(); + } + case ASTNodeDataType::int_t: { + return this->_interpolate<Dimension, int64_t>(); + } + case ASTNodeDataType::double_t: { + return this->_interpolate<Dimension, double>(); + } + case ASTNodeDataType::vector_t: { + switch (data_type.dimension()) { + case 1: { + return this->_interpolate<Dimension, TinyVector<1>>(); + } + case 2: { + return this->_interpolate<Dimension, TinyVector<2>>(); + } + case 3: { + return this->_interpolate<Dimension, TinyVector<3>>(); + } + default: { + std::ostringstream os; + os << "invalid vector dimension " << rang::fgB::red << data_type.dimension() << rang::style::reset; + + throw UnexpectedError(os.str()); + } + } + } + case ASTNodeDataType::matrix_t: { + Assert(data_type.nbColumns() == data_type.nbRows(), "undefined matrix type"); + switch (data_type.nbColumns()) { + case 1: { + return this->_interpolate<Dimension, TinyMatrix<1>>(); + } + case 2: { + return this->_interpolate<Dimension, TinyMatrix<2>>(); + } + case 3: { + return this->_interpolate<Dimension, TinyMatrix<3>>(); + } + default: { + std::ostringstream os; + os << "invalid vector dimension " << rang::fgB::red << data_type.dimension() << rang::style::reset; + + throw UnexpectedError(os.str()); + } + } + } + default: { + std::ostringstream os; + os << "invalid interpolation value type: " << rang::fgB::red << dataTypeName(data_type) << rang::style::reset; + + throw UnexpectedError(os.str()); + } + } +} + +std::shared_ptr<IDiscreteFunction> +DiscreteFunctionInterpoler::interpolate() const +{ + std::shared_ptr<IDiscreteFunction> discrete_function; + switch (m_mesh->dimension()) { + case 1: { + return this->_interpolate<1>(); + } + case 2: { + return this->_interpolate<2>(); + } + case 3: { + return this->_interpolate<3>(); + } + default: { + throw UnexpectedError("invalid dimension"); + } + } + return nullptr; +} diff --git a/src/scheme/DiscreteFunctionInterpoler.hpp b/src/scheme/DiscreteFunctionInterpoler.hpp new file mode 100644 index 0000000000000000000000000000000000000000..758ff2454ec673ba2cc46c0c561eca1a4a256b51 --- /dev/null +++ b/src/scheme/DiscreteFunctionInterpoler.hpp @@ -0,0 +1,39 @@ +#ifndef DISCRETE_FUNCTION_INTERPOLER_HPP +#define DISCRETE_FUNCTION_INTERPOLER_HPP + +#include <language/utils/FunctionSymbolId.hpp> +#include <mesh/IMesh.hpp> +#include <scheme/IDiscreteFunction.hpp> +#include <scheme/IDiscreteFunctionDescriptor.hpp> + +#include <memory> + +class DiscreteFunctionInterpoler +{ + private: + std::shared_ptr<const IMesh> m_mesh; + std::shared_ptr<const IDiscreteFunctionDescriptor> m_discrete_function_descriptor; + const FunctionSymbolId m_function_id; + + template <size_t Dimension, typename DataType> + std::shared_ptr<IDiscreteFunction> _interpolate() const; + + template <size_t Dimension> + std::shared_ptr<IDiscreteFunction> _interpolate() const; + + public: + std::shared_ptr<IDiscreteFunction> interpolate() const; + + DiscreteFunctionInterpoler(const std::shared_ptr<const IMesh>& mesh, + const std::shared_ptr<const IDiscreteFunctionDescriptor>& discrete_function_descriptor, + const FunctionSymbolId& function_id) + : m_mesh{mesh}, m_discrete_function_descriptor{discrete_function_descriptor}, m_function_id{function_id} + {} + + DiscreteFunctionInterpoler(const DiscreteFunctionInterpoler&) = delete; + DiscreteFunctionInterpoler(DiscreteFunctionInterpoler&&) = delete; + + ~DiscreteFunctionInterpoler() = default; +}; + +#endif // DISCRETE_FUNCTION_INTERPOLER_HPP diff --git a/src/scheme/DiscreteFunctionP0.hpp b/src/scheme/DiscreteFunctionP0.hpp new file mode 100644 index 0000000000000000000000000000000000000000..bf97079b776c80fec10e26608cfc6ff5e40a664a --- /dev/null +++ b/src/scheme/DiscreteFunctionP0.hpp @@ -0,0 +1,72 @@ +#ifndef DISCRETE_FUNCTION_P0_HPP +#define DISCRETE_FUNCTION_P0_HPP + +#include <scheme/IDiscreteFunction.hpp> + +#include <language/utils/InterpolateItemValue.hpp> +#include <mesh/Connectivity.hpp> +#include <mesh/Mesh.hpp> +#include <mesh/MeshData.hpp> +#include <mesh/MeshDataManager.hpp> +#include <scheme/DiscreteFunctionDescriptorP0.hpp> + +template <size_t Dimension, typename DataType> +class DiscreteFunctionP0 : public IDiscreteFunction +{ + private: + using MeshType = Mesh<Connectivity<Dimension>>; + + std::shared_ptr<const MeshType> m_mesh; + CellValue<DataType> m_cell_values; + + DiscreteFunctionDescriptorP0 m_discrete_function_descriptor; + + public: + ASTNodeDataType + dataType() const final + { + return ast_node_data_type_from<DataType>; + } + + CellValue<DataType> + cellValues() const + { + return m_cell_values; + } + + std::shared_ptr<const IMesh> + mesh() const + { + return m_mesh; + } + + const IDiscreteFunctionDescriptor& + descriptor() const final + { + return m_discrete_function_descriptor; + } + + PUGS_FORCEINLINE + DataType& + operator[](const CellId& cell_id) const noexcept(NO_ASSERT) + { + return m_cell_values[cell_id]; + } + + DiscreteFunctionP0(const std::shared_ptr<const MeshType>& mesh, const FunctionSymbolId& function_id) : m_mesh(mesh) + { + using MeshDataType = MeshData<Dimension>; + MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh); + + m_cell_values = + InterpolateItemValue<DataType(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(function_id, + mesh_data.xj()); + } + + DiscreteFunctionP0(const DiscreteFunctionP0&) noexcept = default; + DiscreteFunctionP0(DiscreteFunctionP0&&) noexcept = default; + + ~DiscreteFunctionP0() = default; +}; + +#endif // DISCRETE_FUNCTION_P0_HPP diff --git a/src/scheme/DiscreteFunctionType.hpp b/src/scheme/DiscreteFunctionType.hpp new file mode 100644 index 0000000000000000000000000000000000000000..debf863499df64cd161ac0b7c20a1a5ce4657107 --- /dev/null +++ b/src/scheme/DiscreteFunctionType.hpp @@ -0,0 +1,9 @@ +#ifndef DISCRETE_FUNCTION_TYPE_HPP +#define DISCRETE_FUNCTION_TYPE_HPP + +enum class DiscreteFunctionType +{ + P0 +}; + +#endif // DISCRETE_FUNCTION_TYPE_HPP diff --git a/src/scheme/IDiscreteFunction.hpp b/src/scheme/IDiscreteFunction.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a8bf77f43efb48b03a8967eb16d81966fde86570 --- /dev/null +++ b/src/scheme/IDiscreteFunction.hpp @@ -0,0 +1,26 @@ +#ifndef I_DISCRETE_FUNCTION_HPP +#define I_DISCRETE_FUNCTION_HPP + +class IMesh; +class IDiscreteFunctionDescriptor; + +#include <language/utils/ASTNodeDataTypeTraits.hpp> +#include <memory> + +class IDiscreteFunction +{ + public: + virtual std::shared_ptr<const IMesh> mesh() const = 0; + virtual const IDiscreteFunctionDescriptor& descriptor() const = 0; + virtual ASTNodeDataType dataType() const = 0; + + IDiscreteFunction() = default; + + IDiscreteFunction(const IDiscreteFunction&) = default; + + IDiscreteFunction(IDiscreteFunction&&) noexcept = default; + + virtual ~IDiscreteFunction() noexcept = default; +}; + +#endif // I_DISCRETE_FUNCTION_HPP diff --git a/src/scheme/IDiscreteFunctionDescriptor.hpp b/src/scheme/IDiscreteFunctionDescriptor.hpp new file mode 100644 index 0000000000000000000000000000000000000000..19d411507e087c0e3262f8e712aa393d96796b20 --- /dev/null +++ b/src/scheme/IDiscreteFunctionDescriptor.hpp @@ -0,0 +1,20 @@ +#ifndef I_DISCRETE_FUNCTION_DESCRIPTOR_HPP +#define I_DISCRETE_FUNCTION_DESCRIPTOR_HPP + +#include <scheme/DiscreteFunctionType.hpp> + +class IDiscreteFunctionDescriptor +{ + public: + virtual DiscreteFunctionType type() const = 0; + + IDiscreteFunctionDescriptor() noexcept = default; + + IDiscreteFunctionDescriptor(const IDiscreteFunctionDescriptor&) = default; + + IDiscreteFunctionDescriptor(IDiscreteFunctionDescriptor&&) noexcept = default; + + virtual ~IDiscreteFunctionDescriptor() noexcept = default; +}; + +#endif // I_DISCRETE_FUNCTION_DESCRIPTOR_HPP diff --git a/src/utils/PugsTraits.hpp b/src/utils/PugsTraits.hpp index f51b455cb5ea4efa678dd980d1fec2721ab1fd2a..e1dc8b48d6b15fa05ac9b9b9378385e351fadb2b 100644 --- a/src/utils/PugsTraits.hpp +++ b/src/utils/PugsTraits.hpp @@ -3,6 +3,7 @@ #include <cstddef> #include <memory> +#include <tuple> #include <type_traits> #include <variant> #include <vector> @@ -40,7 +41,7 @@ inline constexpr bool is_shared_ptr_v = false; template <typename T> inline constexpr bool is_shared_ptr_v<std::shared_ptr<T>> = true; -// Traits is_shared_ptr +// Traits is_unique_ptr template <typename T> inline constexpr bool is_unique_ptr_v = false; @@ -76,6 +77,14 @@ inline constexpr bool is_std_vector_v = false; template <typename T> inline constexpr bool is_std_vector_v<std::vector<T>> = true; +// Traits is_std_tuple + +template <typename... T> +inline constexpr bool is_std_tuple_v = false; + +template <typename... T> +inline constexpr bool is_std_tuple_v<std::tuple<T...>> = true; + // Traits is_tiny_vector template <typename T> diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2ae89a2790455514249a6097ff4dbe748e7a0839..c24b57517dc2261a930605823d0c1a736d6c6a2d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -116,6 +116,8 @@ target_link_libraries (unit_tests PugsLanguage PugsMesh PugsAlgebra + PugsScheme + PugsOutput PugsUtils kokkos ${PARMETIS_LIBRARIES} @@ -137,7 +139,10 @@ target_link_libraries (mpi_unit_tests PugsMesh PugsAlgebra PugsUtils - PugsLanguageUtils PugsUtils + PugsLanguageUtils + PugsScheme + PugsOutput + PugsUtils PugsAlgebra PugsMesh kokkos diff --git a/tests/test_BuiltinFunctionEmbedder.cpp b/tests/test_BuiltinFunctionEmbedder.cpp index 2651c0e94659c1bbffc0bd47e6ab946cce18e3c9..ba88ac0b0a0e3d6e7c3a16977e03437a1e14a05c 100644 --- a/tests/test_BuiltinFunctionEmbedder.cpp +++ b/tests/test_BuiltinFunctionEmbedder.cpp @@ -286,6 +286,62 @@ TEST_CASE("BuiltinFunctionEmbedder", "[language]") REQUIRE(i_embedded_c->getReturnDataType() == ASTNodeDataType::double_t); } + SECTION("R*R -> R*R^2*shared_double BuiltinFunctionEmbedder") + { + std::function c = [](double a, double b) -> std::tuple<double, TinyVector<2>, std::shared_ptr<double>> { + return std::make_tuple(a + b, TinyVector<2>{b, -a}, std::make_shared<double>(a - b)); + }; + + std::unique_ptr<IBuiltinFunctionEmbedder> i_embedded_c = std::make_unique< + BuiltinFunctionEmbedder<std::tuple<double, TinyVector<2>, std::shared_ptr<double>>(double, double)>>(c); + + const double a = 3.2; + const double b = 1.5; + + REQUIRE(a + b == std::get<0>(c(a, b))); + REQUIRE(TinyVector<2>{b, -a} == std::get<1>(c(a, b))); + REQUIRE(a - b == *std::get<2>(c(a, b))); + const AggregateDataVariant value_list = + std::get<AggregateDataVariant>(i_embedded_c->apply(std::vector<DataVariant>{a, b})); + + REQUIRE(std::get<double>(value_list[0]) == a + b); + REQUIRE(std::get<TinyVector<2>>(value_list[1]) == TinyVector<2>{b, -a}); + auto data_type = i_embedded_c->getReturnDataType(); + REQUIRE(data_type == ASTNodeDataType::list_t); + + REQUIRE(*data_type.contentTypeList()[0] == ASTNodeDataType::double_t); + REQUIRE(*data_type.contentTypeList()[1] == ASTNodeDataType::build<ASTNodeDataType::vector_t>(2)); + REQUIRE(*data_type.contentTypeList()[2] == ast_node_data_type_from<std::shared_ptr<double>>); + } + + SECTION("void -> N*R*shared_double BuiltinFunctionEmbedder") + { + std::function c = [](void) -> std::tuple<uint64_t, double, std::shared_ptr<double>> { + uint64_t a = 1; + double b = 3.5; + return std::make_tuple(a, b, std::make_shared<double>(a + b)); + }; + + std::unique_ptr<IBuiltinFunctionEmbedder> i_embedded_c = + std::make_unique<BuiltinFunctionEmbedder<std::tuple<uint64_t, double, std::shared_ptr<double>>(void)>>(c); + + REQUIRE(1ul == std::get<0>(c())); + REQUIRE(3.5 == std::get<1>(c())); + REQUIRE((1ul + 3.5) == *std::get<2>(c())); + const AggregateDataVariant value_list = + std::get<AggregateDataVariant>(i_embedded_c->apply(std::vector<DataVariant>{})); + + REQUIRE(std::get<uint64_t>(value_list[0]) == 1ul); + REQUIRE(std::get<double>(value_list[1]) == 3.5); + + auto data_type = i_embedded_c->getReturnDataType(); + REQUIRE(data_type == ASTNodeDataType::list_t); + + REQUIRE(*data_type.contentTypeList()[0] == ASTNodeDataType::unsigned_int_t); + REQUIRE(*data_type.contentTypeList()[1] == ASTNodeDataType::double_t); + REQUIRE(*data_type.contentTypeList()[2] == ast_node_data_type_from<std::shared_ptr<double>>); + } + SECTION("void(void) BuiltinFunctionEmbedder") { std::function c = [](void) -> void {};