diff --git a/applications/MedApplication/CMakeLists.txt b/applications/MedApplication/CMakeLists.txt new file mode 100644 index 000000000000..0049e0ff248f --- /dev/null +++ b/applications/MedApplication/CMakeLists.txt @@ -0,0 +1,83 @@ +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +message("**** configuring KratosMedApplication ****") + +################### PYBIND11 +include(pybind11Tools) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) # for FindMED +find_package(MED REQUIRED) + +# MED uses HDF +# TODO check interoperability with the HDFapp, both in serial and parallel +if (NOT HDF5_FOUND) + find_package(HDF5 REQUIRED COMPONENTS C) +endif() + +include_directories(${MED_INCLUDE_DIR} ${HDF5_INCLUDE_DIRS}) + +include_directories( ${KRATOS_SOURCE_DIR}/kratos ) + +## Med Core sources +file(GLOB_RECURSE KRATOS_MED_APPLICATION_CORE + ${CMAKE_CURRENT_SOURCE_DIR}/med_application.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/custom_io/*.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/custom_utilities/*.cpp +) + +## Med testing sources +if(${KRATOS_BUILD_TESTING} MATCHES ON) + file(GLOB_RECURSE KRATOS_MED_APPLICATION_TESTING_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/tests/*.cpp) +endif(${KRATOS_BUILD_TESTING} MATCHES ON) + +## Med python interface sources +file(GLOB_RECURSE KRATOS_MED_APPLICATION_PYTHON_INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/custom_python/*.cpp) + +add_library(KratosMedCore SHARED ${KRATOS_MED_APPLICATION_CORE} ${KRATOS_MED_APPLICATION_TESTING_SOURCES}) +target_link_libraries(KratosMedCore PUBLIC KratosCore ${MED_LIBRARIES} ${HDF5_C_LIBRARIES}) +set_target_properties(KratosMedCore PROPERTIES COMPILE_DEFINITIONS "MED_APPLICATION=EXPORT,API") +target_include_directories(KratosMedCore SYSTEM PRIVATE hdf5::hdf5) + + +############################################################### +## define library Kratos which defines the basic python interface +pybind11_add_module(KratosMedApplication MODULE THIN_LTO ${KRATOS_MED_APPLICATION_PYTHON_INTERFACE}) +target_link_libraries(KratosMedApplication PRIVATE KratosMedCore) +set_target_properties(KratosMedApplication PROPERTIES PREFIX "") + +# changing the .dll suffix to .pyd (Windows) +if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") + set_target_properties(KratosMedApplication PROPERTIES SUFFIX .pyd) +endif(${CMAKE_SYSTEM_NAME} MATCHES "Windows") + +# changing the .dylib suffix to .so (OS X) +if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set_target_properties(KratosMedApplication PROPERTIES SUFFIX .so) +endif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + +# Add to the KratosMultiphisics Python module +kratos_python_install(${INSTALL_PYTHON_USING_LINKS} ${CMAKE_CURRENT_SOURCE_DIR}/MedApplication.py KratosMultiphysics/MedApplication/__init__.py ) + +# Install python files +get_filename_component (CURRENT_DIR_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) +kratos_python_install_directory(${INSTALL_PYTHON_USING_LINKS} ${CMAKE_CURRENT_SOURCE_DIR}/python_scripts KratosMultiphysics/${CURRENT_DIR_NAME} ) + +# Kratos Testing. Install everything except sources to ensure that reference and configuration files are copied. +if(${INSTALL_TESTING_FILES} MATCHES ON ) + get_filename_component (CURRENT_DIR_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests DESTINATION applications/${CURRENT_DIR_NAME} + PATTERN "*.git" EXCLUDE + PATTERN "*.c" EXCLUDE + PATTERN "*.h" EXCLUDE + PATTERN "*.cpp" EXCLUDE + PATTERN "*.hpp" EXCLUDE + ) +endif(${INSTALL_TESTING_FILES} MATCHES ON) + +# Install targets +install(TARGETS KratosMedCore DESTINATION libs ) +install(TARGETS KratosMedApplication DESTINATION libs ) + +# Define custom targets +set(KRATOS_KERNEL "${KRATOS_KERNEL};KratosMedCore" PARENT_SCOPE) +set(KRATOS_PYTHON_INTERFACE "${KRATOS_PYTHON_INTERFACE};KratosMedApplication" PARENT_SCOPE) diff --git a/applications/MedApplication/FindMED.cmake b/applications/MedApplication/FindMED.cmake new file mode 100644 index 000000000000..547acc0a2e68 --- /dev/null +++ b/applications/MedApplication/FindMED.cmake @@ -0,0 +1,38 @@ + +# include(FindPackageHandleStandardArgs) +# find_package_handle_standard_args + + + + + + +# Find the MED includes and libraries +# +# MED_INCLUDE_DIR - where to find autopack.h +# MED_LIBRARIES - List of fully qualified libraries to link against. +# MED_FOUND - Do not attempt to use if "no" or undefined. + +FIND_PATH(MED_INCLUDE_DIR "med.h" + PATHS + "${MED_ROOT}/include" + /usr/local/include + /usr/include +) + +FIND_LIBRARY(MED_LIBRARY medC + PATHS + "${MED_ROOT}/lib" + /usr/local/lib/* + /usr/lib/* +) + +IF(MED_INCLUDE_DIR AND MED_LIBRARY) + SET( MED_LIBRARIES ${MED_LIBRARY}) + SET( MED_FOUND "YES" ) + message(STATUS "MED found!") + message(STATUS "MED includes: ${MED_INCLUDE_DIR}") + message(STATUS "MED libraries: ${MED_LIBRARY}") +else() + message(WARNING "finding MED failed, please try to set the var MED_ROOT") +ENDIF() diff --git a/applications/MedApplication/MedApplication.py b/applications/MedApplication/MedApplication.py new file mode 100644 index 000000000000..abfda436cbbd --- /dev/null +++ b/applications/MedApplication/MedApplication.py @@ -0,0 +1,7 @@ +# Application dependent names and paths +from KratosMultiphysics import _ImportApplication +from KratosMedApplication import * +application = KratosMedApplication() +application_name = "KratosMedApplication" + +_ImportApplication(application, application_name) diff --git a/applications/MedApplication/README.md b/applications/MedApplication/README.md new file mode 100644 index 000000000000..a59748b918a0 --- /dev/null +++ b/applications/MedApplication/README.md @@ -0,0 +1,12 @@ +# MedApplication + +The Med Application an interface to the MED-library. This library writes med-files, which contain mesh, field results and other data, and is based on [HDF5](https://www.hdfgroup.org/solutions/hdf5/). This format is used by [Salome](https://www.salome-platform.org/) and [Code_Aster](https://code-aster.org). + +## Installation +The MED-library is an external library, which must be installed before the application can be compiled + +On Ubuntu, it can be installed with `sudo apt-get install libmedc-dev`. This installs all required dependencies, including HDF5 + +The source code is available on the Salome website for a manual installation. In this case also HDF5 needs to be installed separately. + +Use `MED_ROOT` to specify the path to the MED installation in the CMake of Kratos. diff --git a/applications/MedApplication/custom_io/med_model_part_io.cpp b/applications/MedApplication/custom_io/med_model_part_io.cpp new file mode 100644 index 000000000000..6652e7482888 --- /dev/null +++ b/applications/MedApplication/custom_io/med_model_part_io.cpp @@ -0,0 +1,600 @@ +// KRATOS __ __ _ _ _ _ _ _ +// | \/ | ___ __| | / \ _ __ _ __ | (_) ___ __ _| |_(_) ___ _ ___ +// | |\/| |/ _ \/ _` | / _ \ | '_ \| '_ \| | |/ __/ _` | __| |/ _ \| '_ | +// | | | | __/ (_| |/ ___ \| |_) | |_) | | | (_| (_| | |_| | (_) | | | | +// |_| |_|\___|\__,_/_/ \_\ .__/| .__/|_|_|\___\__,_|\__|_|\___/|_| |_| +// |_| |_| +// License: BSD License +// Kratos default license: kratos/license.txt +// +// Main authors: Philipp Bucher (https://github.com/philbucher) +// + +// System includes +#include + +// External includes + +// Project includes +#include "med_inc.h" +#include "med_model_part_io.h" +#include "includes/model_part_io.h" +#include "utilities/parallel_utilities.h" +#include "utilities/variable_utils.h" + +namespace Kratos { + +namespace { + +static const std::map KratosToMedGeometryType { + { GeometryData::KratosGeometryType::Kratos_Point2D, MED_POINT1 }, + { GeometryData::KratosGeometryType::Kratos_Point3D, MED_POINT1 }, + + { GeometryData::KratosGeometryType::Kratos_Line2D2, MED_SEG2 }, + { GeometryData::KratosGeometryType::Kratos_Line3D2, MED_SEG2 }, + { GeometryData::KratosGeometryType::Kratos_Line2D3, MED_SEG3 }, + { GeometryData::KratosGeometryType::Kratos_Line3D3, MED_SEG3 }, + + { GeometryData::KratosGeometryType::Kratos_Triangle2D3, MED_TRIA3 }, + { GeometryData::KratosGeometryType::Kratos_Triangle3D3, MED_TRIA3 }, + { GeometryData::KratosGeometryType::Kratos_Triangle2D6, MED_TRIA6 }, + { GeometryData::KratosGeometryType::Kratos_Triangle3D6, MED_TRIA6 }, + + { GeometryData::KratosGeometryType::Kratos_Quadrilateral2D4, MED_QUAD4 }, + { GeometryData::KratosGeometryType::Kratos_Quadrilateral3D4, MED_QUAD4 }, + { GeometryData::KratosGeometryType::Kratos_Quadrilateral2D8, MED_QUAD8 }, + { GeometryData::KratosGeometryType::Kratos_Quadrilateral3D8, MED_QUAD8 }, + { GeometryData::KratosGeometryType::Kratos_Quadrilateral2D9, MED_QUAD9 }, + { GeometryData::KratosGeometryType::Kratos_Quadrilateral3D9, MED_QUAD9 }, + + { GeometryData::KratosGeometryType::Kratos_Tetrahedra3D4, MED_TETRA4 }, + { GeometryData::KratosGeometryType::Kratos_Tetrahedra3D10, MED_TETRA10 }, + + { GeometryData::KratosGeometryType::Kratos_Pyramid3D5, MED_PYRA5 }, + { GeometryData::KratosGeometryType::Kratos_Pyramid3D13, MED_PYRA13 }, + + { GeometryData::KratosGeometryType::Kratos_Prism3D6, MED_PENTA6 }, + { GeometryData::KratosGeometryType::Kratos_Prism3D15, MED_PENTA15 }, + + { GeometryData::KratosGeometryType::Kratos_Hexahedra3D8, MED_HEXA8 }, + { GeometryData::KratosGeometryType::Kratos_Hexahedra3D20, MED_HEXA20 }, + { GeometryData::KratosGeometryType::Kratos_Hexahedra3D27, MED_HEXA27 } +}; + +void CheckMEDErrorCode(const int ierr, const std::string& MEDCallName) +{ + KRATOS_ERROR_IF(ierr < 0) << MEDCallName << " failed with error code " << ierr << "." << std::endl; +} + +template +void CheckConnectivitiesSize( + const std::size_t ExpectedSize, + const std::vector& Conns) +{ + KRATOS_DEBUG_ERROR_IF_NOT(Conns.size() == ExpectedSize) << "Connectivities must have a size of " << ExpectedSize << ", but have " << Conns.size() << "!" << std::endl; +}; + +template +std::function&)> GetReorderFunction(const med_geometry_type MedGeomType) +{ + switch (MedGeomType) + { + case MED_TRIA3: + return [](auto& Connectivities){ + CheckConnectivitiesSize(3, Connectivities); + std::swap(Connectivities[1], Connectivities[2]); + }; + + case MED_TRIA6: + KRATOS_ERROR << "MED_TRIA6 is not implemented!" << std::endl; + + case MED_QUAD4: + return [](auto& Connectivities){ + CheckConnectivitiesSize(4, Connectivities); + std::swap(Connectivities[1], Connectivities[3]); + }; + + case MED_QUAD8: + KRATOS_ERROR << "MED_QUAD8 is med_quad8 is not implemented!" << std::endl; + + case MED_QUAD9: // should be same as MED_QUAD8 + KRATOS_ERROR << "MED_QUAD9 is not implemented!" << std::endl; + + case MED_PYRA5: + KRATOS_ERROR << "MED_PYRA5 is not implemented!" << std::endl; + + case MED_PYRA13: + KRATOS_ERROR << "MED_PYRA13 is not implemented!" << std::endl; + + default: + return [](auto& Connectivities){ + // does nothing if no reordering is needed + }; + } +} + +std::string GetKratosGeometryName( + const med_geometry_type MedGeomType, + const int Dimension) +{ + switch (MedGeomType) + { + case MED_POINT1: + return Dimension == 2 ? "Point2D" : "Point3D"; + case MED_SEG2: + return Dimension == 2 ? "Line2D2" : "Line3D2"; + case MED_SEG3: + return Dimension == 2 ? "Line2D3" : "Line3D3"; + case MED_TRIA3: + return Dimension == 2 ? "Triangle2D3" : "Triangle3D3"; + case MED_TRIA6: + return Dimension == 2 ? "Triangle2D6" : "Triangle3D6"; + case MED_QUAD4: + return Dimension == 2 ? "Quadrilateral2D4" : "Quadrilateral3D4"; + case MED_QUAD8: + return Dimension == 2 ? "Quadrilateral2D8" : "Quadrilateral3D8"; + case MED_QUAD9: + return Dimension == 2 ? "Quadrilateral2D9" : "Quadrilateral3D9"; + case MED_TETRA4: + return "Tetrahedra3D4"; + case MED_TETRA10: + return "Tetrahedra3D10"; + case MED_PYRA5: + return "Pyramid3D5"; + case MED_PYRA13: + return "Pyramid3D13"; + case MED_PENTA6: + return "Prism3D6"; + case MED_PENTA15: + return "Prism3D15"; + case MED_HEXA8: + return "Hexahedra3D8"; + case MED_HEXA20: + return "Hexahedra3D20"; + case MED_HEXA27: + return "Hexahedra3D27"; + default: + KRATOS_ERROR << "MED geometry type " << MedGeomType << " is not available!" << std::endl; + } +} + + +int GetNumberOfNodes( + const med_idt FileHandle, + const char* pMeshName) +{ + KRATOS_TRY + + // indicators if mesh has changed compared to previous step + // not of interest + med_bool coordinate_changement; + med_bool geo_transformation; + + return MEDmeshnEntity( + FileHandle, + pMeshName, MED_NO_DT, MED_NO_IT , + MED_NODE, MED_NO_GEOTYPE, + MED_COORDINATE, MED_NO_CMODE, + &coordinate_changement, &geo_transformation); + + KRATOS_CATCH("") +} + +auto GetNodeCoordinates( + const med_idt FileHandle, + const char* pMeshName, + const int NumberOfNodes, + const int Dimension) +{ + KRATOS_TRY + + std::vector coords(NumberOfNodes*Dimension); + + const auto err = MEDmeshNodeCoordinateRd( + FileHandle, pMeshName, + MED_NO_DT, MED_NO_IT, + MED_FULL_INTERLACE, + coords.data()); + + CheckMEDErrorCode(err, "MEDmeshNodeCoordinateRd"); + + return coords; + + KRATOS_CATCH("") +} + +} // anonymous namespace + +class MedModelPartIO::MedFileHandler +{ +public: + MedFileHandler( + const std::filesystem::path& rFileName, + const Kratos::Flags Options) : + mFileName(rFileName) + { + KRATOS_TRY + + KRATOS_ERROR_IF(Options.Is(IO::APPEND)) << "Appending to med files is not supported!" << std::endl; + KRATOS_ERROR_IF(Options.Is(IO::READ) && Options.Is(IO::WRITE)) << "Either reading OR writing is possible, not both!" << std::endl; + + mIsReadMode = Options.IsNot(IO::WRITE); + + // Set the mode (consistent with ModelPartIO) + // read only by default, unless other settings are specified + med_access_mode open_mode; + + if (mIsReadMode) { + open_mode = MED_ACC_RDONLY; + + // check if file exists + KRATOS_ERROR_IF(!std::filesystem::exists(rFileName)) << "File " << rFileName << " does not exist!" << std::endl; + + // basic checks if the file is compatible with the MED library + med_bool hdf_ok; + med_bool med_ok; + const med_err err = MEDfileCompatibility(rFileName.c_str(), &hdf_ok, &med_ok); + CheckMEDErrorCode(err, "MEDfileCompatibility"); + + KRATOS_ERROR_IF(err != 0) << "A problem occured while trying to check the compatibility of file " << rFileName << "!" << std::endl; + KRATOS_ERROR_IF(hdf_ok != MED_TRUE) << "A problem with HDF occured while trying to open file " << rFileName << "!" << std::endl; + KRATOS_ERROR_IF(med_ok != MED_TRUE) << "A problem with MED occured while trying to open file " << rFileName << "! This is most likely because the version of MED used to write the file is newer than the version used to read it" << std::endl; + } else { + open_mode = MED_ACC_CREAT; + mMeshName = "Kratos_Mesh"; // Maybe could use the name of the ModelPart (this is what is displayed in Salome) + } + + mFileHandle = MEDfileOpen(rFileName.c_str(), open_mode); + KRATOS_ERROR_IF(mFileHandle < 0) << "A problem occured while opening file " << rFileName << "!" << std::endl; + + if (mIsReadMode) { + // when reading the mesh, it is necessary to querry more information upfront + + const int num_meshes = MEDnMesh(mFileHandle); + KRATOS_ERROR_IF(num_meshes != 1) << "Expected one mesh, but file " << mFileName << " contains " << num_meshes << " meshes!" << std::endl; + + mMeshName.resize(MED_NAME_SIZE+1); + med_int space_dim = MEDmeshnAxis(mFileHandle, 1); + med_int mesh_dim; + med_mesh_type mesh_type; + std::string description(MED_COMMENT_SIZE+1, '\0'); + std::string dt_unit(MED_SNAME_SIZE+1, '\0'); + med_sorting_type sorting_type; + med_int n_step; + med_axis_type axis_type; + std::string axis_name(MED_SNAME_SIZE*space_dim+1, '\0'); + std::string axis_unit(MED_SNAME_SIZE*space_dim+1, '\0'); + + med_err err = MEDmeshInfo( + mFileHandle, + 1, + mMeshName.data(), + &space_dim, + &mesh_dim, + &mesh_type, + description.data(), + dt_unit.data(), + &sorting_type, + &n_step, + &axis_type, + axis_name.data(), + axis_unit.data()); + CheckMEDErrorCode(err, "MEDmeshInfo"); + + mMeshName.erase(std::find(mMeshName.begin(), mMeshName.end(), '\0'), mMeshName.end()); + mDimension = space_dim; + } + + KRATOS_CATCH("") + } + + med_idt GetFileHandle() const + { + return mFileHandle; + } + + const char* GetMeshName() const + { + return mMeshName.c_str(); + } + + bool IsReadMode() const + { + return mIsReadMode; + } + + int GetDimension() const + { + KRATOS_ERROR_IF_NOT(mDimension.has_value()) << "Dimension can only be querried in read mode!"; + return mDimension.value(); + } + + ~MedFileHandler() + { + KRATOS_TRY + + KRATOS_WARNING_IF("MedModelPartIO", MEDfileClose(mFileHandle) < 0) << "Closing of file " << mFileName << " failed!" << std::endl; + + KRATOS_CATCH("") + } + +private: + std::filesystem::path mFileName; + med_idt mFileHandle; + std::string mMeshName; + bool mIsReadMode; + std::optional mDimension; +}; + +MedModelPartIO::MedModelPartIO(const std::filesystem::path& rFileName, const Flags Options) + : mFileName(rFileName), mOptions(Options) +{ + KRATOS_TRY + + mpFileHandler = Kratos::make_shared(rFileName, Options); + + KRATOS_CATCH("") +} + +void MedModelPartIO::ReadModelPart(ModelPart& rThisModelPart) +{ + KRATOS_TRY + + using NodePointerType = ModelPart::NodeType::Pointer; + + KRATOS_ERROR_IF_NOT(mpFileHandler->IsReadMode()) << "MedModelPartIO needs to be created in read mode to read a ModelPart!" << std::endl; + + // reading nodes + const int num_nodes = GetNumberOfNodes(mpFileHandler->GetFileHandle(), mpFileHandler->GetMeshName()); + + if (num_nodes == 0) { + KRATOS_WARNING("MedModelPartIO") << "Med file " << mFileName << " does not contain any entities!" << std::endl; + return; + } + + const int dimension = mpFileHandler->GetDimension(); + + const auto node_coords = GetNodeCoordinates( + mpFileHandler->GetFileHandle(), + mpFileHandler->GetMeshName(), + num_nodes, + dimension); + + for (int i=0; i coords{0,0,0}; + for (int j=0; jGetFileHandle(), + mpFileHandler->GetMeshName(), + MED_NO_DT, MED_NO_IT, + MED_CELL, MED_GEO_ALL, + MED_CONNECTIVITY, MED_NODAL, + &coordinatechangement, &geotransformation); // TODO error if smaller zero, holds probably for the other functions too that return med_int + + int num_geometries_total = 0; + + // looping geometry types + for (int it_geo=1; it_geo<=num_geometry_types; ++it_geo) { + med_geometry_type geo_type; + + std::string geotypename; + geotypename.resize(MED_NAME_SIZE +1); + + // get geometry type + med_err err = MEDmeshEntityInfo( + mpFileHandler->GetFileHandle(), + mpFileHandler->GetMeshName(), + MED_NO_DT, MED_NO_IT, + MED_CELL, it_geo, + geotypename.data(), &geo_type); + CheckMEDErrorCode(err, "MEDmeshEntityInfo"); + + // how many cells of type geotype ? + const int num_geometries = MEDmeshnEntity( + mpFileHandler->GetFileHandle(), + mpFileHandler->GetMeshName(), + MED_NO_DT, MED_NO_IT, + MED_CELL, geo_type, + MED_CONNECTIVITY, MED_NODAL, + &coordinatechangement, &geotransformation); + + // read cells connectivity in the mesh + const int num_nodes_geo_type = geo_type%100; + std::vector connectivity(num_geometries * num_nodes_geo_type); + + err = MEDmeshElementConnectivityRd( + mpFileHandler->GetFileHandle(), + mpFileHandler->GetMeshName(), + MED_NO_DT, MED_NO_IT, + MED_CELL, geo_type, + MED_NODAL, MED_FULL_INTERLACE, + connectivity.data()); + CheckMEDErrorCode(err, "MEDmeshElementConnectivityRd"); + + // create geometries + const std::string kratos_geo_name = GetKratosGeometryName(geo_type, dimension); + const auto reorder_fct = GetReorderFunction(geo_type); + + std::vector geom_node_ids(num_nodes_geo_type); + + for (std::size_t i=0; i 0) << "Read " << num_geometries_total << " geometries in total" << std::endl; + + KRATOS_CATCH("") +} + +void MedModelPartIO::WriteModelPart(const ModelPart& rThisModelPart) +{ + KRATOS_TRY + + // TODO what happens if this function is called multiple times? + // will it overwrite the mesh? + // or just crash? + + KRATOS_ERROR_IF(mpFileHandler->IsReadMode()) << "MedModelPartIO needs to be created in write mode to write a ModelPart!" << std::endl; + + // TODO use this? + // MEDfileCommentWr(mpFileHandler->GetFileHandle(), "A 2D unstructured mesh : 15 nodes, 12 cells"); + + constexpr med_int dimension = 3; // in Kratos, everything is 3D + + med_err err = MEDmeshCr( + mpFileHandler->GetFileHandle(), + mpFileHandler->GetMeshName(), // TODO use name of ModelPart? See comment above, this is what is displayed in Salome TODO check length! + dimension , //spacedim + dimension , //meshdim + MED_UNSTRUCTURED_MESH, + "Kratos med", // description + "", + MED_SORT_DTIT, + MED_CARTESIAN, + "", + ""); + CheckMEDErrorCode(err, "MEDmeshCr"); + + const std::vector nodal_coords = VariableUtils().GetCurrentPositionsVector>(rThisModelPart.Nodes(), dimension); + + KRATOS_WARNING_IF("MedModelPartIO", rThisModelPart.NumberOfNodes() == 0) << "ModelPart \"" << rThisModelPart.FullName() << "\" does not contain any entities!" << std::endl; + + err = MEDmeshNodeCoordinateWr( + mpFileHandler->GetFileHandle(), + mpFileHandler->GetMeshName(), + MED_NO_DT, + MED_NO_IT, + 0.0, + MED_FULL_INTERLACE, + rThisModelPart.NumberOfNodes(), + nodal_coords.data()); + CheckMEDErrorCode(err, "MEDmeshNodeCoordinateWr"); + + if (rThisModelPart.NumberOfGeometries() == 0) { + return; + } + + // we deliberately do not care about IDs + std::unordered_map kratos_id_to_med_id; + int med_id = 1; // starting from 1, since salome cannot parse 0 as node ids + for (const auto& r_node : rThisModelPart.Nodes()) { + kratos_id_to_med_id[r_node.Id()] = med_id++; + } + + using ConnectivitiesType = std::vector; + using ConnectivitiesVector = std::vector; + + ConnectivitiesVector connectivities; + connectivities.reserve(rThisModelPart.NumberOfGeometries()/3); // assuming that three different types of geometries exist + + auto write_geometries = [this]( + const med_geometry_type MedGeomType, + const std::size_t NumberOfPoints, + ConnectivitiesVector& Connectivities) { + + + const auto reorder_fct = GetReorderFunction(MedGeomType); + + auto GetMedConnectivities = [&reorder_fct]( + const std::size_t NumberOfPoints, + ConnectivitiesVector& Connectivities) { + + std::vector med_conn(Connectivities.size() * NumberOfPoints); + + // reorder and flatten the connectivities + IndexPartition(Connectivities.size()).for_each([&](const std::size_t i) { + reorder_fct(Connectivities[i]); + std::copy(Connectivities[i].begin(), Connectivities[i].end(), med_conn.begin()+(i*NumberOfPoints)); + }); + + return med_conn; + }; + + const std::vector med_conn = GetMedConnectivities(NumberOfPoints, Connectivities); + + auto mederr = MEDmeshElementConnectivityWr ( + mpFileHandler->GetFileHandle(), + mpFileHandler->GetMeshName(), + MED_NO_DT, MED_NO_IT , 0.0, + MED_CELL, MedGeomType , + MED_NODAL, MED_FULL_INTERLACE, + Connectivities.size(), med_conn.data()); + CheckMEDErrorCode(mederr, "MEDmeshElementConnectivityWr"); + + }; + + std::unordered_map conn_map; + std::unordered_map np_map; // TODO this can be solved better + + for (const auto& r_geom : rThisModelPart.Geometries()) { + auto this_geom_type = r_geom.GetGeometryType(); + + ConnectivitiesType conn; + for (const auto& r_node : r_geom.Points()) { + conn.push_back(kratos_id_to_med_id[r_node.Id()]); + } + conn_map[this_geom_type].push_back(conn); + np_map[this_geom_type] = r_geom.PointsNumber(); + + } + + // entities of a type have to be written at the same time + // maybe if opening in append mode it would also work without + for (auto& [geom_type, conn] : conn_map) { + const auto med_geom_type = KratosToMedGeometryType.at(geom_type); + write_geometries(med_geom_type, np_map[geom_type], conn); + } + + KRATOS_CATCH("") +} + +void MedModelPartIO::DivideInputToPartitions(SizeType NumberOfPartitions, + const PartitioningInfo& rPartitioningInfo) +{ + // these are not used in ModelPartIO, as the partitioned files are created independently + std::stringbuf dummy_strbuf; + auto dummy_stream(Kratos::make_shared(&dummy_strbuf)); + + ModelPartIO(dummy_stream).DivideInputToPartitions( + NumberOfPartitions, + rPartitioningInfo); +} + +void MedModelPartIO::DivideInputToPartitions(Kratos::shared_ptr * pStreams, + SizeType NumberOfPartitions, + const PartitioningInfo& rPartitioningInfo) +{ + // these are not used in ModelPartIO, streams are passed from outside + std::stringbuf dummy_strbuf; + auto dummy_stream(Kratos::make_shared(&dummy_strbuf)); + + ModelPartIO(dummy_stream).DivideInputToPartitions( + pStreams, + NumberOfPartitions, + rPartitioningInfo); +} + +} // namespace Kratos. diff --git a/applications/MedApplication/custom_io/med_model_part_io.h b/applications/MedApplication/custom_io/med_model_part_io.h new file mode 100644 index 000000000000..3fdce4d66bb1 --- /dev/null +++ b/applications/MedApplication/custom_io/med_model_part_io.h @@ -0,0 +1,256 @@ +// KRATOS __ __ _ _ _ _ _ _ +// | \/ | ___ __| | / \ _ __ _ __ | (_) ___ __ _| |_(_) ___ _ ___ +// | |\/| |/ _ \/ _` | / _ \ | '_ \| '_ \| | |/ __/ _` | __| |/ _ \| '_ | +// | | | | __/ (_| |/ ___ \| |_) | |_) | | | (_| (_| | |_| | (_) | | | | +// |_| |_|\___|\__,_/_/ \_\ .__/| .__/|_|_|\___\__,_|\__|_|\___/|_| |_| +// |_| |_| +// License: BSD License +// Kratos default license: kratos/license.txt +// +// Main authors: Philipp Bucher (https://github.com/philbucher) +// + +#pragma once + +// System includes +#include + +// External includes + +// Project includes +#include "includes/io.h" + + +namespace Kratos { + +///@addtogroup MedApplication +///@{ + +///@name Kratos Globals +///@{ + +///@} +///@name Type Definitions +///@{ + +///@} +///@name Enum's +///@{ + +///@} +///@name Functions +///@{ + +///@} +///@name Kratos Classes +///@{ + +/// Short class definition. +/** Detail class definition. +*/ +class KRATOS_API(MED_APPLICATION) MedModelPartIO : public IO +{ +public: + ///@name Type Definitions + ///@{ + + /// Pointer definition of MedModelPartIO + KRATOS_CLASS_POINTER_DEFINITION(MedModelPartIO); + + ///@} + ///@name Life Cycle + ///@{ + + /// Constructor with filename. + MedModelPartIO( + const std::filesystem::path& rFileName, + const Flags Options = IO::READ); + + /// Copy constructor. + MedModelPartIO(MedModelPartIO const& rOther) = delete; + + ///@} + ///@name Operators + ///@{ + + /// Assignment operator. + MedModelPartIO& operator=(MedModelPartIO const& rOther) = delete; + + ///@} + ///@name Operations + ///@{ + + /** + * @brief This method reads the model part + * @param rThisModelPart The model part to be read + */ + void ReadModelPart(ModelPart& rThisModelPart) override; + + /** + * @brief This method writes the model part + * @param rThisModelPart The model part to be written + */ + void WriteModelPart(const ModelPart& rThisModelPart) override; + + /** + * @brief This method divides a model part into partitions + * @param NumberOfPartitions The number of partitions + * @param rPartitioningInfo Information about partitioning of entities + */ + void DivideInputToPartitions(SizeType NumberOfPartitions, + const PartitioningInfo& rPartitioningInfo) override; + + /** + * @brief This method divides a model part into partitions + * @param pStreams The stream pointer + * @param NumberOfPartitions The number of partitions + * @param rPartitioningInfo Information about partitioning of entities + */ + void DivideInputToPartitions(Kratos::shared_ptr * pStreams, + SizeType NumberOfPartitions, + const PartitioningInfo& rPartitioningInfo) override; + + ///@} + ///@name Access + ///@{ + + + ///@} + ///@name Inquiry + ///@{ + + + ///@} + ///@name Input and output + ///@{ + + /// Turn back information as a string. + std::string Info() const override + { + return "MedModelPartIO"; + } + + /// Print information about this object. + void PrintInfo(std::ostream& rOStream) const override + { + rOStream << "MedModelPartIO"; + } + + /// Print object's data. + void PrintData(std::ostream& rOStream) const override + { + } + + ///@} + ///@name Friends + ///@{ + + + ///@} + +protected: + ///@name Protected static Member Variables + ///@{ + + + ///@} + ///@name Protected member Variables + ///@{ + + + ///@} + ///@name Protected Operators + ///@{ + + + ///@} + ///@name Protected Operations + ///@{ + + + ///@} + ///@name Protected Access + ///@{ + + + ///@} + ///@name Protected Inquiry + ///@{ + + + ///@} + ///@name Protected LifeCycle + ///@{ + + + ///@} + +private: + ///@name Static Member Variables + ///@{ + + + ///@} + ///@name Member Variables + ///@{ + + std::filesystem::path mFileName; + + Flags mOptions; + + class MedFileHandler; // forward declared to avoid "med.h" include in header + Kratos::shared_ptr mpFileHandler; + + ///@} + ///@name Private Operators + ///@{ + + + ///@} + ///@name Private Operations + ///@{ + + ///@} + ///@name Private Access + ///@{ + + + ///@} + ///@name Private Inquiry + ///@{ + + + ///@} + +}; // Class MedModelPartIO + +///@} + +///@name Type Definitions +///@{ + + +///@} +///@name Input and output +///@{ + + +/// input stream function +inline std::istream& operator >> (std::istream& rIStream, + MedModelPartIO& rThis); + +/// output stream function +inline std::ostream& operator << (std::ostream& rOStream, + const MedModelPartIO& rThis) +{ + rThis.PrintInfo(rOStream); + rOStream << std::endl; + rThis.PrintData(rOStream); + + return rOStream; +} +///@} + +///@} addtogroup block + +} // namespace Kratos. diff --git a/applications/MedApplication/custom_python/add_custom_io_to_python.cpp b/applications/MedApplication/custom_python/add_custom_io_to_python.cpp new file mode 100644 index 000000000000..f34dca20ab86 --- /dev/null +++ b/applications/MedApplication/custom_python/add_custom_io_to_python.cpp @@ -0,0 +1,35 @@ +// KRATOS __ __ _ _ _ _ _ _ +// | \/ | ___ __| | / \ _ __ _ __ | (_) ___ __ _| |_(_) ___ _ ___ +// | |\/| |/ _ \/ _` | / _ \ | '_ \| '_ \| | |/ __/ _` | __| |/ _ \| '_ | +// | | | | __/ (_| |/ ___ \| |_) | |_) | | | (_| (_| | |_| | (_) | | | | +// |_| |_|\___|\__,_/_/ \_\ .__/| .__/|_|_|\___\__,_|\__|_|\___/|_| |_| +// |_| |_| +// License: BSD License +// Kratos default license: kratos/license.txt +// +// Main authors: Philipp Bucher (https://github.com/philbucher) +// + +// System includes + +// External includes + +// Project includes +#include "includes/define_python.h" +#include "custom_python/add_custom_io_to_python.h" +#include "custom_io/med_model_part_io.h" + + +namespace Kratos::Python { + +void AddCustomIOToPython(pybind11::module& m) +{ + namespace py = pybind11; + + py::class_(m,"MedModelPartIO") + .def(py::init()) + .def(py::init()) + ; +} + +} // namespace Kratos::Python diff --git a/applications/MedApplication/custom_python/add_custom_io_to_python.h b/applications/MedApplication/custom_python/add_custom_io_to_python.h new file mode 100644 index 000000000000..a6261a35f24b --- /dev/null +++ b/applications/MedApplication/custom_python/add_custom_io_to_python.h @@ -0,0 +1,26 @@ +// KRATOS __ __ _ _ _ _ _ _ +// | \/ | ___ __| | / \ _ __ _ __ | (_) ___ __ _| |_(_) ___ _ ___ +// | |\/| |/ _ \/ _` | / _ \ | '_ \| '_ \| | |/ __/ _` | __| |/ _ \| '_ | +// | | | | __/ (_| |/ ___ \| |_) | |_) | | | (_| (_| | |_| | (_) | | | | +// |_| |_|\___|\__,_/_/ \_\ .__/| .__/|_|_|\___\__,_|\__|_|\___/|_| |_| +// |_| |_| +// License: BSD License +// Kratos default license: kratos/license.txt +// +// Main authors: Philipp Bucher (https://github.com/philbucher) +// + +#pragma once + +// System includes + +// External includes +#include + +// Project includes + +namespace Kratos::Python { + +void AddCustomIOToPython(pybind11::module& m); + +} // namespace Kratos::Python diff --git a/applications/MedApplication/custom_python/add_custom_utilities_to_python.cpp b/applications/MedApplication/custom_python/add_custom_utilities_to_python.cpp new file mode 100644 index 000000000000..1867eff774d0 --- /dev/null +++ b/applications/MedApplication/custom_python/add_custom_utilities_to_python.cpp @@ -0,0 +1,35 @@ +// KRATOS __ __ _ _ _ _ _ _ +// | \/ | ___ __| | / \ _ __ _ __ | (_) ___ __ _| |_(_) ___ _ ___ +// | |\/| |/ _ \/ _` | / _ \ | '_ \| '_ \| | |/ __/ _` | __| |/ _ \| '_ | +// | | | | __/ (_| |/ ___ \| |_) | |_) | | | (_| (_| | |_| | (_) | | | | +// |_| |_|\___|\__,_/_/ \_\ .__/| .__/|_|_|\___\__,_|\__|_|\___/|_| |_| +// |_| |_| +// License: BSD License +// Kratos default license: kratos/license.txt +// +// Main authors: Philipp Bucher (https://github.com/philbucher) +// + +// System includes + +// External includes + +// Project includes +#include "includes/define_python.h" +#include "custom_python/add_custom_utilities_to_python.h" +#include "custom_utilities/med_testing_utilities.h" + + +namespace Kratos::Python { + +void AddCustomUtilitiesToPython(pybind11::module& m) +{ + namespace py = pybind11; + + py::class_(m,"MedTestingUtilities") + .def_static("CheckModelPartsAreEqual", &MedTestingUtilities::CheckModelPartsAreEqual) + .def_static("AddGeometriesFromElements", &MedTestingUtilities::AddGeometriesFromElements) + ; +} + +} // namespace Kratos::Python diff --git a/applications/MedApplication/custom_python/add_custom_utilities_to_python.h b/applications/MedApplication/custom_python/add_custom_utilities_to_python.h new file mode 100644 index 000000000000..e2fec7772701 --- /dev/null +++ b/applications/MedApplication/custom_python/add_custom_utilities_to_python.h @@ -0,0 +1,26 @@ +// KRATOS __ __ _ _ _ _ _ _ +// | \/ | ___ __| | / \ _ __ _ __ | (_) ___ __ _| |_(_) ___ _ ___ +// | |\/| |/ _ \/ _` | / _ \ | '_ \| '_ \| | |/ __/ _` | __| |/ _ \| '_ | +// | | | | __/ (_| |/ ___ \| |_) | |_) | | | (_| (_| | |_| | (_) | | | | +// |_| |_|\___|\__,_/_/ \_\ .__/| .__/|_|_|\___\__,_|\__|_|\___/|_| |_| +// |_| |_| +// License: BSD License +// Kratos default license: kratos/license.txt +// +// Main authors: Philipp Bucher (https://github.com/philbucher) +// + +#pragma once + +// System includes + +// External includes +#include + +// Project includes + +namespace Kratos::Python { + +void AddCustomUtilitiesToPython(pybind11::module& m); + +} // namespace Kratos::Python diff --git a/applications/MedApplication/custom_python/med_python_application.cpp b/applications/MedApplication/custom_python/med_python_application.cpp new file mode 100644 index 000000000000..fa48707fe786 --- /dev/null +++ b/applications/MedApplication/custom_python/med_python_application.cpp @@ -0,0 +1,40 @@ +// KRATOS __ __ _ _ _ _ _ _ +// | \/ | ___ __| | / \ _ __ _ __ | (_) ___ __ _| |_(_) ___ _ ___ +// | |\/| |/ _ \/ _` | / _ \ | '_ \| '_ \| | |/ __/ _` | __| |/ _ \| '_ | +// | | | | __/ (_| |/ ___ \| |_) | |_) | | | (_| (_| | |_| | (_) | | | | +// |_| |_|\___|\__,_/_/ \_\ .__/| .__/|_|_|\___\__,_|\__|_|\___/|_| |_| +// |_| |_| +// License: BSD License +// Kratos default license: kratos/license.txt +// +// Main authors: Philipp Bucher (https://github.com/philbucher) +// + +// System includes + +// External includes + +// Project includes +#include "includes/define_python.h" +#include "med_application.h" +#include "custom_python/add_custom_io_to_python.h" +#include "custom_python/add_custom_utilities_to_python.h" + + +namespace Kratos::Python { + +PYBIND11_MODULE(KratosMedApplication,m) +{ + namespace py = pybind11; + + py::class_(m, "KratosMedApplication") + .def(py::init<>()) + ; + + AddCustomIOToPython(m); + AddCustomUtilitiesToPython(m); +} + +} // namespace Kratos::Python diff --git a/applications/MedApplication/custom_utilities/med_testing_utilities.cpp b/applications/MedApplication/custom_utilities/med_testing_utilities.cpp new file mode 100644 index 000000000000..8b6cb57bbcd0 --- /dev/null +++ b/applications/MedApplication/custom_utilities/med_testing_utilities.cpp @@ -0,0 +1,197 @@ +// KRATOS __ __ _ _ _ _ _ _ +// | \/ | ___ __| | / \ _ __ _ __ | (_) ___ __ _| |_(_) ___ _ ___ +// | |\/| |/ _ \/ _` | / _ \ | '_ \| '_ \| | |/ __/ _` | __| |/ _ \| '_ | +// | | | | __/ (_| |/ ___ \| |_) | |_) | | | (_| (_| | |_| | (_) | | | | +// |_| |_|\___|\__,_/_/ \_\ .__/| .__/|_|_|\___\__,_|\__|_|\___/|_| |_| +// |_| |_| +// License: BSD License +// Kratos default license: kratos/license.txt +// +// Main authors: Philipp Bucher (https://github.com/philbucher) +// + +// System includes + +// External includes + +// Project includes +#include "includes/checks.h" +#include "utilities/parallel_utilities.h" +#include "utilities/reduction_utilities.h" +#include "med_testing_utilities.h" + + +namespace Kratos { + +namespace { // helpers namespace + +using NodeType = ModelPart::NodeType; +using GeometryType = ModelPart::GeometryType; +using GeometryContainerType = ModelPart::GeometryContainerType; + +template +bool contains( + const std::vector& rVec, + const T& rValue) +{ + return std::find(rVec.begin(), rVec.end(), rValue) != rVec.end(); +} + +// check if the geometries have the same nodes +// for this to be the case, two conditions need to be fulflled: +// 1: geometries have same number of nodes +// 2: nodes of both geometries are in same order and in same coords +bool have_same_nodes( + const GeometryType& rGeom1, + const GeometryType& rGeom2) +{ + if (rGeom1.PointsNumber() != rGeom2.PointsNumber()) return false; + + for (std::size_t i=0; i 1e-12) return false; + } + + return true; +} + +void CheckEntitiesAreEqual( + const NodeType& rNode1, + const NodeType& rNode2) +{ + KRATOS_TRY + + // KRATOS_CHECK_EQUAL(rNode1.Id(), rNode2.Id()); // the MEDApp deliberately does not care about IDs + + KRATOS_CHECK_DOUBLE_EQUAL(rNode1.X(), rNode2.X()); + KRATOS_CHECK_DOUBLE_EQUAL(rNode1.X0(), rNode2.X0()); + + KRATOS_CHECK_DOUBLE_EQUAL(rNode1.Y(), rNode2.Y()); + KRATOS_CHECK_DOUBLE_EQUAL(rNode1.Y0(), rNode2.Y0()); + + KRATOS_CHECK_DOUBLE_EQUAL(rNode1.Z(), rNode2.Z()); + KRATOS_CHECK_DOUBLE_EQUAL(rNode1.Z0(), rNode2.Z0()); + + KRATOS_CATCH("") +} + +void CheckGeometriesAreEqual( + const GeometryType& rGeom1, + const GeometryType& rGeom2) +{ + KRATOS_TRY + + // KRATOS_CHECK_EQUAL(rGeom1.Id(), rGeom2.Id()); // the MEDApp deliberately does not care about IDs + + KRATOS_CHECK_EQUAL(rGeom1.PointsNumber(), rGeom2.PointsNumber()); + + KRATOS_CHECK(GeometryType::IsSame(rGeom1, rGeom2)); + + for (std::size_t i=0; i +void CheckEntitiesAreEqual( + const TContainerType& rEntities1, + const TContainerType& rEntities2) +{ + KRATOS_TRY + + // basic checks + KRATOS_CHECK_EQUAL(rEntities1.size(), rEntities2.size()); + + // check entities + for (std::size_t i=0; i>(rModelPart.Nodes(), [](const auto& rNode){ + return rNode.Id(); + }); + }; + + KRATOS_CHECK_EQUAL(min_id(rModelPart1), 1); + KRATOS_CHECK_EQUAL(min_id(rModelPart2), 1); + + // check geometries + CheckGeometriesAreEqual(rModelPart1, rModelPart2); + + KRATOS_CHECK_EQUAL(rModelPart1.NumberOfSubModelParts(), rModelPart2.NumberOfSubModelParts()); + + const auto& r_smp2_names = rModelPart2.GetSubModelPartNames(); + + for (const auto& r_smp_name : rModelPart1.GetSubModelPartNames()) { + KRATOS_CHECK(contains(r_smp2_names, r_smp_name)); + CheckModelPartsAreEqual(rModelPart1.GetSubModelPart(r_smp_name), rModelPart1.GetSubModelPart(r_smp_name)); + } + + KRATOS_CATCH("") +} + + +void MedTestingUtilities::AddGeometriesFromElements( + ModelPart& rModelPart) +{ + KRATOS_TRY + + for (const auto& r_elem : rModelPart.Elements()) { + rModelPart.AddGeometry(r_elem.pGetGeometry()); + } + + KRATOS_CATCH("") +} + +} // namespace Kratos diff --git a/applications/MedApplication/custom_utilities/med_testing_utilities.h b/applications/MedApplication/custom_utilities/med_testing_utilities.h new file mode 100644 index 000000000000..1e45da99c387 --- /dev/null +++ b/applications/MedApplication/custom_utilities/med_testing_utilities.h @@ -0,0 +1,200 @@ +// KRATOS __ __ _ _ _ _ _ _ +// | \/ | ___ __| | / \ _ __ _ __ | (_) ___ __ _| |_(_) ___ _ ___ +// | |\/| |/ _ \/ _` | / _ \ | '_ \| '_ \| | |/ __/ _` | __| |/ _ \| '_ | +// | | | | __/ (_| |/ ___ \| |_) | |_) | | | (_| (_| | |_| | (_) | | | | +// |_| |_|\___|\__,_/_/ \_\ .__/| .__/|_|_|\___\__,_|\__|_|\___/|_| |_| +// |_| |_| +// License: BSD License +// Kratos default license: kratos/license.txt +// +// Main authors: Philipp Bucher (https://github.com/philbucher) +// + +#pragma once + +// System includes + +// External includes + +// Project includes +#include "includes/model_part.h" + + +namespace Kratos { + +///@addtogroup ApplicationNameApplication +///@{ + +///@name Kratos Classes +///@{ + +/// Short class definition. +/** Detail class definition. +*/ +class KRATOS_API(MED_APPLICATION) MedTestingUtilities +{ +public: + ///@name Type Definitions + ///@{ + + /// Pointer definition of MedTestingUtilities + KRATOS_CLASS_POINTER_DEFINITION(MedTestingUtilities); + + ///@} + ///@name Life Cycle + ///@{ + + /// Default constructor. + MedTestingUtilities() = delete; + + /// Copy constructor. + MedTestingUtilities(MedTestingUtilities const& rOther) = delete; + + ///@} + ///@name Operators + ///@{ + + /// Assignment operator. + MedTestingUtilities& operator=(MedTestingUtilities const& rOther) = delete; + + ///@} + ///@name Operations + ///@{ + + static void CheckModelPartsAreEqual( + const ModelPart& rModelPart1, + const ModelPart& rModelPart2); + + static void AddGeometriesFromElements( + ModelPart& rModelPart); + + ///@} + ///@name Access + ///@{ + + + ///@} + ///@name Inquiry + ///@{ + + + ///@} + ///@name Input and output + ///@{ + + /// Turn back information as a string. + std::string Info() const; + + /// Print information about this object. + void PrintInfo(std::ostream& rOStream) const; + + /// Print object's data. + void PrintData(std::ostream& rOStream) const; + + + ///@} + ///@name Friends + ///@{ + + + ///@} + +protected: + ///@name Protected static Member Variables + ///@{ + + + ///@} + ///@name Protected member Variables + ///@{ + + + ///@} + ///@name Protected Operators + ///@{ + + + ///@} + ///@name Protected Operations + ///@{ + + + ///@} + ///@name Protected Access + ///@{ + + + ///@} + ///@name Protected Inquiry + ///@{ + + + ///@} + ///@name Protected LifeCycle + ///@{ + + + ///@} + +private: + ///@name Static Member Variables + ///@{ + + + ///@} + ///@name Member Variables + ///@{ + + + ///@} + ///@name Private Operators + ///@{ + + + ///@} + ///@name Private Operations + ///@{ + + + ///@} + ///@name Private Access + ///@{ + + + ///@} + ///@name Private Inquiry + ///@{ + + + ///@} + +}; // Class MedTestingUtilities + +///@} + +///@name Type Definitions +///@{ + +///@} +///@name Input and output +///@{ + +/// input stream function +inline std::istream& operator >> (std::istream& rIStream, + MedTestingUtilities& rThis); + +/// output stream function +inline std::ostream& operator << (std::ostream& rOStream, + const MedTestingUtilities& rThis) +{ + rThis.PrintInfo(rOStream); + rOStream << std::endl; + rThis.PrintData(rOStream); + + return rOStream; +} +///@} + +///@} addtogroup block + +} // namespace Kratos diff --git a/applications/MedApplication/med_application.cpp b/applications/MedApplication/med_application.cpp new file mode 100644 index 000000000000..fcf5abdaa673 --- /dev/null +++ b/applications/MedApplication/med_application.cpp @@ -0,0 +1,42 @@ +// KRATOS __ __ _ _ _ _ _ _ +// | \/ | ___ __| | / \ _ __ _ __ | (_) ___ __ _| |_(_) ___ _ ___ +// | |\/| |/ _ \/ _` | / _ \ | '_ \| '_ \| | |/ __/ _` | __| |/ _ \| '_ | +// | | | | __/ (_| |/ ___ \| |_) | |_) | | | (_| (_| | |_| | (_) | | | | +// |_| |_|\___|\__,_/_/ \_\ .__/| .__/|_|_|\___\__,_|\__|_|\___/|_| |_| +// |_| |_| +// License: BSD License +// Kratos default license: kratos/license.txt +// +// Main authors: Philipp Bucher (https://github.com/philbucher) +// + +// System includes + +// External includes + +// Project includes +#include "med_inc.h" +#include "med_application.h" + + +namespace Kratos { + +KratosMedApplication::KratosMedApplication(): + KratosApplication("MedApplication") +{ + // check if the library that was used to compile is the same as the one that is loaded at runtime + // they must match, otherwise random errors can occur + med_int v_med_major, v_med_minor, v_med_release; + MEDlibraryNumVersion(&v_med_major, &v_med_minor, &v_med_release); + + KRATOS_ERROR_IF(v_med_major != MED_MAJOR_NUM || + v_med_minor != MED_NUM_MINEUR || + v_med_release != MED_NUM_RELEASE) << "The MED library that was used during compilation (v" << v_med_major << "." << v_med_minor << "." << v_med_release << ") is different from the one loaded at runtime (v" << MED_MAJOR_NUM << "." << MED_NUM_MINEUR << "." << MED_NUM_RELEASE << ")!\nThis causes problems with reading/writing MED files, please check your paths for loading the library (e.g. \"PATH\" or \"LD_LIBRARY_PATH\")" << std::endl; +} + +void KratosMedApplication::Register() +{ + KRATOS_INFO("") << "Initializing KratosMedApplication..." << std::endl; +} + +} // namespace Kratos. diff --git a/applications/MedApplication/med_application.h b/applications/MedApplication/med_application.h new file mode 100644 index 000000000000..c4a01bbf62b5 --- /dev/null +++ b/applications/MedApplication/med_application.h @@ -0,0 +1,209 @@ +// KRATOS __ __ _ _ _ _ _ _ +// | \/ | ___ __| | / \ _ __ _ __ | (_) ___ __ _| |_(_) ___ _ ___ +// | |\/| |/ _ \/ _` | / _ \ | '_ \| '_ \| | |/ __/ _` | __| |/ _ \| '_ | +// | | | | __/ (_| |/ ___ \| |_) | |_) | | | (_| (_| | |_| | (_) | | | | +// |_| |_|\___|\__,_/_/ \_\ .__/| .__/|_|_|\___\__,_|\__|_|\___/|_| |_| +// |_| |_| +// License: BSD License +// Kratos default license: kratos/license.txt +// +// Main authors: Philipp Bucher (https://github.com/philbucher) +// + +#pragma once + +// System includes + + +// External includes + + +// Project includes +#include "includes/kratos_application.h" + + +namespace Kratos { + +///@name Kratos Globals +///@{ + +///@} +///@name Type Definitions +///@{ + +///@} +///@name Enum's +///@{ + +///@} +///@name Functions +///@{ + +///@} +///@name Kratos Classes +///@{ + +class KRATOS_API(MED_APPLICATION) KratosMedApplication : public KratosApplication { +public: + ///@name Type Definitions + ///@{ + + /// Pointer definition of KratosMedApplication + KRATOS_CLASS_POINTER_DEFINITION(KratosMedApplication); + + ///@} + ///@name Life Cycle + ///@{ + + /// Default constructor. + KratosMedApplication(); + + /// Copy constructor. + KratosMedApplication(KratosMedApplication const& rOther) = delete; + + ///@} + ///@name Operators + ///@{ + + /// Assignment operator. + KratosMedApplication& operator=(KratosMedApplication const& rOther) = delete; + + ///@} + ///@name Operations + ///@{ + + void Register() override; + + ///@} + ///@name Access + ///@{ + + + ///@} + ///@name Inquiry + ///@{ + + + ///@} + ///@name Input and output + ///@{ + + /// Turn back information as a string. + std::string Info() const override + { + return "KratosMedApplication"; + } + + /// Print information about this object. + void PrintInfo(std::ostream& rOStream) const override + { + rOStream << Info(); + PrintData(rOStream); + } + + ///// Print object's data. + void PrintData(std::ostream& rOStream) const override + { + KRATOS_WATCH("in my application"); + KRATOS_WATCH(KratosComponents::GetComponents().size() ); + + rOStream << "Variables:" << std::endl; + KratosComponents().PrintData(rOStream); + rOStream << std::endl; + rOStream << "Elements:" << std::endl; + KratosComponents().PrintData(rOStream); + rOStream << std::endl; + rOStream << "Conditions:" << std::endl; + KratosComponents().PrintData(rOStream); + } + + ///@} + ///@name Friends + ///@{ + + + ///@} + +protected: + ///@name Protected static Member Variables + ///@{ + + + ///@} + ///@name Protected member Variables + ///@{ + + + ///@} + ///@name Protected Operators + ///@{ + + + ///@} + ///@name Protected Operations + ///@{ + + + ///@} + ///@name Protected Access + ///@{ + + + ///@} + ///@name Protected Inquiry + ///@{ + + + ///@} + ///@name Protected LifeCycle + ///@{ + + + ///@} + +private: + ///@name Static Member Variables + ///@{ + + // static const ApplicationCondition msApplicationCondition; + + ///@} + ///@name Member Variables + ///@{ + + ///@} + ///@name Private Operators + ///@{ + + + ///@} + ///@name Private Operations + ///@{ + + + ///@} + ///@name Private Access + ///@{ + + + ///@} + ///@name Private Inquiry + ///@{ + + + ///@} + +}; // Class KratosMedApplication + +///@} + +///@name Type Definitions +///@{ + +///@} +///@name Input and output +///@{ + +///@} + +} // namespace Kratos. diff --git a/applications/MedApplication/med_inc.h b/applications/MedApplication/med_inc.h new file mode 100644 index 000000000000..d536de71fc45 --- /dev/null +++ b/applications/MedApplication/med_inc.h @@ -0,0 +1,21 @@ +// KRATOS __ __ _ _ _ _ _ _ +// | \/ | ___ __| | / \ _ __ _ __ | (_) ___ __ _| |_(_) ___ _ ___ +// | |\/| |/ _ \/ _` | / _ \ | '_ \| '_ \| | |/ __/ _` | __| |/ _ \| '_ | +// | | | | __/ (_| |/ ___ \| |_) | |_) | | | (_| (_| | |_| | (_) | | | | +// |_| |_|\___|\__,_/_/ \_\ .__/| .__/|_|_|\___\__,_|\__|_|\___/|_| |_| +// |_| |_| +// License: BSD License +// Kratos default license: kratos/license.txt +// +// Main authors: Philipp Bucher (https://github.com/philbucher) +// + +#pragma once + +// Those dummy structs are necessary when the MED-library is compiled with MPI support. +// Then the header also contains functions which use those MPI-types +struct MPI_Comm; +struct MPI_Info; + +// External includes +#include "med.h" diff --git a/applications/MedApplication/tests/cpp_tests/test_med_io_write_read_model_part.cpp b/applications/MedApplication/tests/cpp_tests/test_med_io_write_read_model_part.cpp new file mode 100644 index 000000000000..f1807bc633ec --- /dev/null +++ b/applications/MedApplication/tests/cpp_tests/test_med_io_write_read_model_part.cpp @@ -0,0 +1,200 @@ +// KRATOS __ __ _ _ _ _ _ _ +// | \/ | ___ __| | / \ _ __ _ __ | (_) ___ __ _| |_(_) ___ _ ___ +// | |\/| |/ _ \/ _` | / _ \ | '_ \| '_ \| | |/ __/ _` | __| |/ _ \| '_ | +// | | | | __/ (_| |/ ___ \| |_) | |_) | | | (_| (_| | |_| | (_) | | | | +// |_| |_|\___|\__,_/_/ \_\ .__/| .__/|_|_|\___\__,_|\__|_|\___/|_| |_| +// |_| |_| +// License: BSD License +// Kratos default license: kratos/license.txt +// +// Main authors: Philipp Bucher (https://github.com/philbucher) +// + +// System includes + +// External includes + +// Project includes +#include "containers/model.h" +#include "geometries/quadrilateral_2d_4.h" +#include "processes/structured_mesh_generator_process.h" +#include "testing/testing.h" +#include "custom_io/med_model_part_io.h" +#include "custom_utilities/med_testing_utilities.h" + +namespace Kratos::Testing { + +namespace { // helpers namespace + +void MedWriteReadModelPart( + const std::filesystem::path& rFileName, + const std::function& rPopulateFunction) +{ + Model model; + auto& test_model_part_write = model.CreateModelPart("test_write"); + auto& test_model_part_read = model.CreateModelPart("test_read"); + + auto full_name = rFileName; + full_name.replace_extension(".med"); + + rPopulateFunction(test_model_part_write); + + { // encapsulating to ensure memory (aka file handle) is freed + MedModelPartIO io_write(full_name, IO::WRITE); + io_write.WriteModelPart(test_model_part_write); + } + { // encapsulating to ensure memory (aka file handle) is freed + MedModelPartIO io_read(full_name); + io_read.ReadModelPart(test_model_part_read); + } + + // remove before checking ModelParts, as would be left over if comparison fails + if (std::filesystem::exists(full_name)) { + std::filesystem::remove(full_name); + } + + MedTestingUtilities::CheckModelPartsAreEqual(test_model_part_write, test_model_part_read); +} + +} // helpers namespace + +KRATOS_TEST_CASE_IN_SUITE(WriteReadMedEmpty, KratosMedFastSuite) +{ + MedWriteReadModelPart(this->Name(), [](ModelPart& rModelPart){ + // deliberately do not create any entities + }); +} + +KRATOS_TEST_CASE_IN_SUITE(WriteReadMedNodes, KratosMedFastSuite) +{ + MedWriteReadModelPart(this->Name(), [](ModelPart& rModelPart){ + int node_id = 0; + for (int x=0; x<20; ++x) { + for (int y=0; y<10; ++y) { + for (int z=0; z<15; ++z) { + rModelPart.CreateNewNode(++node_id, x, y, z); + } + } + } + }); +} + +KRATOS_TEST_CASE_IN_SUITE(WriteReadMedNodesNonConsecutiveNodeIds, KratosMedFastSuite) +{ + MedWriteReadModelPart(this->Name(), [](ModelPart& rModelPart){ + int node_id = 1; + for (int x=0; x<20; ++x) { + for (int y=0; y<10; ++y) { + for (int z=0; z<15; ++z) { + rModelPart.CreateNewNode(node_id, x, y, z); + node_id += 5; + } + } + } + }); +} + +KRATOS_TEST_CASE_IN_SUITE(WriteRead2DLineMesh, KratosMedFastSuite) +{ + MedWriteReadModelPart(this->Name(), [](ModelPart& rModelPart){ + constexpr std::size_t num_nodes = 10; + for (std::size_t i=0; i{i+1, i+2}); + } + }); +} + +KRATOS_TEST_CASE_IN_SUITE(WriteRead2DLineMeshNonConsecutiveNodeIds, KratosMedFastSuite) +{ + MedWriteReadModelPart(this->Name(), [](ModelPart& rModelPart){ + constexpr std::size_t num_nodes = 10; + for (std::size_t i=0; iId(); + } + for (std::size_t i=0; i{i*10+1, (i+1)*10+1}); + } + }); +} + +KRATOS_TEST_CASE_IN_SUITE(WriteRead2DTriangularMesh, KratosMedFastSuite) +{ + MedWriteReadModelPart(this->Name(), [](ModelPart& rModelPart){ + auto p_point_1 = Kratos::make_intrusive(1, 0.0, 0.0, 0.0); + auto p_point_2 = Kratos::make_intrusive(2, 0.0, 1.0, 0.0); + auto p_point_3 = Kratos::make_intrusive(3, 1.0, 1.0, 0.0); + auto p_point_4 = Kratos::make_intrusive(4, 1.0, 0.0, 0.0); + + Quadrilateral2D4 geometry(p_point_1, p_point_2, p_point_3, p_point_4); + + Parameters mesher_parameters(R"( + { + "number_of_divisions": 7, + "element_name": "Element2D3N", + "create_skin_sub_model_part" : false, + "create_body_sub_model_part" : false + })"); + + StructuredMeshGeneratorProcess(geometry, rModelPart, mesher_parameters).Execute(); + // create geometries! + MedTestingUtilities::AddGeometriesFromElements(rModelPart); + }); +} + +KRATOS_TEST_CASE_IN_SUITE(WriteRead2DQuadrilateralMesh, KratosMedFastSuite) +{ + MedWriteReadModelPart(this->Name(), [](ModelPart& rModelPart){ + constexpr std::size_t num_quads = 10; + int node_id = 0; + for (std::size_t i=0; i{ + (i*2)+1, + (i*2)+3, + (i*2)+4, + (i*2)+2 + }); + } + }); +} + +KRATOS_TEST_CASE_IN_SUITE(WriteRead2DTriAndQuadMesh, KratosMedFastSuite) +{ + MedWriteReadModelPart(this->Name(), [](ModelPart& rModelPart){ + constexpr std::size_t num_quads = 10; + int node_id = 0; + for (std::size_t i=0; i{ + (i*2)+1, + (i*2)+3, + (i*2)+4, + (i*2)+2 + }); + } + + // then writing all triangles + for (std::size_t i=0; i{ + (i*2)+4, + static_cast(node_id), + (i*2)+2 + }); + } + }); +} + +} // Kratos::Testing diff --git a/applications/MedApplication/tests/cpp_tests/test_med_model_part_io.cpp b/applications/MedApplication/tests/cpp_tests/test_med_model_part_io.cpp new file mode 100644 index 000000000000..80a8cd6194b8 --- /dev/null +++ b/applications/MedApplication/tests/cpp_tests/test_med_model_part_io.cpp @@ -0,0 +1,58 @@ +// KRATOS __ __ _ _ _ _ _ _ +// | \/ | ___ __| | / \ _ __ _ __ | (_) ___ __ _| |_(_) ___ _ ___ +// | |\/| |/ _ \/ _` | / _ \ | '_ \| '_ \| | |/ __/ _` | __| |/ _ \| '_ | +// | | | | __/ (_| |/ ___ \| |_) | |_) | | | (_| (_| | |_| | (_) | | | | +// |_| |_|\___|\__,_/_/ \_\ .__/| .__/|_|_|\___\__,_|\__|_|\___/|_| |_| +// |_| |_| +// License: BSD License +// Kratos default license: kratos/license.txt +// +// Main authors: Philipp Bucher (https://github.com/philbucher) +// + +// System includes + +// External includes + +// Project includes +#include "testing/testing.h" +#include "custom_io/med_model_part_io.h" + +namespace Kratos::Testing { + +KRATOS_TEST_CASE_IN_SUITE(MedModelpartIO_NonExistingFile_read, KratosMedFastSuite) +{ + const std::filesystem::path file_path(this->Name() + ".txt"); + KRATOS_CHECK_IS_FALSE(std::filesystem::exists(file_path)); // make sure there are no leftovers + + KRATOS_CHECK_EXCEPTION_IS_THROWN( + MedModelPartIO dummy(file_path), + "File \""+file_path.string()+"\" does not exist!"); + + KRATOS_CHECK_IS_FALSE(std::filesystem::exists(file_path)); +} + +KRATOS_TEST_CASE_IN_SUITE(MedModelpartIO_NonExistingFile_write, KratosMedFastSuite) +{ + const std::filesystem::path file_path(this->Name() + ".txt"); + KRATOS_CHECK_IS_FALSE(std::filesystem::exists(file_path)); // make sure there are no leftovers + + MedModelPartIO(file_path, IO::WRITE); + KRATOS_CHECK(std::filesystem::exists(file_path)); + + std::filesystem::remove(file_path); // clean leftover +} + +KRATOS_TEST_CASE_IN_SUITE(MedModelpartIO_TextFile, KratosMedFastSuite) +{ + const std::filesystem::path file_path(this->Name() + ".txt"); + std::ofstream output(file_path); // create a dummy file (that is not a hdf file) + + KRATOS_CHECK_EXCEPTION_IS_THROWN( + MedModelPartIO dummy(file_path), + "A problem with HDF occured while trying to open file \""+file_path.string()+"\"!"); + + std::filesystem::remove(file_path); // clean leftover +} + +} // Kratos::Testing diff --git a/applications/MedApplication/tests/med_files/empty/mesh.med b/applications/MedApplication/tests/med_files/empty/mesh.med new file mode 100644 index 000000000000..a60de9d0e2ce Binary files /dev/null and b/applications/MedApplication/tests/med_files/empty/mesh.med differ diff --git a/applications/MedApplication/tests/med_files/hexahedral_8N/create_mesh.py b/applications/MedApplication/tests/med_files/hexahedral_8N/create_mesh.py new file mode 100644 index 000000000000..bc7091a1c59c --- /dev/null +++ b/applications/MedApplication/tests/med_files/hexahedral_8N/create_mesh.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python + +### +### This file is generated automatically by SALOME v9.6.0 with dump python functionality +### + +import salome + +salome.salome_init() +import salome_notebook +notebook = salome_notebook.NoteBook() + +### +### GEOM component +### + +import GEOM +from salome.geom import geomBuilder +import math +import SALOMEDS + + +geompy = geomBuilder.New() + +O = geompy.MakeVertex(0, 0, 0) +OX = geompy.MakeVectorDXDYDZ(1, 0, 0) +OY = geompy.MakeVectorDXDYDZ(0, 1, 0) +OZ = geompy.MakeVectorDXDYDZ(0, 0, 1) +Box_1 = geompy.MakeBoxDXDYDZ(200, 200, 200) +geompy.addToStudy( O, 'O' ) +geompy.addToStudy( OX, 'OX' ) +geompy.addToStudy( OY, 'OY' ) +geompy.addToStudy( OZ, 'OZ' ) +geompy.addToStudy( Box_1, 'Box_1' ) + +### +### SMESH component +### + +import SMESH, SALOMEDS +from salome.smesh import smeshBuilder + +smesh = smeshBuilder.New() +#smesh.SetEnablePublish( False ) # Set to False to avoid publish in study if not needed or in some particular situations: + # multiples meshes built in parallel, complex and numerous mesh edition (performance) + +Mesh_1 = smesh.Mesh(Box_1) +Regular_1D = Mesh_1.Segment() +Number_of_Segments_1 = Regular_1D.NumberOfSegments(50) +Quadrangle_2D = Mesh_1.Quadrangle(algo=smeshBuilder.QUADRANGLE) +Hexa_3D = Mesh_1.Hexahedron(algo=smeshBuilder.Hexa) +Number_of_Segments_1.SetNumberOfSegments( 3 ) +isDone = Mesh_1.Compute() +smesh.SetName(Mesh_1, 'Mesh_1') +try: + Mesh_1.ExportMED(r'applications/MedApplication/tests/med_files/hexahedral_8N/mesh.med',auto_groups=0,version=41,overwrite=1,meshPart=None,autoDimension=1) + pass +except: + print('ExportMED() failed. Invalid file name?') + + +## Set names of Mesh objects +smesh.SetName(Regular_1D.GetAlgorithm(), 'Regular_1D') +smesh.SetName(Hexa_3D.GetAlgorithm(), 'Hexa_3D') +smesh.SetName(Quadrangle_2D.GetAlgorithm(), 'Quadrangle_2D') +smesh.SetName(Number_of_Segments_1, 'Number of Segments_1') +smesh.SetName(Mesh_1.GetMesh(), 'Mesh_1') + + +if salome.sg.hasDesktop(): + salome.sg.updateObjBrowser() diff --git a/applications/MedApplication/tests/med_files/hexahedral_8N/mesh.med b/applications/MedApplication/tests/med_files/hexahedral_8N/mesh.med new file mode 100644 index 000000000000..bad5523461ac Binary files /dev/null and b/applications/MedApplication/tests/med_files/hexahedral_8N/mesh.med differ diff --git a/applications/MedApplication/tests/med_files/only_nodes/mesh.med b/applications/MedApplication/tests/med_files/only_nodes/mesh.med new file mode 100644 index 000000000000..6854c695199f Binary files /dev/null and b/applications/MedApplication/tests/med_files/only_nodes/mesh.med differ diff --git a/applications/MedApplication/tests/med_files/quadrilateral_4N_2d/create_mesh.py b/applications/MedApplication/tests/med_files/quadrilateral_4N_2d/create_mesh.py new file mode 100644 index 000000000000..8713b45370cd --- /dev/null +++ b/applications/MedApplication/tests/med_files/quadrilateral_4N_2d/create_mesh.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +### +### This file is generated automatically by SALOME v9.6.0 with dump python functionality +### + +import salome + +salome.salome_init() +import salome_notebook +notebook = salome_notebook.NoteBook() + +### +### GEOM component +### + +import GEOM +from salome.geom import geomBuilder +import math +import SALOMEDS + + +geompy = geomBuilder.New() + +O = geompy.MakeVertex(0, 0, 0) +OX = geompy.MakeVectorDXDYDZ(1, 0, 0) +OY = geompy.MakeVectorDXDYDZ(0, 1, 0) +OZ = geompy.MakeVectorDXDYDZ(0, 0, 1) +Face_1 = geompy.MakeFaceHW(100, 100, 1) +geompy.addToStudy( O, 'O' ) +geompy.addToStudy( OX, 'OX' ) +geompy.addToStudy( OY, 'OY' ) +geompy.addToStudy( OZ, 'OZ' ) +geompy.addToStudy( Face_1, 'Face_1' ) + +### +### SMESH component +### + +import SMESH, SALOMEDS +from salome.smesh import smeshBuilder + +smesh = smeshBuilder.New() +#smesh.SetEnablePublish( False ) # Set to False to avoid publish in study if not needed or in some particular situations: + # multiples meshes built in parallel, complex and numerous mesh edition (performance) + +#hyp_8.SetLength( 14.1421 ) ### not created Object +NETGEN_2D_Parameters_1 = smesh.CreateHypothesisByAverageLength( 'NETGEN_Parameters_2D', 'NETGENEngine', 14.1421, 1 ) +Mesh_1 = smesh.Mesh(Face_1) +status = Mesh_1.AddHypothesis( Face_1, NETGEN_2D_Parameters_1 ) +NETGEN_1D_2D = Mesh_1.Triangle(algo=smeshBuilder.NETGEN_1D2D) +isDone = Mesh_1.Compute() + + +## Set names of Mesh objects +smesh.SetName(NETGEN_1D_2D.GetAlgorithm(), 'NETGEN 1D-2D') +smesh.SetName(NETGEN_2D_Parameters_1, 'NETGEN 2D Parameters_1') +smesh.SetName(Mesh_1.GetMesh(), 'Mesh_1') + + +if salome.sg.hasDesktop(): + salome.sg.updateObjBrowser() diff --git a/applications/MedApplication/tests/med_files/quadrilateral_4N_2d/mesh.med b/applications/MedApplication/tests/med_files/quadrilateral_4N_2d/mesh.med new file mode 100644 index 000000000000..1a45e8ef3b28 Binary files /dev/null and b/applications/MedApplication/tests/med_files/quadrilateral_4N_2d/mesh.med differ diff --git a/applications/MedApplication/tests/med_files/tetrahedral_4N/create_mesh.py b/applications/MedApplication/tests/med_files/tetrahedral_4N/create_mesh.py new file mode 100644 index 000000000000..3859f7de0a30 --- /dev/null +++ b/applications/MedApplication/tests/med_files/tetrahedral_4N/create_mesh.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python + +### +### This file is generated automatically by SALOME v9.6.0 with dump python functionality +### + +import salome + +salome.salome_init() +import salome_notebook +notebook = salome_notebook.NoteBook() + +### +### GEOM component +### + +import GEOM +from salome.geom import geomBuilder +import math +import SALOMEDS + + +geompy = geomBuilder.New() + +O = geompy.MakeVertex(0, 0, 0) +OX = geompy.MakeVectorDXDYDZ(1, 0, 0) +OY = geompy.MakeVectorDXDYDZ(0, 1, 0) +OZ = geompy.MakeVectorDXDYDZ(0, 0, 1) +Box_1 = geompy.MakeBoxDXDYDZ(200, 200, 200) +geompy.addToStudy( O, 'O' ) +geompy.addToStudy( OX, 'OX' ) +geompy.addToStudy( OY, 'OY' ) +geompy.addToStudy( OZ, 'OZ' ) +geompy.addToStudy( Box_1, 'Box_1' ) + +### +### SMESH component +### + +import SMESH, SALOMEDS +from salome.smesh import smeshBuilder + +smesh = smeshBuilder.New() +#smesh.SetEnablePublish( False ) # Set to False to avoid publish in study if not needed or in some particular situations: + # multiples meshes built in parallel, complex and numerous mesh edition (performance) + +#hyp_16.SetLength( 34.641 ) ### not created Object +NETGEN_3D_Parameters_1 = smesh.CreateHypothesisByAverageLength( 'NETGEN_Parameters', 'NETGENEngine', 34.641, 0 ) +Mesh_1 = smesh.Mesh(Box_1) +status = Mesh_1.AddHypothesis( Box_1, NETGEN_3D_Parameters_1 ) +NETGEN_1D_2D_3D = Mesh_1.Tetrahedron(algo=smeshBuilder.NETGEN_1D2D3D) +NETGEN_3D_Parameters_1.SetMaxSize( 100 ) +NETGEN_3D_Parameters_1.SetMinSize( 0.34641 ) +NETGEN_3D_Parameters_1.SetSecondOrder( 0 ) +NETGEN_3D_Parameters_1.SetOptimize( 1 ) +NETGEN_3D_Parameters_1.SetFineness( 2 ) +NETGEN_3D_Parameters_1.SetChordalError( 17.3205 ) +NETGEN_3D_Parameters_1.SetChordalErrorEnabled( 1 ) +NETGEN_3D_Parameters_1.SetUseSurfaceCurvature( 1 ) +NETGEN_3D_Parameters_1.SetFuseEdges( 1 ) +NETGEN_3D_Parameters_1.SetCheckChartBoundary( 0 ) +NETGEN_3D_Parameters_1.SetQuadAllowed( 0 ) +NETGEN_3D_Parameters_1.SetCheckChartBoundary( 3 ) +isDone = Mesh_1.Compute() +smesh.SetName(Mesh_1, 'Mesh_1') +try: + Mesh_1.ExportMED(r'applications/MedApplication/tests/med_files/tetrahedral_4N/mesh.med',auto_groups=0,version=41,overwrite=1,meshPart=None,autoDimension=1) + pass +except: + print('ExportMED() failed. Invalid file name?') + + +## Set names of Mesh objects +smesh.SetName(NETGEN_1D_2D_3D.GetAlgorithm(), 'NETGEN 1D-2D-3D') +smesh.SetName(NETGEN_3D_Parameters_1, 'NETGEN 3D Parameters_1') +smesh.SetName(Mesh_1.GetMesh(), 'Mesh_1') + + +if salome.sg.hasDesktop(): + salome.sg.updateObjBrowser() diff --git a/applications/MedApplication/tests/med_files/tetrahedral_4N/mesh.med b/applications/MedApplication/tests/med_files/tetrahedral_4N/mesh.med new file mode 100644 index 000000000000..baab49cf5552 Binary files /dev/null and b/applications/MedApplication/tests/med_files/tetrahedral_4N/mesh.med differ diff --git a/applications/MedApplication/tests/med_files/triangle_3N_2d/create_mesh.py b/applications/MedApplication/tests/med_files/triangle_3N_2d/create_mesh.py new file mode 100644 index 000000000000..86e9d0c4e8b1 --- /dev/null +++ b/applications/MedApplication/tests/med_files/triangle_3N_2d/create_mesh.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +### +### This file is generated automatically by SALOME v9.6.0 with dump python functionality +### + +import salome + +salome.salome_init() +import salome_notebook +notebook = salome_notebook.NoteBook() + +### +### GEOM component +### + +import GEOM +from salome.geom import geomBuilder +import math +import SALOMEDS + + +geompy = geomBuilder.New() + +O = geompy.MakeVertex(0, 0, 0) +OX = geompy.MakeVectorDXDYDZ(1, 0, 0) +OY = geompy.MakeVectorDXDYDZ(0, 1, 0) +OZ = geompy.MakeVectorDXDYDZ(0, 0, 1) +Face_1 = geompy.MakeFaceHW(100, 100, 1) +geompy.addToStudy( O, 'O' ) +geompy.addToStudy( OX, 'OX' ) +geompy.addToStudy( OY, 'OY' ) +geompy.addToStudy( OZ, 'OZ' ) +geompy.addToStudy( Face_1, 'Face_1' ) + +### +### SMESH component +### + +import SMESH, SALOMEDS +from salome.smesh import smeshBuilder + +smesh = smeshBuilder.New() +#smesh.SetEnablePublish( False ) # Set to False to avoid publish in study if not needed or in some particular situations: + # multiples meshes built in parallel, complex and numerous mesh edition (performance) + +#hyp_0.SetLength( 14.1421 ) ### not created Object +NETGEN_2D_Parameters_1 = smesh.CreateHypothesisByAverageLength( 'NETGEN_Parameters_2D', 'NETGENEngine', 14.1421, 0 ) +Mesh_1 = smesh.Mesh(Face_1) +status = Mesh_1.AddHypothesis( Face_1, NETGEN_2D_Parameters_1 ) +NETGEN_1D_2D = Mesh_1.Triangle(algo=smeshBuilder.NETGEN_1D2D) +isDone = Mesh_1.Compute() + + +## Set names of Mesh objects +smesh.SetName(NETGEN_1D_2D.GetAlgorithm(), 'NETGEN 1D-2D') +smesh.SetName(NETGEN_2D_Parameters_1, 'NETGEN 2D Parameters_1') +smesh.SetName(Mesh_1.GetMesh(), 'Mesh_1') + + +if salome.sg.hasDesktop(): + salome.sg.updateObjBrowser() diff --git a/applications/MedApplication/tests/med_files/triangle_3N_2d/mesh.med b/applications/MedApplication/tests/med_files/triangle_3N_2d/mesh.med new file mode 100644 index 000000000000..c16e5287a8d9 Binary files /dev/null and b/applications/MedApplication/tests/med_files/triangle_3N_2d/mesh.med differ diff --git a/applications/MedApplication/tests/med_files/triangle_3N_3d/create_mesh.py b/applications/MedApplication/tests/med_files/triangle_3N_3d/create_mesh.py new file mode 100644 index 000000000000..b51ceafe260b --- /dev/null +++ b/applications/MedApplication/tests/med_files/triangle_3N_3d/create_mesh.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python + +### +### This file is generated automatically by SALOME v9.6.0 with dump python functionality +### + +import salome + +salome.salome_init() +import salome_notebook +notebook = salome_notebook.NoteBook() + +### +### GEOM component +### + +import GEOM +from salome.geom import geomBuilder +import math +import SALOMEDS + + +geompy = geomBuilder.New() + +O = geompy.MakeVertex(0, 0, 0) +OX = geompy.MakeVectorDXDYDZ(1, 0, 0) +OY = geompy.MakeVectorDXDYDZ(0, 1, 0) +OZ = geompy.MakeVectorDXDYDZ(0, 0, 1) +Vertex_1 = geompy.MakeVertex(1, 0, 0.1) +Vertex_2 = geompy.MakeVertex(0, 1, 0) +Line_1 = geompy.MakeLineTwoPnt(Vertex_2, Vertex_1) +Line_2 = geompy.MakeLineTwoPnt(O, Vertex_2) +Line_3 = geompy.MakeLineTwoPnt(O, Vertex_1) +Face_1 = geompy.MakeFaceWires([Line_1, Line_2, Line_3], 1) +geompy.addToStudy( O, 'O' ) +geompy.addToStudy( OX, 'OX' ) +geompy.addToStudy( OY, 'OY' ) +geompy.addToStudy( OZ, 'OZ' ) +geompy.addToStudy( Vertex_1, 'Vertex_1' ) +geompy.addToStudy( Vertex_2, 'Vertex_2' ) +geompy.addToStudy( Line_1, 'Line_1' ) +geompy.addToStudy( Line_2, 'Line_2' ) +geompy.addToStudy( Line_3, 'Line_3' ) +geompy.addToStudy( Face_1, 'Face_1' ) + +### +### SMESH component +### + +import SMESH, SALOMEDS +from salome.smesh import smeshBuilder + +smesh = smeshBuilder.New() +#smesh.SetEnablePublish( False ) # Set to False to avoid publish in study if not needed or in some particular situations: + # multiples meshes built in parallel, complex and numerous mesh edition (performance) + +#hyp_12.SetLength( 0.141774 ) ### not created Object +NETGEN_2D_Parameters_1 = smesh.CreateHypothesisByAverageLength( 'NETGEN_Parameters_2D', 'NETGENEngine', 0.141774, 0 ) +Mesh_1 = smesh.Mesh(Face_1) +status = Mesh_1.AddHypothesis( Face_1, NETGEN_2D_Parameters_1 ) +NETGEN_1D_2D = Mesh_1.Triangle(algo=smeshBuilder.NETGEN_1D2D) +isDone = Mesh_1.Compute() + + +## Set names of Mesh objects +smesh.SetName(NETGEN_1D_2D.GetAlgorithm(), 'NETGEN 1D-2D') +smesh.SetName(NETGEN_2D_Parameters_1, 'NETGEN 2D Parameters_1') +smesh.SetName(Mesh_1.GetMesh(), 'Mesh_1') + + +if salome.sg.hasDesktop(): + salome.sg.updateObjBrowser() diff --git a/applications/MedApplication/tests/med_files/triangle_3N_3d/mesh.med b/applications/MedApplication/tests/med_files/triangle_3N_3d/mesh.med new file mode 100644 index 000000000000..f78cd1488267 Binary files /dev/null and b/applications/MedApplication/tests/med_files/triangle_3N_3d/mesh.med differ diff --git a/applications/MedApplication/tests/run_cpp_unit_tests.py b/applications/MedApplication/tests/run_cpp_unit_tests.py new file mode 100644 index 000000000000..f0d8e31cfa96 --- /dev/null +++ b/applications/MedApplication/tests/run_cpp_unit_tests.py @@ -0,0 +1,9 @@ +import KratosMultiphysics as KM +from KratosMultiphysics.MedApplication import * +import sys +if len(sys.argv) < 2: + KM.Tester.SetVerbosity(KM.Tester.Verbosity.PROGRESS) # TESTS_OUTPUTS + KM.Tester.RunTestSuite("KratosMedFastSuite") +else : + KM.Tester.SetVerbosity(KM.Tester.Verbosity.TESTS_OUTPUTS) + KM.Tester.RunTestCases(sys.argv[1]) diff --git a/applications/MedApplication/tests/test_MedApplication.py b/applications/MedApplication/tests/test_MedApplication.py new file mode 100644 index 000000000000..068e617fe356 --- /dev/null +++ b/applications/MedApplication/tests/test_MedApplication.py @@ -0,0 +1,44 @@ +import KratosMultiphysics.KratosUnittest as KratosUnittest + +# Import the tests of test-classes to create the suites +import test_med_model_part_io + +def AssembleTestSuites(): + ''' Populates the test suites to run. + + Populates the test suites to run. At least, it should pupulate the suites: + "small", "nighlty" and "all" + + Return + ------ + + suites: A dictionary of suites + The set of suites with its test_cases added. + ''' + + suites = KratosUnittest.KratosSuites + + # Create a test suit with the selected tests (Small tests): + # smallSuite will contain the following tests: + # - testSmallExample + smallSuite = suites['small'] + smallSuite.addTests(KratosUnittest.TestLoader().loadTestsFromTestCases([test_med_model_part_io.TestMedModelPartIO])) + + # Create a test suit with the selected tests + # nightSuite will contain the following tests: + # - testSmallExample + # - testNightlyFirstExample + # - testNightlySecondExample + nightSuite = suites['nightly'] + nightSuite.addTests(smallSuite) + + # Create a test suit that contains all the tests from every testCase + # in the list: + allSuite = suites['all'] + allSuite.addTests(nightSuite) + + return suites + + +if __name__ == '__main__': + KratosUnittest.runTests(AssembleTestSuites()) diff --git a/applications/MedApplication/tests/test_med_model_part_io.py b/applications/MedApplication/tests/test_med_model_part_io.py new file mode 100644 index 000000000000..04a6c80f0a8a --- /dev/null +++ b/applications/MedApplication/tests/test_med_model_part_io.py @@ -0,0 +1,117 @@ +import KratosMultiphysics as KM +import KratosMultiphysics.KratosUnittest as KratosUnittest + +import KratosMultiphysics.MedApplication as KratosMed + +from KratosMultiphysics.kratos_utilities import DeleteFileIfExisting + +from pathlib import Path + + + +def GetMedPath(med_path, med_name="mesh.med"): + return Path(__file__).absolute().parent / "med_files" / med_path / med_name + + +class TestMedModelPartIO(KratosUnittest.TestCase): + + def setUp(self): + self.model = KM.Model() + self.mp_read_1 = self.model.CreateModelPart("read_1") + self.mp_read_2 = self.model.CreateModelPart("read_2") + + def _execute_tests(self, med_path, check_fct): + med_io_read_1 = KratosMed.MedModelPartIO(GetMedPath(med_path)) + med_io_read_1.ReadModelPart(self.mp_read_1) + + with self.subTest("check_model_part"): + check_fct(self.mp_read_1) + + with self.subTest("read_write_read"): + med_temp_path = GetMedPath(med_path, "temp.med") + DeleteFileIfExisting(med_temp_path) # make sure there are no leftovers from previous tests + self.addCleanup(DeleteFileIfExisting, med_temp_path) # clean up after test + + med_io_write = KratosMed.MedModelPartIO(med_temp_path, KM.IO.WRITE) + med_io_write.WriteModelPart(self.mp_read_1) + + med_io_read_2 = KratosMed.MedModelPartIO(med_temp_path, KM.IO.READ) + med_io_read_2.ReadModelPart(self.mp_read_2) + + KratosMed.MedTestingUtilities.CheckModelPartsAreEqual(self.mp_read_1, self.mp_read_2) + + def test_empty_med_file(self): + def mp_check_fct(model_part): + self.assertEqual(model_part.NumberOfNodes(), 0) + + self._execute_tests("empty", mp_check_fct) + + def test_only_nodes(self): + def mp_check_fct(model_part): + self.assertEqual(model_part.NumberOfNodes(), 4) + + exp_coords = [ + (0,0,0), (0,0,1),(0,1,1),(1,1,1) + ] + + for coords, node in zip(exp_coords, model_part.Nodes): + self.assertAlmostEqual(node.X, coords[0]) + self.assertAlmostEqual(node.X0, coords[0]) + self.assertAlmostEqual(node.Y, coords[1]) + self.assertAlmostEqual(node.Y0, coords[1]) + self.assertAlmostEqual(node.Z, coords[2]) + self.assertAlmostEqual(node.Z0, coords[2]) + + self._execute_tests("only_nodes", mp_check_fct) + + def test_nodes_with_sub_meshes(self): + self.skipTest("This test is not yet implemented") + raise NotImplementedError + + def test_2D_mesh(self): + self.skipTest("This test is not yet implemented") + raise NotImplementedError + + def test_2D_mesh_with_sub_meshes(self): + self.skipTest("This test is not yet implemented") + raise NotImplementedError + + def test_2D_mesh_in_3D_space(self): + self.skipTest("This test is not yet implemented") + raise NotImplementedError + + def test_2D_mesh_in_3D_space_with_sub_meshes(self): + self.skipTest("This test is not yet implemented") + raise NotImplementedError + + def test_3D_mesh(self): + self.skipTest("This test is not yet implemented") + raise NotImplementedError + + def test_3D_mesh_with_sub_meshes(self): + self.skipTest("This test is not yet implemented") + raise NotImplementedError + + def test_line_2N_linear_mesh(self): + self.skipTest("This test is not yet implemented") + raise NotImplementedError + + def test_triangle_3N_linear_mesh(self): + self.skipTest("This test is not yet implemented") + raise NotImplementedError + + def test_quadrilateral_4N_linear_mesh(self): + self.skipTest("This test is not yet implemented") + raise NotImplementedError + + def test_tetrahedra_4N_linear_mesh(self): + self.skipTest("This test is not yet implemented") + raise NotImplementedError + + def test_hexahedra_8N_linear_mesh(self): + self.skipTest("This test is not yet implemented") + raise NotImplementedError + + +if __name__ == '__main__': + KratosUnittest.main()