1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2020 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 
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:
~SingletonCache()65   ~SingletonCache()
66   {
67     for (auto & elem : elem_list)
68       {
69         delete elem;
70         elem = nullptr;
71       }
72 
73     elem_list.clear();
74 
75     for (auto & node : node_list)
76       {
77         delete node;
78         node = nullptr;
79       }
80 
81     node_list.clear();
82   }
83 
84   std::vector<Node *> node_list;
85   std::vector<Elem *> elem_list;
86 };
87 
88 // singleton object, dynamically created and then
89 // removed at program exit
90 SingletonCache * singleton_cache = nullptr;
91 
92 
93 
read_ref_elem(const ElemType type_in,std::istream & in)94 void read_ref_elem (const ElemType type_in,
95                     std::istream & in)
96 {
97   libmesh_assert (singleton_cache != nullptr);
98 
99   std::string dummy;
100   unsigned int n_elem, n_nodes, elem_type_read, nn;
101   double x, y, z;
102 
103   in >> dummy;
104   in >> n_elem;  /**/ std::getline (in, dummy); libmesh_assert_equal_to (n_elem, 1);
105   in >> n_nodes; /**/ std::getline (in, dummy);
106   in >> dummy;   /**/ std::getline (in, dummy);
107   in >> dummy;   /**/ std::getline (in, dummy);
108   in >> dummy;   /**/ std::getline (in, dummy);
109   in >> dummy;   /**/ std::getline (in, dummy);
110   in >> n_elem;  /**/ std::getline (in, dummy); libmesh_assert_equal_to (n_elem, 1);
111 
112   in >> elem_type_read;
113 
114   libmesh_assert_less (elem_type_read, INVALID_ELEM);
115   libmesh_assert_equal_to (elem_type_read, static_cast<unsigned int>(type_in));
116   libmesh_assert_equal_to (n_nodes, Elem::type_to_n_nodes_map[elem_type_read]);
117 
118   // Construct elem of appropriate type
119   std::unique_ptr<Elem> uelem = Elem::build(type_in);
120 
121   // We are expecting an identity map, so assert it!
122   for (unsigned int n=0; n<n_nodes; n++)
123     {
124       in >> nn;
125       libmesh_assert_equal_to (n,nn);
126     }
127 
128   for (unsigned int n=0; n<n_nodes; n++)
129     {
130       in >> x >> y >> z;
131 
132       Node * node = new Node(x,y,z,n);
133       singleton_cache->node_list.push_back(node);
134 
135       uelem->set_node(n) = node;
136     }
137 
138   // it is entirely possible we ran out of file or encountered
139   // another error.  If so, throw an error.
140   libmesh_error_msg_if(!in, "ERROR while creating element singleton!");
141 
142   // Release the pointer into the care of the singleton_cache
143   singleton_cache->elem_list.push_back (uelem.release());
144 
145   // Also store it in the array.
146   ref_elem_map[type_in] = singleton_cache->elem_list.back();
147 }
148 
149 
150 
init_ref_elem_table()151 void init_ref_elem_table()
152 {
153   // outside mutex - if this pointer is set, we can trust it.
154   if (singleton_cache != nullptr)
155     return;
156 
157   // playing with fire here - lock before touching shared
158   // data structures
159   InitMutex::scoped_lock lock(init_mtx);
160 
161   // inside mutex - pointer may have changed while waiting
162   // for the lock to acquire, check it again.
163   if (singleton_cache != nullptr)
164     return;
165 
166   // OK, if we get here we have the lock and we are not
167   // initialized.  populate singleton.
168   singleton_cache = new SingletonCache;
169 
170   // initialize the reference file table
171   {
172     ref_elem_file.clear();
173 
174     // 1D elements
175     ref_elem_file[EDGE2]    = ElemDataStrings::one_edge;
176     ref_elem_file[EDGE3]    = ElemDataStrings::one_edge3;
177     ref_elem_file[EDGE4]    = ElemDataStrings::one_edge4;
178 
179     // 2D elements
180     ref_elem_file[TRI3]     = ElemDataStrings::one_tri;
181     ref_elem_file[TRI6]     = ElemDataStrings::one_tri6;
182 
183     ref_elem_file[QUAD4]    = ElemDataStrings::one_quad;
184     ref_elem_file[QUAD8]    = ElemDataStrings::one_quad8;
185     ref_elem_file[QUAD9]    = ElemDataStrings::one_quad9;
186 
187     // 3D elements
188     ref_elem_file[HEX8]     = ElemDataStrings::one_hex;
189     ref_elem_file[HEX20]    = ElemDataStrings::one_hex20;
190     ref_elem_file[HEX27]    = ElemDataStrings::one_hex27;
191 
192     ref_elem_file[TET4]     = ElemDataStrings::one_tet;
193     ref_elem_file[TET10]    = ElemDataStrings::one_tet10;
194 
195     ref_elem_file[PRISM6]   = ElemDataStrings::one_prism;
196     ref_elem_file[PRISM15]  = ElemDataStrings::one_prism15;
197     ref_elem_file[PRISM18]  = ElemDataStrings::one_prism18;
198 
199     ref_elem_file[PYRAMID5] = ElemDataStrings::one_pyramid;
200     ref_elem_file[PYRAMID13] = ElemDataStrings::one_pyramid13;
201     ref_elem_file[PYRAMID14] = ElemDataStrings::one_pyramid14;
202   }
203 
204   // Read'em
205   for (const auto & pr : ref_elem_file)
206     {
207       std::istringstream stream(pr.second);
208       read_ref_elem(pr.first, stream);
209     }
210 }
211 
212 
213 // no reason to do this at startup -
214 // data structures will get initialized *if*
215 // ReferenceElem::get() is ever called.
216 // // Class to setup singleton data
217 // class ReferenceElemSetup : public Singleton::Setup
218 // {
219 //   void setup ()
220 //   {
221 //     init_ref_elem_table();
222 //   }
223 // } reference_elem_setup;
224 
225 } // anonymous namespace
226 
227 
228 
229 //----------------------------------------------------------------------------
230 // external API Implementation
231 namespace libMesh
232 {
233 namespace ReferenceElem
234 {
get(const ElemType type_in)235 const Elem & get (const ElemType type_in)
236 {
237   ElemType base_type = type_in;
238 
239   // For shell elements, use non shell type as the base type
240   if (type_in == TRISHELL3)
241     base_type = TRI3;
242 
243   if (type_in == QUADSHELL4)
244     base_type = QUAD4;
245 
246   if (type_in == QUADSHELL8)
247     base_type = QUAD8;
248 
249   init_ref_elem_table();
250 
251   // Throw an error if the user asked for an ElemType that we don't
252   // have a reference element for.
253   libmesh_error_msg_if(ref_elem_map[base_type] == nullptr || type_in == INVALID_ELEM,
254                        "No reference elem data available for ElemType " << type_in
255                        << " = " << Utility::enum_to_string(type_in) << ".");
256 
257   return *ref_elem_map[base_type];
258 }
259 } // namespace ReferenceElem
260 } // namespace libMesh
261