Skip to content
Snippets Groups Projects
Select Git revision
  • 6359a4040230005c87ac84d0fccd76a683faedda
  • master default protected
  • comm_opt
  • clem_dev
  • clem_dev_corrected
  • kokkos
  • devel
  • bcast_debug
  • MC/high_order_geometry
  • MC/mpi_nonblocking
  • MC/multigpu
  • MC/lifting_oneshot
  • MC/physent
  • curls_marco
  • MC/gefpg_lua_binding
  • emdant/dg-cherry-pick-8f1f09f5
  • v0.3.0
  • v0.2.0
  • v0.1.0
19 results

gmsh_io.cpp

Blame
  • gmsh_io.cpp 17.81 KiB
    #include <iostream>
    #include <sstream>
    #include <cassert>
    
    #include "gmsh_io.h"
    #include "entity.h"
    
    #ifdef USE_MPI
    #include <mpi/mpi.h>
    #endif /* USE_MPI */
    
    std::string
    quadrature_name(int order)
    {
        std::stringstream ss;
        ss << "Gauss" << order;
        return ss.str();
    }
    
    std::string
    basis_func_name(int order)
    {
        (void) order;
        std::stringstream ss;
        //ss << "Lagrange";
        ss << "H1Legendre" << order;
        return ss.str();
    }
    
    std::string
    basis_grad_name(int order)
    {
        (void) order;
        std::stringstream ss;
        //ss << "GradLagrange";
        ss << "GradH1Legendre" << order;
        return ss.str();
    }
    
    model::model()
        : geometric_order(1), approximation_order(1)
    #ifdef USE_MPI
        , num_partitions(1)
    #endif /* USE_MPI */
    {}
    
    model::model(int pgo, int pao)
        : geometric_order(pgo), approximation_order(pao)
    #ifdef USE_MPI
        , num_partitions(1)
    #endif /* USE_MPI */
    {
        assert(geometric_order > 0);
        assert(approximation_order > 0);
    }
    
    model::~model()
    {
        //gmsh::clear();
    }
    
    
    void
    model::make_boundary_to_faces_map(void)
    {
    #ifdef USE_MPI
        ASSERT_MPI_RANK_0;
    #endif /* USE_MPI */
    
        gvp_t gmsh_entities;
    
        /* Create a table mapping the tag of a boundary to the list of
         * its face keys. This must happen before import_gmsh_entities()
         * because otherwise you get spurious 2D entities (and this in
         * turn because the constructor of entity calls addDiscreteEntity()
         * to get all the element faces) .*/
        gm::getEntities(gmsh_entities, DIMENSION(2));
        for (auto [dim, tag] : gmsh_entities)
        {
    #ifdef USE_MPI
            if (num_partitions > 1)
            {
                /* If the model is partitioned and entity has no parents,
                 * it means that there are no elements in the entity on which
                 * we will compute. Skip it. */
                int pdim, ptag;
                gmsh::model::getParent(dim, tag, pdim, ptag);
    
                if (ptag == -1)
                    continue;
    
                /* This stores which partitions share a boundary. It will be
                 * used to determine who communicates with who. */
                std::vector<int> parts;
                gm::getPartitions(dim, tag, parts);
                comm_map[tag] = parts;
                if (pdim == dim)
                    surface_to_parent_map[tag] = ptag;
            }
    #endif /* USE_MPI */
    
            std::vector<int> elemTypes;
            gmm::getElementTypes(elemTypes, dim, tag);
            assert(elemTypes.size() == 1);
    
            /* Get all the face keys for a given boundary. */
            for (auto& elemType : elemTypes)
            {
                element_key_factory ekf(DIMENSION(2), tag, elemType);
                boundary_map[tag] = ekf.get_keys();
            }
        }
    
    #ifdef USE_MPI
        //for (auto& sp : surface_to_parent_map)
        //    std::cout << sp.first << " -> " << sp.second << std::endl;
    
        assert( IFF(comm_map.size() == 0, num_partitions == 1) );
    #endif /* USE_MPI */
    }
    
    #ifdef USE_MPI
    void
    model::make_partition_to_entities_map()
    {
        ASSERT_MPI_RANK_0;
    
        gvp_t gmsh_entities;
    
        gm::getEntities(gmsh_entities, DIMENSION(3));
        for (auto [dim, tag] : gmsh_entities)
        {
            if (num_partitions > 1)
            {
                /* If the model is partitioned and entity has no parents,
                 * it means that there are no elements in the entity on which
                 * we will compute. Skip it. */
                int pdim, ptag;
                gmsh::model::getParent(dim, tag, pdim, ptag);
    
                if (ptag == -1)
                    continue;
    
                std::vector<int> parts;
                gm::getPartitions(dim, tag, parts);
                assert(parts.size() == 1);
                partition_map[ parts[0] ].push_back(tag);
                partition_inverse_map[tag] = parts[0];
            }
        }
    
        for (auto& [pn, tags] : partition_map)
            std::sort(tags.begin(), tags.end()); /* For lookups */
    }
    #endif /* USE_MPI */
    
    void
    model::update_connectivity(const entity& e, size_t entity_index)
    {
    #ifdef USE_MPI
        //ASSERT_MPI_RANK_0;
    #endif /* USE_MPI */
    
        for (size_t iT = 0; iT < e.num_cells(); iT++)
        {
            for (size_t iF = 0; iF < e.num_faces_per_elem(); iF++)
            {
                size_t findex = e.num_faces_per_elem() * iT  + iF;
                neighbour_descriptor cd(entity_index, iT, iF);
                element_key fk( e.face(findex) );
                conn.connect( fk, cd );
            }
        }
    }
    
    #ifdef USE_MPI
    int
    model::my_partition(void) const
    {
        /* Here we are assuming that GMSH enumerates mesh partitions
         * sequentially and starting from 1. */
        int rank;
        MPI_Comm_rank(MPI_COMM_WORLD, &rank);
        return rank+1;
    }
    #endif /* USE_MPI */
    
    void
    model::import_gmsh_entities_rank0(void)
    {
    #ifdef USE_MPI
        ASSERT_MPI_RANK_0;
    #endif /* USE_MPI */
    
        gvp_t gmsh_entities;
        gm::getEntities(gmsh_entities, DIMENSION(3));
    
        size_t entity_index = 0;
        size_t dof_base = 0;
        size_t flux_base = 0;
        size_t index_base = 0;
    #ifdef USE_MPI
        size_t global_dof_base = 0;
    #endif /* USE_MPI */
    
        for (auto [dim, tag] : gmsh_entities)
        {
    #ifdef USE_MPI
            if (num_partitions > 1)
            {
                /* If the model is partitioned and entity has no parents,
                 * it means that there are no elements in the entity on which
                 * we will compute. Skip it. */
                int pdim, ptag;
                gmsh::model::getParent(dim, tag, pdim, ptag);
    
                if (ptag == -1)
                    continue;
            }
    #endif /* USE_MPI */
            std::vector<int> elemTypes;
            gmm::getElementTypes(elemTypes, dim, tag);
            
            assert(elemTypes.size() == 1);
            if (elemTypes.size() != 1)
                throw std::invalid_argument("Only one element type per entity is allowed");
    
            for (auto& elemType : elemTypes)
            {
                entity e({.dim = dim, .tag = tag, .etype = elemType,
                          .gorder = geometric_order, .aorder = approximation_order});
                
                e.sort_by_orientation();
    
    #ifdef USE_MPI
                if (num_partitions > 1)
                {
                    /* If there are multiple partitions, decide to which one this
                     * entity belongs to. If the partition is remote, send entity
                     * to the remote process. The entity is saved also locally
                     * to have all the data for postprocessing later. */
                    auto mp = my_partition();
                    auto tag_partition = partition_inverse_map.at(tag);
                    bool entity_is_remote = ( tag_partition != mp );
                
                    if (entity_is_remote)
                    {
                        dof_range dr(e, global_dof_base);
                        global_dof_ranges.emplace(tag, std::move(dr));
                        global_dof_base += e.num_dofs();
    
                        assert(tag_partition > 0);
                        int rank = tag_partition - 1;
                        e.mpi_send(rank, MPI_COMM_WORLD);
                        std::cout << "Sending entity " << tag << " to " << rank << " " << e.num_dofs() << std::endl;
                        /* Save locally the entity */
                        remote_entities[tag_partition].push_back( std::move(e) );
                        continue;
                    }
                }
                std::cout << "Keeping entity " << tag << std::endl;
    #endif /* USE_MPI */
                for (size_t i = 0; i < e.num_cells(); i++)
                {
                    const auto& pe = e.cell(i);
                    auto eitor = etag_to_entity_offset.find( pe.element_tag() );
                    if ( eitor != etag_to_entity_offset.end() )
                        throw std::logic_error("Duplicate tag");
    
                    etag_to_entity_offset[ pe.element_tag() ] = std::make_pair(entity_index, i);
                }
    
                e.base(dof_base, flux_base, index_base);
                e.number(entity_index);
                dof_base += e.num_dofs();
                flux_base += e.num_fluxes();
                index_base += e.num_cells();
    
    #ifdef USE_MPI
                dof_range dr(e, global_dof_base);
                global_dof_ranges.emplace(tag, std::move(dr));
                global_dof_base += e.num_dofs();
    #endif /* USE_MPI */
    
                update_connectivity(e, entity_index);
                entities.push_back( std::move(e) );
                entity_index++;
            }
        }
    
    #ifdef USE_MPI
        /* Compute DOF offset in the remote entities saved locally. */
        assert( IMPLIES(num_partitions == 1, remote_entities.size() == 0) );
        for (auto& re : remote_entities)
        {
            entity_index = 0;
            dof_base = 0;
            flux_base = 0;
            index_base = 0;
            for (auto& e : re.second)
            {
                e.base(dof_base, flux_base, index_base);
                e.number(entity_index);
                dof_base += e.num_dofs();
                flux_base += e.num_fluxes();
                index_base += e.num_cells();
                entity_index++;
            }
        }
    #endif
    }
    
    #ifdef USE_MPI
    void
    model::import_gmsh_entities_rankN(void)
    {
        int mp = my_partition();
    
        size_t entity_index = 0;
        size_t dof_base = 0;
        size_t flux_base = 0;
        size_t index_base = 0;
    
        for (auto& rtag : partition_map.at(mp))
        {
            entity e;
            e.mpi_recv(0, MPI_COMM_WORLD);
            std::cout << "Receiving " << rtag << ", rank " << mp-1 << " " << e.num_dofs() << std::endl;
    
            for (size_t i = 0; i < e.num_cells(); i++)
            {
                const auto& pe = e.cell(i);
                auto eitor = etag_to_entity_offset.find( pe.element_tag() );
                if ( eitor != etag_to_entity_offset.end() )
                    throw std::logic_error("Duplicate tag");
    
                etag_to_entity_offset[ pe.element_tag() ] = std::make_pair(entity_index, i);
            }
    
            e.base(dof_base, flux_base, index_base);
            e.number(entity_index);
            dof_base += e.num_dofs();
            flux_base += e.num_fluxes();
            index_base += e.num_cells();
    
            update_connectivity(e, entity_index);
    
            entities.push_back( std::move(e) );
            entity_index++;
        }
    }
    
    bool
    model::is_interprocess_boundary(int tag)
    {
        if (num_partitions > 1)
            return comm_map.at(tag).size() == 2;
        
        return false;
    }
    
    #endif /* USE_MPI */
    
    void
    model::map_boundaries(void)
    {
    #ifdef USE_MPI
        //ASSERT_MPI_RANK_0;
    #endif /* USE_MPI */
    
        /* Make a vector mapping element_key to entity tag */
        using bfk_t = std::pair<element_key, int>;
        std::vector<bfk_t> bfk;
        for (auto& [tag, keys] : boundary_map)
            for (auto& k : keys)
                bfk.push_back( std::make_pair(k, tag) );
    
        /* Sort it for lookups */
        auto comp = [](const bfk_t& a, const bfk_t& b) -> bool {
            return a.first < b.first;
        };
        std::sort(bfk.begin(), bfk.end(), comp);
    
        bnd_descriptors.resize( num_faces() );
        size_t fbase = 0;
        /* For each entity */
        for (auto& e : entities)
        {
            /* and for each face */
            for (size_t iF = 0; iF < e.num_faces(); iF++)
            {
                /* get the element key */
                element_key fk(e.face(iF));
                auto lbcomp = [](const bfk_t& a, const element_key& b) -> bool {
                    return a.first < b;
                };
                /* and lookup it in the vector we created before. */
                auto itor = std::lower_bound(bfk.begin(), bfk.end(), fk, lbcomp);
                if ( (itor == bfk.end()) or not ((*itor).first == fk) )
                    continue;
                
                /* If found */
                boundary_descriptor bd;
                /* determine if it is an interface or a boundary */
                if (conn.num_neighbours(fk) == 1)
                {
    #ifdef USE_MPI
                    if ( is_interprocess_boundary( (*itor).second ) )
                        bd.type = face_type::INTERPROCESS_BOUNDARY;
                    else
    #endif /* USE_MPI */
                        bd.type = face_type::BOUNDARY;
                }
                else
                {
                    bd.type = face_type::INTERFACE;
                }
                /* and store the gmsh tag in the descriptor. */
                bd.gmsh_entity = (*itor).second;
    #ifdef USE_MPI
                if (num_partitions > 1)
                {
                    auto sitor = surface_to_parent_map.find( (*itor).second );
                    if ( sitor != surface_to_parent_map.end() )
                        bd.parent_entity = surface_to_parent_map.at( (*itor).second );
                }
    #endif /* USE_MPI */
                /* Finally, put the descriptor in the global array of faces. */
                bnd_descriptors.at(fbase + iF) = bd;
            }
            fbase += e.num_faces();
        }
    
    #if 0
        size_t normal = 0;
        size_t interface = 0;
        size_t boundary = 0;
        size_t ipc_boundary = 0;
        for (auto& bd : bnd_descriptors)
        {
            switch (bd.type)
            {
                case face_type::NONE: normal++; break;
                case face_type::INTERFACE: interface++; break;
                case face_type::BOUNDARY: boundary++; break;
    #ifdef USE_MPI
                case face_type::INTERPROCESS_BOUNDARY: ipc_boundary++; break;
    #endif /* USE_MPI */
            }
        }
    
        std::cout << bfk.size() << " " << bnd_descriptors.size() << std::endl;
        std::cout << normal << " " << interface << " " << boundary << " " << ipc_boundary << std::endl;
    #endif
    }
    
    void
    model::populate_from_gmsh_rank0()
    {
    #ifdef USE_MPI
        ASSERT_MPI_RANK_0;
    #endif /* USE_MPI */
    
        gmm::setOrder( geometric_order );
        make_boundary_to_faces_map();
    
    #ifdef USE_MPI
        make_partition_to_entities_map();
    
        /* At this point, the following members are populated: 
         * - geometric_order, approximation_order and num_partitions 
         * - boundary_map
         * - conn_map
         * - partition_map
         * We broadcast them to the other processes
         */
        MPI_Bcast(&geometric_order, 1, MPI_INT, 0, MPI_COMM_WORLD);
        MPI_Bcast(&approximation_order, 1, MPI_INT, 0, MPI_COMM_WORLD);
        MPI_Bcast(&num_partitions, 1, MPI_INT, 0, MPI_COMM_WORLD);
    
        priv_MPI_Bcast(boundary_map, 0, MPI_COMM_WORLD);
        priv_MPI_Bcast(comm_map, 0, MPI_COMM_WORLD);
        priv_MPI_Bcast(partition_map, 0, MPI_COMM_WORLD);
        priv_MPI_Bcast(surface_to_parent_map, 0, MPI_COMM_WORLD);
    
        /*
        for (auto& [tag, vec] : partition_map)
        {
            std::cout << tag << ": " << vec.size() << " -> ";
            for (auto& v : vec)
                std::cout << v << " ";
            std::cout << std::endl;
        }
        */
    
    #endif /* USE_MPI */
    
        entities.clear();
        import_gmsh_entities_rank0();
        map_boundaries(); /* This must happen after import_gmsh_entities(). */
    }
    
    #ifdef USE_MPI
    void
    model::populate_from_gmsh_rankN()
    {
        MPI_Bcast(&geometric_order, 1, MPI_INT, 0, MPI_COMM_WORLD);
        MPI_Bcast(&approximation_order, 1, MPI_INT, 0, MPI_COMM_WORLD);
        MPI_Bcast(&num_partitions, 1, MPI_INT, 0, MPI_COMM_WORLD);
    
        priv_MPI_Bcast(boundary_map, 0, MPI_COMM_WORLD);
        priv_MPI_Bcast(comm_map, 0, MPI_COMM_WORLD);
        priv_MPI_Bcast(partition_map, 0, MPI_COMM_WORLD);
        priv_MPI_Bcast(surface_to_parent_map, 0, MPI_COMM_WORLD);
    
        /*
        for (auto& [tag, vec] : partition_map)
        {
            std::cout << tag << ": " << vec.size() << " -> ";
            for (auto& v : vec)
                std::cout << v << " ";
            std::cout << std::endl;
        }
        */
        entities.clear();
        import_gmsh_entities_rankN();
        map_boundaries();
    }
    #endif /* USE_MPI */
    
    void
    model::populate_from_gmsh()
    {
    #ifdef USE_MPI
        int rank;
        MPI_Comm_rank(MPI_COMM_WORLD, &rank);
        if (rank == 0)
            populate_from_gmsh_rank0();
        else
            populate_from_gmsh_rankN();
    #else /* USE_MPI */
        populate_from_gmsh_rank0();
    #endif /* USE_MPI */
    }
    
    void
    model::build()
    {
        generate_mesh();
        make_boundary_to_faces_map();
        populate_from_gmsh_rank0();
    }
    
    void
    model::build(double sf)
    {
        generate_mesh(sf);
        make_boundary_to_faces_map();
        populate_from_gmsh_rank0();
    }
    
    void
    model::generate_mesh()
    {
        gmm::generate( DIMENSION(3) );
    }
    
    void
    model::generate_mesh(double sf)
    {
        gmm::generate( DIMENSION(3) );
    
        std::vector<double> tr = {sf,  0,  0,  0,
                                   0, sf,  0,  0,
                                   0,  0, sf,  0 };
        gmm::affineTransform(tr);
    }
    
    #ifdef USE_MPI
    void
    model::partition_mesh(int parts)
    {
        gmm::partition(parts);
        num_partitions = parts;
    }
    
    entity&
    model::entity_global_lookup(int tag)
    {
        /* Look for tag between local entities */
        for (auto& e : entities)
            if (e.gmsh_tag() == tag)
                return e;
    
        /* Try between remote entities */
        int partition = partition_inverse_map.at(tag);
        for (auto& e : remote_entities[partition])
            if (e.gmsh_tag() == tag)
                return e;
        
        throw std::invalid_argument("tag not found");
    }
    
    const entity&
    model::entity_global_lookup(int tag) const
    {
        /* Look for tag between local entities */
        for (const auto& e : entities)
            if (e.gmsh_tag() == tag)
                return e;
    
        /* Try between remote entities */
        int partition = partition_inverse_map.at(tag);
        for (const auto& e : remote_entities.at(partition))
            if (e.gmsh_tag() == tag)
                return e;
        
        throw std::invalid_argument("tag not found");
    }
    #endif /* USE_MPI */
    
    std::vector<entity>::const_iterator
    model::begin() const
    {
        return entities.begin();
    }
    
    std::vector<entity>::const_iterator
    model::end() const
    {
        return entities.end();
    }
    
    std::vector<entity>::iterator
    model::begin()
    {
        return entities.begin();
    }
    
    std::vector<entity>::iterator
    model::end()
    {
        return entities.end();
    }
    
    entity&
    model::entity_at(size_t pos)
    {
        return entities.at(pos);
    }
    
    const entity&
    model::entity_at(size_t pos) const
    {
        return entities.at(pos);
    }
    
    size_t
    model::num_dofs(void) const
    {
        size_t ret = 0;
        for (const auto& e : entities)
            ret += e.num_dofs();
    
        return ret;
    }
    
    size_t
    model::num_fluxes(void) const
    {
        size_t ret = 0;
        for (const auto& e : entities)
            ret += e.num_fluxes();
    
        return ret;
    }
    
    size_t
    model::num_cells(void) const
    {
        size_t ret = 0;
        for (const auto& e : entities)
            ret += e.num_cells();
    
        return ret;
    }
    
    size_t
    model::num_faces(void) const
    {
        size_t ret = 0;
        for (const auto& e : entities)
            ret += e.num_faces();
    
        return ret;
    }
    
    size_t
    model::num_entities(void) const
    {
        return entities.size();
    }
    
    
    
    model::entofs_pair
    model::lookup_tag(size_t tag) const
    {
        return etag_to_entity_offset.at(tag);
    }