29 #if defined(_MSC_VER) && !defined(_USE_MATH_DEFINES) 30 # define _USE_MATH_DEFINES 44 #include <jsoncons/json.hpp> 45 #include <jsoncons_ext/csv/csv.hpp> 57 constexpr std::array<std::tuple<VTKCellType, const char *, int>, 5> vtk2inp{
58 {{VTK_TRIANGLE,
"CPS3", 2},
59 {VTK_QUAD,
"CPS4", 2},
60 {VTK_TETRA,
"C3D4", 3},
61 {VTK_HEXAHEDRON,
"C3D8", 3},
62 {VTK_WEDGE,
"C3D6", 3}}};
64 int inpSide2vtkSide(VTKCellType
cellType,
int inpSide) {
84 default:
return inpSide - 1;
88 int vtkSide2inpSide(VTKCellType cellType,
int vtkSide) {
108 default:
return vtkSide + 1;
114 InpParser() =
default;
121 Elem(vtkIdType
id, VTKCellType cellType, std::vector<vtkIdType>
points)
122 : id(id), cellType(cellType), points(
std::move(points)) {}
123 Elem(vtkIdType
id, VTKCellType cellType)
124 : id(id), cellType(cellType),
points() {}
139 std::vector<std::pair<vtkIdType, std::vector<double>>>
nodes{};
144 std::map<std::string, std::vector<Elem>>
elems{};
148 std::map<std::string, std::vector<vtkIdType>>
elSets{};
152 std::map<std::string, std::vector<vtkIdType>>
nodeSets{};
157 std::map<std::string, std::vector<std::pair<vtkIdType, int>>>
surfaces{};
164 void parseLine(
const std::string &line) {
165 if (line.at(0) ==
'*') {
166 if (!this->setState(parseKwLine(line))) {
171 bool continueNextLine{
false};
172 auto parsed = parseCSV(line);
173 for (
const auto &field : parsed[0].array_range()) {
174 if (field.is_null()) {
175 continueNextLine =
true;
177 }
else if (field.is_array()) {
179 field[1].as_string());
181 this->
currKwParams.emplace(field.as_string(), std::string{});
188 if (this->
currKw == Keyword::UNKNOWN) {
return; }
189 jsoncons::json parsed = parseCSV(line);
190 auto parsedRange = parsed.array_range();
191 auto iter = parsedRange.begin();
192 bool continueNextLine = (parsedRange.end() - 1)->is_null();
193 if (this->
currKw == Keyword::NODE) {
195 this->
currId = (iter++)->as<vtkIdType>();
199 this->
mesh_.nodes.emplace_back();
200 auto &back = this->
mesh_.nodes.back();
201 back.first = this->
currId;
202 back.second.reserve(3);
204 auto &back = this->
mesh_.nodes.back();
205 for (; iter != parsedRange.end(); ++iter) {
206 if (!iter->is_null()) {
207 back.second.emplace_back(iter->as_double());
210 }
else if (this->
currKw == Keyword::ELEMENT) {
214 this->
currId = (iter++)->as<vtkIdType>();
217 auto &back = elSet.back();
218 for (; iter != parsedRange.end(); ++iter) {
219 if (!iter->is_null()) {
220 back.points.emplace_back(iter->as<vtkIdType>());
224 }
else if (this->
currKw == Keyword::SURFACE) {
227 iterType->second !=
"ELEMENT") {
229 auto iterElSet = this->
mesh_.elSets.find(iter->as_string());
230 if (iterElSet != this->
mesh_.elSets.end()) {
232 auto side = std::stoi(iter->as_string().substr(1));
233 for (
const auto &elem : iterElSet->second) {
234 surf.emplace_back(elem, side);
237 auto elem = (iter++)->as<vtkIdType>();
238 auto side = std::stoi(iter->as_string().substr(1));
239 surf.emplace_back(elem, side);
242 }
else if (this->
currKw == Keyword::NSET ||
243 this->
currKw == Keyword::ELSET) {
244 auto iterGenerate = this->
currKwParams.find(
"GENERATE");
245 auto &
set = this->
currKw == Keyword::NSET
249 auto lower = (iter++)->as<vtkIdType>();
250 auto upper = (iter++)->as<vtkIdType>();
251 auto stride = iter == parsedRange.end() ? 1 : (iter++)->as<int>();
252 for (
auto i = lower; i <= upper; i += stride) {
256 for (; iter != parsedRange.end(); ++iter) {
257 if (!iter->is_null()) {
259 this->
currKw == Keyword::NSET
260 ? this->
mesh_.nodeSets.find(iter->as_string())
261 : this->
mesh_.elSets.find(iter->as_string());
262 if (iterOtherSet != (this->
currKw == Keyword::NSET
263 ? this->
mesh_.nodeSets.end()
264 : this->
mesh_.elSets.end())) {
265 set.insert(
set.end(), iterOtherSet->second.begin(),
266 iterOtherSet->second.end());
268 set.emplace_back(iter->as<vtkIdType>());
283 Mesh &getMesh() {
return mesh_; }
310 enum class Continue {
NONE, KEYWORD, DATA };
334 static std::tuple<Keyword, std::map<std::string, std::string>,
bool>
335 parseKwLine(
const std::string &line) {
336 static constexpr std::array<std::pair<Keyword, const char *>, 5> matchArr{
337 {{Keyword::NODE,
"*NODE"},
338 {Keyword::ELEMENT,
"*ELEMENT"},
339 {Keyword::ELSET,
"*ELSET"},
340 {Keyword::NSET,
"*NSET"},
341 {Keyword::SURFACE,
"*SURFACE"}}};
342 if (line.rfind(
"**", 0) == 0) {
343 return {Keyword::COMMENT, {},
false};
345 Keyword kw{Keyword::UNKNOWN};
346 std::map<std::string, std::string> params{};
347 auto parsed = parseCSV(line);
348 auto range = parsed.array_range();
349 auto iter = range.begin();
350 auto keywordStr = iter->as_string();
352 for (
const auto &match : matchArr) {
353 if (keywordStr == match.second) {
359 bool continueNextLine = (range.end() - 1)->is_null();
360 for (; iter != range.end(); ++iter) {
361 if (!iter->is_null()) {
362 if (iter->is_array()) {
363 auto key = iter->at(0).as_string();
365 params.emplace(std::move(key), iter->at(1).as_string());
367 auto key = iter->as_string();
369 params[std::move(key)];
373 return {kw, std::move(params), continueNextLine};
383 std::tuple<Keyword, std::map<std::string, std::string>,
bool> kwLine) {
384 if (std::get<0>(kwLine) != Keyword::COMMENT) {
385 this->
currKw = std::get<0>(kwLine);
389 if (this->
currKw == Keyword::ELEMENT) {
390 auto &cellTypeStr = this->currKwParams.at(
"TYPE");
391 auto cellTypeIter = std::find_if(
392 vtk2inp.begin(), vtk2inp.end(),
393 [&cellTypeStr](
const decltype(vtk2inp)::value_type &x) {
394 auto cellType = std::get<1>(x);
396 cellType, cellType + std::char_traits<char>::length(cellType),
397 cellTypeStr.begin());
399 if (cellTypeIter != vtk2inp.end()) {
400 auto dim = std::get<2>(*cellTypeIter);
402 if (this->
mesh_.maxDim < dim) {
403 this->
mesh_.maxDim = dim;
404 this->
mesh_.elems.clear();
405 }
else if (this->
mesh_.maxDim > dim) {
410 std::cerr <<
"Unsupported cell type " << cellTypeStr <<
'\n';
412 auto elSetParamIter = this->currKwParams.find(
"ELSET");
413 if (elSetParamIter != this->currKwParams.end()) {
414 this->
currSet = elSetParamIter->second;
417 if (this->
currKw == Keyword::ELSET) {
418 this->
currSet = this->currKwParams.at(
"ELSET");
420 if (this->
currKw == Keyword::NODE) {
421 auto nsetIter = this->currKwParams.find(
"NSET");
422 if (nsetIter != this->currKwParams.end()) {
423 this->
currSet = nsetIter->second;
426 if (this->
currKw == Keyword::NSET) {
427 this->
currSet = this->currKwParams.at(
"NSET");
429 if (this->
currKw == Keyword::SURFACE) {
430 this->
currSet = this->currKwParams.at(
"NAME");
447 static jsoncons::json parseCSV(
const std::string &line) {
448 return jsoncons::csv::decode_csv<jsoncons::json>(
449 line, jsoncons::csv::basic_csv_options<char>{}
450 .assume_header(
false)
451 .subfield_delimiter(
'=')
452 .unquoted_empty_value_is_null(
true)
457 template <
typename T>
458 void writeCheckWidth(std::ostream &outStream,
const T &
data,
459 const std::string &delimiter =
", ",
460 std::size_t maxWidth = 80, std::size_t maxEntries = 16) {
461 std::size_t width = 0;
462 std::size_t entries = 0;
463 auto lenDelimiter = delimiter.size();
464 for (
auto iter = data.begin(); iter != data.end(); ++iter) {
465 bool last = iter == data.end() - 1;
466 width += iter->size() + (last ? 0 : lenDelimiter);
468 if (width > maxWidth || entries > maxEntries) {
470 width = iter->size() + (last ? 0 : lenDelimiter);
482 void writeCells(std::ostream &outStream, vtkDataSet *data,
483 const std::string &geo,
const std::string &groupArrName) {
485 std::map<std::pair<VTKCellType, int>, std::vector<vtkIdType>> elemBlocks{};
486 auto entArr = data->GetCellData()->GetArray(groupArrName.c_str());
487 for (vtkIdType i = 0; i < data->GetNumberOfCells(); ++i) {
488 elemBlocks[{
static_cast<VTKCellType
>(data->GetCellType(i)),
489 entArr ? static_cast<int>(entArr->GetComponent(i, 0)) : 0}]
494 gmsh::model::setCurrent(geo);
497 for (
const auto &
elems : elemBlocks) {
498 auto cellType =
elems.first.first;
500 std::find_if(vtk2inp.begin(), vtk2inp.end(),
501 [
cellType](
const decltype(vtk2inp)::value_type &x) {
502 return std::get<0>(x) == cellType;
504 if (inpType == vtk2inp.end()) {
505 std::cerr <<
"Unsupported element type" << cellType <<
'\n';
511 gmsh::model::getEntityName(gmsh::model::getDimension(),
512 elems.first.second, elSet);
515 outStream <<
"*ELEMENT, TYPE=" << std::get<1>(*inpType);
516 if (!elSet.empty()) { outStream <<
" ELSET=" << elSet; }
518 for (
const auto &cellIdx :
elems.second) {
519 auto cell = data->GetCell(cellIdx);
520 std::vector<std::string> outStr{};
521 outStr.reserve(cell->GetNumberOfPoints() + 1);
522 outStr.emplace_back(std::to_string(cellIdx + 1));
523 if (cellType == VTK_WEDGE) {
524 outStr.emplace_back(std::to_string(cell->GetPointId(0) + 1));
525 outStr.emplace_back(std::to_string(cell->GetPointId(2) + 1));
526 outStr.emplace_back(std::to_string(cell->GetPointId(1) + 1));
527 outStr.emplace_back(std::to_string(cell->GetPointId(3) + 1));
528 outStr.emplace_back(std::to_string(cell->GetPointId(5) + 1));
529 outStr.emplace_back(std::to_string(cell->GetPointId(4) + 1));
531 for (vtkIdType k = 0; k < cell->GetNumberOfPoints(); ++k) {
532 outStr.emplace_back(std::to_string(cell->GetPointId(k) + 1));
535 writeCheckWidth(outStream, outStr);
552 template <
typename Func>
553 void eachGmshPhyGroup(vtkDataSet *dataSet, vtkDataArray *entArr,
int dim,
555 std::map<int, std::vector<std::string>> ent2PhysGroup;
557 gmsh::vectorpair dimTags;
558 gmsh::model::getEntities(dimTags, dim);
559 for (
const auto &dimTag : dimTags) {
560 std::vector<int> phyGrpIds{};
561 gmsh::model::getPhysicalGroupsForEntity(dimTag.first, dimTag.second,
563 for (
const auto &phyGrp : phyGrpIds) {
564 auto &phyGrpNameVec = ent2PhysGroup[dimTag.second];
565 phyGrpNameVec.emplace_back();
566 auto &phyGrpName = phyGrpNameVec.back();
567 gmsh::model::getPhysicalName(dimTag.first, phyGrp, phyGrpName);
568 if (phyGrpName.empty()) {
570 phyGrpName =
"PhysicalVolume";
571 }
else if (dim == 2) {
572 phyGrpName =
"PhysicalSurface";
574 phyGrpName =
"PhysicalGroup";
576 phyGrpName += std::to_string(phyGrp);
577 gmsh::model::setPhysicalName(dimTag.first, phyGrp, phyGrpName);
582 for (vtkIdType i = 0; i < dataSet->GetNumberOfCells(); ++i) {
583 for (
const auto &phyGrp :
584 ent2PhysGroup[static_cast<int>(entArr->GetComponent(i, 0))]) {
610 std::ofstream outFile(fileName);
612 outFile <<
"*NODE\n";
614 for (vtkIdType i = 0; i <
points->GetNumberOfPoints(); ++i) {
615 double *point =
points->GetPoint(i);
616 std::array<std::string, 4> outStr{
617 std::to_string(i + 1), std::to_string(point[0]),
618 std::to_string(point[1]), std::to_string(point[2])};
619 writeCheckWidth(outFile, outStr);
623 writeCells(outFile,
mesh, gm.geo, gm.link);
626 if (!elSet.second.empty()) {
627 outFile <<
"*ELSET, ELSET=" << elSet.first <<
'\n';
628 std::vector<std::string> outStr;
629 outStr.reserve(elSet.second.size());
630 std::transform(elSet.second.begin(), elSet.second.end(),
631 std::back_inserter(outStr),
632 [](
int x) {
return std::to_string(x + 1); });
633 writeCheckWidth(outFile, outStr);
638 if (!nodeSet.second.empty()) {
639 outFile <<
"*NSET, NSET=" << nodeSet.first <<
'\n';
640 std::vector<std::string> outStr;
641 outStr.reserve(nodeSet.second.size());
642 std::transform(nodeSet.second.begin(), nodeSet.second.end(),
643 std::back_inserter(outStr),
644 [](
int x) {
return std::to_string(x + 1); });
645 writeCheckWidth(outFile, outStr);
649 if (gm.sideSet.sides) {
650 auto origCellArr = gm.sideSet.getOrigCellArr();
651 auto cellFaceArr = gm.sideSet.getCellFaceArr();
653 if (!surface.second.empty()) {
654 outFile <<
"*SURFACE, NAME=" << surface.first <<
", TYPE=ELEMENT\n";
655 for (
const auto &
id : surface.second) {
656 auto origCellIdx = origCellArr->GetTypedComponent(
id, 0);
658 static_cast<VTKCellType
>(
mesh->GetCellType(origCellIdx));
659 outFile << origCellIdx + 1 <<
", S" 660 << vtkSide2inpSide(origCellType,
661 cellFaceArr->GetTypedComponent(
id, 0))
688 if (!gm.geo.empty()) {
689 gmsh::model::setCurrent(gm.geo);
690 if (!gm.link.empty() && gm.mesh->GetNumberOfCells() > 0) {
692 gm.mesh, gm.mesh->GetCellData()->GetArray(gm.link.c_str()),
693 gm.mesh->GetCell(0)->GetCellDimension(),
694 [
this](
const std::string &phyGroup, vtkIdType cellIdx) {
697 for (vtkIdType i = 0; i < cell->GetNumberOfPoints(); ++i) {
702 if (gm.sideSet.sides && gm.sideSet.sides->GetNumberOfCells() > 0) {
703 gm.findSide2OrigCell();
705 gm.sideSet.sides, gm.sideSet.getGeoEntArr(),
706 gm.sideSet.sides->GetCell(0)->GetCellDimension(),
707 [
this](
const std::string &phyGroup, vtkIdType cellIdx) {
710 for (vtkIdType i = 0; i < cell->GetNumberOfPoints(); ++i) {
720 const std::string &fileName) {
722 std::map<std::string, std::set<vtkIdType>> outNSets, outElSets, outSurfaces;
724 std::string geoName{};
725 std::string linkName{};
726 if (fileName.empty()) {
return {{
mesh, {}, {}, {}}, {}}; }
727 std::ifstream inFile(fileName);
730 while (std::getline(inFile, line)) { parser.parseLine(line); }
731 auto parserMesh = parser.getMesh();
733 std::map<vtkIdType, vtkIdType> nodeInpId2GMIdx;
734 if (!parserMesh.nodes.empty()) {
736 points->Allocate(parserMesh.nodes.size());
737 for (
const auto &parserNode : parserMesh.nodes) {
738 nodeInpId2GMIdx[parserNode.first] =
739 points->InsertNextPoint(parserNode.second.data());
741 mesh->SetPoints(points);
743 auto nodeMapper = [&nodeInpId2GMIdx](vtkIdType x) {
744 return nodeInpId2GMIdx.at(x);
746 for (
const auto &nSet : parserMesh.nodeSets) {
747 auto &outNSet = outNSets[nSet.first];
748 std::transform(nSet.second.begin(), nSet.second.end(),
749 std::inserter(outNSet, outNSet.end()), nodeMapper);
751 std::map<vtkIdType, vtkIdType> inpId2GMIdx;
752 if (!parserMesh.elems.empty()) {
753 mesh->Allocate(parserMesh.elems.size());
757 gmsh::model::add(geoName);
758 gmsh::model::setCurrent(geoName);
761 vtkNew<vtkIntArray> geoEntArr;
762 geoEntArr->SetName(linkName.c_str());
766 for (
const auto &elemSet : parserMesh.elems) {
768 auto entTag = gmsh::model::addDiscreteEntity(parserMesh.maxDim);
769 gmsh::model::setEntityName(parserMesh.maxDim, entTag, elemSet.first);
771 for (
const auto &elem : elemSet.second) {
772 auto points = elem.points;
775 if (elem.cellType == VTK_WEDGE) {
779 inpId2GMIdx[elem.id] =
781 geoEntArr->InsertNextValue(entTag);
787 mesh->GetCellData()->AddArray(geoEntArr);
789 for (
const auto &elSet : parserMesh.elSets) {
790 auto &outElSet = outElSets[elSet.first];
791 for (
const auto &elem : elSet.second) {
792 auto iterCellIdxMap = inpId2GMIdx.find(elem);
793 if (iterCellIdxMap != inpId2GMIdx.end()) {
794 outElSet.emplace(iterCellIdxMap->second);
798 if (!parserMesh.surfaces.empty()) {
800 sideSetPD->SetPoints(
mesh->GetPoints());
801 sideSetPD->Allocate();
802 vtkNew<vtkIntArray> entArr;
803 vtkNew<vtkIdTypeArray> origCellArr;
804 origCellArr->SetNumberOfComponents(2);
805 vtkNew<vtkIntArray> cellFaceArr;
806 cellFaceArr->SetNumberOfComponents(2);
810 for (
const auto &surf : parserMesh.surfaces) {
811 auto &outSurface = outSurfaces[surf.first];
813 auto entTag = gmsh::model::addDiscreteEntity(parserMesh.maxDim - 1);
815 gmsh::model::addPhysicalGroup(parserMesh.maxDim - 1, {entTag});
816 gmsh::model::setPhysicalName(parserMesh.maxDim - 1, phyGroupTag,
819 for (
const auto &side : surf.second) {
820 auto iterCellIdxMap = inpId2GMIdx.find(side.first);
821 if (iterCellIdxMap != inpId2GMIdx.end()) {
822 auto origCell =
mesh->GetCell(iterCellIdxMap->second);
823 auto vtkSide = inpSide2vtkSide(
824 static_cast<VTKCellType>(origCell->GetCellType()), side.second);
825 auto sideCell = origCell->GetCellDimension() == 2
826 ? origCell->GetEdge(vtkSide)
827 : origCell->GetFace(vtkSide);
828 auto sideIdx = sideSetPD->InsertNextCell(sideCell->GetCellType(),
829 sideCell->GetPointIds());
830 entArr->InsertTypedComponent(sideIdx, 0, entTag);
831 origCellArr->InsertTuple2(sideIdx, iterCellIdxMap->second, -1);
832 cellFaceArr->InsertTuple2(sideIdx, vtkSide, -1);
833 outSurface.emplace(sideIdx);
840 sideSet = {sideSetPD, entArr, origCellArr, cellFaceArr};
842 return {{
mesh, std::move(geoName), std::move(linkName), sideSet},
843 {std::move(outElSets), std::move(outNSets), std::move(outSurfaces)}};
std::map< std::string, std::vector< vtkIdType > > elSets
Map from ELSET name to element ids (ids given by .inp file)
std::map< std::string, std::set< vtkIdType > > surfaces
SURFACE keyword; indexing matches GeoMesh::sideSet.
std::map< std::string, std::set< vtkIdType > > nodeSets
NSET keyword; indexing matches GeoMesh::mesh.
Class representing meshes in CalculiX input deck (similar to ABAQUS)
virtual void report(std::ostream &out) const =0
Print a report about the mesh.
data_type data
Edge/face with sorted point ids (a, b, c, ...) is located at some index i in data[b], with data[b][i].first == [a, c] (for edges, third point id treated as -1).
static std::pair< GeoMesh, InpSets > inp2GM(const std::string &fileName)
std::vector< std::pair< vtkIdType, std::vector< double > > > nodes
Each entry stores (node ID in .inp file, coordinates)
vtkSmartPointer< vtkUnstructuredGrid > mesh
virtual void reconstructGeo()
Construct the geometry from the mesh alone.
static constexpr auto GEO_ENT_DEFAULT_NAME
std::map< std::string, std::vector< vtkIdType > > nodeSets
Map from NSET name to node ids (ids given by .inp file)
geoMeshBase * New(MeshType meshType)
Create a new mesh object.
VTKCellType currCellType
TYPE, if inside data lines following ELEMENT keyword.
static void Initialize()
Initialize Gmsh.
std::map< std::string, std::set< vtkIdType > > elSets
ELSET keyword; indexing matches GeoMesh::mesh.
Keyword currKw
current keyword
std::string currSet
Name of ELSET/NSET/Surface (empty string for ELEMENT keyword that does not specify ELSET) ...
void toUpper(std::string &str)
std::string getRandomString(int length)
vtkIdType id
id in .inp file
void resetNative() override
vtkSmartPointer< vtkPolyData > sides
Cells represent edges/faces of some GeoMesh.
std::shared_ptr< meshBase > mesh
static inpGeoMesh * Read(const std::string &fileName)
std::vector< vtkIdType > points
points given by id in .inp file
std::map< std::string, std::vector< std::pair< vtkIdType, int > > > surfaces
Map from SURFACE name to (element id, side) (id and side both use .inp IDs)
vtkStandardNewMacro(exoGeoMesh)
mesh_(gen_->CreateMesh(false))
Continue currContinue
If previous line was keyword line that is continued, KEYWORD.
std::map< std::string, std::vector< Elem > > elems
If the ELEMENT keyword line contains ELSET=name, then the element is stored in elems[name], otherwise, stored in elems[std::string{}].
std::map< std::string, std::string > currKwParams
Params in current keyword line.
void reconstructGeo() override
Construct the geometry from the mesh alone.
vtkIdType currId
If previous line was data line that is continued and current keyword is NODE or ELEMEMT, the ID of the node/cell being processed.
virtual void write(const std::string &fileName)=0
Write mesh to file.
abstract class to specify geometry and mesh data
InpSets inpSets_
Holds data specific to inp format.
const GeoMesh & getGeoMesh() const
Get the underlying geometry object.