libMesh
Loading...
Searching...
No Matches
abaqus_io.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// Local includes
19#include "libmesh/abaqus_io.h"
20#include "libmesh/point.h"
21#include "libmesh/elem.h"
22#include "libmesh/enum_to_string.h"
23#include "libmesh/boundary_info.h"
24#include "libmesh/utility.h"
25
26// gzstream for reading compressed files as a stream
27#ifdef LIBMESH_HAVE_GZSTREAM
28# include "gzstream.h"
29#endif
30
31// C++ includes
32#include <unordered_map>
33#include <string>
34#include <cstdlib> // std::strtol
35#include <sstream>
36#include <cctype> // isspace
37
38// Anonymous namespace to hold mapping Data for Abaqus/libMesh element types
39namespace
40{
41using namespace libMesh;
42
47struct ElementDefinition
48{
49 // Maps (zero-based!) Abaqus local node numbers to libmesh local node numbers
50 std::vector<unsigned> abaqus_zero_based_node_id_to_libmesh_node_id;
51
52 // Maps (zero-based!) Abaqus side numbers to libmesh side numbers
53 std::vector<unsigned short> abaqus_zero_based_side_id_to_libmesh_side_id;
54};
55
59std::map<ElemType, ElementDefinition> eletypes;
60
64void add_eletype_entry(ElemType libmesh_elem_type,
65 const unsigned * node_map,
66 unsigned node_map_size,
67 const unsigned short * side_map,
68 unsigned side_map_size)
69{
70 // If map entry does not exist, this will create it
71 ElementDefinition & map_entry = eletypes[libmesh_elem_type];
72
73
74 // Use the "swap trick" from Scott Meyer's "Effective STL" to swap
75 // an unnamed temporary vector into the map_entry's vector. Note:
76 // the vector(iter, iter) constructor is used.
77 std::vector<unsigned>
78 (node_map, node_map+node_map_size).swap
79 (map_entry.abaqus_zero_based_node_id_to_libmesh_node_id);
80
81 std::vector<unsigned short>
82 (side_map, side_map+side_map_size).swap
83 (map_entry.abaqus_zero_based_side_id_to_libmesh_side_id);
84}
85
86
90void init_eletypes ()
91{
92 // This should happen only once. The first time this method is
93 // called the eletypes data structure will be empty, and we will
94 // fill it. Any subsequent calls will find an initialized
95 // eletypes map and will do nothing.
96 if (eletypes.empty())
97 {
98 {
99 // NODEELEM
100 const unsigned int node_map[] = {0}; // identity
101 const unsigned short side_map[] = {0}; // identity
102 add_eletype_entry(NODEELEM, node_map, 1, side_map, 1);
103 }
104
105 {
106 // EDGE2
107 const unsigned int node_map[] = {0,1}; // identity
108 const unsigned short side_map[] = {0,1}; // identity
109 add_eletype_entry(EDGE2, node_map, 2, side_map, 2);
110 }
111
112 {
113 // TRI3
114 const unsigned int node_map[] = {0,1,2}; // identity
115 const unsigned short side_map[] = {0,1,2}; // identity
116 add_eletype_entry(TRI3, node_map, 3, side_map, 3);
117 }
118
119 {
120 // TRI6
121 const unsigned int node_map[] = {0,1,2,3,4,5}; // identity
122 const unsigned short side_map[] = {0,1,2}; // identity
123 add_eletype_entry(TRI6, node_map, 6, side_map, 3);
124 }
125
126 {
127 // QUAD4
128 const unsigned int node_map[] = {0,1,2,3}; // identity
129 const unsigned short side_map[] = {0,1,2,3}; // identity
130 add_eletype_entry(QUAD4, node_map, 4, side_map, 4);
131 }
132
133 {
134 // QUAD8
135 const unsigned int node_map[] = {0,1,2,3,4,5,6,7}; // identity
136 const unsigned short side_map[] = {0,1,2,3}; // identity
137 add_eletype_entry(QUAD8, node_map, 8, side_map, 4);
138 }
139
140 {
141 // TET4
142 const unsigned int node_map[] = {0,1,2,3}; // identity
143 const unsigned short side_map[] = {0,1,2,3}; // identity
144 add_eletype_entry(TET4, node_map, 4, side_map, 4);
145 }
146
147 {
148 // TET10
149 const unsigned int node_map[] = {0,1,2,3,4,5,6,7,8,9}; // identity
150 const unsigned short side_map[] = {0,1,2,3}; // identity
151 add_eletype_entry(TET10, node_map, 10, side_map, 4);
152 }
153
154 {
155 // HEX8
156 const unsigned int node_map[] = {0,1,2,3,4,5,6,7}; // identity
157 const unsigned short side_map[] = {0,5,1,2,3,4}; // inverse = 0,2,3,4,5,1
158 add_eletype_entry(HEX8, node_map, 8, side_map, 6);
159 }
160
161 {
162 // HEX20
163 const unsigned int node_map[] = // map is its own inverse
164 {0,1,2,3,4,5,6,7,8,9,10,11,16,17,18,19,12,13,14,15};
165 const unsigned short side_map[] = // inverse = 0,2,3,4,5,1
166 {0,5,1,2,3,4};
167 add_eletype_entry(HEX20, node_map, 20, side_map, 6);
168 }
169
170 {
171 // HEX27
172 const unsigned int node_map[] = // inverse = ...,21,23,24,25,26,22,20
173 {0,1,2,3,4,5,6,7,8,9,10,11,16,17,18,19,12,13,14,15,26,20,25,21,22,23,24};
174 const unsigned short side_map[] = // inverse = 0,2,3,4,5,1
175 {0,5,1,2,3,4};
176 add_eletype_entry(HEX27, node_map, 27, side_map, 6);
177 }
178
179 {
180 // PRISM6
181 const unsigned int node_map[] = {0,1,2,3,4,5}; // identity
182 const unsigned short side_map[] = {0,4,1,2,3}; // inverse = 0,2,3,4,1
183 add_eletype_entry(PRISM6, node_map, 6, side_map, 5);
184 }
185
186 {
187 // PRISM15
188 const unsigned int node_map[] = // map is its own inverse
189 {0,1,2,3,4,5,6,7,8,12,13,14,9,10,11};
190 const unsigned short side_map[] = // inverse = 0,2,3,4,1
191 {0,4,1,2,3};
192 add_eletype_entry(PRISM15, node_map, 15, side_map, 5);
193 }
194
195 {
196 // PRISM18
197 const unsigned int node_map[] = // map is its own inverse
198 {0,1,2,3,4,5,6,7,8,12,13,14,9,10,11,15,16,17};
199 const unsigned short side_map[] = // inverse = 0,2,3,4,1
200 {0,4,1,2,3};
201 add_eletype_entry(PRISM18, node_map, 18, side_map, 5);
202 }
203 } // if (eletypes.empty())
204}
205} // anonymous namespace
206
207
208
209
210
211namespace libMesh
212{
213
215 MeshInput<MeshBase> (mesh_in),
216 build_sidesets_from_nodesets(false),
217 _already_seen_part(false)
218{
219}
220
221
222
223AbaqusIO::~AbaqusIO () = default;
224
225
226
227void AbaqusIO::read (const std::string & fname)
228{
229 // Get a reference to the mesh we are reading
230 MeshBase & the_mesh = MeshInput<MeshBase>::mesh();
231
232 // Clear any existing mesh data
233 the_mesh.clear();
234
235 // Open stream for reading
236 const bool gzipped_file = Utility::ends_with(fname, ".gz");
237
238 if (gzipped_file)
239 {
240#ifdef LIBMESH_HAVE_GZSTREAM
241 auto inf = std::make_unique<igzstream>();
242 libmesh_assert(inf);
243 inf->open(fname.c_str(), std::ios::in);
244 _in = std::move(inf); // class takes ownership as base class pointer
245#else
246 libmesh_error_msg("ERROR: need gzstream to handle .gz files!!!");
247#endif
248 }
249 else
250 {
251 auto inf = std::make_unique<std::ifstream>();
252 libmesh_assert(inf);
253
254 std::string new_name = Utility::unzip_file(fname);
255 inf->open(new_name.c_str(), std::ios::in);
256 libmesh_assert(inf->good());
257 _in = std::move(inf); // class takes ownership as base class pointer
258 }
259
260 // Initialize the elems_of_dimension array. We will use this in a
261 // "1-based" manner so that elems_of_dimension[d]==true means
262 // elements of dimension d have been seen.
263 elems_of_dimension.resize(4, false);
264
265 // Read file line-by-line... this is based on a set of different
266 // test input files. I have not looked at the full input file
267 // specs for Abaqus.
268 std::string s;
269 while (true)
270 {
271 // Try to read something. This may set EOF!
272 std::getline(*_in, s);
273
274 if (*_in)
275 {
276 // Process s...
277 //
278 // There are many sections in Abaqus files, we read some
279 // but others are just ignored... Some sections may occur
280 // more than once. For example for a hybrid grid, you
281 // will have multiple *Element sections...
282
283 // Some Abaqus files use all upper-case for section names,
284 // so we will just convert s to uppercase
285 std::string upper(s);
286 std::transform(upper.begin(), upper.end(), upper.begin(), ::toupper);
287
288 // 0.) Look for the "*Part" section
289 if (upper.find("*PART") == static_cast<std::string::size_type>(0))
290 {
291 libmesh_error_msg_if
293 "We currently don't support reading Abaqus files with multiple PART sections");
294
295 _already_seen_part = true;
296 }
297
298 // 1.) Look for the "*Nodes" section
299 if (upper.find("*NODE") == static_cast<std::string::size_type>(0))
300 {
301 // Some sections that begin with *NODE are actually
302 // unrelated sections which we want to skip. I
303 // have only seen this with a single space, but it would
304 // probably be more robust to remove whitespace before
305 // making this check.
306 bool skip_this_section = false;
307 std::vector<std::string> keywords_to_ignore = {"OUTPUT", "PRINT", "FILE", "RESPONSE"};
308 for (auto & affix : keywords_to_ignore) {
309 std::stringstream keyword;
310 keyword << "*NODE " << affix;
311 if (upper.find(keyword.str()) == static_cast<std::string::size_type>(0))
312 skip_this_section = true;
313 }
314 if (skip_this_section)
315 continue;
316
317 // Some *Node sections also specify an Nset name on the same line.
318 // Look for one here.
319 std::string nset_name = this->parse_label(s, "nset");
320
321 // Process any lines of comments that may be present
323
324 // Read a block of nodes
325 this->read_nodes(nset_name);
326 }
327
328
329
330 // 2.) Look for the "*Element" section
331 else if (upper.find("*ELEMENT,") == static_cast<std::string::size_type>(0))
332 {
333 // Some sections that begin with *ELEMENT are actually
334 // "*ELEMENT OUTPUT" sections which we want to skip. I
335 // have only seen this with a single space, but it would
336 // probably be more robust to remove whitespace before
337 // making this check.
338 bool skip_this_section = false;
339 std::vector<std::string> keywords_to_ignore = {"OUTPUT", "MATRIX", "PROPERTIES", "RESPONSE"};
340 for (auto & affix : keywords_to_ignore) {
341 std::stringstream keyword;
342 keyword << "*ELEMENT " << affix;
343 if (upper.find(keyword.str()) == static_cast<std::string::size_type>(0))
344 skip_this_section = true;
345 }
346 if (skip_this_section)
347 continue;
348
349 // Some *Element sections also specify an Elset name on the same line.
350 // Look for one here.
351 std::string elset_name = this->parse_label(s, "elset");
352
353 // Process any lines of comments that may be present
355
356 // Read a block of elements
357 this->read_elements(upper, elset_name);
358 }
359
360
361
362 // 3.) Look for a Nodeset section
363 else if (upper.find("*NSET") == static_cast<std::string::size_type>(0))
364 {
365 std::string nset_name = this->parse_label(s, "nset");
366
367 // I haven't seen an unnamed nset yet, but let's detect it
368 // just in case...
369 libmesh_error_msg_if(nset_name == "", "Unnamed nset encountered!");
370
371 // Is this a "generated" nset, i.e. one which has three
372 // entries corresponding to (first, last, stride)?
373 bool is_generated = this->detect_generated_set(upper);
374
375 // Process any lines of comments that may be present
377
378 // Read the IDs, storing them in _nodeset_ids
379 if (is_generated)
380 this->generate_ids(nset_name, _nodeset_ids);
381 else
382 this->read_ids(nset_name, _nodeset_ids);
383 } // *Nodeset
384
385
386
387 // 4.) Look for an Elset section
388 else if (upper.find("*ELSET") == static_cast<std::string::size_type>(0))
389 {
390 std::string elset_name = this->parse_label(s, "elset");
391
392 // I haven't seen an unnamed elset yet, but let's detect it
393 // just in case...
394 libmesh_error_msg_if(elset_name == "", "Unnamed elset encountered!");
395
396 // Is this a "generated" elset, i.e. one which has three
397 // entries corresponding to (first, last, stride)?
398 bool is_generated = this->detect_generated_set(upper);
399
400 // Process any lines of comments that may be present
402
403 // Read the IDs, storing them in _elemset_ids
404 if (is_generated)
405 this->generate_ids(elset_name, _elemset_ids);
406 else
407 this->read_ids(elset_name, _elemset_ids);
408 } // *Elset
409
410
411
412 // 5.) Look for a Surface section. Need to be a little
413 // careful, since there are also "surface interaction"
414 // sections we don't want to read here.
415 else if (upper.find("*SURFACE,") == static_cast<std::string::size_type>(0))
416 {
417 // Get the name from the Name=Foo label. This will be the map key.
418 std::string sideset_name = this->parse_label(s, "name");
419
420 // Some (all?) surfaces also declare a type, which can
421 // help us figure out how they should be parsed, so we
422 // pass this to the read_sideset() function.
423 std::string sideset_type = this->parse_label(s, "type");
424
425 // Process any lines of comments that may be present
427
428 // Read the sideset IDs
429 this->read_sideset(sideset_name, sideset_type, _sideset_ids);
430 }
431
432 // Derived classes could override this to add support for additional sections
433 extended_parsing(upper);
434
435 continue;
436 } // if (*_in)
437
438 // If !file, check to see if EOF was set. If so, break out
439 // of while loop.
440 if (_in->eof())
441 break;
442
443 // If !in and !in.eof(), stream is in a bad state!
444 libmesh_error_msg("Stream is bad! Perhaps the file: " << fname << " does not exist?");
445 } // while
446
447 // Set the Mesh dimension based on the highest dimension element seen.
449
450 // Set element IDs based on the element sets.
451 this->assign_subdomain_ids();
452
453 // Assign nodeset values to the BoundaryInfo object
455
456 // Assign sideset values in the BoundaryInfo object
457 this->assign_sideset_ids();
458
459 // If the Abaqus file contains only nodesets, we can have libmesh
460 // generate sidesets from them. This BoundaryInfo function currently
461 // *overwrites* existing sidesets in surprising ways, so we don't
462 // call it if there are already sidesets present in the original file.
465
466 // Delete lower-dimensional elements from the Mesh. We assume these
467 // were only used for setting BCs, and aren't part of the actual
468 // Mesh.
469 {
470 unsigned char max_dim = this->max_elem_dimension_seen();
471
472 for (auto & elem : the_mesh.element_ptr_range())
473 if (elem->dim() < max_dim)
474 the_mesh.delete_elem(elem);
475 }
476}
477
478
479
480
481
482
483
484void AbaqusIO::read_nodes(std::string nset_name)
485{
486 // Get a reference to the mesh we are reading
487 MeshBase & the_mesh = MeshInput<MeshBase>::mesh();
488
489 // In the input files I have, Abaqus neither tells what
490 // the mesh dimension is nor how many nodes it has...
491 //
492 // The node line format is:
493 // id, x, y, z
494 // and you do have to parse out the commas.
495 // The z-coordinate will only be present for 3D meshes
496
497 // Temporary variables for parsing lines of text
498 char c;
499 std::string line;
500
501 // We need to duplicate some of the read_ids code if this *NODE
502 // section also defines an NSET. We'll set up the id_storage
503 // pointer and push back IDs into this vector in the loop below...
504 std::vector<dof_id_type> * id_storage = nullptr;
505 if (nset_name != "")
506 id_storage = &(_nodeset_ids[nset_name]);
507
508 // We will read nodes until the next line begins with *, since that will be the
509 // next section.
510 // TODO: Is Abaqus guaranteed to start the line with '*' or can there be leading white space?
511 while (_in->peek() != '*' && _in->peek() != EOF)
512 {
513 // Read an entire line which corresponds to a single point's id
514 // and (x,y,z) values.
515 std::getline(*_in, line);
516
517 // Remove all whitespace characters from the line. This way we
518 // can do the remaining parsing without worrying about tabs,
519 // different numbers of spaces, etc.
520 strip_ws(line);
521
522 // Make a stream out of the modified line so we can stream values
523 // from it in the usual way.
524 std::stringstream ss(line);
525
526 // Values to be read in from file
527 dof_id_type abaqus_node_id=0;
528 Real x=0, y=0, z=0;
529
530 // Note: we assume *at least* 2D points here, should we worry about
531 // trying to read 1D Abaqus meshes?
532 ss >> abaqus_node_id >> c >> x >> c >> y;
533
534 // Peek at the next character. If it is a comma, then there is another
535 // value to read!
536 if (ss.peek() == ',')
537 ss >> c >> z;
538
539 // If this *NODE section defines an NSET, also store the abaqus ID in id_storage
540 if (id_storage)
541 id_storage->push_back(abaqus_node_id);
542
543 // Convert from Abaqus 1-based to libMesh 0-based numbering
544 libmesh_error_msg_if(abaqus_node_id < 1,
545 "Invalid Abaqus node ID found");
546 const dof_id_type libmesh_node_id = abaqus_node_id-1;
547
548 libmesh_error_msg_if(the_mesh.query_node_ptr(libmesh_node_id),
549 "Duplicate Abaqus node ID found");
550
551 // Add the point to the mesh using libmesh's numbering,
552 // and post-increment the libmesh node counter.
553 the_mesh.add_point(Point(x,y,z), libmesh_node_id);
554 } // while
555}
556
557
558
559
560
561void AbaqusIO::read_elements(std::string upper, std::string elset_name)
562{
563 // Get a reference to the mesh we are reading
564 MeshBase & the_mesh = MeshInput<MeshBase>::mesh();
565
566 // initialize the eletypes map (eletypes is a file-global variable)
567 init_eletypes();
568
569 ElemType elem_type = INVALID_ELEM;
570 unsigned n_nodes_per_elem = 0;
571
572 // Within s, we should have "type=XXXX"
573 if (upper.find("T3D2") != std::string::npos ||
574 upper.find("B31") != std::string::npos)
575 {
576 elem_type = EDGE2;
577 n_nodes_per_elem = 2;
578 elems_of_dimension[1] = true;
579 }
580 else if (upper.find("B32") != std::string::npos)
581 {
582 elem_type = EDGE3;
583 n_nodes_per_elem = 3;
584 elems_of_dimension[1] = true;
585 }
586 else if (upper.find("S3") != std::string::npos ||
587 upper.find("CPE3") != std::string::npos ||
588 upper.find("2D3") != std::string::npos)
589 {
590 elem_type = TRI3;
591 n_nodes_per_elem = 3;
592 elems_of_dimension[2] = true;
593 }
594 else if (upper.find("CPE4") != std::string::npos ||
595 upper.find("S4") != std::string::npos ||
596 upper.find("CPEG4") != std::string::npos ||
597 upper.find("2D4") != std::string::npos)
598 {
599 elem_type = QUAD4;
600 n_nodes_per_elem = 4;
601 elems_of_dimension[2] = true;
602 }
603 else if (upper.find("CPE6") != std::string::npos ||
604 upper.find("S6") != std::string::npos ||
605 upper.find("CPEG6") != std::string::npos ||
606 upper.find("2D6") != std::string::npos)
607 {
608 elem_type = TRI6;
609 n_nodes_per_elem = 6;
610 elems_of_dimension[2] = true;
611 }
612 else if (upper.find("CPE8") != std::string::npos ||
613 upper.find("S8") != std::string::npos ||
614 upper.find("CPEG8") != std::string::npos ||
615 upper.find("2D8") != std::string::npos)
616 {
617 elem_type = QUAD8;
618 n_nodes_per_elem = 8;
619 elems_of_dimension[2] = true;
620 }
621 else if (upper.find("3D8") != std::string::npos)
622 {
623 elem_type = HEX8;
624 n_nodes_per_elem = 8;
625 elems_of_dimension[3] = true;
626 }
627 else if (upper.find("3D4") != std::string::npos)
628 {
629 elem_type = TET4;
630 n_nodes_per_elem = 4;
631 elems_of_dimension[3] = true;
632 }
633 else if (upper.find("3D20") != std::string::npos)
634 {
635 elem_type = HEX20;
636 n_nodes_per_elem = 20;
637 elems_of_dimension[3] = true;
638 }
639 else if (upper.find("3D27") != std::string::npos)
640 {
641 elem_type = HEX27;
642 n_nodes_per_elem = 27;
643 elems_of_dimension[3] = true;
644 }
645 else if (upper.find("3D6") != std::string::npos)
646 {
647 elem_type = PRISM6;
648 n_nodes_per_elem = 6;
649 elems_of_dimension[3] = true;
650 }
651 else if (upper.find("3D15") != std::string::npos)
652 {
653 elem_type = PRISM15;
654 n_nodes_per_elem = 15;
655 elems_of_dimension[3] = true;
656 }
657 else if (upper.find("3D10") != std::string::npos)
658 {
659 elem_type = TET10;
660 n_nodes_per_elem = 10;
661 elems_of_dimension[3] = true;
662 }
663 else if (upper.find("MASS") != std::string::npos)
664 {
665 // "MASS" elements are used to indicate point masses in Abaqus
666 // models. These are mapped to NODEELEMs in libmesh.
667 elem_type = NODEELEM;
668 n_nodes_per_elem = 1;
669 elems_of_dimension[0] = true;
670 }
671 else
672 libmesh_error_msg("Unrecognized element type: " << upper);
673
674 // Insert the elem type we detected into the set of all elem types for this mesh
675 _elem_types.insert(elem_type);
676
677 // Grab a reference to the element definition for this element type
678 const ElementDefinition & eledef = eletypes[elem_type];
679
680 // If the element definition was not found, the call above would have
681 // created one with an uninitialized struct. Check for that here...
682 libmesh_error_msg_if
683 (eledef.abaqus_zero_based_node_id_to_libmesh_node_id.size() == 0,
684 "No Abaqus->LibMesh mapping information for ElemType "
685 << Utility::enum_to_string(elem_type) << "!");
686
687 // We will read elements until the next line begins with *, since that will be the
688 // next section.
689 while (_in->peek() != '*' && _in->peek() != EOF)
690 {
691 // Read the element ID, it is the first number on each line. It is
692 // followed by a comma, so read that also. We will need this ID later
693 // when we try to assign subdomain IDs
694 dof_id_type abaqus_elem_id = 0;
695 char c;
696 *_in >> abaqus_elem_id >> c;
697
698 // Add an element of the appropriate type to the Mesh, with the
699 // abaqus element ID.
700 std::unique_ptr<Elem> new_elem = Elem::build(elem_type);
701 new_elem->set_id() = abaqus_elem_id-1;
702
703 Elem * elem = the_mesh.add_elem(std::move(new_elem));
704
705 // The count of the total number of IDs read for the current element.
706 unsigned id_count=0;
707
708 // Continue reading line-by-line until we have read enough nodes for this element
709 while (id_count < n_nodes_per_elem)
710 {
711 // Read entire line (up to carriage return) of comma-separated values
712 std::string csv_line;
713 std::getline(*_in, csv_line);
714
715 // Create a stream object out of the current line
716 std::stringstream line_stream(csv_line);
717
718 // Process the comma-separated values
719 std::string cell;
720 while (std::getline(line_stream, cell, ','))
721 {
722 dof_id_type abaqus_global_node_id;
723 bool success = string_to_num(cell, abaqus_global_node_id);
724
725 if (success)
726 {
727 // Map the id'th element ID (Abaqus 1-based numbering) to LibMesh numbering
728 libmesh_error_msg_if(abaqus_global_node_id < 1,
729 "Invalid Abaqus node ID found");
730 const dof_id_type libmesh_global_node_id = abaqus_global_node_id-1;
731
732 // Grab the node pointer from the mesh for this ID
733 Node * node = the_mesh.node_ptr(libmesh_global_node_id);
734
735 // If node_ptr() returns nullptr, it may mean we have not yet read the
736 // *Nodes section, though I assumed that always came before the *Elements section...
737 libmesh_error_msg_if
738 (node == nullptr,
739 "Error! Mesh::node_ptr() returned nullptr. Either no node exists with ID "
740 << libmesh_global_node_id
741 << " or perhaps this input file has *Elements defined before *Nodes?");
742
743 // Note: id_count is the zero-based abaqus (elem local) node index. We therefore map
744 // it to a libmesh elem local node index using the element definition map
745 unsigned libmesh_elem_local_node_id =
746 eledef.abaqus_zero_based_node_id_to_libmesh_node_id[id_count];
747
748 // Set this node pointer within the element.
749 elem->set_node(libmesh_elem_local_node_id, node);
750
751 // Increment the count of IDs read for this element
752 id_count++;
753 } // end if (success)
754 } // end while getline(',')
755 } // end while (id_count)
756
757 // Ensure that we read *exactly* as many nodes as we were expecting to, no more.
758 libmesh_error_msg_if
759 (id_count != n_nodes_per_elem,
760 "Error: Needed to read "
761 << n_nodes_per_elem
762 << " nodes, but read "
763 << id_count
764 << " instead!");
765
766 // If we are recording Elset IDs, add this element to the correct set for later processing.
767 // Make sure to add it with the Abaqus ID, not the libmesh one!
768 if (elset_name != "")
769 _elemset_ids[elset_name].push_back(abaqus_elem_id);
770 } // end while (peek)
771}
772
773
774
775
776std::string AbaqusIO::parse_label(std::string line, std::string label_name) const
777{
778 // Handle files which have weird line endings from e.g. windows.
779 // You can check what kind of line endings you have with 'cat -vet'.
780 // For example, some files may have two kinds of line endings like:
781 //
782 // 4997,^I496,^I532,^I487,^I948^M$
783 //
784 // and we don't want to deal with this when extracting a label, so
785 // just remove all the space characters, which should include all
786 // kinds of remaining newlines. (I don't think Abaqus allows
787 // whitespace in label names.)
788 strip_ws(line);
789
790 // Do all string comparisons in upper-case
791 std::string
792 upper_line(line),
793 upper_label_name(label_name);
794 std::transform(upper_line.begin(), upper_line.end(), upper_line.begin(), ::toupper);
795 std::transform(upper_label_name.begin(), upper_label_name.end(), upper_label_name.begin(), ::toupper);
796
797 // Get index of start of "label="
798 size_t label_index = upper_line.find(upper_label_name + "=");
799
800 if (label_index != std::string::npos)
801 {
802 // Location of the first comma following "label="
803 size_t comma_index = upper_line.find(",", label_index);
804
805 // Construct iterators from which to build the sub-string.
806 // Note: The +1 while initializing beg is to skip past the "=" which follows the label name
807 std::string::iterator
808 beg = line.begin() + label_name.size() + 1 + label_index,
809 end = (comma_index == std::string::npos) ? line.end() : line.begin() + comma_index;
810
811 return std::string(beg, end);
812 }
813
814 // The label index was not found, return the empty string
815 return std::string("");
816}
817
818
819
820
821bool AbaqusIO::detect_generated_set(std::string upper) const
822{
823 // Avoid issues with weird line endings, spaces before commas, etc.
824 strip_ws(upper);
825
826 // Check each comma-separated value in "upper" to see if it is the generate flag.
827 std::string cell;
828 std::stringstream line_stream(upper);
829 while (std::getline(line_stream, cell, ','))
830 if (cell == "GENERATE")
831 return true;
832
833 return false;
834}
835
836
837
838void AbaqusIO::read_ids(std::string set_name, container_t & container)
839{
840 // Grab a reference to a vector that will hold all the IDs
841 std::vector<dof_id_type> & id_storage = container[set_name];
842
843 // Read until the start of another section is detected, or EOF is encountered
844 while (_in->peek() != '*' && _in->peek() != EOF)
845 {
846 // Read entire comma-separated line into a string
847 std::string csv_line;
848 std::getline(*_in, csv_line);
849
850 // On that line, use std::getline again to parse each
851 // comma-separated entry.
852 std::string cell;
853 std::stringstream line_stream(csv_line);
854 while (std::getline(line_stream, cell, ','))
855 {
856 dof_id_type id;
857 bool success = string_to_num(cell, id);
858
859 // Note that lists of comma-separated values in abaqus also
860 // *end* with a comma, so the last call to getline on a given
861 // line will get an empty string, which we must detect.
862 if (success)
863 id_storage.push_back(id);
864 }
865 }
866}
867
868
869
870
871void AbaqusIO::generate_ids(std::string set_name, container_t & container)
872{
873 // Grab a reference to a vector that will hold all the IDs
874 std::vector<dof_id_type> & id_storage = container[set_name];
875
876 // Read until the start of another section is detected, or EOF is
877 // encountered. "generate" sections seem to only have one line,
878 // although I suppose it's possible they could have more.
879 while (_in->peek() != '*' && _in->peek() != EOF)
880 {
881 // Read entire comma-separated line into a string
882 std::string csv_line;
883 std::getline(*_in, csv_line);
884
885 // Remove all whitespaces from csv_line.
886 strip_ws(csv_line);
887
888 // Create a new stringstream object from the string, and stream
889 // in the comma-separated values.
890 char c;
891 dof_id_type start, end, stride;
892 std::stringstream line_stream(csv_line);
893 line_stream >> start >> c >> end >> c >> stride;
894
895 // Generate entries in the id_storage. Note: each element can
896 // only belong to a single Elset (since this corresponds to the
897 // subdomain_id) so if an element appears in multiple Elsets,
898 // the "last" one (alphabetically, based on set name) in the
899 // _elemset_ids map will "win".
900 for (dof_id_type current = start; current <= end; current += stride)
901 id_storage.push_back(current);
902 }
903}
904
905
906
907
908void AbaqusIO::read_sideset(const std::string & sideset_name,
909 const std::string & sideset_type,
910 sideset_container_t & container)
911{
912 // Grab a reference to a vector that will hold all the IDs
913 std::vector<std::pair<dof_id_type, unsigned>> & id_storage = container[sideset_name];
914
915 // Variables for storing values read in from file
916 unsigned side_id=0;
917 char c;
918 std::string elem_id_or_set, dummy;
919
920 // Read until the start of another section is detected, or EOF is encountered
921 while (_in->peek() != '*' && _in->peek() != EOF)
922 {
923 // Read first string up to and including the comma, which is discarded.
924 std::getline(*_in, elem_id_or_set, ',');
925
926 // Strip any leading or trailing trailing whitespace from this
927 // string, since some Abaqus files may have this.
928 strip_ws(elem_id_or_set);
929
930 // Handle sidesets of type "NODE"
931 if (sideset_type == "NODE")
932 {
933 // Create a sideset from a nodeset. This is not currently
934 // supported, so print a warning and keep going.
935 if (_nodeset_ids.count(elem_id_or_set))
936 libMesh::out << "Warning: skipped creating a sideset from a "
937 << "nodeset, this is not yet supported."
938 << std::endl;
939 }
940
941 // Otherwise, we assume it's a sideset of the form: "391, S2" or "Elset_1, S3".
942 // If it's not one of these forms, we'll throw an error instead
943 // of letting the stream get into a bad state.
944 else // sideset_type != "NODE"
945 {
946 // Read the character "S", followed by the side id. Note: the >> operator
947 // eats whitespace until it reaches a valid character, so this should work
948 // whether or not there is a space after the previous comma.
949 *_in >> c >> side_id;
950
951 // Try to convert first string to an integer.
952 dof_id_type elem_id;
953 bool success = string_to_num(elem_id_or_set, elem_id);
954
955 if (success)
956 {
957 // if the side set is of the form of "391, S2"
958 id_storage.emplace_back(elem_id, side_id);
959 }
960
961 if (!success)
962 {
963 // if the side set is of the form of "Elset_1, S3"
964 const auto & vec = libmesh_map_find(_elemset_ids, elem_id_or_set);
965 for (const auto & elem_id_in_elset : vec)
966 id_storage.emplace_back(elem_id_in_elset, side_id);
967 }
968 }
969
970 // Successful or not, we extract the remaining characters on the
971 // line, including the newline, to (hopefully) go to the next section.
972 std::getline(*_in, dummy);
973 } // while
974}
975
976
977
978
980{
981 // Get a reference to the mesh we are reading
982 MeshBase & the_mesh = MeshInput<MeshBase>::mesh();
983
984 // The number of elemsets we've found while reading
985 std::size_t n_elemsets = _elemset_ids.size();
986
987 // Fill in a temporary map with (ElemType, index) pairs based on the _elem_types set. This
988 // will allow us to easily look up this index in the loop below.
989 std::map<ElemType, unsigned> elem_types_map;
990 {
991 unsigned ctr=0;
992 for (const auto & type : _elem_types)
993 elem_types_map[type] = ctr++;
994 }
995
996 // Loop over each Elemset and assign subdomain IDs to Mesh elements
997 {
998 // The maximum element dimension seen while reading the Mesh
999 unsigned char max_dim = this->max_elem_dimension_seen();
1000
1001 // The elemset_id counter assigns a logical numbering to the _elemset_ids keys
1002 container_t::iterator it = _elemset_ids.begin(), end = _elemset_ids.end();
1003 for (unsigned elemset_id=0; it != end; ++it, ++elemset_id)
1004 {
1005 // Grab a reference to the vector of IDs
1006 std::vector<dof_id_type> & id_vector = it->second;
1007
1008 // Loop over this vector
1009 for (const auto & id : id_vector)
1010 {
1011 // Map the id'th element ID (Abaqus 1-based numbering) to LibMesh numbering
1012 libmesh_error_msg_if(id < 1,
1013 "Invalid Abaqus element ID found");
1014 const dof_id_type libmesh_elem_id = id-1;
1015
1016 // Get reference to that element
1017 Elem & elem = the_mesh.elem_ref(libmesh_elem_id);
1018
1019 // We won't assign subdomain ids to lower-dimensional
1020 // elements, as they are assumed to represent boundary
1021 // conditions. Since lower-dimensional elements can
1022 // appear in multiple sidesets, it doesn't make sense to
1023 // assign them a single subdomain id... only the last one
1024 // assigned would actually "stick".
1025 if (elem.dim() < max_dim)
1026 break;
1027
1028 // Compute the proper subdomain ID, based on the formula in the
1029 // documentation for this function.
1030 subdomain_id_type computed_id = cast_int<subdomain_id_type>
1031 (elemset_id + (elem_types_map[elem.type()] * n_elemsets));
1032
1033 // Assign this ID to the element in question
1034 elem.subdomain_id() = computed_id;
1035
1036 // We will also assign a unique name to the computed_id,
1037 // which is created by appending the geometric element
1038 // name to the elset name provided by the user in the
1039 // Abaqus file.
1040 std::string computed_name = it->first + "_" + Utility::enum_to_string(elem.type());
1041 the_mesh.set_subdomain_name(computed_id, computed_name);
1042 }
1043 }
1044 }
1045}
1046
1047
1048
1049
1051{
1052 // Get a reference to the mesh we are reading
1053 MeshBase & the_mesh = MeshInput<MeshBase>::mesh();
1054
1055 // Iterate over the container of nodesets
1056 container_t::iterator it = _nodeset_ids.begin(), end = _nodeset_ids.end();
1057 for (boundary_id_type current_id=0; it != end; ++it, ++current_id)
1058 {
1059 // Associate current_id with the name we determined earlier
1060 the_mesh.get_boundary_info().nodeset_name(current_id) = it->first;
1061
1062 // Get a reference to the current vector of nodeset ID values
1063 std::vector<dof_id_type> & nodeset_ids = it->second;
1064
1065 for (const auto & id : nodeset_ids)
1066 {
1067 // Map the id'th element ID (Abaqus 1-based numbering) to LibMesh numbering
1068 libmesh_error_msg_if(id < 1,
1069 "Invalid Abaqus node ID found");
1070 const dof_id_type libmesh_global_node_id = id-1;
1071
1072 // Get node pointer from the mesh
1073 Node * node = the_mesh.node_ptr(libmesh_global_node_id);
1074
1075 libmesh_error_msg_if(node == nullptr,
1076 "Error! Mesh::node_ptr() returned nullptr!");
1077
1078 // Add this node with the current_id (which is determined by the
1079 // alphabetical ordering of the map) to the BoundaryInfo object
1080 the_mesh.get_boundary_info().add_node(node, current_id);
1081 }
1082 }
1083}
1084
1085
1086
1087
1089{
1090 // Get a reference to the mesh we are reading
1091 MeshBase & the_mesh = MeshInput<MeshBase>::mesh();
1092
1093 // initialize the eletypes map (eletypes is a file-global variable)
1094 init_eletypes();
1095
1096 // Iterate over the container of sidesets
1097 {
1098 sideset_container_t::iterator it = _sideset_ids.begin(), end = _sideset_ids.end();
1099 for (boundary_id_type current_id=0; it != end; ++it, ++current_id)
1100 {
1101 // Associate current_id with the name we determined earlier
1102 the_mesh.get_boundary_info().sideset_name(current_id) = it->first;
1103
1104 // Get a reference to the current vector of nodeset ID values
1105 std::vector<std::pair<dof_id_type,unsigned>> & sideset_ids = it->second;
1106
1107 for (const auto & [abaqus_elem_id, abaqus_side_number] : sideset_ids)
1108 {
1109 // Map the id'th element ID (Abaqus 1-based numbering) to LibMesh numbering
1110 libmesh_error_msg_if(abaqus_elem_id < 1,
1111 "Invalid Abaqus element ID found");
1112 const dof_id_type libmesh_elem_id = abaqus_elem_id-1;
1113
1114 // Get a reference to that element
1115 Elem & elem = the_mesh.elem_ref(libmesh_elem_id);
1116
1117 // Grab a reference to the element definition for this element type
1118 const ElementDefinition & eledef = eletypes[elem.type()];
1119
1120 // If the element definition was not found, the call above would have
1121 // created one with an uninitialized struct. Check for that here...
1122 libmesh_error_msg_if(eledef.abaqus_zero_based_side_id_to_libmesh_side_id.size() == 0,
1123 "No Abaqus->LibMesh mapping information for ElemType "
1124 << Utility::enum_to_string(elem.type()) << "!");
1125
1126 // Add this node with the current_id (which is determined by the
1127 // alphabetical ordering of the map). Side numbers in Abaqus are 1-based,
1128 // so we subtract 1 here before passing the abaqus side number to the
1129 // mapping array
1130 the_mesh.get_boundary_info().add_side
1131 (&elem,
1132 eledef.abaqus_zero_based_side_id_to_libmesh_side_id[abaqus_side_number-1],
1133 current_id);
1134 }
1135 }
1136 }
1137
1138
1139 // Some elsets (if they contain lower-dimensional elements) also
1140 // define sidesets. So loop over them and build a searchable data
1141 // structure we can use to assign sidesets.
1142 {
1143 unsigned char max_dim = this->max_elem_dimension_seen();
1144
1145 // multimap from lower-dimensional-element-hash-key to
1146 // pair(lower-dimensional-element, boundary_id). The
1147 // lower-dimensional element is used to verify the results of the
1148 // hash table search. The boundary_id will be used to set a
1149 // boundary ID on a higher-dimensional element. We use a multimap
1150 // because the lower-dimensional elements can belong to more than
1151 // 1 sideset, and multiple lower-dimensional elements can hash to
1152 // the same value, but this is very rare.
1153 std::unordered_multimap<dof_id_type, std::pair<Elem *, boundary_id_type>> provide_bcs;
1154
1155 // The elemset_id counter assigns a logical numbering to the
1156 // _elemset_ids keys. We are going to use these ids as boundary
1157 // ids, so elemset_id is of type boundary_id_type.
1158 container_t::iterator it = _elemset_ids.begin(), end = _elemset_ids.end();
1159 for (boundary_id_type elemset_id=0; it != end; ++it, ++elemset_id)
1160 {
1161 // Grab a reference to the vector of IDs
1162 std::vector<dof_id_type> & id_vector = it->second;
1163
1164 // Loop over this vector
1165 for (const auto & id : id_vector)
1166 {
1167 // Map the id'th element ID (Abaqus 1-based numbering) to LibMesh numbering
1168 libmesh_error_msg_if(id < 1,
1169 "Invalid Abaqus element ID found");
1170 const dof_id_type libmesh_elem_id = id-1;
1171
1172 // Get a reference to that element
1173 Elem & elem = the_mesh.elem_ref(libmesh_elem_id);
1174
1175 // If the element dimension is equal to the maximum
1176 // dimension seen, we can break out of this for loop --
1177 // this elset does not contain sideset information.
1178 if (elem.dim() == max_dim)
1179 break;
1180
1181 // If the element dimension is zero, this elset contains
1182 // NodeElems which AFAIK are not used for sidesets, they
1183 // are only used to define point masses.
1184 if (elem.dim() == 0)
1185 break;
1186
1187 // We can only handle elements that are *exactly*
1188 // one dimension lower than the max element
1189 // dimension. Not sure if "edge" BCs in 3D
1190 // actually make sense/are required...
1191 libmesh_error_msg_if(elem.dim()+1 != max_dim,
1192 "ERROR: Expected boundary element of dimension " << max_dim-1 << " but got " << elem.dim());
1193
1194 // Insert the current (key, pair(elem,id)) into the multimap.
1195 provide_bcs.emplace(elem.key(), std::make_pair(&elem, elemset_id));
1196
1197 // Associate the name of this sideset with the ID we've
1198 // chosen. It's not necessary to do this for every
1199 // element in the set, but it's convenient to do it here
1200 // since we have all the necessary information...
1201 the_mesh.get_boundary_info().sideset_name(elemset_id) = it->first;
1202 }
1203 }
1204
1205 // Loop over elements and try to assign boundary information
1206 for (auto & elem : the_mesh.active_element_ptr_range())
1207 if (elem->dim() == max_dim)
1208 for (auto sn : elem->side_index_range())
1209 {
1210 // This is a max-dimension element that may require BCs.
1211 // For each of its sides, including internal sides, we'll
1212 // see if a lower-dimensional element provides boundary
1213 // information for it. Note that we have not yet called
1214 // find_neighbors(), so we can't use elem->neighbor(sn) in
1215 // this algorithm...
1216 auto bounds = provide_bcs.equal_range (elem->key(sn));
1217
1218 // Add boundary information for each side in the range.
1219 for (const auto & pr : as_range(bounds))
1220 {
1221 // We'll need to compare the lower dimensional element against the current side.
1222 std::unique_ptr<Elem> side (elem->build_side_ptr(sn));
1223
1224 // Extract the relevant data. We don't need the key for anything.
1225 Elem * lower_dim_elem = pr.second.first;
1226 boundary_id_type bid = pr.second.second;
1227
1228 // This was a hash, so it might not be perfect. Let's verify...
1229 if (*lower_dim_elem == *side)
1230 the_mesh.get_boundary_info().add_side(elem, sn, bid);
1231 }
1232 }
1233 }
1234}
1235
1236
1237
1239{
1240 std::string dummy;
1241 while (true)
1242 {
1243 // We assume we are at the beginning of a line that may be
1244 // comments or may be data. We need to only discard the line if
1245 // it begins with **, but we must avoid calling std::getline()
1246 // since there's no way to put that back.
1247 if (_in->peek() == '*')
1248 {
1249 // The first character was a star, so actually read it from the stream.
1250 _in->get();
1251
1252 // Peek at the next character...
1253 if (_in->peek() == '*')
1254 {
1255 // OK, second character was star also, by definition this
1256 // line must be a comment! Read the rest of the line and discard!
1257 std::getline(*_in, dummy);
1258 }
1259 else
1260 {
1261 // The second character was _not_ a star, so put back the first star
1262 // we pulled out so that the line can be parsed correctly by somebody
1263 // else!
1264 _in->unget();
1265
1266 // Finally, break out of the while loop, we are done parsing comments
1267 break;
1268 }
1269 }
1270 else
1271 {
1272 // First character was not *, so this line must be data! Break out of the
1273 // while loop!
1274 break;
1275 }
1276 }
1277}
1278
1279
1280
1282{
1283 unsigned char max_dim = 0;
1284
1285 unsigned char elem_dimensions_size = cast_int<unsigned char>
1286 (elems_of_dimension.size());
1287 // The elems_of_dimension array is 1-based in the UNV reader
1288 for (unsigned char i=1; i<elem_dimensions_size; ++i)
1289 if (elems_of_dimension[i])
1290 max_dim = i;
1291
1292 return max_dim;
1293}
1294
1295bool AbaqusIO::string_to_num(const std::string &input, dof_id_type &output) const
1296{
1297 char *endptr;
1298 output = cast_int<dof_id_type>(std::strtol(input.c_str(), &endptr, /*base=*/10));
1299
1300 return (output != 0 || endptr != input.c_str());
1301}
1302
1303void AbaqusIO::strip_ws(std::string &line) const
1304{
1305 line.erase(std::remove_if(line.begin(), line.end(),
1306 [](unsigned char const c)
1307 { return std::isspace(c); }),
1308 line.end());
1309}
1310
1311} // namespace
void read_sideset(const std::string &sideset_name, const std::string &sideset_type, sideset_container_t &container)
This function reads a sideset from the input file.
Definition abaqus_io.C:908
virtual ~AbaqusIO()
Destructor.
std::map< std::string, std::vector< std::pair< dof_id_type, unsigned > > > sideset_container_t
Type of the data structure for storing the (elem ID, side) pairs defining sidesets.
Definition abaqus_io.h:80
std::map< std::string, std::vector< dof_id_type > > container_t
The type of data structure used to store Node and Elemset IDs.
Definition abaqus_io.h:73
container_t _elemset_ids
Definition abaqus_io.h:201
void assign_subdomain_ids()
This function is called after all the elements have been read and assigns element subdomain IDs.
Definition abaqus_io.C:979
void process_and_discard_comments()
Any of the various sections can start with some number of lines of comments, which start with "**".
Definition abaqus_io.C:1238
std::set< ElemType > _elem_types
A set of the different geometric element types detected when reading the mesh.
Definition abaqus_io.h:213
void assign_boundary_node_ids()
This function assigns boundary IDs to node sets based on the alphabetical order in which the sets are...
Definition abaqus_io.C:1050
virtual void extended_parsing(const std::string &)
Override this callback to implement support for more sections in derived classes.
Definition abaqus_io.h:241
virtual void read(const std::string &name) override
This method implements reading a mesh from a specified file.
Definition abaqus_io.C:227
std::unique_ptr< std::istream > _in
Stream object used to interact with the file.
Definition abaqus_io.h:207
void assign_sideset_ids()
Called at the end of the read() function, assigns any sideset IDs found when reading the file to the ...
Definition abaqus_io.C:1088
std::string parse_label(std::string line, std::string label_name) const
This function parses a label of the form foo=bar from a comma-delimited line of the form ....
Definition abaqus_io.C:776
bool build_sidesets_from_nodesets
Default false.
Definition abaqus_io.h:67
void read_nodes(std::string nset_name)
This function parses a block of nodes in the Abaqus file once such a block has been found.
Definition abaqus_io.C:484
bool string_to_num(const std::string &input, dof_id_type &output) const
Attempts to convert the input string to a numerical value using strtol.
Definition abaqus_io.C:1295
bool detect_generated_set(std::string upper) const
Definition abaqus_io.C:821
unsigned char max_elem_dimension_seen()
Definition abaqus_io.C:1281
void strip_ws(std::string &line) const
Removes all whitespace characters from "line".
Definition abaqus_io.C:1303
container_t _nodeset_ids
Abaqus writes nodesets and elemsets with labels.
Definition abaqus_io.h:200
bool _already_seen_part
This flag gets set to true after the first "*PART" section we see.
Definition abaqus_io.h:221
void read_ids(std::string set_name, container_t &container)
This function reads all the IDs for the current node or element set of the given name,...
Definition abaqus_io.C:838
AbaqusIO(MeshBase &mesh)
Constructor.
Definition abaqus_io.C:214
void read_elements(std::string upper, std::string elset_name)
This function parses a block of elements in the Abaqus file.
Definition abaqus_io.C:561
sideset_container_t _sideset_ids
Definition abaqus_io.h:202
void generate_ids(std::string set_name, container_t &container)
This function handles "generated" nset and elset sections.
Definition abaqus_io.C:871
std::string & sideset_name(boundary_id_type id)
void build_side_list_from_node_list(const std::set< boundary_id_type > &nodeset_list={})
Adds sides to a sideset if every node on that side are in the same sideset.
std::size_t n_boundary_conds() const
std::string & nodeset_name(boundary_id_type id)
void add_node(const Node *node, const boundary_id_type id)
Add Node node with boundary id id to the boundary information data structures.
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
Add side side of element number elem with boundary id id to the boundary information data structure.
This is the base class from which all geometric element types are derived.
Definition elem.h:96
virtual Node *& set_node(const unsigned int i)
Definition elem.h:2567
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition elem.C:442
virtual unsigned short dim() const =0
subdomain_id_type subdomain_id() const
Definition elem.h:2591
virtual dof_id_type key(const unsigned int s) const =0
virtual ElemType type() const =0
This is the MeshBase class.
Definition mesh_base.h:81
void set_subdomain_name(subdomain_id_type id, const std::string &name, bool synchronous=false)
Sets the name for the provided id.
Definition mesh_base.C:1914
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition mesh_base.h:170
virtual const Node * node_ptr(const dof_id_type i) const =0
virtual void delete_elem(Elem *e)=0
Removes element e from the mesh.
virtual const Node * query_node_ptr(const dof_id_type i) const =0
void set_mesh_dimension(unsigned char d)
Resets the logical dimension of the mesh.
Definition mesh_base.h:423
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
Add a new Node at Point p to the end of the vertex array, with processor_id procid.
virtual void clear()
Deletes all the element and node data that is currently stored.
Definition mesh_base.C:1036
virtual Elem * add_elem(Elem *e)=0
Add elem e to the end of the element array.
virtual const Elem & elem_ref(const dof_id_type i) const
Definition mesh_base.h:788
This class defines an abstract interface for Mesh input.
Definition mesh_input.h:49
std::vector< bool > elems_of_dimension
A vector of bools describing what dimension elements have been encountered when reading a mesh.
Definition mesh_input.h:107
A Node is like a Point, but with more information.
Definition node.h:55
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
std::string enum_to_string(const T e)
bool ends_with(std::string_view superstring, std::string_view suffix)
Look for a substring at the very end of a string.
Definition utility.C:213
std::string unzip_file(std::string_view name)
Create an unzipped copy of a bz2 or xz file, returning the name of the now-unzipped file that can be ...
Definition utility.C:164
The libMesh namespace provides an interface to certain functionality in the library.
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
ElemType
Defines an enum for geometric element types.
int8_t boundary_id_type
Definition id_types.h:51
libmesh_assert(ctx)
OStreamProxy out
uint8_t dof_id_type
Definition id_types.h:67
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const boundary_id_type side_id