libMesh
reference_elem.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 
19 
20 // Local includes
21 #include "libmesh/node.h"
22 #include "libmesh/elem.h"
23 #include "libmesh/reference_elem.h"
24 #include "libmesh/libmesh_singleton.h"
25 #include "libmesh/threads.h"
26 #include "libmesh/enum_to_string.h"
27 #include "libmesh/enum_elem_type.h"
28 
29 // C++ includes
30 #include <map>
31 #include <sstream>
32 #include <memory> // std::unique_ptr
33 
34 
35 //-----------------------------------------------
36 // anonymous namespace for implementation details
37 namespace
38 {
39 using namespace libMesh;
40 
41 namespace ElemDataStrings
42 {
43 // GCC 5.2.0 warns about overlength strings in the auto-generated
44 // reference_elem.data file.
45 #pragma GCC diagnostic ignored "-Woverlength-strings"
46 #include "reference_elem.data"
47 #pragma GCC diagnostic warning "-Woverlength-strings"
48 }
49 
50 typedef Threads::spin_mutex InitMutex;
51 
52 // Mutex for thread safety.
53 InitMutex init_mtx;
54 
55 // map from ElemType to reference element file system object name
56 typedef std::map<ElemType, const char *> FileMapType;
57 FileMapType ref_elem_file;
58 Elem * ref_elem_map[INVALID_ELEM];
59 
60 
61 
62 class SingletonCache : public libMesh::Singleton
63 {
64 public:
65  virtual ~SingletonCache() = default;
66 
67  std::vector<std::unique_ptr<Node>> node_list;
68  std::vector<std::unique_ptr<Elem>> elem_list;
69 };
70 
71 // From [0], regarding the lifetime of the singleton_cache variable:
72 //
73 // "All variables at namespace level, including the anonymous
74 // namespace and function local static variable have static
75 // storage duration unless they are declared thread_local."
76 //
77 // Variables with static storage duration are destroyed at the end of
78 // the program execution. From [1],
79 //
80 // "If it is a pointer to the data which is static ... then like all
81 // other dynamically allocated data, it will only be destructed when
82 // you delete it. There are two frequent solutions:
83 // * use a smart pointer, which has a destructor which deletes it, or
84 // * don't delete it; in most cases, there's really no reason to call the
85 // destructor, and if you happen to use the instance in the destructors
86 // of other static objects, you'll run into an order of destruction
87 // problem."
88 //
89 // I tried making the singleton_cache a std::unique_ptr, but this resulted
90 // in a segfault during program shutdown which appeared to come from the
91 // single_cache unique_ptr's destructor. I didn't investigate whether the
92 // issue was caused by a double deletion or what, but it appears that the
93 // first suggestion above may not be valid in general.
94 //
95 // [0]: https://stackoverflow.com/questions/24342393/how-anonymous-namespaces-avoids-making-global-static-variable
96 // [1]: https://stackoverflow.com/questions/6850009/c-deleting-static-data
97 SingletonCache * singleton_cache = nullptr;
98 
99 
100 
101 void read_ref_elem (const ElemType type_in,
102  std::istream & in)
103 {
104  libmesh_assert (singleton_cache != nullptr);
105 
106  std::string dummy;
107  unsigned int n_elem, n_nodes, elem_type_read, nn;
108  double x, y, z;
109 
110  in >> dummy;
111  in >> n_elem; std::getline (in, dummy); libmesh_assert_equal_to (n_elem, 1);
112  in >> n_nodes; std::getline (in, dummy);
113  in >> dummy; std::getline (in, dummy);
114  in >> dummy; std::getline (in, dummy);
115  in >> dummy; std::getline (in, dummy);
116  in >> dummy; std::getline (in, dummy);
117  in >> n_elem; std::getline (in, dummy); libmesh_assert_equal_to (n_elem, 1);
118 
119  in >> elem_type_read;
120 
121  libmesh_assert_less (elem_type_read, INVALID_ELEM);
122  libmesh_assert_equal_to (elem_type_read, static_cast<unsigned int>(type_in));
123  libmesh_assert_equal_to (n_nodes, Elem::type_to_n_nodes_map[elem_type_read]);
124 
125  // Construct elem of appropriate type, store in the elem_list
126  auto & uelem = singleton_cache->elem_list.emplace_back(Elem::build(type_in));
127 
128  // We are expecting an identity map, so assert it!
129  for (unsigned int n=0; n<n_nodes; n++)
130  {
131  in >> nn;
132  libmesh_assert_equal_to (n,nn);
133  }
134 
135  for (unsigned int n=0; n<n_nodes; n++)
136  {
137  in >> x >> y >> z;
138 
139  auto & new_node =
140  singleton_cache->node_list.emplace_back(Node::build(x,y,z,n));
141 
142  uelem->set_node(n, new_node.get());
143  }
144 
145  // it is entirely possible we ran out of file or encountered
146  // another error. If so, throw an error.
147  libmesh_error_msg_if(!in, "ERROR while creating element singleton!");
148 
149  // Also store a pointer to the newly created Elem in the ref_elem_map array.
150  ref_elem_map[type_in] = uelem.get();
151 }
152 
153 
154 
155 void init_ref_elem_table()
156 {
157  // outside mutex - if this pointer is set, we can trust it.
158  if (singleton_cache != nullptr)
159  return;
160 
161  // playing with fire here - lock before touching shared
162  // data structures
163  InitMutex::scoped_lock lock(init_mtx);
164 
165  // inside mutex - pointer may have changed while waiting
166  // for the lock to acquire, check it again.
167  if (singleton_cache != nullptr)
168  return;
169 
170  // OK, if we get here we have the lock and we are not
171  // initialized. populate singleton. Note that we do not
172  // use a smart pointer to manage the singleton_cache variable
173  // since variables with static storage duration are destroyed
174  // automatically at the end of program execution.
175  singleton_cache = new SingletonCache;
176 
177  // initialize the reference file table
178  {
179  ref_elem_file.clear();
180 
181  // 0D elements
182  ref_elem_file[NODEELEM] = ElemDataStrings::one_nodeelem;
183 
184  // 1D elements
185  ref_elem_file[EDGE2] = ElemDataStrings::one_edge;
186  ref_elem_file[EDGE3] = ElemDataStrings::one_edge3;
187  ref_elem_file[EDGE4] = ElemDataStrings::one_edge4;
188 
189  // 2D elements
190  ref_elem_file[TRI3] = ElemDataStrings::one_tri;
191  ref_elem_file[TRI6] = ElemDataStrings::one_tri6;
192  ref_elem_file[TRI7] = ElemDataStrings::one_tri7;
193 
194  ref_elem_file[QUAD4] = ElemDataStrings::one_quad;
195  ref_elem_file[QUAD8] = ElemDataStrings::one_quad8;
196  ref_elem_file[QUAD9] = ElemDataStrings::one_quad9;
197 
198  // 3D elements
199  ref_elem_file[HEX8] = ElemDataStrings::one_hex;
200  ref_elem_file[HEX20] = ElemDataStrings::one_hex20;
201  ref_elem_file[HEX27] = ElemDataStrings::one_hex27;
202 
203  ref_elem_file[TET4] = ElemDataStrings::one_tet;
204  ref_elem_file[TET10] = ElemDataStrings::one_tet10;
205  ref_elem_file[TET14] = ElemDataStrings::one_tet14;
206 
207  ref_elem_file[PRISM6] = ElemDataStrings::one_prism;
208  ref_elem_file[PRISM15] = ElemDataStrings::one_prism15;
209  ref_elem_file[PRISM18] = ElemDataStrings::one_prism18;
210  ref_elem_file[PRISM20] = ElemDataStrings::one_prism20;
211  ref_elem_file[PRISM21] = ElemDataStrings::one_prism21;
212 
213  ref_elem_file[PYRAMID5] = ElemDataStrings::one_pyramid;
214  ref_elem_file[PYRAMID13] = ElemDataStrings::one_pyramid13;
215  ref_elem_file[PYRAMID14] = ElemDataStrings::one_pyramid14;
216  ref_elem_file[PYRAMID18] = ElemDataStrings::one_pyramid18;
217 
218  // No entry here for C0POLYGON or C0POLYHEDRON - the reference
219  // element there depends on the number of nodes and can't be
220  // precomputed.
221  }
222 
223  // Read'em
224  for (const auto & [elem_type, filename] : ref_elem_file)
225  {
226  std::istringstream stream(filename);
227  read_ref_elem(elem_type, stream);
228  }
229 }
230 
231 
232 // no reason to do this at startup -
233 // data structures will get initialized *if*
234 // ReferenceElem::get() is ever called.
235 // // Class to setup singleton data
236 // class ReferenceElemSetup : public Singleton::Setup
237 // {
238 // void setup ()
239 // {
240 // init_ref_elem_table();
241 // }
242 // } reference_elem_setup;
243 
244 } // anonymous namespace
245 
246 
247 
248 //----------------------------------------------------------------------------
249 // external API Implementation
250 namespace libMesh
251 {
252 namespace ReferenceElem
253 {
254 const Elem & get (const ElemType type_in)
255 {
256  ElemType base_type = type_in;
257 
258  // For shell elements, use non shell type as the base type
259  if (type_in == TRISHELL3)
260  base_type = TRI3;
261 
262  if (type_in == QUADSHELL4)
263  base_type = QUAD4;
264 
265  if (type_in == QUADSHELL8)
266  base_type = QUAD8;
267 
268  if (type_in == QUADSHELL9)
269  base_type = QUAD9;
270 
271  init_ref_elem_table();
272 
273  // Throw an error if the user asked for an ElemType that we don't
274  // have a reference element for.
275  libmesh_error_msg_if(ref_elem_map[base_type] == nullptr || type_in == INVALID_ELEM,
276  "No reference elem data available for ElemType " << type_in
277  << " = " << Utility::enum_to_string(type_in) << ".");
278 
279  return *ref_elem_map[base_type];
280 }
281 } // namespace ReferenceElem
282 } // namespace libMesh
Base class for all library singleton objects.
ElemType
Defines an enum for geometric element types.
dof_id_type n_elem(const MeshBase::const_element_iterator &begin, const MeshBase::const_element_iterator &end)
Count up the number of elements of a specific type (as defined by an iterator range).
Definition: mesh_tools.C:1004
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
The libMesh namespace provides an interface to certain functionality in the library.
static const unsigned int type_to_n_nodes_map[INVALID_ELEM]
This array maps the integer representation of the ElemType enum to the number of nodes in the element...
Definition: elem.h:643
const dof_id_type n_nodes
Definition: tecplot_io.C:67
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition: elem.C:442
libmesh_assert(ctx)
std::string enum_to_string(const T e)
static std::unique_ptr< Node > build(const Node &n)
Definition: node.h:315