diff --git a/include/viennaps/models/psGeometricDistributionModels.hpp b/include/viennaps/models/psGeometricDistributionModels.hpp index bff0d5d6..19907fa7 100644 --- a/include/viennaps/models/psGeometricDistributionModels.hpp +++ b/include/viennaps/models/psGeometricDistributionModels.hpp @@ -32,6 +32,13 @@ class SphereDistribution : public ProcessModelCPU { assert(geomModel != nullptr); geomModel->addMaskMaterial(material); } + + void applyToSingleMaterial(const Material material) { + auto geomModel = this->getGeometricModel(); + assert(geomModel != nullptr); + geomModel->setSingleMaterial(true); + geomModel->addMaskMaterial(material); + } }; template @@ -66,6 +73,13 @@ class BoxDistribution : public ProcessModelCPU { assert(geomModel != nullptr); geomModel->addMaskMaterial(material); } + + void applyToSingleMaterial(const Material material) { + auto geomModel = this->getGeometricModel(); + assert(geomModel != nullptr); + geomModel->setSingleMaterial(true); + geomModel->addMaskMaterial(material); + } }; template diff --git a/include/viennaps/process/psGeometricModel.hpp b/include/viennaps/process/psGeometricModel.hpp index 549887c2..fd1e6b84 100644 --- a/include/viennaps/process/psGeometricModel.hpp +++ b/include/viennaps/process/psGeometricModel.hpp @@ -16,6 +16,7 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class GeometricModel { SmartPointer> mask = nullptr; std::vector maskMaterials; bool isDepo = false; + bool applySingleMaterial = false; public: GeometricModel() = default; @@ -49,6 +50,10 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class GeometricModel { auto &getMaskMaterials() const { return maskMaterials; } auto isDeposition() const { return isDepo; } void setDeposition(bool deposition) { isDepo = deposition; } + auto isSingleMaterial() const { return applySingleMaterial; } + void setSingleMaterial(bool singleMaterial) { + applySingleMaterial = singleMaterial; + } }; } // namespace viennaps diff --git a/include/viennaps/process/psGeometricProcessStrategy.hpp b/include/viennaps/process/psGeometricProcessStrategy.hpp index fbe8ee01..09aabbc6 100644 --- a/include/viennaps/process/psGeometricProcessStrategy.hpp +++ b/include/viennaps/process/psGeometricProcessStrategy.hpp @@ -44,54 +44,71 @@ class GeometricProcessStrategy : public ProcessStrategy { return ProcessResult::INVALID_INPUT; } - auto maskLevelSet = SmartPointer>::New( - domain->getGrid()); - bool foundMaterial = false; - - auto const &levelSets = domain->getLevelSets(); - for (auto &material : maskMaterials) { - for (int j = 0; j < levelSets.size(); ++j) { - if (materialMap->getMaterialAtIdx(j) == material) { - auto lsCopy = SmartPointer>::New( - levelSets[j]); - - // remove all lower level sets that are not mask materials - for (int k = j - 1; k >= 0; --k) { - if (MaterialMap::isMaterial(materialMap->getMaterialAtIdx(k), - maskMaterials)) - continue; - - viennals::BooleanOperation( - lsCopy, levelSets[k], - viennals::BooleanOperationEnum::RELATIVE_COMPLEMENT) - .apply(); - } - - if (foundMaterial) { - // union with mask level set - viennals::BooleanOperation( - maskLevelSet, lsCopy, viennals::BooleanOperationEnum::UNION) - .apply(); - } else { - maskLevelSet = lsCopy; - foundMaterial = true; + if (geometricModel->isSingleMaterial() && maskMaterials.size() == 1) { + // operation should be applied to a single material, so we use the + // surface as mask and exclude the material from the surface + mask = viennals::Domain::New(domain->getSurface()); + auto materialLS = domain->getMaterialLevelSet(maskMaterials[0]); + if (materialLS) { + viennals::BooleanOperation( + mask, materialLS, + viennals::BooleanOperationEnum::RELATIVE_COMPLEMENT) + .apply(); + VIENNACORE_LOG_DEBUG( + "Created mask level set from single mask material: " + + materialMap->toString(maskMaterials[0])); + } + } else { + auto maskLevelSet = + viennals::Domain::New(domain->getGrid()); + bool foundMaterial = false; + auto const &levelSets = domain->getLevelSets(); + for (auto &material : maskMaterials) { + for (int j = 0; j < levelSets.size(); ++j) { + if (materialMap->getMaterialAtIdx(j) == material) { + auto lsCopy = + viennals::Domain::New(levelSets[j]); + + // remove all lower level sets that are not mask materials + for (int k = j - 1; k >= 0; --k) { + if (MaterialMap::isMaterial(materialMap->getMaterialAtIdx(k), + maskMaterials)) + continue; + + viennals::BooleanOperation( + lsCopy, levelSets[k], + viennals::BooleanOperationEnum::RELATIVE_COMPLEMENT) + .apply(); + } + + if (foundMaterial) { + // union with mask level set + viennals::BooleanOperation( + maskLevelSet, lsCopy, + viennals::BooleanOperationEnum::UNION) + .apply(); + } else { + maskLevelSet = lsCopy; + foundMaterial = true; + } } } } - } - if (maskLevelSet->getNumberOfPoints() > 0) { - mask = maskLevelSet; - if (Logger::hasDebug()) { - auto dbgMesh = viennals::Mesh::New(); - viennals::ToMesh(mask, dbgMesh).apply(); - viennals::VTKWriter(dbgMesh, "geometric_mask_debug") - .apply(); + if (maskLevelSet->getNumberOfPoints() > 0) { + mask = maskLevelSet; + if (Logger::hasDebug()) { + auto dbgMesh = viennals::Mesh::New(); + viennals::ToMesh(mask, dbgMesh).apply(); + viennals::VTKWriter(dbgMesh, "geometric_mask_debug") + .apply(); + } + } else { + VIENNACORE_LOG_WARNING( + "None of the specified mask materials were found in the " + "domain, " + "cannot create mask level set from mask materials."); } - } else { - VIENNACORE_LOG_WARNING( - "None of the specified mask materials were found in the domain, " - "cannot create mask level set from mask materials."); } } } @@ -104,8 +121,7 @@ class GeometricProcessStrategy : public ProcessStrategy { // last level set. for (int i = context.domain->getNumberOfLevelSets() - 1; i >= 0; --i) { viennals::BooleanOperation( - context.domain->getLevelSets()[i], - context.domain->getLevelSets().back(), + context.domain->getLevelSets()[i], context.domain->getSurface(), viennals::BooleanOperationEnum::INTERSECT) .apply(); } diff --git a/include/viennaps/psDomain.hpp b/include/viennaps/psDomain.hpp index 73670eb6..d1670579 100644 --- a/include/viennaps/psDomain.hpp +++ b/include/viennaps/psDomain.hpp @@ -447,6 +447,7 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Domain { return levelSets_.back()->getGrid().getBoundaryConditions(); } + // Returns a set of all materials present in the domain. auto getMaterialsInDomain() const { std::set materials; for (std::size_t i = 0; i < materialMap_->size(); i++) { @@ -455,6 +456,43 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Domain { return materials; } + // Returns a Level-Set representing the specified material in the domain. If + // the material is not present in the domain, it returns an empty Level-Set. + auto getMaterialLevelSet(const Material material) const { + lsDomainType levelSet = nullptr; + + for (int i = 0; i < levelSets_.size(); i++) { + if (materialMap_->getMaterialAtIdx(i) == material) { + auto lsCopy = + SmartPointer>::New(levelSets_[i]); + + // remove lower level set + if (i > 0) { + viennals::BooleanOperation( + lsCopy, levelSets_[i - 1], + viennals::BooleanOperationEnum::RELATIVE_COMPLEMENT) + .apply(); + } + + if (levelSet) { + // add to level set + viennals::BooleanOperation( + levelSet, lsCopy, viennals::BooleanOperationEnum::UNION) + .apply(); + } else { + levelSet = lsCopy; + } + } + } + + if (!levelSet) { + VIENNACORE_LOG_WARNING("Material " + MaterialMap::toString(material) + + " not found in domain."); + } + + return levelSet; + } + void print(std::ostream &out = std::cout, bool hrle = false) const { constexpr std::string_view separator = "*****************************************\n"; @@ -655,8 +693,8 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Domain { private: void materialMapCheck() const { if (materialMap_->size() != levelSets_.size()) { - VIENNACORE_LOG_WARNING( - "Size mismatch in material map and number of Level-Sets in domain."); + VIENNACORE_LOG_WARNING("Size mismatch in material map and number of " + "Level-Sets in domain."); } } diff --git a/python/pyWrapDimension.hpp b/python/pyWrapDimension.hpp index 3dff3ed2..60b85987 100644 --- a/python/pyWrapDimension.hpp +++ b/python/pyWrapDimension.hpp @@ -538,6 +538,10 @@ template void bindApi(py::module &module) { .def("getLevelSets", &Domain::getLevelSets) .def("getMaterialsInDomain", &Domain::getMaterialsInDomain, "Get the material IDs present in the domain.") + .def("getMaterialLevelSet", &Domain::getMaterialLevelSet, + py::arg("material"), + "Returns a Level-Set representing the specified material in the " + "domain.") .def("getSurface", &Domain::getSurface, "Get the surface level set.") .def("getCellSet", &Domain::getCellSet, "Get the cell set.") @@ -564,6 +568,7 @@ template void bindApi(py::module &module) { "Get the surface mesh of the domain") .def("getHullMesh", &Domain::getHullMesh, py::arg("bottomExtension") = 0.0, py::arg("sharpCorners") = false) + .def("getDiskMesh", &Domain::getDiskMesh) // Save to file .def("saveLevelSetMesh", &Domain::saveLevelSetMesh, py::arg("filename"), py::arg("width") = 1, @@ -576,6 +581,7 @@ template void bindApi(py::module &module) { .def("saveHullMesh", &Domain::saveHullMesh, py::arg("filename"), py::arg("bottomExtension") = 0.0, py::arg("sharpCorners") = false, "Save the hull of the domain.") + .def("saveDiskMesh", &Domain::saveDiskMesh, py::arg("filename")) .def("saveVolumeMesh", &Domain::saveVolumeMesh, py::arg("filename"), py::arg("wrappingLayerEpsilon") = 1e-2, "Save the volume representation of the domain.") @@ -1055,6 +1061,9 @@ template void bindApi(py::module &module) { }), py::arg("radius")) .def("addMaskMaterial", &SphereDistribution::addMaskMaterial, + py::arg("material")) + .def("applyToSingleMaterial", + &SphereDistribution::applyToSingleMaterial, py::arg("material")); // Box Distribution @@ -1070,7 +1079,9 @@ template void bindApi(py::module &module) { }), py::arg("halfAxes")) .def("addMaskMaterial", &BoxDistribution::addMaskMaterial, - py::arg("material")); + py::arg("material")) + .def("applyToSingleMaterial", + &BoxDistribution::applyToSingleMaterial, py::arg("material")); // Custom Sphere Distribution py::class_, diff --git a/python/viennaps/d2/__init__.pyi b/python/viennaps/d2/__init__.pyi index 379610a0..001d57de 100644 --- a/python/viennaps/d2/__init__.pyi +++ b/python/viennaps/d2/__init__.pyi @@ -27,6 +27,8 @@ class BoxDistribution(ProcessModel): ... def addMaskMaterial(self, material: viennaps._core.Material) -> None: ... + def applyToSingleMaterial(self, material: viennaps._core.Material) -> None: + ... class CF4O2Etching(ProcessModel): @typing.overload def __init__(self) -> None: @@ -302,6 +304,8 @@ class Domain: """ Get the cell set. """ + def getDiskMesh(self) -> viennals._core.Mesh: + ... def getGrid(self) -> viennals.d2.hrleGrid: """ Get the grid @@ -318,6 +322,10 @@ class Domain: """ def getLevelSets(self) -> list[viennals.d2.Domain]: ... + def getMaterialLevelSet(self, material: viennaps._core.Material) -> viennals.d2.Domain: + """ + Returns a Level-Set representing the specified material in the domain. + """ def getMaterialMap(self) -> viennaps._core.MaterialMap: ... def getMaterialsInDomain(self) -> set[viennaps._core.Material]: @@ -378,6 +386,8 @@ class Domain: ... def removeTopLevelSet(self) -> None: ... + def saveDiskMesh(self, filename: str) -> None: + ... def saveHullMesh(self, filename: str, bottomExtension: typing.SupportsFloat | typing.SupportsIndex = 0.0, sharpCorners: bool = False) -> None: """ Save the hull of the domain. @@ -934,6 +944,8 @@ class SphereDistribution(ProcessModel): ... def addMaskMaterial(self, material: viennaps._core.Material) -> None: ... + def applyToSingleMaterial(self, material: viennaps._core.Material) -> None: + ... class StencilLocalLaxFriedrichsScalar: @staticmethod def setMaxDissipation(maxDissipation: typing.SupportsFloat | typing.SupportsIndex) -> None: diff --git a/python/viennaps/d3/__init__.pyi b/python/viennaps/d3/__init__.pyi index cba880fe..16ad1878 100644 --- a/python/viennaps/d3/__init__.pyi +++ b/python/viennaps/d3/__init__.pyi @@ -27,6 +27,8 @@ class BoxDistribution(ProcessModel): ... def addMaskMaterial(self, material: viennaps._core.Material) -> None: ... + def applyToSingleMaterial(self, material: viennaps._core.Material) -> None: + ... class CF4O2Etching(ProcessModel): @typing.overload def __init__(self) -> None: @@ -302,6 +304,8 @@ class Domain: """ Get the cell set. """ + def getDiskMesh(self) -> viennals._core.Mesh: + ... def getGrid(self) -> viennals.d3.hrleGrid: """ Get the grid @@ -318,6 +322,10 @@ class Domain: """ def getLevelSets(self) -> list[viennals.d3.Domain]: ... + def getMaterialLevelSet(self, material: viennaps._core.Material) -> viennals.d3.Domain: + """ + Returns a Level-Set representing the specified material in the domain. + """ def getMaterialMap(self) -> viennaps._core.MaterialMap: ... def getMaterialsInDomain(self) -> set[viennaps._core.Material]: @@ -378,6 +386,8 @@ class Domain: ... def removeTopLevelSet(self) -> None: ... + def saveDiskMesh(self, filename: str) -> None: + ... def saveHullMesh(self, filename: str, bottomExtension: typing.SupportsFloat | typing.SupportsIndex = 0.0, sharpCorners: bool = False) -> None: """ Save the hull of the domain. @@ -934,6 +944,8 @@ class SphereDistribution(ProcessModel): ... def addMaskMaterial(self, material: viennaps._core.Material) -> None: ... + def applyToSingleMaterial(self, material: viennaps._core.Material) -> None: + ... class StencilLocalLaxFriedrichsScalar: @staticmethod def setMaxDissipation(maxDissipation: typing.SupportsFloat | typing.SupportsIndex) -> None: