diff --git a/MeshGeoToolsLib/IdentifySubdomainMesh.cpp b/MeshGeoToolsLib/IdentifySubdomainMesh.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e69a202d4b8ca26db3fe3fb64bb42c4ca287641b --- /dev/null +++ b/MeshGeoToolsLib/IdentifySubdomainMesh.cpp @@ -0,0 +1,225 @@ +/** + * \file + * \copyright + * Copyright (c) 2012-2018, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + */ + +#include <map> +#include <vector> + +#include "MeshLib/Elements/Element.h" +#include "MeshLib/Mesh.h" +#include "MeshLib/Node.h" + +#include "MeshNodeSearcher.h" + +namespace +{ +/// Find one-to-one mapping of subdomain nodes to the bulk mesh nodes and +/// returns the map of ids. +std::vector<std::size_t> identifySubdomainMeshNodes( + MeshLib::Mesh const& subdomain_mesh, + MeshGeoToolsLib::MeshNodeSearcher const& mesh_node_searcher) +{ + // Convert nodes pointers needed for the mesh_node_searcher algorithm. + auto const& nodes = subdomain_mesh.getNodes(); + std::vector<MathLib::Point3dWithID*> subdomain_points{begin(nodes), + end(nodes)}; + + auto const& bulk_node_ids = + mesh_node_searcher.getMeshNodeIDs(subdomain_points); + + if (bulk_node_ids.size() != subdomain_mesh.getNumberOfNodes()) + { + OGS_FATAL( + "Expected to find exactly one node in the bulk mesh for each node " + "of the subdomain; Found %d nodes in the bulk mesh out of %d nodes " + "in the subdomain.", + bulk_node_ids.size(), subdomain_mesh.getNumberOfNodes()); + } + + return bulk_node_ids; +} + +/// Find all elements which consist of all of the given node ids. +/// \note It is recommended to include only base nodes of elements into the +/// search \c node_ids. +std::vector<std::size_t> findElementsInMesh( + MeshLib::Mesh const& mesh, std::vector<std::size_t> const& node_ids) +{ + // + // Collect all element ids for all nodes. + // + std::vector<std::size_t> common_element_ids; + // Every node is connected to at least one element. + auto const nnodes = node_ids.size(); + common_element_ids.reserve(nnodes); + + for (auto const node_id : node_ids) + { + auto const& connected_elements = mesh.getNode(node_id)->getElements(); + std::transform( + begin(connected_elements), end(connected_elements), + back_inserter(common_element_ids), + [](MeshLib::Element const* const e) { return e->getID(); }); + } + + // + // Count how often an element is shared by all nodes. + // + std::map<std::size_t, int> element_counts; + for (auto const element_id : common_element_ids) + { + element_counts[element_id]++; + } + + // + // Elements which are shared by as many nodes as the input nodes are the + // desired elements. + // + std::vector<std::size_t> element_ids; + for (auto const& pair : element_counts) + { + if (pair.second == static_cast<int>(nnodes)) + { + element_ids.push_back(pair.first); + } + } + + return element_ids; +} + +/// Tries to find unique elements' ids in the bulk mesh. +/// The subdomain elements' nodes are mapped to the bulk mesh and are then used +/// to identify the bulk mesh elements. +std::vector<std::size_t> identifySubdomainMeshElements( + MeshLib::Mesh const& subdomain_mesh, MeshLib::Mesh const& bulk_mesh) +{ + auto& properties = subdomain_mesh.getProperties(); + auto const& bulk_node_ids = + *properties.getPropertyVector<std::size_t>("bulk_node_ids"); + + // Initialize with very large value + std::vector<std::size_t> bulk_element_ids_map( + subdomain_mesh.getNumberOfElements(), -1); + + for (auto* const e : subdomain_mesh.getElements()) + { + std::vector<std::size_t> element_node_ids(e->getNumberOfBaseNodes()); + for (unsigned n = 0; n < e->getNumberOfBaseNodes(); ++n) + { + element_node_ids[n] = e->getNodeIndex(n); + } + std::vector<std::size_t> element_node_ids_bulk( + e->getNumberOfBaseNodes()); + std::transform(begin(element_node_ids), end(element_node_ids), + begin(element_node_ids_bulk), + [&bulk_node_ids](std::size_t const id) { + return bulk_node_ids[id]; + }); + auto const& bulk_element_ids = + findElementsInMesh(bulk_mesh, element_node_ids_bulk); + + if (bulk_element_ids.empty()) + { + ERR("No element could be found for the subdomain element %d. " + "Corresponding bulk mesh node ids are:", + e->getID()); + for (auto const i : element_node_ids_bulk) + ERR("\t%d", i); + OGS_FATAL( + "Expect exactly one element to be found in the bulk mesh."); + } + if (bulk_element_ids.size() != 1) + { + ERR("%d elements found for the subdomain element %d. " + "Corresponding bulk mesh node ids are:", + bulk_element_ids.size(), e->getID()); + for (auto const i : element_node_ids_bulk) + ERR("\t%d", i); + ERR("The found bulk mesh element ids are:"); + for (auto const i : bulk_element_ids) + ERR("\t%d", i); + OGS_FATAL( + "Expect exactly one element to be found in the bulk mesh."); + } + + bulk_element_ids_map[e->getID()] = bulk_element_ids.front(); + } + + return bulk_element_ids_map; +} + +/// Updates or checks the existing mesh's property with the given values. +void updateOrCheckExistingSubdomainProperty( + MeshLib::Mesh& mesh, std::string const& property_name, + std::vector<std::size_t> values, MeshLib::MeshItemType const mesh_item_type, + bool const force_overwrite) +{ + auto& properties = mesh.getProperties(); + if (!properties.existsPropertyVector<std::size_t>(property_name)) + { + addPropertyToMesh(mesh, property_name, mesh_item_type, 1, values); + return; + } + + // + // Check the existing property against new values. + // + auto& original_property = + *properties.getPropertyVector<std::size_t>(property_name); + if (std::equal(begin(original_property), end(original_property), + begin(values), end(values))) + { + INFO( + "There is already a '%s' property present in the subdomain mesh " + "'%s' and it is equal to the newly computed values.", + property_name.c_str(), mesh.getName().c_str()); + return; + } + + // + // Property differs. Notify and update if forced. + // + WARN( + "There is already a '%s' property present in the subdomain mesh '%s' " + "and it is not equal to the newly computed values.", + property_name.c_str(), + mesh.getName().c_str()); + + if (!force_overwrite) + { + OGS_FATAL("The force overwrite flag was not specified, exiting."); + } + + INFO("Overwriting '%s' property.", property_name.c_str()); + original_property.resize(values.size()); + std::copy(begin(values), end(values), begin(original_property)); +} +} // namespace + +namespace MeshGeoToolsLib +{ +void identifySubdomainMesh(MeshLib::Mesh& subdomain_mesh, + MeshLib::Mesh const& bulk_mesh, + MeshNodeSearcher const& mesh_node_searcher, + bool const force_overwrite = false) +{ + auto const& bulk_node_ids = + identifySubdomainMeshNodes(subdomain_mesh, mesh_node_searcher); + + updateOrCheckExistingSubdomainProperty( + subdomain_mesh, "bulk_node_ids", bulk_node_ids, + MeshLib::MeshItemType::Node, force_overwrite); + + auto const& bulk_element_ids = + identifySubdomainMeshElements(subdomain_mesh, bulk_mesh); + + updateOrCheckExistingSubdomainProperty( + subdomain_mesh, "bulk_element_ids", bulk_element_ids, + MeshLib::MeshItemType::Cell, force_overwrite); +} +} // namespace MeshGeoToolsLib diff --git a/MeshGeoToolsLib/IdentifySubdomainMesh.h b/MeshGeoToolsLib/IdentifySubdomainMesh.h new file mode 100644 index 0000000000000000000000000000000000000000..9c12013997a39383416dc63b67dbc84c6fbf06e8 --- /dev/null +++ b/MeshGeoToolsLib/IdentifySubdomainMesh.h @@ -0,0 +1,31 @@ +/** + * \file + * \copyright + * Copyright (c) 2012-2018, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + */ + +namespace MeshGeoToolsLib +{ +class SearchLength; +class MeshNodeSearcher; +} +namespace MeshLib +{ +class Mesh; +} + +namespace MeshGeoToolsLib +{ +/// Geometrically finds nodes and elements of the subdomain mesh in the bulk +/// mesh, and updates or verifies the corresponding bulk_node_ids and +/// bulk_element_ids properties. +/// +/// In case of unexpected results OGS_FATAL is called. +void identifySubdomainMesh(MeshLib::Mesh& subdomain_mesh, + MeshLib::Mesh const& bulk_mesh, + MeshNodeSearcher const& mesh_node_searcher, + bool const force_overwrite = false); +} // namespace MeshGeoToolsLib