Skip to content

Commit 4a4ff77

Browse files
authored
feat: geom model apply to single material (#225)
* feat(geom model): apply to single material * python bindings * fix: missing bindings for disk mesh * fix: missing python bindings
1 parent 0e10096 commit 4a4ff77

7 files changed

Lines changed: 156 additions & 48 deletions

File tree

include/viennaps/models/psGeometricDistributionModels.hpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ class SphereDistribution : public ProcessModelCPU<NumericType, D> {
3232
assert(geomModel != nullptr);
3333
geomModel->addMaskMaterial(material);
3434
}
35+
36+
void applyToSingleMaterial(const Material material) {
37+
auto geomModel = this->getGeometricModel();
38+
assert(geomModel != nullptr);
39+
geomModel->setSingleMaterial(true);
40+
geomModel->addMaskMaterial(material);
41+
}
3542
};
3643

3744
template <typename NumericType, int D>
@@ -66,6 +73,13 @@ class BoxDistribution : public ProcessModelCPU<NumericType, D> {
6673
assert(geomModel != nullptr);
6774
geomModel->addMaskMaterial(material);
6875
}
76+
77+
void applyToSingleMaterial(const Material material) {
78+
auto geomModel = this->getGeometricModel();
79+
assert(geomModel != nullptr);
80+
geomModel->setSingleMaterial(true);
81+
geomModel->addMaskMaterial(material);
82+
}
6983
};
7084

7185
template <typename NumericType, int D>

include/viennaps/process/psGeometricModel.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class GeometricModel {
1616
SmartPointer<viennals::Domain<NumericType, D>> mask = nullptr;
1717
std::vector<Material> maskMaterials;
1818
bool isDepo = false;
19+
bool applySingleMaterial = false;
1920

2021
public:
2122
GeometricModel() = default;
@@ -49,6 +50,10 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class GeometricModel {
4950
auto &getMaskMaterials() const { return maskMaterials; }
5051
auto isDeposition() const { return isDepo; }
5152
void setDeposition(bool deposition) { isDepo = deposition; }
53+
auto isSingleMaterial() const { return applySingleMaterial; }
54+
void setSingleMaterial(bool singleMaterial) {
55+
applySingleMaterial = singleMaterial;
56+
}
5257
};
5358

5459
} // namespace viennaps

include/viennaps/process/psGeometricProcessStrategy.hpp

Lines changed: 61 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -44,54 +44,71 @@ class GeometricProcessStrategy : public ProcessStrategy<NumericType, D> {
4444
return ProcessResult::INVALID_INPUT;
4545
}
4646

47-
auto maskLevelSet = SmartPointer<viennals::Domain<NumericType, D>>::New(
48-
domain->getGrid());
49-
bool foundMaterial = false;
50-
51-
auto const &levelSets = domain->getLevelSets();
52-
for (auto &material : maskMaterials) {
53-
for (int j = 0; j < levelSets.size(); ++j) {
54-
if (materialMap->getMaterialAtIdx(j) == material) {
55-
auto lsCopy = SmartPointer<viennals::Domain<NumericType, D>>::New(
56-
levelSets[j]);
57-
58-
// remove all lower level sets that are not mask materials
59-
for (int k = j - 1; k >= 0; --k) {
60-
if (MaterialMap::isMaterial(materialMap->getMaterialAtIdx(k),
61-
maskMaterials))
62-
continue;
63-
64-
viennals::BooleanOperation<NumericType, D>(
65-
lsCopy, levelSets[k],
66-
viennals::BooleanOperationEnum::RELATIVE_COMPLEMENT)
67-
.apply();
68-
}
69-
70-
if (foundMaterial) {
71-
// union with mask level set
72-
viennals::BooleanOperation<NumericType, D>(
73-
maskLevelSet, lsCopy, viennals::BooleanOperationEnum::UNION)
74-
.apply();
75-
} else {
76-
maskLevelSet = lsCopy;
77-
foundMaterial = true;
47+
if (geometricModel->isSingleMaterial() && maskMaterials.size() == 1) {
48+
// operation should be applied to a single material, so we use the
49+
// surface as mask and exclude the material from the surface
50+
mask = viennals::Domain<NumericType, D>::New(domain->getSurface());
51+
auto materialLS = domain->getMaterialLevelSet(maskMaterials[0]);
52+
if (materialLS) {
53+
viennals::BooleanOperation<NumericType, D>(
54+
mask, materialLS,
55+
viennals::BooleanOperationEnum::RELATIVE_COMPLEMENT)
56+
.apply();
57+
VIENNACORE_LOG_DEBUG(
58+
"Created mask level set from single mask material: " +
59+
materialMap->toString(maskMaterials[0]));
60+
}
61+
} else {
62+
auto maskLevelSet =
63+
viennals::Domain<NumericType, D>::New(domain->getGrid());
64+
bool foundMaterial = false;
65+
auto const &levelSets = domain->getLevelSets();
66+
for (auto &material : maskMaterials) {
67+
for (int j = 0; j < levelSets.size(); ++j) {
68+
if (materialMap->getMaterialAtIdx(j) == material) {
69+
auto lsCopy =
70+
viennals::Domain<NumericType, D>::New(levelSets[j]);
71+
72+
// remove all lower level sets that are not mask materials
73+
for (int k = j - 1; k >= 0; --k) {
74+
if (MaterialMap::isMaterial(materialMap->getMaterialAtIdx(k),
75+
maskMaterials))
76+
continue;
77+
78+
viennals::BooleanOperation<NumericType, D>(
79+
lsCopy, levelSets[k],
80+
viennals::BooleanOperationEnum::RELATIVE_COMPLEMENT)
81+
.apply();
82+
}
83+
84+
if (foundMaterial) {
85+
// union with mask level set
86+
viennals::BooleanOperation<NumericType, D>(
87+
maskLevelSet, lsCopy,
88+
viennals::BooleanOperationEnum::UNION)
89+
.apply();
90+
} else {
91+
maskLevelSet = lsCopy;
92+
foundMaterial = true;
93+
}
7894
}
7995
}
8096
}
81-
}
8297

83-
if (maskLevelSet->getNumberOfPoints() > 0) {
84-
mask = maskLevelSet;
85-
if (Logger::hasDebug()) {
86-
auto dbgMesh = viennals::Mesh<NumericType>::New();
87-
viennals::ToMesh<NumericType, D>(mask, dbgMesh).apply();
88-
viennals::VTKWriter<NumericType>(dbgMesh, "geometric_mask_debug")
89-
.apply();
98+
if (maskLevelSet->getNumberOfPoints() > 0) {
99+
mask = maskLevelSet;
100+
if (Logger::hasDebug()) {
101+
auto dbgMesh = viennals::Mesh<NumericType>::New();
102+
viennals::ToMesh<NumericType, D>(mask, dbgMesh).apply();
103+
viennals::VTKWriter<NumericType>(dbgMesh, "geometric_mask_debug")
104+
.apply();
105+
}
106+
} else {
107+
VIENNACORE_LOG_WARNING(
108+
"None of the specified mask materials were found in the "
109+
"domain, "
110+
"cannot create mask level set from mask materials.");
90111
}
91-
} else {
92-
VIENNACORE_LOG_WARNING(
93-
"None of the specified mask materials were found in the domain, "
94-
"cannot create mask level set from mask materials.");
95112
}
96113
}
97114
}
@@ -104,8 +121,7 @@ class GeometricProcessStrategy : public ProcessStrategy<NumericType, D> {
104121
// last level set.
105122
for (int i = context.domain->getNumberOfLevelSets() - 1; i >= 0; --i) {
106123
viennals::BooleanOperation<NumericType, D>(
107-
context.domain->getLevelSets()[i],
108-
context.domain->getLevelSets().back(),
124+
context.domain->getLevelSets()[i], context.domain->getSurface(),
109125
viennals::BooleanOperationEnum::INTERSECT)
110126
.apply();
111127
}

include/viennaps/psDomain.hpp

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,7 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Domain {
447447
return levelSets_.back()->getGrid().getBoundaryConditions();
448448
}
449449

450+
// Returns a set of all materials present in the domain.
450451
auto getMaterialsInDomain() const {
451452
std::set<Material> materials;
452453
for (std::size_t i = 0; i < materialMap_->size(); i++) {
@@ -455,6 +456,43 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Domain {
455456
return materials;
456457
}
457458

459+
// Returns a Level-Set representing the specified material in the domain. If
460+
// the material is not present in the domain, it returns an empty Level-Set.
461+
auto getMaterialLevelSet(const Material material) const {
462+
lsDomainType levelSet = nullptr;
463+
464+
for (int i = 0; i < levelSets_.size(); i++) {
465+
if (materialMap_->getMaterialAtIdx(i) == material) {
466+
auto lsCopy =
467+
SmartPointer<viennals::Domain<NumericType, D>>::New(levelSets_[i]);
468+
469+
// remove lower level set
470+
if (i > 0) {
471+
viennals::BooleanOperation<NumericType, D>(
472+
lsCopy, levelSets_[i - 1],
473+
viennals::BooleanOperationEnum::RELATIVE_COMPLEMENT)
474+
.apply();
475+
}
476+
477+
if (levelSet) {
478+
// add to level set
479+
viennals::BooleanOperation<NumericType, D>(
480+
levelSet, lsCopy, viennals::BooleanOperationEnum::UNION)
481+
.apply();
482+
} else {
483+
levelSet = lsCopy;
484+
}
485+
}
486+
}
487+
488+
if (!levelSet) {
489+
VIENNACORE_LOG_WARNING("Material " + MaterialMap::toString(material) +
490+
" not found in domain.");
491+
}
492+
493+
return levelSet;
494+
}
495+
458496
void print(std::ostream &out = std::cout, bool hrle = false) const {
459497
constexpr std::string_view separator =
460498
"*****************************************\n";
@@ -655,8 +693,8 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Domain {
655693
private:
656694
void materialMapCheck() const {
657695
if (materialMap_->size() != levelSets_.size()) {
658-
VIENNACORE_LOG_WARNING(
659-
"Size mismatch in material map and number of Level-Sets in domain.");
696+
VIENNACORE_LOG_WARNING("Size mismatch in material map and number of "
697+
"Level-Sets in domain.");
660698
}
661699
}
662700

python/pyWrapDimension.hpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,10 @@ template <int D> void bindApi(py::module &module) {
538538
.def("getLevelSets", &Domain<T, D>::getLevelSets)
539539
.def("getMaterialsInDomain", &Domain<T, D>::getMaterialsInDomain,
540540
"Get the material IDs present in the domain.")
541+
.def("getMaterialLevelSet", &Domain<T, D>::getMaterialLevelSet,
542+
py::arg("material"),
543+
"Returns a Level-Set representing the specified material in the "
544+
"domain.")
541545
.def("getSurface", &Domain<T, D>::getSurface,
542546
"Get the surface level set.")
543547
.def("getCellSet", &Domain<T, D>::getCellSet, "Get the cell set.")
@@ -564,6 +568,7 @@ template <int D> void bindApi(py::module &module) {
564568
"Get the surface mesh of the domain")
565569
.def("getHullMesh", &Domain<T, D>::getHullMesh,
566570
py::arg("bottomExtension") = 0.0, py::arg("sharpCorners") = false)
571+
.def("getDiskMesh", &Domain<T, D>::getDiskMesh)
567572
// Save to file
568573
.def("saveLevelSetMesh", &Domain<T, D>::saveLevelSetMesh,
569574
py::arg("filename"), py::arg("width") = 1,
@@ -576,6 +581,7 @@ template <int D> void bindApi(py::module &module) {
576581
.def("saveHullMesh", &Domain<T, D>::saveHullMesh, py::arg("filename"),
577582
py::arg("bottomExtension") = 0.0, py::arg("sharpCorners") = false,
578583
"Save the hull of the domain.")
584+
.def("saveDiskMesh", &Domain<T, D>::saveDiskMesh, py::arg("filename"))
579585
.def("saveVolumeMesh", &Domain<T, D>::saveVolumeMesh, py::arg("filename"),
580586
py::arg("wrappingLayerEpsilon") = 1e-2,
581587
"Save the volume representation of the domain.")
@@ -1055,6 +1061,9 @@ template <int D> void bindApi(py::module &module) {
10551061
}),
10561062
py::arg("radius"))
10571063
.def("addMaskMaterial", &SphereDistribution<T, D>::addMaskMaterial,
1064+
py::arg("material"))
1065+
.def("applyToSingleMaterial",
1066+
&SphereDistribution<T, D>::applyToSingleMaterial,
10581067
py::arg("material"));
10591068

10601069
// Box Distribution
@@ -1070,7 +1079,9 @@ template <int D> void bindApi(py::module &module) {
10701079
}),
10711080
py::arg("halfAxes"))
10721081
.def("addMaskMaterial", &BoxDistribution<T, D>::addMaskMaterial,
1073-
py::arg("material"));
1082+
py::arg("material"))
1083+
.def("applyToSingleMaterial",
1084+
&BoxDistribution<T, D>::applyToSingleMaterial, py::arg("material"));
10741085

10751086
// Custom Sphere Distribution
10761087
py::class_<CustomSphereDistribution<T, D>,

python/viennaps/d2/__init__.pyi

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ class BoxDistribution(ProcessModel):
2727
...
2828
def addMaskMaterial(self, material: viennaps._core.Material) -> None:
2929
...
30+
def applyToSingleMaterial(self, material: viennaps._core.Material) -> None:
31+
...
3032
class CF4O2Etching(ProcessModel):
3133
@typing.overload
3234
def __init__(self) -> None:
@@ -302,6 +304,8 @@ class Domain:
302304
"""
303305
Get the cell set.
304306
"""
307+
def getDiskMesh(self) -> viennals._core.Mesh:
308+
...
305309
def getGrid(self) -> viennals.d2.hrleGrid:
306310
"""
307311
Get the grid
@@ -318,6 +322,10 @@ class Domain:
318322
"""
319323
def getLevelSets(self) -> list[viennals.d2.Domain]:
320324
...
325+
def getMaterialLevelSet(self, material: viennaps._core.Material) -> viennals.d2.Domain:
326+
"""
327+
Returns a Level-Set representing the specified material in the domain.
328+
"""
321329
def getMaterialMap(self) -> viennaps._core.MaterialMap:
322330
...
323331
def getMaterialsInDomain(self) -> set[viennaps._core.Material]:
@@ -378,6 +386,8 @@ class Domain:
378386
...
379387
def removeTopLevelSet(self) -> None:
380388
...
389+
def saveDiskMesh(self, filename: str) -> None:
390+
...
381391
def saveHullMesh(self, filename: str, bottomExtension: typing.SupportsFloat | typing.SupportsIndex = 0.0, sharpCorners: bool = False) -> None:
382392
"""
383393
Save the hull of the domain.
@@ -934,6 +944,8 @@ class SphereDistribution(ProcessModel):
934944
...
935945
def addMaskMaterial(self, material: viennaps._core.Material) -> None:
936946
...
947+
def applyToSingleMaterial(self, material: viennaps._core.Material) -> None:
948+
...
937949
class StencilLocalLaxFriedrichsScalar:
938950
@staticmethod
939951
def setMaxDissipation(maxDissipation: typing.SupportsFloat | typing.SupportsIndex) -> None:

python/viennaps/d3/__init__.pyi

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ class BoxDistribution(ProcessModel):
2727
...
2828
def addMaskMaterial(self, material: viennaps._core.Material) -> None:
2929
...
30+
def applyToSingleMaterial(self, material: viennaps._core.Material) -> None:
31+
...
3032
class CF4O2Etching(ProcessModel):
3133
@typing.overload
3234
def __init__(self) -> None:
@@ -302,6 +304,8 @@ class Domain:
302304
"""
303305
Get the cell set.
304306
"""
307+
def getDiskMesh(self) -> viennals._core.Mesh:
308+
...
305309
def getGrid(self) -> viennals.d3.hrleGrid:
306310
"""
307311
Get the grid
@@ -318,6 +322,10 @@ class Domain:
318322
"""
319323
def getLevelSets(self) -> list[viennals.d3.Domain]:
320324
...
325+
def getMaterialLevelSet(self, material: viennaps._core.Material) -> viennals.d3.Domain:
326+
"""
327+
Returns a Level-Set representing the specified material in the domain.
328+
"""
321329
def getMaterialMap(self) -> viennaps._core.MaterialMap:
322330
...
323331
def getMaterialsInDomain(self) -> set[viennaps._core.Material]:
@@ -378,6 +386,8 @@ class Domain:
378386
...
379387
def removeTopLevelSet(self) -> None:
380388
...
389+
def saveDiskMesh(self, filename: str) -> None:
390+
...
381391
def saveHullMesh(self, filename: str, bottomExtension: typing.SupportsFloat | typing.SupportsIndex = 0.0, sharpCorners: bool = False) -> None:
382392
"""
383393
Save the hull of the domain.
@@ -934,6 +944,8 @@ class SphereDistribution(ProcessModel):
934944
...
935945
def addMaskMaterial(self, material: viennaps._core.Material) -> None:
936946
...
947+
def applyToSingleMaterial(self, material: viennaps._core.Material) -> None:
948+
...
937949
class StencilLocalLaxFriedrichsScalar:
938950
@staticmethod
939951
def setMaxDissipation(maxDissipation: typing.SupportsFloat | typing.SupportsIndex) -> None:

0 commit comments

Comments
 (0)