Skip to content
Snippets Groups Projects
Select Git revision
  • a4cb746aa0b87a4247f1a29a83d8fa9683c70db7
  • master default protected
  • devel
  • MC/hide_eigen_sol
  • compare_at_gauss_points
  • mixed_interface_fix
  • detect-gmsh-api-version
  • MC/mpi_support
  • MC/volsrcs
  • fix-gmsh-master
  • find-gmsh
  • MC/hipify
  • v0.3.0
  • v0.2.0
  • v0.1.0
15 results

entity.cpp

Blame
  • user avatar
    Matteo Cicuttin authored
    a4cb746a
    History
    entity.cpp 23.79 KiB
    #include <iostream>
    #include <cassert>
    #include "sgr.hpp"
    
    #include "gmsh_io.h"
    #include "entity.h"
    
    #define NUM_TET_FACES 4
    
    entity::entity(const entity_params& ep)
        : dim(ep.dim), tag(ep.tag), elemType(ep.etype), g_order(ep.gorder),
          a_order(ep.aorder), cur_elem_ordering(entity_ordering::GMSH),
          m_dof_base(0), m_flux_base(0), m_index_base(0)
    {
        /* Prepare reference elements */
        reference_elements_factory ref(ep);
        reference_cells = ref.get_elements();
    
        physical_elements_factory pef(ep);
        physical_cells = pef.get_elements();
    
        assert(dim == 3); /* otherwise you need getElementEdgeNodes() */
        gmm::getElementFaceNodes(elemType, 3, faceNodesTags, tag); // 3 is for triangles
        int tag2D = gm::addDiscreteEntity(dim-1);
        int elemType2D = gmm::getElementType("triangle", g_order);
        gmm::addElementsByType(tag2D, elemType2D, {}, faceNodesTags);
        faceNodesTags.clear();
        gmm::getElementsByType(elemType2D, faceTags, faceNodesTags, tag2D);
    
        entity_params ep_2D;
        ep_2D.dim       = 2;
        ep_2D.tag       = tag2D;
        ep_2D.etype     = elemType2D;
        ep_2D.gorder    = g_order; 
        ep_2D.aorder    = a_order; 
    
        reference_elements_factory ref_2D(ep_2D);
        reference_faces = ref_2D.get_elements();
    
        physical_elements_factory pef_2D(ep_2D);
        physical_faces = pef_2D.get_elements();
    
        assert(physical_faces.size() == 4*physical_cells.size());
        assert(faceTags.size() == 4*physical_cells.size());
    }
    
    std::vector<size_t>
    entity::face_tags() const
    {
        return faceTags;
    }
    
    double
    entity::measure(void) const
    {
        auto meas = 0.0;
        for (auto& pe : physical_cells)
            meas += pe.measure();
        
        return meas;
    }
    
    /* Project a function on this entity and return the
     * corresponding vector of dofs on this entity. */
    vecxd
    entity::project(const scalar_function& function) const
    {
        auto num_bf = reference_cells[0].num_basis_functions();
    
        vecxd ret = vecxd::Zero( physical_cells.size() * num_bf );
        for (size_t iT = 0; iT < physical_cells.size(); iT++)
        {
            const auto& pe = physical_cells[iT];
            //assert( pe.orientation() == 0 );
            assert( pe.orientation() < reference_cells.size() );
            const auto& re = reference_cells[pe.orientation()];
            const auto pqps = pe.integration_points();
            const auto dets = pe.determinants();
    
            assert(pqps.size() == dets.size());
    
            matxd mass = matxd::Zero(num_bf, num_bf);
            vecxd rhs = vecxd::Zero(num_bf);
            for (size_t iQp = 0; iQp < pqps.size(); iQp++)
            {
                const auto& pqp = pqps[iQp];
                const vecxd phi = re.basis_functions(iQp);
                mass += pqp.weight() * phi * phi.transpose();
                rhs += pqp.weight() * function(pqp.point()) * phi; 
            }
    
            ret.segment(iT*num_bf, num_bf) = mass.ldlt().solve(rhs);
        }
    
        return ret;
    }
    
    /* Project a function on this entity and store the partial result in
     * the global vector pf at the offset corresponding to this entity. */
    void
    entity::project(const scalar_function& function, vecxd& pf) const
    {
        auto num_bf = reference_cells[0].num_basis_functions();
    
        for (size_t iT = 0; iT < physical_cells.size(); iT++)
        {
            const auto& pe = physical_cells[iT];
            assert( pe.orientation() < reference_cells.size() );
            const auto& re = reference_cells[pe.orientation()];
            const auto pqps = pe.integration_points();
            const auto dets = pe.determinants();
    
            assert(pqps.size() == dets.size());
    
            matxd mass = matxd::Zero(num_bf, num_bf);
            vecxd rhs = vecxd::Zero(num_bf);
            for (size_t iQp = 0; iQp < pqps.size(); iQp++)
            {
                const auto& pqp = pqps[iQp];
                const vecxd phi = re.basis_functions(iQp);
                mass += pqp.weight() * phi * phi.transpose();
                rhs += pqp.weight() * function(pqp.point()) * phi; 
            }
    
            pf.segment(m_dof_base + iT*num_bf, num_bf) = mass.ldlt().solve(rhs);
        }
    }
    
    /* Project a function on this entity and store the partial result in
     * the global vector pf at the offset corresponding to this entity. */
    void
    entity::project(const vector_function& function, vecxd& pfx, vecxd& pfy, vecxd& pfz) const
    {
        auto num_bf = reference_cells[0].num_basis_functions();
    
        for (size_t iT = 0; iT < physical_cells.size(); iT++)
        {
            const auto& pe = physical_cells[iT];
            assert( pe.orientation() < reference_cells.size() );
            const auto& re = reference_cells[pe.orientation()];
            const auto pqps = pe.integration_points();
            const auto dets = pe.determinants();
    
            assert(pqps.size() == dets.size());
    
            matxd mass = matxd::Zero(num_bf, num_bf);
            vecxd rhs_x = vecxd::Zero(num_bf);
            vecxd rhs_y = vecxd::Zero(num_bf);
            vecxd rhs_z = vecxd::Zero(num_bf);
            for (size_t iQp = 0; iQp < pqps.size(); iQp++)
            {
                const auto& pqp = pqps[iQp];
                const vecxd phi = re.basis_functions(iQp);
                mass += pqp.weight() * phi * phi.transpose();
                vec3d fval = function(pqp.point());
                rhs_x += pqp.weight() * fval(0) * phi; 
                rhs_y += pqp.weight() * fval(1) * phi; 
                rhs_z += pqp.weight() * fval(2) * phi; 
            }
    
            Eigen::LDLT<matxd> mass_ldlt(mass);
            pfx.segment(m_dof_base + iT*num_bf, num_bf) = mass_ldlt.solve(rhs_x);
            pfy.segment(m_dof_base + iT*num_bf, num_bf) = mass_ldlt.solve(rhs_y);
            pfz.segment(m_dof_base + iT*num_bf, num_bf) = mass_ldlt.solve(rhs_z);
        }
    }
    
    const reference_element&
    entity::cell_refelem(size_t iO) const
    {
        assert(iO < reference_cells.size());
        return reference_cells[iO];
    }
    
    const reference_element&
    entity::cell_refelem(const physical_element& pe) const
    {
        assert(pe.orientation() < reference_cells.size());
        return reference_cells[pe.orientation()];
    }
    
    size_t
    entity::cell_global_index(size_t iT) const
    {
        assert(iT < physical_cells.size());
        return m_index_base + iT;
    }
    
    size_t
    entity::cell_global_index_by_gmsh(size_t iT) const
    {
        assert(iT < physical_cells.size());
        return m_index_base + physical_cells[iT].original_position();
    }
    
    
    size_t
    entity::cell_local_dof_offset(size_t iT) const
    {
        assert(reference_cells.size() > 0);
        return iT * reference_cells[0].num_basis_functions();
    }
    
    size_t
    entity::cell_global_dof_offset(size_t iT) const
    {
        assert(reference_cells.size() > 0);
        return m_dof_base + iT * reference_cells[0].num_basis_functions();
    }
    
    const reference_element&
    entity::face_refelem(const physical_element& pe) const
    {
        assert(pe.orientation() < reference_faces.size());
        return reference_faces[pe.orientation()];
    }
    
    size_t
    entity::num_cells(void) const
    {
        return physical_cells.size();
    }
    
    size_t
    entity::num_faces(void) const
    {
        return physical_faces.size();
    }
    
    size_t
    entity::num_cell_orientations(void) const
    {
        return reference_cells.size();
    }
    
    std::vector<size_t>
    entity::num_cells_per_orientation(void) const
    {
        std::vector<size_t> ret( num_cell_orientations(), 0 );
        for (auto& pe : physical_cells)
        {
            assert( pe.orientation() < ret.size() );
            ret[ pe.orientation() ]++;
        }
    
        return ret;
    }
    
    const physical_element&
    entity::cell(size_t iT) const
    {
        assert(iT < physical_cells.size());
        return physical_cells[iT];
    }
    
    const physical_element&
    entity::face(size_t iF) const
    {
        assert(iF < physical_faces.size());
        return physical_faces[iF];
    }
    
    size_t
    entity::num_dofs(void) const
    {
        assert(reference_cells.size() > 0);
        return physical_cells.size() * reference_cells[0].num_basis_functions();
    }
    
    size_t
    entity::num_fluxes(void) const
    {
        assert(reference_faces.size() > 0);
        return physical_faces.size() * reference_faces[0].num_basis_functions();
    }
    
    void
    entity::base(size_t d_base, size_t f_base, size_t i_base)
    {
        m_dof_base = d_base;
        m_flux_base = f_base;
        m_index_base = i_base;
    }
    
    size_t
    entity::dof_base(void) const
    {
        return m_dof_base;
    }
    
    size_t
    entity::flux_base(void) const
    {
        return m_flux_base;
    }
    
    size_t
    entity::index_base(void) const
    {
        return m_index_base;
    }
    
    matxd
    entity::mass_matrix(size_t iT) const
    {
        const auto& pe = physical_cells[iT];
        assert( pe.orientation() < reference_cells.size() );
        const auto& re = reference_cells[pe.orientation()];
        const auto rqps = re.integration_points();
        const auto dets = pe.determinants();
        auto num_bf = re.num_basis_functions();
    
            matxd mass = matxd::Zero(num_bf, num_bf);
    
            for (size_t iQp = 0; iQp < rqps.size(); iQp++)
            {
                const auto& qp = rqps[iQp];
                const vecxd phi = re.basis_functions(iQp);
                mass += dets[iQp] * qp.weight() * phi * phi.transpose();
            }
        
        return mass;
    }
    
    void
    entity::sort_by_orientation(void)
    {
        if (cur_elem_ordering == entity_ordering::BY_ORIENTATION)
            return;
    
        /* Sort by orientation and keep GMSH ordering within the same orientation */
        auto comp = [](const physical_element& e1, const physical_element& e2) -> bool {
            auto oe1 = e1.orientation();
            auto oe2 = e2.orientation();
            auto op1 = e1.original_position();
            auto op2 = e2.original_position();
    
            if ( oe1 < oe2 or (oe1 == oe2 and op1 < op2) )
                return true;
    
            return false;
        };
    
        /* Sort cells */
        std::sort(physical_cells.begin(), physical_cells.end(), comp);
    
        /* Sort all the stuff related to faces */
        std::vector<physical_element> new_pf;
        new_pf.resize( physical_faces.size() );
        std::vector<size_t> new_ft;
        new_ft.resize( faceTags.size() );
        std::vector<size_t> new_fnt;
        new_fnt.resize( faceNodesTags.size() );
    
        size_t nodes_per_face = faceNodesTags.size()/faceTags.size();
        assert(nodes_per_face * faceTags.size() == faceNodesTags.size());
    
        for (size_t new_iT = 0; new_iT < physical_cells.size(); new_iT++)
        {
            auto old_iT = physical_cells[new_iT].original_position();
            for (size_t iF = 0; iF < NUM_TET_FACES; iF++)
            {
                auto old_ofs = NUM_TET_FACES*old_iT + iF;
                auto new_ofs = NUM_TET_FACES*new_iT + iF;
                new_pf[new_ofs] = physical_faces[old_ofs];
                new_ft[new_ofs] = faceTags[old_ofs];
    
                for (size_t iN = 0; iN < nodes_per_face; iN++)
                {
                    auto old_ofs_nt = nodes_per_face*old_ofs + iN;
                    auto new_ofs_nt = nodes_per_face*new_ofs + iN;
                    new_fnt[new_ofs_nt] = faceNodesTags[old_ofs_nt];
                }
            }
        }
    
        std::swap(new_pf, physical_faces);
        std::swap(new_ft, faceTags);
        std::swap(new_fnt, faceNodesTags);
    
        cur_elem_ordering = entity_ordering::BY_ORIENTATION;
    }
    
    void
    entity::sort_by_gmsh(void)
    {
        /* Sort by GMSH original ordering */
        auto comp = [](const physical_element& e1, const physical_element& e2) -> bool {
            auto op1 = e1.original_position();
            auto op2 = e2.original_position();
    
            if ( op1 < op2 )
                return true;
    
            return false;
        };
    
        std::sort(physical_cells.begin(), physical_cells.end(), comp);
        cur_elem_ordering = entity_ordering::GMSH;
        throw std::logic_error("NOT SORTED");
    }
    
    int
    entity::gmsh_tag(void) const
    {
        return tag;
    }
    
    int
    entity::gmsh_elem_type(void) const
    {
        return elemType;
    }
    
    entity_ordering
    entity::current_elem_ordering(void) const
    {
        return cur_elem_ordering;
    }
    
    
    void
    entity::populate_differentiation_matrices_planar(entity_data_cpu& ed) const
    {
        /* In the case of geometric order = 1, the inverse of the
         * mass matrix is embedded in the differentiation matrix */
        assert(g_order == 1 and g_order == ed.g_order);
        auto dm_rows = ed.num_orientations * ed.num_bf;
        auto dm_cols = 3 * ed.num_bf;
        ed.differentiation_matrices = matxd::Zero(dm_rows, dm_cols);
    
        for (size_t iO = 0; iO < ed.num_orientations; iO++)
        {
            matxd dm = matxd::Zero(ed.num_bf, 3*ed.num_bf);
    
            auto& re = reference_cells[iO];
            auto qps = re.integration_points();
            auto num_qp = qps.size();
            for (size_t iQp = 0; iQp < num_qp; iQp++)
            {
                auto w = qps[iQp].weight();
                vecxd phi = re.basis_functions(iQp);
                matxd dphi = re.basis_gradients(iQp);
                vecxd du_phi = dphi.col(0);
                vecxd dv_phi = dphi.col(1);
                vecxd dw_phi = dphi.col(2);
    
                dm.block(0, 0*ed.num_bf, ed.num_bf, ed.num_bf) += w * phi * du_phi.transpose();
                dm.block(0, 1*ed.num_bf, ed.num_bf, ed.num_bf) += w * phi * dv_phi.transpose();
                dm.block(0, 2*ed.num_bf, ed.num_bf, ed.num_bf) += w * phi * dw_phi.transpose();
            }
    
            matxd mass = re.mass_matrix();
            Eigen::LDLT<matxd> mass_ldlt(mass);
            if (mass_ldlt.info() != Eigen::Success )
                throw std::invalid_argument("LDLT failed");
    
            size_t base_row = iO * ed.num_bf;
            ed.differentiation_matrices.block(base_row, 0, ed.num_bf, 3*ed.num_bf) =
                mass_ldlt.solve(dm);
        }
    }
    
    void
    entity::populate_differentiation_matrices_curved(entity_data_cpu& ed) const
    {
        assert(g_order > 1 and g_order == ed.g_order);
        auto dm_rows = ed.num_orientations * ed.num_bf * ed.num_qp;
        auto dm_cols = 3 * ed.num_bf;
        ed.differentiation_matrices = matxd::Zero(dm_rows, dm_cols);
    
        for (size_t iO = 0; iO < ed.num_orientations; iO++)
        {
            auto& re = reference_cells[iO];
            auto qps = re.integration_points();
            auto num_qp = qps.size();
            for (size_t iQp = 0; iQp < num_qp; iQp++)
            {
                auto w = qps[iQp].weight();
                vecxd phi = re.basis_functions(iQp);
                matxd dphi = re.basis_gradients(iQp);
                vecxd du_phi = dphi.col(0);
                vecxd dv_phi = dphi.col(1);
                vecxd dw_phi = dphi.col(2);
    
                auto row_ofs = ed.num_bf*ed.num_qp*iO + ed.num_bf*iQp;
    
                ed.differentiation_matrices.block(row_ofs, 0*ed.num_bf, ed.num_bf, ed.num_bf) =
                    w * phi * du_phi.transpose();
    
                ed.differentiation_matrices.block(row_ofs, 1*ed.num_bf, ed.num_bf, ed.num_bf) =
                    w * phi * dv_phi.transpose();
    
                ed.differentiation_matrices.block(row_ofs, 2*ed.num_bf, ed.num_bf, ed.num_bf) =
                    w * phi * dw_phi.transpose();
            }
        }
    }
    
    void
    entity::populate_differentiation_matrices(entity_data_cpu& ed) const
    {
        assert(g_order == ed.g_order);
        if (ed.g_order == 1)
            populate_differentiation_matrices_planar(ed);
        else
            populate_differentiation_matrices_curved(ed);
    }
    
    static size_t face_orientations[] = {
        1, 0, 1, 4,     1, 0, 0, 2,     0, 0, 1, 5,
        1, 1, 0, 0,     0, 1, 1, 3,     0, 1, 0, 1,
        3, 2, 1, 4,     3, 2, 0, 2,     2, 0, 3, 5,
        1, 3, 2, 0,     2, 1, 3, 3,     0, 3, 2, 1,
        5, 2, 3, 4,     3, 4, 2, 2,     4, 2, 3, 5,
        3, 5, 2, 0,     2, 3, 5, 3,     2, 3, 4, 1,
        5, 4, 5, 4,     5, 4, 4, 2,     4, 4, 5, 5,
        5, 5, 4, 0,     4, 5, 5, 3,     4, 5, 4, 1
    };
    
    #define FACE_ORIENTATION(co, fn) ( face_orientations[4*co + fn] )
    
    void
    entity::populate_lifting_matrices_planar(entity_data_cpu& ed,
        const dofs_f2c_t& dofs_f2c) const
    {
        assert(g_order == 1 and g_order == ed.g_order);
        auto lm_rows = ed.num_orientations * ed.num_bf;
        auto lm_cols = NUM_TET_FACES * ed.num_fluxes;
        ed.lifting_matrices = matxd::Zero(lm_rows, lm_cols);
    
        for (size_t iO = 0; iO < ed.num_orientations; iO++)
        {
            if (dofs_f2c.find(iO) == dofs_f2c.end())
                continue;
    
            auto& f2c = dofs_f2c.at(iO);
            assert(f2c.size() == NUM_TET_FACES*ed.num_fluxes);
            matxd lm = matxd::Zero(ed.num_bf, lm_cols);
    
            for (size_t iF = 0; iF < NUM_TET_FACES; iF++)
            {
                auto fO = FACE_ORIENTATION(iO, iF);
                assert(fO < reference_faces.size()); 
                auto &rf = reference_faces[fO];
    
                matxd mm = rf.mass_matrix();
    
                assert(mm.rows() == ed.num_fluxes);
                
                for (size_t i = 0; i < mm.rows(); i++)
                {
                    assert(iF*ed.num_fluxes + i < f2c.size());
                    auto lm_row = f2c[iF*ed.num_fluxes + i];
                    assert(lm_row < ed.num_bf);
                    for (size_t j = 0; j < mm.cols(); j++)
                    {
                        auto lm_col = iF*ed.num_fluxes + j;
                        assert(lm_col < lm_cols);
                        lm(lm_row, lm_col) = mm(i, j);
                    }
                }
            }
    
            auto& re = reference_cells[iO];
            matxd mass = re.mass_matrix();
            Eigen::LDLT<matxd> mass_ldlt(mass);
            if (mass_ldlt.info() != Eigen::Success )
                throw std::invalid_argument("LDLT failed");
    
            size_t base_row = iO * ed.num_bf;
            ed.lifting_matrices.block(base_row, 0, ed.num_bf, lm_cols) =
                mass_ldlt.solve(lm);
    
            //std::cout << "Orientation : " << iO << std::endl;
            //std::cout << mass_ldlt.solve(lm) << std::endl;
        }
    }
    
    void
    entity::populate_lifting_matrices_curved(entity_data_cpu&, const dofs_f2c_t&) const
    {
    }
    
    void
    entity::populate_lifting_matrices(entity_data_cpu& ed) const
    {
        assert(reference_faces.size() > 0);
        auto num_face_bf = reference_faces[0].num_basis_functions();
    
        dofs_f2c_t dofs_f2c;
    
        for (size_t iT = 0; iT < physical_cells.size(); iT++)
        {
            auto& pe = physical_cells[iT];
            auto orient = pe.orientation();
            auto cnt = pe.bf_keys();
    
    #ifndef EXPENSIVE_ASSERTS 
            if ( dofs_f2c.find(orient) != dofs_f2c.end() )
                continue;
    #endif
    
            std::vector<size_t> f2c;
            f2c.reserve( NUM_TET_FACES*num_face_bf );
            for (size_t iF = 0; iF < NUM_TET_FACES; iF++)
            {
                auto face_ofs = NUM_TET_FACES*iT+iF;
                auto& pf = physical_faces[face_ofs];
                auto fnt = pf.bf_keys();
    
                for (size_t i = 0; i < fnt.size(); i++)
                    for (size_t j = 0; j < cnt.size(); j++)
                        if (fnt[i] == cnt[j])
                        {
                            assert(f2c.size() == iF*num_face_bf + i);
                            f2c.push_back(j);
                        }
    
            }
            assert(f2c.size() == NUM_TET_FACES*num_face_bf);
    
    #ifdef EXPENSIVE_ASSERTS
            if ( dofs_f2c.find(orient) != dofs_f2c.end() )
                assert(dofs_f2c[orient] == f2c);
    #endif
    
            dofs_f2c[orient] = f2c;
        }
    
        if (g_order == 1)
            populate_lifting_matrices_planar(ed, dofs_f2c);
        else
            populate_lifting_matrices_curved(ed, dofs_f2c);
    }
    
    void
    entity::populate_jacobians(entity_data_cpu& ed) const
    {
        auto num_elems = num_cells();
    
        if (g_order == 1)
        {
            ed.jacobians = matxd::Zero(3*num_elems, 3);
            ed.cell_determinants = vecxd::Zero(num_elems);
            for (size_t iT = 0; iT < num_elems; iT++)
            {
                auto& pe = physical_cells[iT];
                ed.jacobians.block(3*iT, 0, 3, 3) = pe.jacobian().inverse().transpose();
                ed.cell_determinants(iT) = pe.determinant();
            }
    
            assert(num_faces() == 4*num_elems);
            ed.face_determinants = vecxd(4*num_elems);
            for (size_t iF = 0; iF < num_faces(); iF++)
            {
                auto& pf = physical_faces[iF];
                ed.face_determinants(iF) = pf.determinant();
            }
        }
        else
        {
            ed.jacobians = matxd::Zero(3*num_elems*ed.num_qp, 3);
            ed.cell_determinants = vecxd::Zero(num_elems*ed.num_qp);
            for (size_t iT = 0; iT < num_elems; iT++)
            {
                for (size_t iQp = 0; iQp < ed.num_qp; iQp++)
                {
                    auto& pe = physical_cells[iT];
                    ed.jacobians.block(3*iT*ed.num_qp + 3*iQp, 0, 3, 3) =
                        pe.jacobian(iQp).inverse().transpose();
                    ed.cell_determinants(iT*ed.num_qp + iQp) = pe.determinant(iQp);
                }
            }   
        }
    }
    
    void
    entity::populate_normals(entity_data_cpu& ed) const
    {
        size_t num_face_qps = 1;
        size_t num_elems = num_cells();
        if (g_order == 1)
            ed.normals = matxd::Zero(NUM_TET_FACES*num_elems, 3);
        else
        {
            assert(reference_faces.size() > 0);
            num_face_qps = reference_faces[0].num_integration_points();
            ed.normals = matxd::Zero(NUM_TET_FACES*num_elems*ed.num_qp, 3);
        }
    
        Eigen::Vector3d ref_normal;
        ref_normal(0) = 0; ref_normal(1) = 0; ref_normal(2) = 1;
    
        for (size_t iT = 0; iT < num_cells(); iT++)
        {
            for (size_t iF = 0; iF < NUM_TET_FACES; iF++)
            {
                for (size_t iQp = 0; iQp < num_face_qps; iQp++)
                {
                    auto fnum = NUM_TET_FACES*iT+iF;
                    assert(fnum < physical_faces.size());
                    Eigen::Vector3d n = physical_faces[fnum].jacobian(iQp)*ref_normal;
                    auto norm_offset = (NUM_TET_FACES*iT+iF)*num_face_qps + iQp;
                    assert(norm_offset < ed.normals.rows());
                    ed.normals.block(norm_offset, 0, 1, 3) = n.transpose()/n.norm();
                }
            }
        }
    }   
    
    void
    entity::populate_entity_data(entity_data_cpu& ed) const
    {
        assert(cur_elem_ordering == entity_ordering::BY_ORIENTATION);
        assert(reference_faces.size() > 0);
        assert(reference_cells.size() > 0);
        ed.ordering = cur_elem_ordering;
        ed.g_order = g_order;
        ed.a_order = a_order;
        ed.num_elems = num_cells_per_orientation();
        ed.num_orientations = num_cell_orientations();
        ed.num_bf = reference_cells[0].num_basis_functions();
        ed.num_fluxes = reference_faces[0].num_basis_functions();
        ed.num_qp = (g_order == 1) ? 1 : reference_cells[0].num_integration_points();
        ed.num_face_qp = (g_order == 1) ? 1 : reference_faces[0].num_integration_points();
        ed.dof_base = m_dof_base;
        ed.flux_base = m_flux_base;
    
        populate_differentiation_matrices(ed);
        populate_jacobians(ed);
        populate_normals(ed);
        populate_lifting_matrices(ed);
    
        ed.invmass_matrices = matxd::Zero(ed.num_bf*num_cells(), ed.num_bf);
        for (size_t iT = 0; iT < num_cells(); iT++)
        {
            matxd mass = mass_matrix(iT);
            ed.invmass_matrices.block(iT*ed.num_bf, 0, ed.num_bf, ed.num_bf) =
                mass.inverse();
        }
    
        ed.fluxdofs_mine.resize(4*ed.num_fluxes*num_cells());
        for (size_t iT = 0; iT < num_cells(); iT++)
        {
            auto& pe = physical_cells[iT];
            auto cell_keys = pe.bf_keys();
            auto cell_base = iT * ed.num_bf;
    
            for (size_t iF = 0; iF < 4; iF++)
            {
                auto face_num = 4*iT+iF;
                auto& pf = physical_faces[face_num];
                auto face_keys = pf.bf_keys();
    
                auto face_base = face_num * ed.num_fluxes;
    
                for (size_t i = 0; i < face_keys.size(); i++)
                    for (size_t j = 0; j < cell_keys.size(); j++)
                        if ( face_keys.at(i) == cell_keys.at(j) )
                            ed.fluxdofs_mine.at(face_base + i) = cell_base + j;
            }
        }
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    std::pair<std::vector<point_3d>, std::vector<quadrature_point_3d>>
    integrate(const entity& e, int order)
    {
        auto tag = e.gmsh_tag();
        auto elemType = e.gmsh_elem_type();
    
        std::vector<double>     ips;
        std::vector<double>     iws;
        gmm::getIntegrationPoints(elemType, quadrature_name(order), ips, iws);
    
        auto num_qp = iws.size();
    
        std::vector<double>     ppts;
        std::vector<double>     jacs;
        std::vector<double>     dets;
        gmm::getJacobians(elemType, ips, jacs, dets, ppts, tag);
    
        std::vector<point_3d> retr;
    
        for (size_t iQp = 0; iQp < num_qp; iQp++)
        {
            double u = ips[3*iQp + 0];
            double v = ips[3*iQp + 1];
            double w = ips[3*iQp + 2];
            point_3d p(u,v,w);
            retr.push_back(p);
        }
    
        std::vector<quadrature_point_3d> retp;
        retp.reserve(dets.size());
    
        for (auto& pe : e)
        {
            auto g_pos = pe.original_position();
            auto dets_base = g_pos*num_qp;
            auto ppts_base = 3*g_pos*num_qp;
    
            for (size_t iQp = 0; iQp < num_qp; iQp++)
            {
                double w = dets[dets_base+iQp]*iws[iQp];
                double x = ppts[ppts_base + 3*iQp + 0];
                double y = ppts[ppts_base + 3*iQp + 1];
                double z = ppts[ppts_base + 3*iQp + 2];
                point_3d p(x,y,z);
                quadrature_point_3d q(p,w);
                retp.push_back(q);
            }
        }
        return std::make_pair(retr, retp);
    }