Beispiel #1
0
  bool ElemCutter::is_outside (const Elem &elem,
			       const std::vector<Real> &vertex_distance_func) const
  {
    libmesh_assert_equal_to (elem.n_vertices(), vertex_distance_func.size());

    for (std::vector<Real>::const_iterator it=vertex_distance_func.begin();
	 it!=vertex_distance_func.end(); ++it)
      if (*it < 0.) return false;

    // if the distance function is nonnegative, we are outside
    return true;
  }
void QComposite<QSubCell>::init (const Elem &elem,
                                 const std::vector<Real> &vertex_distance_func,
                                 unsigned int p_level)
{
  libmesh_assert_equal_to (vertex_distance_func.size(), elem.n_vertices());
  libmesh_assert_equal_to (_dim, elem.dim());

  // if we are not cut, revert to simple base class init() method.
  if (!_elem_cutter.is_cut (elem, vertex_distance_func))
    {
      _q_subcell.init (elem.type(), p_level);
      _points  = _q_subcell.get_points();
      _weights = _q_subcell.get_weights();

      //this->print_info();
      return;
    }

  // Get a pointer to the element's reference element.  We want to
  // perform cutting on the reference element such that the quadrature
  // point locations of the subelements live in the reference
  // coordinate system, thereby eliminating the need for inverse
  // mapping.
  const Elem *reference_elem = elem.reference_elem();

  libmesh_assert (reference_elem != NULL);

  _elem_cutter(*reference_elem, vertex_distance_func);
  //_elem_cutter(elem, vertex_distance_func);

  // clear our state & accumulate points from subelements
  _points.clear();
  _weights.clear();

  // inside subelem
  {
    const std::vector<Elem const*> &inside_elem (_elem_cutter.inside_elements());
    std::cout << inside_elem.size() << " elements inside\n";

    this->add_subelem_values(inside_elem);
  }

  // outside subelem
  {
    const std::vector<Elem const*> &outside_elem (_elem_cutter.outside_elements());
    std::cout << outside_elem.size() << " elements outside\n";

    this->add_subelem_values(outside_elem);
  }

  this->print_info();
}
Beispiel #3
0
  bool ElemCutter::is_cut (const Elem &elem,
			   const std::vector<Real> &vertex_distance_func) const
  {
    libmesh_assert_equal_to (elem.n_vertices(), vertex_distance_func.size());

    Real
      vmin = vertex_distance_func.front(),
      vmax = vmin;

    for (std::vector<Real>::const_iterator it=vertex_distance_func.begin();
	 it!=vertex_distance_func.end(); ++it)
      {
	vmin = std::min (vmin, *it);
	vmax = std::max (vmax, *it);
      }

    // if the distance function changes sign, we're cut.
    return (vmin*vmax < 0.);
  }
Beispiel #4
0
void ElemCutter::operator()(const Elem & elem,
                            const std::vector<Real> & vertex_distance_func)

{
  libmesh_assert_equal_to (vertex_distance_func.size(), elem.n_vertices());

  _inside_elem.clear();
  _outside_elem.clear();

  // check for quick return?
  {
    // completely outside?
    if (this->is_outside(elem, vertex_distance_func))
      {
        //std::cout << "element completely outside\n";
        _outside_elem.push_back(& elem);
        return;
      }

    // completely inside?
    else if (this->is_inside(elem, vertex_distance_func))
      {
        //std::cout << "element completely inside\n";
        _inside_elem.push_back(&elem);
        return;
      }

    libmesh_assert (this->is_cut (elem, vertex_distance_func));
  }

  // we now know we are in a cut element, find the intersecting points.
  this->find_intersection_points (elem, vertex_distance_func);

  // and then dispatch the proper method
  switch (elem.dim())
    {
    case 1: this->cut_1D(elem, vertex_distance_func); break;
    case 2: this->cut_2D(elem, vertex_distance_func); break;
    case 3: this->cut_3D(elem, vertex_distance_func); break;
    default: libmesh_error_msg("Invalid element dimension: " << elem.dim());
    }
}
Beispiel #5
0
void ElemCutter::cut_3D (const Elem & elem,
                         const std::vector<Real> & vertex_distance_func)
{
#ifndef LIBMESH_HAVE_TETGEN

  // current implementation requires tetgen!
  libMesh::err << "ERROR: current libMesh ElemCutter 3D implementation requires\n"
               << "       the \"tetgen\" library!\n"
               << std::endl;
  libmesh_not_implemented();

#else // OK, LIBMESH_HAVE_TETGEN

  std::cout << "Inside cut cell element!\n";

  libmesh_assert (_inside_mesh_3D.get()  != nullptr);
  libmesh_assert (_outside_mesh_3D.get() != nullptr);

  _inside_mesh_3D->clear();
  _outside_mesh_3D->clear();

  for (unsigned int v=0; v<elem.n_vertices(); v++)
    {
      if (vertex_distance_func[v] >= 0.)
        _outside_mesh_3D->add_point (elem.point(v));

      if (vertex_distance_func[v] <= 0.)
        _inside_mesh_3D->add_point (elem.point(v));
    }

  for (const auto & pt : _intersection_pts)
    {
      _inside_mesh_3D->add_point(pt);
      _outside_mesh_3D->add_point(pt);
    }


  // Triangulate!
  _tetgen_inside->triangulate_pointset();
  //_inside_mesh_3D->print_info();
  _tetgen_outside->triangulate_pointset();
  //_outside_mesh_3D->print_info();


  // (below generates some horribly expensive meshes,
  //  but seems immune to the 0 volume problem).
  // _tetgen_inside->pointset_convexhull();
  // _inside_mesh_3D->find_neighbors();
  // _inside_mesh_3D->print_info();
  // _tetgen_inside->triangulate_conformingDelaunayMesh (1.e3, 100.);
  // _inside_mesh_3D->print_info();

  // _tetgen_outside->pointset_convexhull();
  // _outside_mesh_3D->find_neighbors();
  // _outside_mesh_3D->print_info();
  // _tetgen_outside->triangulate_conformingDelaunayMesh (1.e3, 100.);
  // _outside_mesh_3D->print_info();

  std::ostringstream name;

  name << "cut_cell_"
       << cut_cntr++
       << ".dat";
  _inside_mesh_3D->write  ("in_"  + name.str());
  _outside_mesh_3D->write ("out_" + name.str());

  // finally, add the elements to our lists.
  _inside_elem.clear();
  _outside_elem.clear();

  for (const auto & elem : _inside_mesh_3D->element_ptr_range())
    if (elem->volume() > std::numeric_limits<Real>::epsilon())
      _inside_elem.push_back (elem);

  for (const auto & elem : _outside_mesh_3D->element_ptr_range())
    if (elem->volume() > std::numeric_limits<Real>::epsilon())
      _outside_elem.push_back (elem);

#endif
}
Beispiel #6
0
void ElemCutter::cut_2D (const Elem & elem,
                         const std::vector<Real> & vertex_distance_func)
{
#ifndef LIBMESH_HAVE_TRIANGLE

  // current implementation requires triangle!
  libMesh::err << "ERROR: current libMesh ElemCutter 2D implementation requires\n"
               << "       the \"triangle\" library!\n"
               << std::endl;
  libmesh_not_implemented();

#else // OK, LIBMESH_HAVE_TRIANGLE

  std::cout << "Inside cut face element!\n";

  libmesh_assert (_inside_mesh_2D.get()  != nullptr);
  libmesh_assert (_outside_mesh_2D.get() != nullptr);

  _inside_mesh_2D->clear();
  _outside_mesh_2D->clear();

  for (unsigned int v=0; v<elem.n_vertices(); v++)
    {
      if (vertex_distance_func[v] >= 0.)
        _outside_mesh_2D->add_point (elem.point(v));

      if (vertex_distance_func[v] <= 0.)
        _inside_mesh_2D->add_point (elem.point(v));
    }

  for (const auto & pt : _intersection_pts)
    {
      _inside_mesh_2D->add_point(pt);
      _outside_mesh_2D->add_point(pt);
    }


  // Customize the variables for the triangulation
  // we will be cutting reference cell, and want as few triangles
  // as possible, so jack this up larger than the area we will be
  // triangulating so we are governed only by accurately defining
  // the boundaries.
  _triangle_inside->desired_area()  = 100.;
  _triangle_outside->desired_area() = 100.;

  // allow for small angles
  _triangle_inside->minimum_angle()  = 5.;
  _triangle_outside->minimum_angle() = 5.;

  // Turn off Laplacian mesh smoothing after generation.
  _triangle_inside->smooth_after_generating()  = false;
  _triangle_outside->smooth_after_generating() = false;

  // Triangulate!
  _triangle_inside->triangulate();
  _triangle_outside->triangulate();

  // std::ostringstream name;

  // name << "cut_face_"
  //  << cut_cntr++
  //  << ".dat";
  // _inside_mesh_2D->write  ("in_"  + name.str());
  // _outside_mesh_2D->write ("out_" + name.str());

  // finally, add the elements to our lists.
  _inside_elem.clear();
  _outside_elem.clear();

  for (const auto & elem : _inside_mesh_2D->element_ptr_range())
    _inside_elem.push_back (elem);

  for (const auto & elem : _outside_mesh_2D->element_ptr_range())
    _outside_elem.push_back (elem);

#endif
}
Beispiel #7
0
void MeshBase::detect_interior_parents()
{
  // This requires an inspection on every processor
  parallel_object_only();

  // Check if the mesh contains mixed dimensions. If so, then set interior parents, otherwise return.
  if (this->elem_dimensions().size() == 1)
    return;

  //This map will be used to set interior parents
  LIBMESH_BEST_UNORDERED_MAP<dof_id_type, std::vector<dof_id_type> > node_to_elem;

  const_element_iterator el  = this->active_elements_begin();
  const_element_iterator end = this->active_elements_end();

  for (; el!=end; ++el)
    {
      const Elem * elem = *el;

      // Populating the node_to_elem map, same as MeshTools::build_nodes_to_elem_map
      for (unsigned int n=0; n<elem->n_vertices(); n++)
        {
          libmesh_assert_less (elem->id(), this->max_elem_id());

          node_to_elem[elem->node(n)].push_back(elem->id());
        }
    }

  // Automatically set interior parents
  el = this->elements_begin();
  for (; el!=end; ++el)
    {
      Elem * element = *el;

      // Ignore an 3D element or an element that already has an interior parent
      if (element->dim()>=LIBMESH_DIM || element->interior_parent())
        continue;

      // Start by generating a SET of elements that are dim+1 to the current
      // element at each vertex of the current element, thus ignoring interior nodes.
      // If one of the SET of elements is empty, then we will not have an interior parent
      // since an interior parent must be connected to all vertices of the current element
      std::vector< std::set<dof_id_type> > neighbors( element->n_vertices() );

      bool found_interior_parents = false;

      for (dof_id_type n=0; n < element->n_vertices(); n++)
        {
          std::vector<dof_id_type> & element_ids = node_to_elem[element->node(n)];
          for (std::vector<dof_id_type>::iterator e_it = element_ids.begin();
               e_it != element_ids.end(); e_it++)
            {
              dof_id_type eid = *e_it;
              if (this->elem(eid)->dim() == element->dim()+1)
                neighbors[n].insert(eid);
            }
          if (neighbors[n].size()>0)
            {
              found_interior_parents = true;
            }
          else
            {
              // We have found an empty set, no reason to continue
              // Ensure we set this flag to false before the break since it could have
              // been set to true for previous vertex
              found_interior_parents = false;
              break;
            }
        }

      // If we have successfully generated a set of elements for each vertex, we will compare
      // the set for vertex 0 will the sets for the vertices until we find a id that exists in
      // all sets.  If found, this is our an interior parent id.  The interior parent id found
      // will be the lowest element id if there is potential for multiple interior parents.
      if (found_interior_parents)
        {
          std::set<dof_id_type> & neighbors_0 = neighbors[0];
          for (std::set<dof_id_type>::iterator e_it = neighbors_0.begin();
               e_it != neighbors_0.end(); e_it++)
            {
              found_interior_parents=false;
              dof_id_type interior_parent_id = *e_it;
              for (dof_id_type n=1; n < element->n_vertices(); n++)
                {
                  if (neighbors[n].find(interior_parent_id)!=neighbors[n].end())
                    {
                      found_interior_parents=true;
                    }
                  else
                    {
                      found_interior_parents=false;
                      break;
                    }
                }
              if (found_interior_parents)
                {
                  element->set_interior_parent(this->elem(interior_parent_id));
                  break;
                }
            }
        }
    }
}
Beispiel #8
0
// The actual implementation of building elements.
void InfElemBuilder::build_inf_elem(const Point& origin,
                                    const bool x_sym,
                                    const bool y_sym,
                                    const bool z_sym,
                                    const bool be_verbose,
                                    std::set< std::pair<dof_id_type,
                                    unsigned int> >* inner_faces)
{
    if (be_verbose)
    {
#ifdef DEBUG
        libMesh::out << " Building Infinite Elements:" << std::endl;
        libMesh::out << "  updating element neighbor tables..." << std::endl;
#else
        libMesh::out << " Verbose mode disabled in non-debug mode." << std::endl;
#endif
    }


    // update element neighbors
    this->_mesh.find_neighbors();

    START_LOG("build_inf_elem()", "InfElemBuilder");

    // A set for storing element number, side number pairs.
    // pair.first == element number, pair.second == side number
    std::set< std::pair<dof_id_type,unsigned int> > faces;
    std::set< std::pair<dof_id_type,unsigned int> > ofaces;

    // A set for storing node numbers on the outer faces.
    std::set<dof_id_type> onodes;

    // The distance to the farthest point in the mesh from the origin
    Real max_r=0.;

    // The index of the farthest point in the mesh from the origin
    int max_r_node = -1;

#ifdef DEBUG
    if (be_verbose)
    {
        libMesh::out << "  collecting boundary sides";
        if (x_sym || y_sym || z_sym)
            libMesh::out << ", skipping sides in symmetry planes..." << std::endl;
        else
            libMesh::out << "..." << std::endl;
    }
#endif

    // Iterate through all elements and sides, collect indices of all active
    // boundary sides in the faces set. Skip sides which lie in symmetry planes.
    // Later, sides of the inner boundary will be sorted out.
    {
        MeshBase::element_iterator       it  = this->_mesh.active_elements_begin();
        const MeshBase::element_iterator end = this->_mesh.active_elements_end();

        for(; it != end; ++it)
        {
            Elem* elem = *it;

            for (unsigned int s=0; s<elem->n_neighbors(); s++)
            {
                // check if elem(e) is on the boundary
                if (elem->neighbor(s) == NULL)
                {
                    // note that it is safe to use the Elem::side() method,
                    // which gives a non-full-ordered element
                    AutoPtr<Elem> side(elem->build_side(s));

                    // bool flags for symmetry detection
                    bool sym_side=false;
                    bool on_x_sym=true;
                    bool on_y_sym=true;
                    bool on_z_sym=true;


                    // Loop over the nodes to check whether they are on the symmetry planes,
                    // and therefore sufficient to use a non-full-ordered side element
                    for(unsigned int n=0; n<side->n_nodes(); n++)
                    {
                        const Point dist_from_origin = this->_mesh.point(side->node(n)) - origin;

                        if(x_sym)
                            if( std::abs(dist_from_origin(0)) > 1.e-3 )
                                on_x_sym=false;

                        if(y_sym)
                            if( std::abs(dist_from_origin(1)) > 1.e-3 )
                                on_y_sym=false;

                        if(z_sym)
                            if( std::abs(dist_from_origin(2)) > 1.e-3 )
                                on_z_sym=false;

                        // 	      if(x_sym)
                        // 		if( std::abs(dist_from_origin(0)) > 1.e-6 )
                        // 		  on_x_sym=false;

                        // 	      if(y_sym)
                        // 		if( std::abs(dist_from_origin(1)) > 1.e-6 )
                        // 		  on_y_sym=false;

                        // 	      if(z_sym)
                        // 		if( std::abs(dist_from_origin(2)) > 1.e-6 )
                        // 		  on_z_sym=false;

                        //find the node most distant from origin

                        Real r = dist_from_origin.size();
                        if (r > max_r)
                        {
                            max_r = r;
                            max_r_node=side->node(n);
                        }

                    }

                    sym_side = (x_sym && on_x_sym) || (y_sym && on_y_sym) || (z_sym && on_z_sym);

                    if (!sym_side)
                        faces.insert( std::make_pair(elem->id(), s) );

                } // neighbor(s) == NULL
            } // sides
        } // elems
    }






    //  If a boundary side has one node on the outer boundary,
    //  all points of this side are on the outer boundary.
    //  Start with the node most distant from origin, which has
    //  to be on the outer boundary, then recursively find all
    //  sides and nodes connected to it. Found sides are moved
    //  from faces to ofaces, nodes are collected in onodes.
    //  Here, the search is done iteratively, because, depending on
    //  the mesh, a very high level of recursion might be necessary.
    if (max_r_node > 0)
        onodes.insert(max_r_node);


    {
        std::set< std::pair<dof_id_type,unsigned int> >::iterator face_it = faces.begin();
        unsigned int facesfound=0;
        while (face_it != faces.end()) {

            std::pair<dof_id_type, unsigned int> p;
            p = *face_it;

            // This has to be a full-ordered side element,
            // since we need the correct n_nodes,
            AutoPtr<Elem> side(this->_mesh.elem(p.first)->build_side(p.second));

            bool found=false;
            for(unsigned int sn=0; sn<side->n_nodes(); sn++)
                if(onodes.count(side->node(sn)))
                {
                    found=true;
                    break;
                }


            // If a new oface is found, include its nodes in onodes
            if(found)
            {
                for(unsigned int sn=0; sn<side->n_nodes(); sn++)
                    onodes.insert(side->node(sn));

                ofaces.insert(p);
                face_it++;			// iteration is done here
                faces.erase(p);

                facesfound++;
            }

            else
                face_it++;			// iteration is done here


            // If at least one new oface was found in this cycle,
            // do another search cycle.
            if(facesfound>0 && face_it == faces.end())
            {
                facesfound = 0;
                face_it    = faces.begin();
            }

        }
    }


#ifdef DEBUG
    if (be_verbose)
        libMesh::out << "  found "
                     << faces.size()
                     << " inner and "
                     << ofaces.size()
                     << " outer boundary faces"
                     << std::endl;
#endif

    // When the user provided a non-null pointer to
    // inner_faces, that implies he wants to have
    // this std::set.  For now, simply copy the data.
    if (inner_faces != NULL)
        *inner_faces = faces;

    // free memory, clear our local variable, no need
    // for it any more.
    faces.clear();


    // outer_nodes maps onodes to their duplicates
    std::map<dof_id_type, Node *> outer_nodes;

    // We may need to pick our own object ids in parallel
    dof_id_type old_max_node_id = _mesh.max_node_id();
    dof_id_type old_max_elem_id = _mesh.max_elem_id();

    // for each boundary node, add an outer_node with
    // double distance from origin.
    std::set<dof_id_type>::iterator on_it = onodes.begin();
    for( ; on_it != onodes.end(); ++on_it)
    {
        Point p = (Point(this->_mesh.point(*on_it)) * 2) - origin;
        if (_mesh.is_serial())
        {
            // Add with a default id in serial
            outer_nodes[*on_it]=this->_mesh.add_point(p);
        }
        else
        {
            // Pick a unique id in parallel
            Node &bnode = _mesh.node(*on_it);
            dof_id_type new_id = bnode.id() + old_max_node_id;
            outer_nodes[*on_it] =
                this->_mesh.add_point(p, new_id,
                                      bnode.processor_id());
        }
    }


#ifdef DEBUG
    // for verbose, remember n_elem
    dof_id_type n_conventional_elem = this->_mesh.n_elem();
#endif


    // build Elems based on boundary side type
    std::set< std::pair<dof_id_type,unsigned int> >::iterator face_it = ofaces.begin();
    for( ; face_it != ofaces.end(); ++face_it)
    {
        // Shortcut to the pair being iterated over
        std::pair<dof_id_type,unsigned int> p = *face_it;

        // build a full-ordered side element to get the base nodes
        AutoPtr<Elem> side(this->_mesh.elem(p.first)->build_side(p.second));

        // create cell depending on side type, assign nodes,
        // use braces to force scope.
        bool is_higher_order_elem = false;
        {
            Elem* el;
            switch(side->type())
            {
            // 3D infinite elements
            // TRIs
            case TRI3:
                el=new InfPrism6;
                break;

            case TRI6:
                el=new InfPrism12;
                is_higher_order_elem = true;
                break;

            // QUADs
            case QUAD4:
                el=new InfHex8;
                break;

            case QUAD8:
                el=new InfHex16;
                is_higher_order_elem = true;
                break;

            case QUAD9:
                el=new InfHex18;

                // the method of assigning nodes (which follows below)
                // omits in the case of QUAD9 the bubble node; therefore
                // we assign these first by hand here.
                el->set_node(16) = side->get_node(8);
                el->set_node(17) = outer_nodes[side->node(8)];
                is_higher_order_elem=true;
                break;

            // 2D infinite elements
            case EDGE2:
                el=new InfQuad4;
                break;

            case EDGE3:
                el=new InfQuad6;
                el->set_node(4) = side->get_node(2);
                break;

            // 1D infinite elements not supported
            default:
                libMesh::out << "InfElemBuilder::build_inf_elem(Point, bool, bool, bool, bool): "
                             << "invalid face element "
                             << std::endl;
                continue;
            }

            // In parallel, assign unique ids to the new element
            if (!_mesh.is_serial())
            {
                Elem *belem = _mesh.elem(p.first);
                el->processor_id() = belem->processor_id();
                // We'd better not have elements with more than 6 sides
                el->set_id (belem->id() * 6 + p.second + old_max_elem_id);
            }

            // assign vertices to the new infinite element
            const unsigned int n_base_vertices = side->n_vertices();
            for(unsigned int i=0; i<n_base_vertices; i++)
            {
                el->set_node(i                ) = side->get_node(i);
                el->set_node(i+n_base_vertices) = outer_nodes[side->node(i)];
            }


            // when this is a higher order element,
            // assign also the nodes in between
            if (is_higher_order_elem)
            {
                // n_safe_base_nodes is the number of nodes in \p side
                // that may be safely assigned using below for loop.
                // Actually, n_safe_base_nodes is _identical_ with el->n_vertices(),
                // since for QUAD9, the 9th node was already assigned above
                const unsigned int n_safe_base_nodes   = el->n_vertices();

                for(unsigned int i=n_base_vertices; i<n_safe_base_nodes; i++)
                {
                    el->set_node(i+n_base_vertices)   = side->get_node(i);
                    el->set_node(i+n_safe_base_nodes) = outer_nodes[side->node(i)];
                }
            }


            // add infinite element to mesh
            this->_mesh.add_elem(el);
        } // el goes out of scope
    } // for


#ifdef DEBUG
    _mesh.libmesh_assert_valid_parallel_ids();

    if (be_verbose)
        libMesh::out << "  added "
                     << this->_mesh.n_elem() - n_conventional_elem
                     << " infinite elements and "
                     << onodes.size()
                     << " nodes to the mesh"
                     << std::endl
                     << std::endl;
#endif

    STOP_LOG("build_inf_elem()", "InfElemBuilder");
}