Skip to content
Snippets Groups Projects
Forked from ogs / ogs
19670 commits behind the upstream repository.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
MeshPartitioning.cpp 17.58 KiB
/*!
  \file MeshPartitioning.cpp
  \date   2016.05

  \brief  Define the members of class MeshPartitioning

  \copyright
  Copyright (c) 2012-2016, 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 "MeshPartitioning.h"

#include <iomanip>
#include <stdio.h> // for binary output

#include "logog/include/logog.hpp"

#include "MeshLib/Node.h"
#include "MeshLib/Elements/Element.h"

namespace MeshLib
{
void MeshPartitioning :: write2METIS(const std::string& file_name)
{
    std::ofstream os(file_name.data(), std::ios::trunc);
    os << _elements.size() <<" \n";
    for (const auto elem : _elements)
    {
        for (unsigned j=0; j<elem->getNNodes(); j++)
        {
            os << elem->getNodeIndex(j) + 1 <<" ";
        }
        os << std::endl;
    }
}

void MeshPartitioning :: partitionByNodeMETIS(const std::string& file_name_base,
        const unsigned npartitions,
        const bool output_binary)
{
    const std::string npartitions_str = std::to_string(npartitions);

    // -- Read partitioned mesh data from METIS
    std::string fname_parts = file_name_base + ".mesh.npart." + npartitions_str;

    std::ifstream npart_in(fname_parts.data());
    if (!npart_in.is_open())
    {
        ERR("Error: cannot open file %s. It may not exist! ", fname_parts.data());
        abort();
    }

    const std::size_t nnodes = _nodes.size();

    std::vector<std::size_t> nodes_partition_ids;
    nodes_partition_ids.reserve(nnodes);

    for (std::size_t i=0; i<nnodes; i++)
    {
        std::size_t part_id;
        npart_in >> part_id >> std::ws;
        nodes_partition_ids.push_back(part_id);
    }
    npart_in.close();
    //TEST  std::remove(fname_parts.c_str());
    // -- Open files for output
    //
    // for ASCII output
    std::fstream os_subd;
    std::fstream os_subd_head;
    std::fstream os_subd_node;
    // for binary output
    FILE *of_bin_cfg = 0;   // File to output headers
    FILE *of_bin_nod = 0;   // File to output an array of all nodes
    FILE *of_bin_ele = 0;   // File to output an array of all non-ghost elements
    FILE *of_bin_ele_g = 0; // File to output an array of all ghost elements

    if (output_binary)
    {
        std::string fname = file_name_base + "_partitioned_msh_cfg" + npartitions_str + ".bin";
        of_bin_cfg = fopen(fname.c_str(), "wb");

        fname = file_name_base + "_partitioned_msh_ele" + npartitions_str + ".bin";
        of_bin_ele = fopen(fname.c_str(), "wb");

        fname = file_name_base + "_partitioned_msh_ele_g" + npartitions_str + ".bin";
        of_bin_ele_g = fopen(fname.c_str(), "wb");

        fname = file_name_base + "_partitioned_msh_nod" + npartitions_str + ".bin";
        of_bin_nod = fopen(fname.c_str(), "wb");
    }
    else
    {
        std::string fname = file_name_base + "_partitioned_cfg" + npartitions_str + ".msh";
        os_subd_head.open(fname.c_str(), std::ios::out|std::ios::trunc );
        const std::string mesh_info = "Subdomain mesh "
                                      "(Number of non-ghost nodes;  Number of non ghost base nodes; Number of elements; "
                                      "Number of ghost elements; Number of base nodes; Number of nodes) "
                                      "Number of the base nodes of the global mesh; Number of the nodes of the global mesh; "
                                      "Number of integers to define elements; Number of integers to define ghost elements; "
                                      "Reserved number.";
        os_subd_head << mesh_info << std::endl;
        os_subd_head << npartitions << std::endl;

        fname = file_name_base + "_partitioned_elems_" + npartitions_str + ".msh";
        os_subd.open(fname.c_str(), std::ios::out|std::ios::trunc );

        fname = file_name_base + "_partitioned_nodes_" + npartitions_str + ".msh";
        os_subd_node.open(fname.c_str(), std::ios::out|std::ios::trunc );

        std::setw(14);
        os_subd.precision(14);
        os_subd.setf(std::ios::scientific);
    }

    PetscInt global_node_id_offset = 0;
    std::vector<bool> nodes_part_reserved;
    nodes_part_reserved.reserve(nnodes);
    std::vector<PetscInt> node_global_ids;
    node_global_ids.reserve(nnodes);
    for (std::size_t i=0; i<nnodes; i++)
    {
        nodes_part_reserved.push_back(false);
        node_global_ids.push_back(0);
    }
    std::vector<bool> nodes_reseved(nnodes);
    std::vector<bool> elems_reseved(_elements.size());

    std::vector<MeshLib::Node*> partition_nodes;  // non-ghost nodes of a partition
    std::vector<std::size_t> partition_start_node_id(npartitions);
    std::vector<std::size_t> partition_end_node_id(npartitions);
    std::vector<std::size_t> end_non_ghost_node_id(npartitions);

    const PetscInt num_headers = 14;
    PetscInt head[num_headers];
    // node rank offset
    head[10] = 0;
    // element rank offset
    head[11] = 0;
    // ghost element rank offset
    head[12] = 0;
    // Reserved
    head[13] = 0;

    for (std::size_t ipart=0; ipart<npartitions; ipart++)
    {
        INFO("Processing partition: %d", ipart);

        partition_start_node_id[ipart] = partition_nodes.size();

        std::fill(nodes_reseved.begin(), nodes_reseved.end(), false);

        // Find non-ghost nodes in this partition
        for (std::size_t i=0; i< nnodes; i++)
        {
            if ( (nodes_partition_ids[i] == ipart) && (!nodes_part_reserved[i]) )
            {
                partition_nodes.push_back(_nodes[i]);
                nodes_part_reserved[i] = true;
                nodes_reseved[i] = true;
            }
        }
        end_non_ghost_node_id[ipart] = partition_nodes.size();

        // Find elements in this partition
        std::vector<const Element*> partition_regular_elements;
        std::vector<const Element*> partition_ghost_elements;
        std::fill(elems_reseved.begin(), elems_reseved.end(), false);
        // For ghost element
        std::vector< std::vector<unsigned> >
        elems_non_ghost_nodes_local_ids(_elements.size());

        const PetscInt num_non_ghost_nodes =  end_non_ghost_node_id[ipart]
                                           - partition_start_node_id[ipart];
        for (PetscInt i=0; i<num_non_ghost_nodes; i++)
        {
            const unsigned node_id = partition_nodes[ i +  partition_start_node_id[ipart] ]->getID();

            // Search the elements connected to this nodes
            std::vector<Element*> const& connected_elems = _nodes[node_id]->getElements();

            for (const auto elem : connected_elems)
            {
                // If checked
                if (elems_reseved[elem->getID()])
                    continue;

                std::vector<unsigned> nonghost_nodes_local_ids;
                unsigned ghost_counter = 0;
                for (unsigned kk=0; kk<elem->getNNodes(); kk++)
                {
                    if (nodes_reseved[elem->getNodeIndex(kk)])
                    {
                        nonghost_nodes_local_ids.push_back(kk);
                    }
                    else
                    {
                        ghost_counter++;
                    }
                }

                // All nodes of this element are inside this partition
                if (ghost_counter == 0)
                {
                    partition_regular_elements.push_back(elem);
                }
                else // ghost element
                {
                    partition_ghost_elements.push_back(elem);
                    elems_non_ghost_nodes_local_ids[elem->getID()]
                        = std::move(nonghost_nodes_local_ids);
                }

                elems_reseved[elem->getID()] = true;
            }
        }

        // Add ghost nodes in ghost elements to the node vector of this partition
        // nodes_reseved is reused without cleaning
        // Mark the non-ghost nodes for each ghost element
        for (const auto ghost_elem : partition_ghost_elements)
        {
            for (unsigned k=0; k<ghost_elem->getNNodes(); k++)
                nodes_reseved[ghost_elem->getNodeIndex(k)] = false;

            // Mark non-ghost nodes
            std::vector<unsigned>& local_node_ids = elems_non_ghost_nodes_local_ids[ghost_elem->getID()];
            for (std::size_t k=0; k<local_node_ids.size(); k++)
                nodes_reseved[ghost_elem->getNodeIndex(local_node_ids[k])] = true;
        }
        // Add the ghost nodes to the node vector of this partition
        for (const auto ghost_elem : partition_ghost_elements)
        {
            for (unsigned k=0; k<ghost_elem->getNNodes(); k++)
            {
                const unsigned node_id = ghost_elem->getNodeIndex(k);
                if (nodes_reseved[node_id])
                    continue;
                nodes_reseved[node_id] = true;
                partition_nodes.push_back(ghost_elem->getNode(k));
            }
        }

        partition_end_node_id[ipart] = partition_nodes.size();
        // Renumber
        PetscInt local_id = 0;

        std::vector<unsigned> nodes_local_ids_partition;
        nodes_local_ids_partition.reserve(nnodes);
        for (std::size_t i=0; i<nnodes; i++)
        {
            nodes_local_ids_partition.push_back(0);
        }

        for (std::size_t i = partition_start_node_id[ipart];
                i < end_non_ghost_node_id[ipart]; i++)
        {
            const unsigned node_id = partition_nodes[i]->getID();
            node_global_ids[node_id] = global_node_id_offset;
            nodes_local_ids_partition[node_id] = local_id;
            local_id++;
            global_node_id_offset++;
        }
        // Assign local IDs to ghost nodes
        for (std::size_t i = end_non_ghost_node_id[ipart];
                i < partition_end_node_id[ipart]; i++)
        {
            nodes_local_ids_partition[partition_nodes[i]->getID()] = local_id;
            local_id++;
        }

        // Count the number of integer variables used to define non-ghost elements
        const PetscInt num_non_ghost_elems = partition_regular_elements.size();
        PetscInt nmb_element_idxs = 3 * num_non_ghost_elems;
        for (PetscInt j=0; j<num_non_ghost_elems; j++)
        {
            nmb_element_idxs += partition_regular_elements[j]->getNNodes();
        }
        const PetscInt num_ghost_elems = partition_ghost_elements.size();
        PetscInt nmb_element_idxs_g = 3 * num_ghost_elems;
        for (PetscInt j=0; j<num_ghost_elems; j++)
        {
            nmb_element_idxs_g += partition_ghost_elements[j]->getNNodes();
        }

        std::string ipart_str = std::to_string(ipart);

        // Number of active elements
        const PetscInt offset_e = num_non_ghost_elems + nmb_element_idxs;
        const PetscInt offset_e_g = num_ghost_elems + nmb_element_idxs_g;

        // Reserved for nodes for high order interpolation
        const PetscInt num_non_ghost_nodes_extra = 0;

        const PetscInt num_nodes_linear_element =   partition_end_node_id[ipart]
                - partition_start_node_id[ipart];
        const PetscInt num_nodes_quadratic_element = 0;

        const PetscInt num_nodes_global_mesh = nnodes;
        // Reserved for nodes for high order interpolation
        const PetscInt num_nodes_global_mesh_extra = nnodes;

        // Write configuration data and elements of this partition
        head[0] = num_nodes_linear_element + num_nodes_quadratic_element;
        head[1] = num_nodes_linear_element;
        head[2] = num_non_ghost_elems;
        head[3] = num_ghost_elems;
        head[4] = num_non_ghost_nodes;
        head[5] = num_non_ghost_nodes + num_non_ghost_nodes_extra; // active nodes
        head[6] = num_nodes_global_mesh;
        head[7] = num_nodes_global_mesh_extra;
        head[8] = nmb_element_idxs;
        head[9] = nmb_element_idxs_g;
        if (output_binary)
        {
            fwrite(head, 1, num_headers * sizeof(PetscInt), of_bin_cfg);

            head[10] += head[0] * sizeof(NodeStruct);
            head[11] += offset_e * sizeof(PetscInt);
            head[12] += offset_e_g * sizeof(PetscInt);

            // Write non-ghost elements
            std::vector<PetscInt> ele_info(offset_e);

            PetscInt counter = num_non_ghost_elems;

            for (PetscInt j = 0; j < num_non_ghost_elems; j++)
            {
                ele_info[j] = counter;
                getElementIntegerVariables(*partition_regular_elements[j], nodes_local_ids_partition,
                                           ele_info, counter);
            }
            fwrite(&ele_info[0], 1, (offset_e) * sizeof(PetscInt), of_bin_ele);

            // Write ghost elements
            ele_info.resize(offset_e_g);
            counter = num_ghost_elems;
            for (PetscInt j = 0; j < num_ghost_elems; j++)
            {
                ele_info[j] = counter;
                getElementIntegerVariables(*partition_ghost_elements[j], nodes_local_ids_partition,
                                           ele_info, counter);
            }
            fwrite(&ele_info[0], 1, (offset_e_g) * sizeof(PetscInt), of_bin_ele_g);
        }
        else
        {

            for (int j=0; j<10; j++)
            {
                os_subd_head << head[j] << " ";
            }
            os_subd_head << " 0 " << std::endl;

            for (const auto elem : partition_regular_elements)
            {
                writeLocalElementNodeIndicies(os_subd, *elem, nodes_local_ids_partition);
            }

            for (const auto elem : partition_ghost_elements)
            {
                writeLocalElementNodeIndicies(os_subd, *elem, nodes_local_ids_partition);
            }
            os_subd << std::endl;
        }
    } // end of for(unsigned ipart=0; ipart<npartitions; ipart++) for partitioning of nodes, elements

    nodes_partition_ids.clear();
    nodes_part_reserved.clear();
    nodes_reseved.clear();
    elems_reseved.clear();

    // Write nodes
    if (output_binary)
    {
        fclose(of_bin_cfg);
        fclose(of_bin_ele);
        fclose(of_bin_ele_g);

        for (std::size_t ipart=0; ipart<npartitions; ipart++)
        {
            const size_t nnodes_part = partition_end_node_id[ipart] - partition_start_node_id[ipart];
            std::vector<NodeStruct> nodes_buffer;
            nodes_buffer.reserve(nnodes_part);

            for (std::size_t i=partition_start_node_id[ipart]; i<partition_end_node_id[ipart]; i++)
            {
                double const* coords = partition_nodes[i]->getCoords();
                NodeStruct node_struct;
                node_struct.id = node_global_ids[partition_nodes[i]->getID()];
                node_struct.x = coords[0];
                node_struct.y = coords[1];
                node_struct.z = coords[2];
                nodes_buffer.emplace_back(node_struct);
            }
            fwrite(&nodes_buffer[0], sizeof(NodeStruct), nnodes_part, of_bin_nod);
        }
        fclose(of_bin_nod);
    }
    else
    {
        os_subd_head.close();
        os_subd.close();
        std::setw(14);
        os_subd_node.precision(14);
        os_subd_node.setf(std::ios::scientific);

        for (std::size_t ipart=0; ipart<npartitions; ipart++)
        {
            for (std::size_t i=partition_start_node_id[ipart]; i<partition_end_node_id[ipart]; i++)
            {
                double const* coords = partition_nodes[i]->getCoords();
                os_subd_node << node_global_ids[partition_nodes[i]->getID()] << " "
                             << coords[0] << " " << coords[1] << " " << coords[2]<<"\n";
            }
            os_subd_node << std::endl;
        }
        os_subd_node.close();
    }

    // Update the node IDs
    for (std::size_t i=0; i<nnodes; i++)
    {
        _nodes[i]->setID(node_global_ids[i]);
    }
    // sort
    std::sort(_nodes.begin(), _nodes.end(),
              [](const MeshLib::Node* a, const MeshLib::Node* b)
                {
                   return a->getID() < b->getID();
                }
             );
}

ElementType MeshPartitioning::getElementType(const Element& elem)
{
    switch ( elem.getCellType() )
    {
        case MeshLib::CellType::LINE2:
            return ElementType::LINE2;
        case MeshLib::CellType::QUAD4:
            return ElementType::QUAD4;
        case MeshLib::CellType::HEX8:
            return ElementType::HEX8;
        case MeshLib::CellType::TRI3:
            return ElementType::TRI3;
        case MeshLib::CellType::PYRAMID5:
            return ElementType::PYRAMID5;
        case MeshLib::CellType::PRISM6:
            return ElementType::PRISM6;
        default:
            ERR("Invalid element type in element %d", elem.getID());
            abort();
    }
}

void MeshPartitioning::getElementIntegerVariables(const Element& elem,
        const std::vector<unsigned>& local_node_ids,
        std::vector<PetscInt>& elem_info,
        PetscInt& counter)
{
    unsigned mat_id = 0; // Materical ID to be set from the mesh data
    const PetscInt nn = elem.getNNodes();;
    elem_info[counter++] = mat_id;
    elem_info[counter++] = static_cast<unsigned>(getElementType(elem)) + 1;
    elem_info[counter++] = nn;

    for(PetscInt i=0; i<nn; i++)
    {
        elem_info[counter++] = local_node_ids[elem.getNodeIndex(i)];
    }
}

void MeshPartitioning::writeLocalElementNodeIndicies(std::ostream& os, const Element& elem,
        const std::vector<unsigned>& local_node_ids)
{
    unsigned mat_id = 0; // Materical ID to be set from the mesh data
    os << mat_id << " " << static_cast<unsigned>(getElementType(elem)) + 1 << " "
       << elem.getNNodes() <<" ";
    for(unsigned i=0; i<elem.getNNodes(); i++)
    {
        os << local_node_ids[elem.getNodeIndex(i)] << " ";
    }
    os << "\n";
}

}   // namespace MeshLib