29 #if defined(_MSC_VER) && !defined(_USE_MATH_DEFINES) 30 # define _USE_MATH_DEFINES 41 #include <vtkConnectivityFilter.h> 42 #include <vtkIdTypeArray.h> 43 #include <vtkIntArray.h> 45 #include <vtkPolyDataNormals.h> 46 #include <vtkThreshold.h> 47 #include <vtkDataSetTriangleFilter.h> 61 int geoMeshBase::getGmshTypeFromVTKType(
int vtkType) {
63 case VTK_VERTEX:
return MSH_PNT;
73 case VTK_QUADRATIC_EDGE:
return MSH_LIN_3;
74 case VTK_QUADRATIC_TRIANGLE:
return MSH_TRI_6;
75 case VTK_QUADRATIC_QUAD:
return MSH_QUA_8;
76 case VTK_BIQUADRATIC_QUAD:
return MSH_QUA_9;
78 case VTK_QUADRATIC_HEXAHEDRON:
return MSH_HEX_20;
79 case VTK_TRIQUADRATIC_HEXAHEDRON:
return MSH_HEX_27;
89 void addGlobalIds(vtkDataSet *dataSet) {
90 vtkNew<vtkIdTypeArray> cellIds{};
91 cellIds->SetNumberOfTuples(dataSet->GetNumberOfCells());
92 for (vtkIdType i = 0; i < dataSet->GetNumberOfCells(); ++i) {
93 cellIds->SetValue(i, i);
95 cellIds->SetName(
"GlobalIds");
96 dataSet->GetCellData()->AddArray(cellIds);
102 int local2GlobalRegions(vtkIdTypeArray *globalIds,
103 vtkIdTypeArray *localRegionId,
104 vtkIntArray *globalRegionId,
int oldRegion,
105 int regionOffset,
int numNewRegions,
106 std::map<
int, std::vector<int>> &old2NewRegion) {
107 for (vtkIdType i = 0; i < globalIds->GetNumberOfTuples(); ++i) {
108 globalRegionId->SetTypedComponent(
109 globalIds->GetTypedComponent(i, 0), 0,
110 localRegionId->GetTypedComponent(i, 0) + regionOffset);
112 auto newRegionIter = old2NewRegion.emplace(
113 std::piecewise_construct, std::forward_as_tuple(oldRegion),
114 std::forward_as_tuple(numNewRegions));
115 auto &newRegionVec = newRegionIter.first->second;
116 std::iota(newRegionVec.begin(), newRegionVec.end(), regionOffset);
117 return regionOffset + numNewRegions;
120 std::map<int, std::vector<int>> getConnectedEnts(vtkUnstructuredGrid *dataSet,
121 const std::string &geoName) {
122 vtkSmartPointer<vtkDataArray> geoArray = vtkArrayDownCast<vtkDataArray>(
123 dataSet->GetCellData()->GetAbstractArray(geoName.c_str()));
126 std::set<int> geoEntities;
128 for (vtkIdType i = 0; i < geoArray->GetNumberOfValues(); ++i) {
129 geoEntities.insert(static_cast<int>(geoArray->GetComponent(i, 0)));
132 std::cout <<
"No geometry array found." << std::endl;
133 geoEntities.insert(1);
135 vtkSmartPointer<vtkIntArray> newGeoArray =
137 newGeoArray->SetNumberOfValues(dataSet->GetNumberOfCells());
138 newGeoArray->SetName(geoName.c_str());
139 for (vtkIdType i = 0; i < dataSet->GetNumberOfCells(); ++i) {
140 newGeoArray->SetTypedComponent(i, 0, 1);
142 dataSet->GetCellData()->AddArray(newGeoArray);
145 vtkNew<vtkIntArray> connectedRegionId{};
146 connectedRegionId->SetNumberOfValues(dataSet->GetNumberOfCells());
148 int regionOffset = 1;
149 std::map<int, std::vector<int>> geoId2regionIds;
153 addGlobalIds(dataSet);
158 tf->SetInputData(dataSet);
159 tf->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_CELLS,
163 vtkSmartPointer<vtkConnectivityFilter> cf =
166 cf->SetInputConnection(tf->GetOutputPort());
167 cf->SetExtractionModeToAllRegions();
168 cf->ColorRegionsOn();
171 for (
const auto &geoEntity : geoEntities) {
172 std::cout <<
"Processing " << geoEntity << std::endl;
174 tf->ThresholdBetween(geoEntity, geoEntity);
180 vtkSmartPointer<vtkUnstructuredGrid> ug =
181 vtkUnstructuredGrid::SafeDownCast(cf->GetOutput());
186 auto componentId = vtkArrayDownCast<vtkIdTypeArray>(
187 ug->GetCellData()->GetAbstractArray(
"RegionId"));
188 auto globalId = vtkArrayDownCast<vtkIdTypeArray>(
189 ug->GetCellData()->GetAbstractArray(
"GlobalIds"));
194 regionOffset = local2GlobalRegions(
195 globalId, componentId, connectedRegionId, geoEntity, regionOffset,
196 cf->GetNumberOfExtractedRegions(), geoId2regionIds);
199 connectedRegionId->SetName(geoName.c_str());
200 dataSet->GetCellData()->AddArray(connectedRegionId);
201 return geoId2regionIds;
206 int colorLinesByAngle(vtkDataSet *lines,
double angleThreshold) {
207 vtkNew<vtkIdTypeArray> colorArr;
208 colorArr->SetName(
"RegionId");
209 colorArr->SetNumberOfTuples(lines->GetNumberOfCells());
210 colorArr->FillTypedComponent(0, -1);
211 vtkIdType nextRegion = 0;
212 std::vector<vtkIdType> cellsToVisit;
213 vtkNew<vtkIdList> neighborCells;
214 vtkNew<vtkGenericCell> tempCell;
215 for (vtkIdType i = 0; i < lines->GetNumberOfCells(); ++i) {
216 if (colorArr->GetTypedComponent(i, 0) == -1) {
217 cellsToVisit.clear();
218 cellsToVisit.emplace_back(i);
219 colorArr->SetTypedComponent(i, 0, nextRegion);
220 while (!cellsToVisit.empty()) {
221 auto cellIdx = cellsToVisit.back();
222 cellsToVisit.pop_back();
223 lines->GetCell(cellIdx, tempCell);
224 auto points = tempCell->GetPointIds();
225 assert(
points->GetNumberOfIds() == 2);
226 std::array<double, 3> vec{};
228 lines->GetPoint(
points->GetId(1), vec.data());
229 auto u = lines->GetPoint(
points->GetId(0));
230 std::transform(vec.begin(), vec.end(), u, vec.begin(),
231 std::minus<double>());
233 for (
int j = 0; j < 2; ++j) {
235 std::transform(vec.begin(), vec.end(), vec.begin(),
236 [](
double x) {
return -1 * x; });
238 auto sharedPointId =
points->GetId(j);
239 lines->GetPointCells(sharedPointId, neighborCells);
240 for (vtkIdType k = 0; k < neighborCells->GetNumberOfIds(); ++k) {
241 auto otherCellIdx = neighborCells->GetId(k);
242 if (colorArr->GetTypedComponent(otherCellIdx, 0) == -1) {
243 auto otherCellPoints =
244 lines->GetCell(otherCellIdx)->GetPointIds();
245 auto otherPointId = otherCellPoints->GetId(0) == sharedPointId
246 ? otherCellPoints->GetId(1)
247 : otherCellPoints->GetId(0);
248 std::array<double, 3> otherVec{};
249 lines->GetPoint(otherPointId, otherVec.data());
250 auto u = lines->GetPoint(sharedPointId);
251 std::transform(otherVec.begin(), otherVec.end(), u,
252 otherVec.begin(), std::minus<double>());
254 vtkMath::AngleBetweenVectors(vec.data(), otherVec.data());
255 if (std::abs(angle -
vtkMath::Pi()) < angleThreshold) {
256 cellsToVisit.emplace_back(otherCellIdx);
257 colorArr->SetTypedComponent(otherCellIdx, 0, nextRegion);
266 lines->GetCellData()->AddArray(colorArr);
273 std::map<int, std::vector<int>> splitSurfaces(
274 vtkPolyData *
surfaces,
const std::string &surfaceArrayName,
int max_dim,
275 double angleThreshold,
int numBoundEnts, vtkIntArray *sideGeoEntArr) {
276 std::map<int, std::vector<int>> old2newSurfs{};
277 vtkNew<vtkThreshold> selectBoundary{};
278 selectBoundary->SetInputData(surfaces);
279 selectBoundary->SetInputArrayToProcess(0, 0, 0,
280 vtkDataObject::FIELD_ASSOCIATION_CELLS,
281 surfaceArrayName.c_str());
283 vtkSmartPointer<vtkPolyDataNormals> pdn;
284 vtkSmartPointer<vtkPolyData> pdnInput;
285 vtkSmartPointer<vtkConnectivityFilter> pdConn{};
289 pdn->SetConsistency(
false);
290 pdn->SetSplitting(
true);
291 pdn->SetFeatureAngle(vtkMath::DegreesFromRadians(angleThreshold));
294 pdConn->SetExtractionModeToAllRegions();
295 pdConn->ColorRegionsOn();
298 int regionOffset = 1;
299 for (
int i = 0; i < numBoundEnts; ++i) {
300 vtkSmartPointer<vtkDataSet> coloredDS;
302 selectBoundary->ThresholdBetween(i, i);
304 selectBoundary->Update();
305 coloredDS = selectBoundary->GetOutput();
306 numRegions = colorLinesByAngle(coloredDS, angleThreshold);
308 selectBoundary->Update();
309 auto thresholdOut = selectBoundary->GetOutput();
310 pdnInput->SetPoints(thresholdOut->GetPoints());
311 pdnInput->SetPolys(thresholdOut->GetCells());
312 pdnInput->GetCellData()->ShallowCopy(thresholdOut->GetCellData());
314 pdn->SetInputData(pdnInput);
315 pdConn->SetInputConnection(pdn->GetOutputPort());
317 coloredDS = pdConn->GetOutput();
318 numRegions = pdConn->GetNumberOfExtractedRegions();
320 auto globalId = vtkArrayDownCast<vtkIdTypeArray>(
321 coloredDS->GetCellData()->GetArray(
"GlobalIds"));
322 auto splitSurfId = vtkArrayDownCast<vtkIdTypeArray>(
323 coloredDS->GetCellData()->GetArray(
"RegionId"));
324 regionOffset = local2GlobalRegions(globalId, splitSurfId, sideGeoEntArr, i,
325 regionOffset, numRegions, old2newSurfs);
327 surfaces->GetCellData()->RemoveArray(surfaceArrayName.c_str());
328 surfaces->GetCellData()->AddArray(sideGeoEntArr);
333 void constructGeoGmsh(vtkDataSet *dataSet,
const int dim,
334 vtkIntArray *sideGeoEntArr,
335 const std::map<
int, std::vector<int>> &vol2surf,
336 const std::map<
int, std::vector<int>> &material2GeoEnt) {
337 std::vector<double> coords(3 * dataSet->GetNumberOfPoints());
338 for (vtkIdType i = 0; i < dataSet->GetNumberOfPoints(); ++i) {
339 dataSet->GetPoint(i, coords.data() + 3 * i);
341 std::map<int, std::vector<std::size_t>> splitSurf2NodeTags;
342 const int numPoints = dim;
343 for (vtkIdType i = 0; i < dataSet->GetNumberOfCells(); ++i) {
344 auto &nodeTags = splitSurf2NodeTags[sideGeoEntArr->GetTypedComponent(i, 0)];
345 auto pointsBegin = dataSet->GetCell(i)->GetPointIds()->GetPointer(0);
346 std::transform(pointsBegin, pointsBegin + numPoints,
347 std::back_inserter(nodeTags),
348 [](vtkIdType x) {
return x + 1; });
350 bool firstSurf =
true;
351 for (
const auto &surf : splitSurf2NodeTags) {
352 auto gmshTag = gmsh::model::addDiscreteEntity(dim - 1, surf.first);
355 gmsh::model::mesh::addNodes(dim - 1, gmshTag, {}, coords);
358 gmsh::model::mesh::addElementsByType(
361 gmsh::model::mesh::reclassifyNodes();
363 gmsh::model::mesh::createTopology();
364 gmsh::model::mesh::createGeometry();
365 for (
const auto &vol : vol2surf) {
366 std::vector<int> postClassifySurfs;
368 auto curveLoop = gmsh::model::geo::addCurveLoop(vol.second);
369 gmsh::model::geo::addPlaneSurface({curveLoop}, vol.first);
371 auto surfLoop = gmsh::model::geo::addSurfaceLoop(vol.second);
372 gmsh::model::geo::addVolume({surfLoop}, vol.first);
375 gmsh::model::geo::synchronize();
376 for (
const auto &phyGrp : material2GeoEnt) {
377 gmsh::model::addPhysicalGroup(dim, phyGrp.second, phyGrp.first);
386 vtkSmartPointer<vtkPolyData> computeDiscreteGeoFromMsh(
387 vtkUnstructuredGrid *dataSetOriginal,
const std::string &geoName,
388 double angleThreshold) {
392 auto material2GeoEnt = getConnectedEnts(dataSetOriginal, geoName);
395 vtkNew<vtkUnstructuredGrid> dataSet;
396 dataSet->ShallowCopy(dataSetOriginal);
406 for (vtkIdType i = 0; i < dataSet->GetNumberOfCells(); ++i) {
407 max_dim = std::max(dataSet->GetCell(i)->GetCellDimension(), max_dim);
410 vtkNew<dataSetRegionBoundaryFilter> dsrbf;
412 dsrbf->SetMaterialArrayName(geoName);
413 dsrbf->SetDimension(max_dim);
414 dsrbf->SetInputData(dataSet);
417 vtkSmartPointer<vtkPolyData> sidePD = dsrbf->GetOutput();
418 auto boundary2MeshRegion =
419 vtkArrayDownCast<vtkIntArray>(sidePD->GetFieldData()->GetAbstractArray(
420 dsrbf->GetRegionToMaterialArrayName().c_str()));
421 addGlobalIds(sidePD);
422 vtkNew<vtkIntArray> sideGeoEntArr;
423 sideGeoEntArr->SetName(
"GeoEnt");
424 sideGeoEntArr->SetNumberOfTuples(sidePD->GetNumberOfCells());
425 int numBoundEnts = boundary2MeshRegion->GetNumberOfTuples();
426 auto surf2ClassifiedSurfs =
427 splitSurfaces(sidePD, dsrbf->GetBoundaryRegionArrayName(), max_dim,
428 angleThreshold, numBoundEnts, sideGeoEntArr);
429 std::map<int, std::vector<int>> vol2surf;
430 for (vtkIdType i = 0; i < boundary2MeshRegion->GetNumberOfTuples(); ++i) {
431 auto &splitSurfs = surf2ClassifiedSurfs[i];
432 for (vtkIdType j = 0; j < 2; ++j) {
433 auto vol = boundary2MeshRegion->GetTypedComponent(i, j);
435 auto &boundary = vol2surf[vol];
436 boundary.insert(boundary.end(), splitSurfs.begin(), splitSurfs.end());
440 sidePD->GetFieldData()->RemoveArray(boundary2MeshRegion->GetName());
448 constructGeoGmsh(sidePD, max_dim, sideGeoEntArr, vol2surf, material2GeoEnt);
450 vtkNew<vtkDataSetTriangleFilter> trif;
451 trif->SetInputData(sidePD);
453 auto triangleSurf = trif->GetOutput();
454 constructGeoGmsh(triangleSurf, max_dim,
455 vtkArrayDownCast<vtkIntArray>(
456 triangleSurf->GetCellData()->GetAbstractArray(
457 sideGeoEntArr->GetName())),
458 vol2surf, material2GeoEnt);
472 gmsh::model::remove();
vtkSmartPointer< vtkUnstructuredGrid > mesh
virtual void reconstructGeo()
Construct the geometry from the mesh alone.
static constexpr auto GEO_ENT_DEFAULT_NAME
geoMeshBase * New(MeshType meshType)
Create a new mesh object.
std::string getRandomString(int length)
vtkSmartPointer< vtkPolyData > sides
Cells represent edges/faces of some GeoMesh.
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)
double _angleThreshold
Dihedral angle threshold (in radians) to classify surfaces (Default: 30 degrees)