From 5bcfadc532da8d17474e19fab3eb11d0f52bb06f Mon Sep 17 00:00:00 2001 From: reiter Date: Tue, 24 Mar 2026 11:30:27 +0100 Subject: [PATCH 01/32] feat: add function to insert mask at front of material stack --- include/viennaps/psDomain.hpp | 20 ++++++++++++++++++++ python/pyWrapDimension.hpp | 5 +++++ 2 files changed, 25 insertions(+) diff --git a/include/viennaps/psDomain.hpp b/include/viennaps/psDomain.hpp index 562dea01..10377406 100644 --- a/include/viennaps/psDomain.hpp +++ b/include/viennaps/psDomain.hpp @@ -191,6 +191,26 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Domain { materialMapCheck(); } + void insertMask(lsDomainType mask, const Material material = Material::Mask) { + // insert mask at front of level set vector + levelSets_.insert(levelSets_.begin(), mask); + for (std::size_t i = 1; i < levelSets_.size(); i++) { + viennals::BooleanOperation( + levelSets_.at(i), mask, viennals::BooleanOperationEnum::UNION) + .apply(); + } + + auto newMatMap = MaterialMapType::New(); + newMatMap->insertNextMaterial(material); + if (materialMap_) { + for (std::size_t i = 0; i < materialMap_->size(); i++) { + newMatMap->insertNextMaterial(materialMap_->getMaterialAtIdx(i)); + } + } + materialMap_ = newMatMap; + materialMapCheck(); + } + // Copy the top Level-Set and insert it in the domain (e.g. in order to // capture depositing material on top of the surface). void duplicateTopLevelSet(const Material material) { diff --git a/python/pyWrapDimension.hpp b/python/pyWrapDimension.hpp index c8c7883d..8cc221ae 100644 --- a/python/pyWrapDimension.hpp +++ b/python/pyWrapDimension.hpp @@ -493,6 +493,11 @@ template void bindApi(py::module &module) { &Domain::insertNextLevelSetAsMaterial, py::arg("levelSet"), py::arg("material"), py::arg("wrapLowerLevelSet") = true, "Insert a level set to domain as a material.") + .def("insertMask", &Domain::insertMask, py::arg("mask"), + py::arg("material") = Material::Mask, + "Insert a mask level set to the domain. The mask is inserted at the " + "front of the level set vector and can be used to exclude areas " + "from processes.") .def("duplicateTopLevelSet", &Domain::duplicateTopLevelSet, "Duplicate the top level set. Should be used before a deposition " "process.") From 4507984bfa9d3fc6fde1cea06fce9150fe17f9d3 Mon Sep 17 00:00:00 2001 From: reiter Date: Wed, 25 Mar 2026 13:48:21 +0100 Subject: [PATCH 02/32] feat: directional process rate based --- .../viennaps/models/psDirectionalProcess.hpp | 66 ++++++++++++++++- include/viennaps/psMaterials.hpp | 73 +++++++++++++++++++ python/pyWrapDimension.hpp | 5 ++ python/viennaps/_core/__init__.pyi | 1 + python/viennaps/d2/__init__.pyi | 7 ++ python/viennaps/d3/__init__.pyi | 7 ++ tests/directionalEtch/directionalEtch.cpp | 26 +++++++ tests/directionalEtch/test_directional_3d.py | 42 ++++++++++- 8 files changed, 222 insertions(+), 5 deletions(-) diff --git a/include/viennaps/models/psDirectionalProcess.hpp b/include/viennaps/models/psDirectionalProcess.hpp index ac439ede..a616159a 100644 --- a/include/viennaps/models/psDirectionalProcess.hpp +++ b/include/viennaps/models/psDirectionalProcess.hpp @@ -145,6 +145,36 @@ class DirectionalVelocityField : public VelocityField { } }; +template +class DirectionalVelocityFieldSimple : public VelocityField { + using MaterialRateMap = MaterialValueMap>; + const Vec3D direction_; + const MaterialRateMap materialRates_; + +public: + DirectionalVelocityFieldSimple(const MaterialRateMap &materialRates, + const Vec3D &direction) + : materialRates_(materialRates), direction_(direction) {} + + NumericType getScalarVelocity(const Vec3D &coordinate, + int material, + const Vec3D &normalVector, + unsigned long pointId) override { + const auto &rate = + materialRates_.getRef(MaterialMap::mapToMaterial(material)); + return rate.second; // isotropic velocity + } + + Vec3D getVectorVelocity(const Vec3D &coordinate, + int material, + const Vec3D &normalVector, + unsigned long pointId) override { + const auto &rate = + materialRates_.getRef(MaterialMap::mapToMaterial(material)); + return direction_ * rate.first; // directional velocity + } +}; + } // namespace impl /// Directional rate with multiple rate sets and masking materials. @@ -152,6 +182,18 @@ template class DirectionalProcess : public ProcessModelCPU { public: using RateSet = impl::RateSet; + using RateMap = MaterialValueMap>; + + DirectionalProcess( + const Vec3D &direction, + std::unordered_map> + materialRates, + NumericType defaultDirectionalRate = 0., + NumericType defaultIsotropicRate = 0.) { + RateMap rateMap(std::move(materialRates)); + rateMap.setDefault({defaultDirectionalRate, defaultIsotropicRate}); + initialize(std::move(rateMap), direction); + } DirectionalProcess(const Vec3D &direction, NumericType directionalVelocity, @@ -191,10 +233,20 @@ class DirectionalProcess : public ProcessModelCPU { } private: - void initialize(std::vector &&rateSets) { - // Default surface model - auto surfModel = SmartPointer>::New(); + void initialize(RateMap &&rateMap, const Vec3D &direction) { + // Store process data + // TODO + // Velocity field with rate map + auto velField = + SmartPointer>::New( + rateMap, direction); + this->setVelocityField(velField); + + baseData(); + } + + void initialize(std::vector &&rateSets) { // Store process data processMetaData["DirectionalVelocity"] = std::vector(); processMetaData["IsotropicVelocity"] = std::vector(); @@ -219,8 +271,14 @@ class DirectionalProcess : public ProcessModelCPU { auto velField = SmartPointer>::New( std::move(rateSets)); - this->setVelocityField(velField); + + baseData(); + } + + void baseData() { + // Default surface model + auto surfModel = SmartPointer>::New(); this->setSurfaceModel(surfModel); this->setProcessName("DirectionalProcess"); } diff --git a/include/viennaps/psMaterials.hpp b/include/viennaps/psMaterials.hpp index f20fd4e0..2ca48bd5 100644 --- a/include/viennaps/psMaterials.hpp +++ b/include/viennaps/psMaterials.hpp @@ -51,6 +51,7 @@ enum class MaterialCategory : uint8_t { X(19, SiBCN, OxideNitride, 2.3, false, 0x00897b) \ X(20, SiCOH, OxideNitride, 1.9, false, 0x26c6da) \ X(21, SiOCN, OxideNitride, 2.1, false, 0x00acc1) \ + X(22, BulkSi, Silicon, 2.33, false, 0xc79a08) \ /* Oxides and nitrides */ \ X(30, SiO2, OxideNitride, 2.2, false, 0x66ccff) \ X(31, Al2O3, OxideNitride, 3.95, false, 0x90caf9) \ @@ -170,6 +171,78 @@ constexpr double density(Material m) { return info(m).density_gcm3; } constexpr bool isConductive(Material m) { return info(m).conductive; } constexpr uint32_t color(Material m) { return info(m).colorHex; } +template class MaterialValueMap { +public: + using Value = T; + + MaterialValueMap() = default; + + // constructor from unordered_map + MaterialValueMap(const std::unordered_map &map) { + for (const auto &[key, value] : map) { + set(key, value); + } + } + + // generic constructor (works with map, vector, initializer_list, etc.) + template explicit MaterialValueMap(const MapLike &mapLike) { + for (const auto &[key, value] : mapLike) { + set(key, value); + } + } + + // set value for a material + void set(Material m, const T &value) { + auto idx = toIndex(m); + values_[idx] = value; + isSet_[idx] = true; + } + + // perfect-forwarding overload + template void emplace(Material m, Args &&...args) { + auto idx = toIndex(m); + values_[idx] = T(std::forward(args)...); + isSet_[idx] = true; + } + + // get value or default T{} + [[nodiscard]] T get(Material m) const { + auto idx = toIndex(m); + return isSet_[idx] ? values_[idx] : T{}; + } + + // get reference (no copy) + [[nodiscard]] const T &getRef(Material m) const { + auto idx = toIndex(m); + return isSet_[idx] ? values_[idx] : default_; + } + + void setDefault(const T &v) { default_ = v; } + + // check if user provided a value + [[nodiscard]] bool has(Material m) const { return isSet_[toIndex(m)]; } + + // remove value -> fallback to default + void clear(Material m) { isSet_[toIndex(m)] = false; } + + void clearAll() { + for (auto &b : isSet_) { + b = false; + } + } + +private: + static constexpr std::size_t toIndex(Material m) { + return static_cast(m); + } + + std::array values_{}; + std::array isSet_{}; + + // default instance used for reference return + T default_{}; +}; + #ifndef __CUDACC__ /// A class that wraps the viennals MaterialMap class and provides a more user /// friendly interface. It also provides a mapping from the integer material id diff --git a/python/pyWrapDimension.hpp b/python/pyWrapDimension.hpp index 8cc221ae..9a8cc5fb 100644 --- a/python/pyWrapDimension.hpp +++ b/python/pyWrapDimension.hpp @@ -929,6 +929,11 @@ template void bindApi(py::module &module) { // DirectionalProcess py::class_, SmartPointer>>( module, "DirectionalProcess", processModel) + .def(py::init &, + std::unordered_map>, T, T>(), + py::arg("direction"), py::arg("materialRates"), + py::arg("defaultDirectionalRate") = 0., + py::arg("defaultIsotropicRate") = 0.) .def(py::init &, T, T, Material, bool>(), py::arg("direction"), py::arg("directionalVelocity"), py::arg("isotropicVelocity") = 0., diff --git a/python/viennaps/_core/__init__.pyi b/python/viennaps/_core/__init__.pyi index 3d990282..5ac366df 100644 --- a/python/viennaps/_core/__init__.pyi +++ b/python/viennaps/_core/__init__.pyi @@ -810,6 +810,7 @@ class Material(enum.IntEnum): Au: typing.ClassVar[Material] # value = BN: typing.ClassVar[Material] # value = BPSG: typing.ClassVar[Material] # value = + BulkSi: typing.ClassVar[Material] # value = C: typing.ClassVar[Material] # value = Co: typing.ClassVar[Material] # value = CoW: typing.ClassVar[Material] # value = diff --git a/python/viennaps/d2/__init__.pyi b/python/viennaps/d2/__init__.pyi index d0fccca7..cea359a7 100644 --- a/python/viennaps/d2/__init__.pyi +++ b/python/viennaps/d2/__init__.pyi @@ -199,6 +199,9 @@ class DenseCellSet: """ class DirectionalProcess(ProcessModel): @typing.overload + def __init__(self, direction: typing.Annotated[collections.abc.Sequence[typing.SupportsFloat | typing.SupportsIndex], "FixedSize(3)"], materialRates: collections.abc.Mapping[viennaps._core.Material, tuple[typing.SupportsFloat | typing.SupportsIndex, typing.SupportsFloat | typing.SupportsIndex]], defaultDirectionalRate: typing.SupportsFloat | typing.SupportsIndex = 0.0, defaultIsotropicRate: typing.SupportsFloat | typing.SupportsIndex = 0.0) -> None: + ... + @typing.overload def __init__(self, direction: typing.Annotated[collections.abc.Sequence[typing.SupportsFloat | typing.SupportsIndex], "FixedSize(3)"], directionalVelocity: typing.SupportsFloat | typing.SupportsIndex, isotropicVelocity: typing.SupportsFloat | typing.SupportsIndex = 0.0, maskMaterial: viennaps._core.Material = ..., calculateVisibility: bool = True) -> None: ... @typing.overload @@ -336,6 +339,10 @@ class Domain: """ Get the surface mesh of the domain """ + def insertMask(self, mask: viennals.d2.Domain, material: viennaps._core.Material = ...) -> None: + """ + Insert a mask level set to the domain. The mask is inserted at the front of the level set vector and can be used to exclude areas from processes. + """ def insertNextLevelSetAsMaterial(self, levelSet: viennals.d2.Domain, material: viennaps._core.Material, wrapLowerLevelSet: bool = True) -> None: """ Insert a level set to domain as a material. diff --git a/python/viennaps/d3/__init__.pyi b/python/viennaps/d3/__init__.pyi index 0275d06a..ae6618a0 100644 --- a/python/viennaps/d3/__init__.pyi +++ b/python/viennaps/d3/__init__.pyi @@ -199,6 +199,9 @@ class DenseCellSet: """ class DirectionalProcess(ProcessModel): @typing.overload + def __init__(self, direction: typing.Annotated[collections.abc.Sequence[typing.SupportsFloat | typing.SupportsIndex], "FixedSize(3)"], materialRates: collections.abc.Mapping[viennaps._core.Material, tuple[typing.SupportsFloat | typing.SupportsIndex, typing.SupportsFloat | typing.SupportsIndex]], defaultDirectionalRate: typing.SupportsFloat | typing.SupportsIndex = 0.0, defaultIsotropicRate: typing.SupportsFloat | typing.SupportsIndex = 0.0) -> None: + ... + @typing.overload def __init__(self, direction: typing.Annotated[collections.abc.Sequence[typing.SupportsFloat | typing.SupportsIndex], "FixedSize(3)"], directionalVelocity: typing.SupportsFloat | typing.SupportsIndex, isotropicVelocity: typing.SupportsFloat | typing.SupportsIndex = 0.0, maskMaterial: viennaps._core.Material = ..., calculateVisibility: bool = True) -> None: ... @typing.overload @@ -336,6 +339,10 @@ class Domain: """ Get the surface mesh of the domain """ + def insertMask(self, mask: viennals.d3.Domain, material: viennaps._core.Material = ...) -> None: + """ + Insert a mask level set to the domain. The mask is inserted at the front of the level set vector and can be used to exclude areas from processes. + """ def insertNextLevelSetAsMaterial(self, levelSet: viennals.d3.Domain, material: viennaps._core.Material, wrapLowerLevelSet: bool = True) -> None: """ Insert a level set to domain as a material. diff --git a/tests/directionalEtch/directionalEtch.cpp b/tests/directionalEtch/directionalEtch.cpp index b0039e6f..fc2269df 100644 --- a/tests/directionalEtch/directionalEtch.cpp +++ b/tests/directionalEtch/directionalEtch.cpp @@ -60,6 +60,32 @@ template void RunTest() { VC_TEST_ASSERT(domain->getMaterialMap()->size() == 2); LSTEST_ASSERT_VALID_LS(domain->getLevelSets().back(), NumericType, D); } + + { + auto domain = Domain::New(); + MakeTrench(domain, 1., 10., 10., 2.5, 5., 10., 1., false, + true, Material::Si) + .apply(); + + std::unordered_map> + materialRates; + Vec3D direction{0., 0., 0.}; + direction[D - 1] = -1.; + materialRates[Material::Si] = {1., 0.}; + + auto model = SmartPointer>::New( + direction, materialRates); + + VC_TEST_ASSERT(model->getSurfaceModel()); + VC_TEST_ASSERT(model->getVelocityField()); + + Process(domain, model, 2.).apply(); + + VC_TEST_ASSERT(domain->getLevelSets().size() == 2); + VC_TEST_ASSERT(domain->getMaterialMap()); + VC_TEST_ASSERT(domain->getMaterialMap()->size() == 2); + LSTEST_ASSERT_VALID_LS(domain->getLevelSets().back(), NumericType, D); + } } } // namespace viennacore diff --git a/tests/directionalEtch/test_directional_3d.py b/tests/directionalEtch/test_directional_3d.py index e40e910a..e6cb36f9 100644 --- a/tests/directionalEtch/test_directional_3d.py +++ b/tests/directionalEtch/test_directional_3d.py @@ -1,4 +1,7 @@ -import viennaps3d as vps +import viennaps as vps + +vps.setDimension(3) + def run3D(): vps.Logger.setLogLevel(vps.LogLevel.WARNING) @@ -36,5 +39,42 @@ def run3D(): print("3D DirectionalProcess test passed") + +def testRateBased(): + vps.Logger.setLogLevel(vps.LogLevel.WARNING) + + domain = vps.Domain( + gridDelta=0.95, + xExtent=10.0, + yExtent=10.0, + boundary=vps.BoundaryType.REFLECTIVE_BOUNDARY, + ) + vps.MakeTrench( + domain=domain, + trenchWidth=2.0, + trenchDepth=0.0, + maskHeight=5.0, + maskMaterial=vps.Material.Mask, + material=vps.Material.Si, + ).apply() + + ls = domain.getLevelSets() + print(len(ls)) + + model = vps.DirectionalProcess( + direction=[0.0, 0.0, -1.0], + materialRates={ + vps.Material.Si: (5.0, 0.1), + vps.Material.Mask: (0.5, 0.0), + }, + ) + vps.Process(domain, model, 2.0).apply() + + domain.saveSurfaceMesh("DirectionalProcess3D_RateBased.vtp", addInterfaces=True) + + print("3D DirectionalProcess with rate map test passed") + + if __name__ == "__main__": run3D() + testRateBased() From 53f3251afb91cf7b38547ef88dc45fbf51a2a87e Mon Sep 17 00:00:00 2001 From: reiter Date: Wed, 25 Mar 2026 14:18:47 +0100 Subject: [PATCH 03/32] feat: material value mapping in single particle process rates --- .../models/psSingleParticleProcess.hpp | 100 ++++++++++-------- include/viennaps/psMaterials.hpp | 55 ++++++++-- 2 files changed, 101 insertions(+), 54 deletions(-) diff --git a/include/viennaps/models/psSingleParticleProcess.hpp b/include/viennaps/models/psSingleParticleProcess.hpp index 6d52f4a0..debe885d 100644 --- a/include/viennaps/models/psSingleParticleProcess.hpp +++ b/include/viennaps/models/psSingleParticleProcess.hpp @@ -13,13 +13,12 @@ using namespace viennacore; namespace impl { template class SingleParticleSurfaceModel : public viennaps::SurfaceModel { - const NumericType rate_; - const std::unordered_map materialRates_; + using MaterialRateMap = MaterialValueMap; + const MaterialRateMap materialRates_; public: - SingleParticleSurfaceModel( - NumericType rate, const std::unordered_map &mask) - : rate_(rate), materialRates_(mask) {} + SingleParticleSurfaceModel(MaterialRateMap const &materialRates) + : materialRates_(materialRates) {} SmartPointer> calculateVelocities(SmartPointer> rates, @@ -33,13 +32,9 @@ class SingleParticleSurfaceModel : public viennaps::SurfaceModel { #pragma omp parallel for for (size_t i = 0; i < velocity->size(); i++) { - if (auto matRate = - materialRates_.find(MaterialMap::mapToMaterial(materialIds[i])); - matRate == materialRates_.end()) { - velocity->at(i) = flux->at(i) * rate_; - } else { - velocity->at(i) = flux->at(i) * matRate->second; - } + velocity->at(i) = + flux->at(i) * + materialRates_.getRef(MaterialMap::mapToMaterial(materialIds[i])); } return velocity; @@ -90,15 +85,22 @@ namespace gpu { template class SingleParticleProcess final : public ProcessModelGPU { + NumericType stickingProbability_ = 1.; + NumericType sourceDistributionPower_ = 1.; + MaterialValueMap materialRates_; + public: - SingleParticleProcess(std::unordered_map materialRates, - NumericType rate, NumericType stickingProbability, - NumericType sourceExponent) { + SingleParticleProcess(MaterialValueMap const &materialRates, + NumericType stickingProbability, + NumericType sourceExponent) + : stickingProbability_(stickingProbability), + sourceDistributionPower_(sourceExponent), + materialRates_(materialRates) { // particles viennaray::gpu::Particle particle{ .name = "SingleParticle", - .sticking = stickingProbability, - .cosineExponent = sourceExponent}; + .sticking = stickingProbability_, + .cosineExponent = sourceDistributionPower_}; particle.dataLabels.push_back("particleFlux"); std::unordered_map pMap = {{"SingleParticle", 0}}; @@ -111,7 +113,7 @@ class SingleParticleProcess final : public ProcessModelGPU { // surface model auto surfModel = SmartPointer<::viennaps::impl::SingleParticleSurfaceModel< - NumericType, D>>::New(rate, materialRates); + NumericType, D>>::New(materialRates_); // velocity field auto velField = @@ -123,18 +125,13 @@ class SingleParticleProcess final : public ProcessModelGPU { this->setProcessName("SingleParticleProcess"); this->hasGPU = true; - this->processMetaData["Default Rate"] = std::vector{rate}; this->processMetaData["Sticking Probability"] = - std::vector{stickingProbability}; + std::vector{stickingProbability_}; this->processMetaData["Source Exponent"] = - std::vector{sourceExponent}; - if (!materialRates.empty()) { - for (const auto &pair : materialRates) { - if (pair.first == Material::Undefined) - continue; - this->processMetaData[MaterialMap::toString(pair.first) + " Rate"] = - std::vector{pair.second}; - } + std::vector{sourceDistributionPower_}; + for (auto rate : materialRates_) { + this->processMetaData[MaterialMap::toString(rate.getMaterial()) + + " Rate"] = std::vector{rate.getValue()}; } } }; @@ -145,29 +142,30 @@ class SingleParticleProcess final : public ProcessModelGPU { // reflections. template class SingleParticleProcess : public ProcessModelCPU { - NumericType rate_ = 1.; NumericType stickingProbability_ = 1.; NumericType sourceDistributionPower_ = 1.; - std::unordered_map materialRates_; + MaterialValueMap materialRates_; public: SingleParticleProcess(NumericType rate = 1., NumericType stickingProbability = 1., NumericType sourceDistributionPower = 1., Material maskMaterial = Material::Undefined) - : rate_(rate), stickingProbability_(stickingProbability), + : stickingProbability_(stickingProbability), sourceDistributionPower_(sourceDistributionPower) { - materialRates_[maskMaterial] = 0.; + materialRates_.setDefault(rate); + materialRates_.set(maskMaterial, 0.0); initialize(); } SingleParticleProcess(NumericType rate, NumericType stickingProbability, NumericType sourceDistributionPower, std::vector maskMaterial) - : rate_(rate), stickingProbability_(stickingProbability), + : stickingProbability_(stickingProbability), sourceDistributionPower_(sourceDistributionPower) { + materialRates_.setDefault(rate); for (auto &mat : maskMaterial) { - materialRates_[mat] = 0.; + materialRates_.set(mat, 0.0); } initialize(); } @@ -175,21 +173,33 @@ class SingleParticleProcess : public ProcessModelCPU { SingleParticleProcess(std::unordered_map materialRates, NumericType stickingProbability, NumericType sourceDistributionPower) - : rate_(0.), stickingProbability_(stickingProbability), + : stickingProbability_(stickingProbability), sourceDistributionPower_(sourceDistributionPower), - materialRates_(std::move(materialRates)) { + materialRates_(materialRates) { + materialRates_.setDefault(0.); initialize(); } #ifdef VIENNACORE_COMPILE_GPU SmartPointer> getGPUModel() final { auto model = SmartPointer>::New( - materialRates_, rate_, stickingProbability_, sourceDistributionPower_); + materialRates_, stickingProbability_, sourceDistributionPower_); model->setProcessName(this->getProcessName().value_or("default")); return model; } #endif + void setDefaultRate(NumericType rate) { + materialRates_.setDefault(rate); + this->processMetaData["Default Rate"] = std::vector{rate}; + } + + void setMaterialRate(Material material, NumericType rate) { + materialRates_.set(material, rate); + this->processMetaData[MaterialMap::toString(material) + " Rate"] = + std::vector{rate}; + } + private: void initialize() { // particles @@ -199,7 +209,7 @@ class SingleParticleProcess : public ProcessModelCPU { // surface model auto surfModel = SmartPointer>::New( - rate_, materialRates_); + materialRates_); // velocity field auto velField = SmartPointer>::New(); @@ -210,19 +220,15 @@ class SingleParticleProcess : public ProcessModelCPU { this->setProcessName("SingleParticleProcess"); this->hasGPU = true; - this->processMetaData["Default Rate"] = std::vector{rate_}; + this->processMetaData["Default Rate"] = + std::vector{materialRates_.getDefault()}; this->processMetaData["Sticking Probability"] = std::vector{stickingProbability_}; this->processMetaData["Source Exponent"] = std::vector{sourceDistributionPower_}; - if (!materialRates_.empty()) { - for (const auto &pair : materialRates_) { - if (pair.first == Material::Undefined) - continue; // skip undefined material - - this->processMetaData[MaterialMap::toString(pair.first) + " Rate"] = - std::vector{pair.second}; - } + for (auto mrate : materialRates_) { + this->processMetaData[MaterialMap::toString(mrate.getMaterial()) + + " Rate"] = std::vector{mrate.getValue()}; } } }; diff --git a/include/viennaps/psMaterials.hpp b/include/viennaps/psMaterials.hpp index 2ca48bd5..6e5ad6fc 100644 --- a/include/viennaps/psMaterials.hpp +++ b/include/viennaps/psMaterials.hpp @@ -177,13 +177,6 @@ template class MaterialValueMap { MaterialValueMap() = default; - // constructor from unordered_map - MaterialValueMap(const std::unordered_map &map) { - for (const auto &[key, value] : map) { - set(key, value); - } - } - // generic constructor (works with map, vector, initializer_list, etc.) template explicit MaterialValueMap(const MapLike &mapLike) { for (const auto &[key, value] : mapLike) { @@ -219,6 +212,8 @@ template class MaterialValueMap { void setDefault(const T &v) { default_ = v; } + [[nodiscard]] const T &getDefault() const { return default_; } + // check if user provided a value [[nodiscard]] bool has(Material m) const { return isSet_[toIndex(m)]; } @@ -231,6 +226,52 @@ template class MaterialValueMap { } } + // ================= ITERATOR ================= + struct Entry { + Material material; + const T *value; + bool set; + + [[nodiscard]] bool isSet() const { return set; } + [[nodiscard]] const T &getValue() const { return *value; } + [[nodiscard]] Material getMaterial() const { return material; } + }; + + class Iterator { + public: + Iterator(const MaterialValueMap *map, std::size_t idx) + : map_(map), idx_(idx) { + advanceToValid(); + } + + Iterator &operator++() { + ++idx_; + advanceToValid(); + return *this; + } + + bool operator!=(const Iterator &other) const { return idx_ != other.idx_; } + + Entry operator*() const { + return Entry{static_cast(idx_), &map_->values_[idx_], + map_->isSet_[idx_]}; + } + + private: + void advanceToValid() { + while (idx_ < map_->values_.size() && !map_->isSet_[idx_]) { + ++idx_; + } + } + + const MaterialValueMap *map_; + std::size_t idx_; + }; + + Iterator begin() const { return Iterator(this, 0); } + + Iterator end() const { return Iterator(this, values_.size()); } + private: static constexpr std::size_t toIndex(Material m) { return static_cast(m); From 38a9b3e8cf180640d3f0e655fdc4a6e9738a3c15 Mon Sep 17 00:00:00 2001 From: reiter Date: Wed, 25 Mar 2026 14:27:11 +0100 Subject: [PATCH 04/32] feat: single particle process python bindings updated --- .../models/psSingleParticleProcess.hpp | 18 +++++------ python/pyWrapDimension.hpp | 30 ++++++++----------- python/viennaps/d2/__init__.pyi | 4 +++ python/viennaps/d2/gpu.pyi | 2 +- python/viennaps/d3/__init__.pyi | 4 +++ python/viennaps/d3/gpu.pyi | 2 +- 6 files changed, 31 insertions(+), 29 deletions(-) diff --git a/include/viennaps/models/psSingleParticleProcess.hpp b/include/viennaps/models/psSingleParticleProcess.hpp index debe885d..54708d43 100644 --- a/include/viennaps/models/psSingleParticleProcess.hpp +++ b/include/viennaps/models/psSingleParticleProcess.hpp @@ -180,15 +180,6 @@ class SingleParticleProcess : public ProcessModelCPU { initialize(); } -#ifdef VIENNACORE_COMPILE_GPU - SmartPointer> getGPUModel() final { - auto model = SmartPointer>::New( - materialRates_, stickingProbability_, sourceDistributionPower_); - model->setProcessName(this->getProcessName().value_or("default")); - return model; - } -#endif - void setDefaultRate(NumericType rate) { materialRates_.setDefault(rate); this->processMetaData["Default Rate"] = std::vector{rate}; @@ -200,6 +191,15 @@ class SingleParticleProcess : public ProcessModelCPU { std::vector{rate}; } +#ifdef VIENNACORE_COMPILE_GPU + SmartPointer> getGPUModel() final { + auto model = SmartPointer>::New( + materialRates_, stickingProbability_, sourceDistributionPower_); + model->setProcessName(this->getProcessName().value_or("default")); + return model; + } +#endif + private: void initialize() { // particles diff --git a/python/pyWrapDimension.hpp b/python/pyWrapDimension.hpp index 9a8cc5fb..f8243e8c 100644 --- a/python/pyWrapDimension.hpp +++ b/python/pyWrapDimension.hpp @@ -733,24 +733,18 @@ template void bindApi(py::module &module) { py::class_, SmartPointer>>( module, "SingleParticleProcess", processModel) - .def(py::init([](const T rate, const T sticking, const T power, - const Material mask) { - return SmartPointer>::New( - rate, sticking, power, mask); - }), - py::arg("rate") = 1., py::arg("stickingProbability") = 1., - py::arg("sourceExponent") = 1., + .def(py::init(), py::arg("rate") = 1., + py::arg("stickingProbability") = 1., py::arg("sourceExponent") = 1., py::arg("maskMaterial") = Material::Undefined) - .def(py::init([](const T rate, const T sticking, const T power, - const std::vector &mask) { - return SmartPointer>::New( - rate, sticking, power, mask); - }), - py::arg("rate"), py::arg("stickingProbability"), - py::arg("sourceExponent"), py::arg("maskMaterials")) + .def(py::init>(), py::arg("rate"), + py::arg("stickingProbability"), py::arg("sourceExponent"), + py::arg("maskMaterials")) .def(py::init, T, T>(), py::arg("materialRates"), py::arg("stickingProbability"), - py::arg("sourceExponent")); + py::arg("sourceExponent")) + .def("setDefaultRate", &SingleParticleProcess::setDefaultRate) + .def("setMaterialRate", &SingleParticleProcess::setMaterialRate, + py::arg("material"), py::arg("rate")); // Multi Particle Process py::class_, @@ -1516,9 +1510,9 @@ template void bindApi(py::module &module) { py::class_, SmartPointer>>( m_gpu, "SingleParticleProcess", processModel_gpu) - .def(py::init, T, T, T>(), - py::arg("materialRates"), py::arg("rate"), - py::arg("stickingProbability"), py::arg("sourceExponent")); + .def(py::init const &, T, T>(), + py::arg("materialRates"), py::arg("stickingProbability"), + py::arg("sourceExponent")); // Multi Particle Process py::class_, diff --git a/python/viennaps/d2/__init__.pyi b/python/viennaps/d2/__init__.pyi index cea359a7..266efe2a 100644 --- a/python/viennaps/d2/__init__.pyi +++ b/python/viennaps/d2/__init__.pyi @@ -882,6 +882,10 @@ class SingleParticleProcess(ProcessModel): @typing.overload def __init__(self, materialRates: collections.abc.Mapping[viennaps._core.Material, typing.SupportsFloat | typing.SupportsIndex], stickingProbability: typing.SupportsFloat | typing.SupportsIndex, sourceExponent: typing.SupportsFloat | typing.SupportsIndex) -> None: ... + def setDefaultRate(self, arg0: typing.SupportsFloat | typing.SupportsIndex) -> None: + ... + def setMaterialRate(self, material: viennaps._core.Material, rate: typing.SupportsFloat | typing.SupportsIndex) -> None: + ... class SphereDistribution(ProcessModel): @typing.overload def __init__(self, radius: typing.SupportsFloat | typing.SupportsIndex, mask: viennals.d2.Domain) -> None: diff --git a/python/viennaps/d2/gpu.pyi b/python/viennaps/d2/gpu.pyi index b84f1931..628672ef 100644 --- a/python/viennaps/d2/gpu.pyi +++ b/python/viennaps/d2/gpu.pyi @@ -35,5 +35,5 @@ class SF6O2Etching(ProcessModelGPU): def __init__(self, parameters: viennaps._core.PlasmaEtchingParameters) -> None: ... class SingleParticleProcess(ProcessModelGPU): - def __init__(self, materialRates: collections.abc.Mapping[viennaps._core.Material, typing.SupportsFloat | typing.SupportsIndex], rate: typing.SupportsFloat | typing.SupportsIndex, stickingProbability: typing.SupportsFloat | typing.SupportsIndex, sourceExponent: typing.SupportsFloat | typing.SupportsIndex) -> None: + def __init__(self, materialRates: ..., stickingProbability: typing.SupportsFloat | typing.SupportsIndex, sourceExponent: typing.SupportsFloat | typing.SupportsIndex) -> None: ... diff --git a/python/viennaps/d3/__init__.pyi b/python/viennaps/d3/__init__.pyi index ae6618a0..73e2e8b4 100644 --- a/python/viennaps/d3/__init__.pyi +++ b/python/viennaps/d3/__init__.pyi @@ -882,6 +882,10 @@ class SingleParticleProcess(ProcessModel): @typing.overload def __init__(self, materialRates: collections.abc.Mapping[viennaps._core.Material, typing.SupportsFloat | typing.SupportsIndex], stickingProbability: typing.SupportsFloat | typing.SupportsIndex, sourceExponent: typing.SupportsFloat | typing.SupportsIndex) -> None: ... + def setDefaultRate(self, arg0: typing.SupportsFloat | typing.SupportsIndex) -> None: + ... + def setMaterialRate(self, material: viennaps._core.Material, rate: typing.SupportsFloat | typing.SupportsIndex) -> None: + ... class SphereDistribution(ProcessModel): @typing.overload def __init__(self, radius: typing.SupportsFloat | typing.SupportsIndex, mask: viennals.d3.Domain) -> None: diff --git a/python/viennaps/d3/gpu.pyi b/python/viennaps/d3/gpu.pyi index 32987580..7e0d7d2a 100644 --- a/python/viennaps/d3/gpu.pyi +++ b/python/viennaps/d3/gpu.pyi @@ -35,5 +35,5 @@ class SF6O2Etching(ProcessModelGPU): def __init__(self, parameters: viennaps._core.PlasmaEtchingParameters) -> None: ... class SingleParticleProcess(ProcessModelGPU): - def __init__(self, materialRates: collections.abc.Mapping[viennaps._core.Material, typing.SupportsFloat | typing.SupportsIndex], rate: typing.SupportsFloat | typing.SupportsIndex, stickingProbability: typing.SupportsFloat | typing.SupportsIndex, sourceExponent: typing.SupportsFloat | typing.SupportsIndex) -> None: + def __init__(self, materialRates: ..., stickingProbability: typing.SupportsFloat | typing.SupportsIndex, sourceExponent: typing.SupportsFloat | typing.SupportsIndex) -> None: ... From 241a463644478dcfd54a38f3d4ade5492ebc102a Mon Sep 17 00:00:00 2001 From: reiter Date: Wed, 25 Mar 2026 14:47:25 +0100 Subject: [PATCH 05/32] feat: add mapping in IsotropicProcess --- .../viennaps/models/psIsotropicProcess.hpp | 70 +++++++++---------- .../models/psSingleParticleProcess.hpp | 5 +- python/pyWrapDimension.hpp | 23 +++--- python/viennaps/_core/__init__.pyi | 2 +- python/viennaps/d2/__init__.pyi | 6 +- python/viennaps/d3/__init__.pyi | 6 +- 6 files changed, 55 insertions(+), 57 deletions(-) diff --git a/include/viennaps/models/psIsotropicProcess.hpp b/include/viennaps/models/psIsotropicProcess.hpp index 68af0974..552b97f7 100644 --- a/include/viennaps/models/psIsotropicProcess.hpp +++ b/include/viennaps/models/psIsotropicProcess.hpp @@ -15,24 +15,16 @@ namespace impl { template class IsotropicVelocityField : public VelocityField { - const NumericType rate_ = 1.; - const std::vector> materialRates_; + MaterialValueMap &materialRates_; public: - IsotropicVelocityField(NumericType rate, - std::vector> &&matRates) - : rate_{rate}, materialRates_{std::move(matRates)} {} + IsotropicVelocityField(MaterialValueMap &materialRates) + : materialRates_{materialRates} {} NumericType getScalarVelocity(const Vec3D &, int material, const Vec3D &, unsigned long) override { - for (const auto &materialRate : materialRates_) { - if (material == materialRate.first) { - return materialRate.second; - } - } - - return rate_; + return materialRates_.get(MaterialMap::mapToMaterial(material)); } }; } // namespace impl @@ -43,51 +35,53 @@ class IsotropicProcess : public ProcessModelCPU { public: IsotropicProcess(NumericType isotropicRate, Material maskMaterial = Material::Undefined) { - std::vector> materialRates; - if (maskMaterial != Material::Undefined) { - materialRates.emplace_back(static_cast(maskMaterial), 0.); - } - setup(isotropicRate, std::move(materialRates)); + materialRates.setDefault(isotropicRate); + materialRates.set(maskMaterial, 0.); + setup(); } IsotropicProcess(NumericType isotropicRate, const std::vector &maskMaterials) { - std::vector> materialRates; + materialRates.setDefault(isotropicRate); for (const auto &mat : maskMaterials) { - materialRates.emplace_back(static_cast(mat), 0.); + materialRates.set(mat, 0.); } - setup(isotropicRate, std::move(materialRates)); + setup(); } - IsotropicProcess(std::unordered_map materialRates, - NumericType defaultRate = 0.) { - std::vector> rates; - for (const auto &[mat, rate] : materialRates) { - rates.emplace_back(static_cast(mat), rate); - } - setup(defaultRate, std::move(rates)); + IsotropicProcess(std::unordered_map pMaterialRates, + NumericType defaultRate = 0.) + : materialRates(pMaterialRates) { + materialRates.setDefault(defaultRate); + setup(); + } + + void setIsotropicRate(NumericType isotropicRate) { + materialRates.setDefault(isotropicRate); + addMetaData("Isotropic Rate", isotropicRate); + } + + void setMaterialRate(Material material, NumericType rate) { + materialRates.set(material, rate); + addMetaData(MaterialMap::toString(material) + " Rate", rate); } private: - void setup(NumericType rate, - std::vector> &&materialRates) { + void setup() { // default surface model auto surfModel = SmartPointer>::New(); // store process data (before moving materialRates) - addMetaData("IsotropicRate", rate); - if (!materialRates.empty()) { - for (const auto &materialRate : materialRates) { - addMetaData("Rate " + MaterialMap::toString( - static_cast(materialRate.first)), - materialRate.second); - } + addMetaData("Isotropic Rate", materialRates.getDefault()); + for (const auto &materialRate : materialRates) { + addMetaData("Rate " + MaterialMap::toString(materialRate.getMaterial()), + materialRate.getValue()); } // velocity field auto velField = SmartPointer>::New( - rate, std::move(materialRates)); + materialRates); this->setSurfaceModel(surfModel); this->setVelocityField(velField); @@ -99,6 +93,8 @@ class IsotropicProcess : public ProcessModelCPU { inline void addMetaData(const std::string &key, double value) { processMetaData[key] = std::vector{value}; } + + MaterialValueMap materialRates; }; PS_PRECOMPILE_PRECISION_DIMENSION(IsotropicProcess) diff --git a/include/viennaps/models/psSingleParticleProcess.hpp b/include/viennaps/models/psSingleParticleProcess.hpp index 54708d43..a9335c4c 100644 --- a/include/viennaps/models/psSingleParticleProcess.hpp +++ b/include/viennaps/models/psSingleParticleProcess.hpp @@ -13,11 +13,10 @@ using namespace viennacore; namespace impl { template class SingleParticleSurfaceModel : public viennaps::SurfaceModel { - using MaterialRateMap = MaterialValueMap; - const MaterialRateMap materialRates_; + MaterialValueMap &materialRates_; public: - SingleParticleSurfaceModel(MaterialRateMap const &materialRates) + SingleParticleSurfaceModel(MaterialValueMap &materialRates) : materialRates_(materialRates) {} SmartPointer> diff --git a/python/pyWrapDimension.hpp b/python/pyWrapDimension.hpp index f8243e8c..d513885d 100644 --- a/python/pyWrapDimension.hpp +++ b/python/pyWrapDimension.hpp @@ -905,20 +905,15 @@ template void bindApi(py::module &module) { // Isotropic Process py::class_, SmartPointer>>( module, "IsotropicProcess", processModel) - .def(py::init([](const T rate, const Material mask) { - return SmartPointer>::New(rate, mask); - }), - py::arg("rate") = 1., py::arg("maskMaterial") = Material::Undefined) - .def(py::init([](const T rate, const std::vector mask) { - return SmartPointer>::New(rate, mask); - }), - py::arg("rate"), py::arg("maskMaterial")) - .def(py::init([](std::unordered_map materialRates, - T defaultRate) { - return SmartPointer>::New(materialRates, - defaultRate); - }), - py::arg("materialRates"), py::arg("defaultRate") = 0.); + .def(py::init(), py::arg("rate") = 1., + py::arg("maskMaterial") = Material::Undefined) + .def(py::init const &>(), py::arg("rate") = 1., + py::arg("maskMaterials")) + .def(py::init, T>(), + py::arg("materialRates"), py::arg("defaultRate") = 0.) + .def("setIsotropicRate", &IsotropicProcess::setIsotropicRate) + .def("setMaterialRate", &IsotropicProcess::setMaterialRate, + py::arg("material"), py::arg("rate")); // DirectionalProcess py::class_, SmartPointer>>( diff --git a/python/viennaps/_core/__init__.pyi b/python/viennaps/_core/__init__.pyi index 5ac366df..8e81da1c 100644 --- a/python/viennaps/_core/__init__.pyi +++ b/python/viennaps/_core/__init__.pyi @@ -8,8 +8,8 @@ import typing import viennals._core from viennaps import d2 import viennaps.d2 -import viennaps.d3 from viennaps import d3 +import viennaps.d3 from . import constants from . import gpu from . import util diff --git a/python/viennaps/d2/__init__.pyi b/python/viennaps/d2/__init__.pyi index 266efe2a..2b3641dd 100644 --- a/python/viennaps/d2/__init__.pyi +++ b/python/viennaps/d2/__init__.pyi @@ -545,11 +545,15 @@ class IsotropicProcess(ProcessModel): def __init__(self, rate: typing.SupportsFloat | typing.SupportsIndex = 1.0, maskMaterial: viennaps._core.Material = ...) -> None: ... @typing.overload - def __init__(self, rate: typing.SupportsFloat | typing.SupportsIndex, maskMaterial: collections.abc.Sequence[viennaps._core.Material]) -> None: + def __init__(self, rate: typing.SupportsFloat | typing.SupportsIndex = 1.0, maskMaterials: collections.abc.Sequence[viennaps._core.Material]) -> None: ... @typing.overload def __init__(self, materialRates: collections.abc.Mapping[viennaps._core.Material, typing.SupportsFloat | typing.SupportsIndex], defaultRate: typing.SupportsFloat | typing.SupportsIndex = 0.0) -> None: ... + def setIsotropicRate(self, arg0: typing.SupportsFloat | typing.SupportsIndex) -> None: + ... + def setMaterialRate(self, material: viennaps._core.Material, rate: typing.SupportsFloat | typing.SupportsIndex) -> None: + ... class MakeFin: @typing.overload def __init__(self, domain: Domain, finWidth: typing.SupportsFloat | typing.SupportsIndex, finHeight: typing.SupportsFloat | typing.SupportsIndex, finTaperAngle: typing.SupportsFloat | typing.SupportsIndex = 0.0, maskHeight: typing.SupportsFloat | typing.SupportsIndex = 0, maskTaperAngle: typing.SupportsFloat | typing.SupportsIndex = 0, halfFin: bool = False, material: viennaps._core.Material = ..., maskMaterial: viennaps._core.Material = ...) -> None: diff --git a/python/viennaps/d3/__init__.pyi b/python/viennaps/d3/__init__.pyi index 73e2e8b4..2fff87b5 100644 --- a/python/viennaps/d3/__init__.pyi +++ b/python/viennaps/d3/__init__.pyi @@ -545,11 +545,15 @@ class IsotropicProcess(ProcessModel): def __init__(self, rate: typing.SupportsFloat | typing.SupportsIndex = 1.0, maskMaterial: viennaps._core.Material = ...) -> None: ... @typing.overload - def __init__(self, rate: typing.SupportsFloat | typing.SupportsIndex, maskMaterial: collections.abc.Sequence[viennaps._core.Material]) -> None: + def __init__(self, rate: typing.SupportsFloat | typing.SupportsIndex = 1.0, maskMaterials: collections.abc.Sequence[viennaps._core.Material]) -> None: ... @typing.overload def __init__(self, materialRates: collections.abc.Mapping[viennaps._core.Material, typing.SupportsFloat | typing.SupportsIndex], defaultRate: typing.SupportsFloat | typing.SupportsIndex = 0.0) -> None: ... + def setIsotropicRate(self, arg0: typing.SupportsFloat | typing.SupportsIndex) -> None: + ... + def setMaterialRate(self, material: viennaps._core.Material, rate: typing.SupportsFloat | typing.SupportsIndex) -> None: + ... class MakeFin: @typing.overload def __init__(self, domain: Domain, finWidth: typing.SupportsFloat | typing.SupportsIndex, finHeight: typing.SupportsFloat | typing.SupportsIndex, finTaperAngle: typing.SupportsFloat | typing.SupportsIndex = 0.0, maskHeight: typing.SupportsFloat | typing.SupportsIndex = 0, maskTaperAngle: typing.SupportsFloat | typing.SupportsIndex = 0, halfFin: bool = False, material: viennaps._core.Material = ..., maskMaterial: viennaps._core.Material = ...) -> None: From 20cb88a6aaf842dfe3220dce38c27e77f62c691b Mon Sep 17 00:00:00 2001 From: reiter Date: Wed, 25 Mar 2026 16:22:18 +0100 Subject: [PATCH 06/32] refactor: material mapping, allow custom material registration --- CMakeLists.txt | 3 +- examples/exampleProcess/velocityField.hpp | 2 +- gpu/models/PlasmaEtching.cuh | 13 +- .../psBuiltInMaterial.hpp} | 305 ++++----------- include/viennaps/materials/psMaterial.hpp | 139 +++++++ .../viennaps/materials/psMaterialRegistry.hpp | 143 +++++++ .../viennaps/materials/psMaterialValueMap.hpp | 161 ++++++++ include/viennaps/materials/psMaterials.hpp | 183 +++++++++ include/viennaps/models/psCF4O2Parameters.hpp | 2 +- include/viennaps/models/psCSVFileProcess.hpp | 2 +- .../viennaps/models/psDirectionalProcess.hpp | 2 +- .../viennaps/models/psFaradayCageEtching.hpp | 2 +- .../viennaps/models/psFluorocarbonEtching.hpp | 3 +- include/viennaps/models/psIonBeamEtching.hpp | 2 +- .../viennaps/models/psIonBeamParameters.hpp | 2 +- .../viennaps/models/psIsotropicProcess.hpp | 2 +- .../models/psMultiParticleProcess.hpp | 2 +- .../viennaps/models/psSelectiveEpitaxy.hpp | 2 +- .../models/psSingleParticleProcess.hpp | 2 +- include/viennaps/models/psWetEtching.hpp | 2 +- .../viennaps/process/psTranslationField.hpp | 2 +- include/viennaps/psDomain.hpp | 12 +- include/viennaps/psReader.hpp | 27 +- include/viennaps/psUtil.hpp | 2 +- include/viennaps/psWriter.hpp | 22 +- python/pyWrap.cpp | 67 +++- python/pyWrapDimension.hpp | 32 ++ python/viennaps/__init__.pyi | 5 +- python/viennaps/_core/__init__.pyi | 365 +++++++++++++----- python/viennaps/d2/__init__.pyi | 20 + python/viennaps/d3/__init__.pyi | 20 + tests/materialMap/materialMap.cpp | 32 +- tests/materialRegistry/CMakeLists.txt | 7 + tests/materialRegistry/materialRegistry.cpp | 34 ++ tests/readAndWrite/readAndWrite.cpp | 2 +- 35 files changed, 1240 insertions(+), 383 deletions(-) rename include/viennaps/{psMaterials.hpp => materials/psBuiltInMaterial.hpp} (54%) create mode 100644 include/viennaps/materials/psMaterial.hpp create mode 100644 include/viennaps/materials/psMaterialRegistry.hpp create mode 100644 include/viennaps/materials/psMaterialValueMap.hpp create mode 100644 include/viennaps/materials/psMaterials.hpp create mode 100644 tests/materialRegistry/CMakeLists.txt create mode 100644 tests/materialRegistry/materialRegistry.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d38250b3..4484358e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -181,7 +181,8 @@ if(VIENNAPS_PRECOMPILE_HEADERS) VIENNACORE_COMPILE_SHARED_LIB=1) target_sources( ${LIB_NAME} PRIVATE "lib/specMain.cpp" "lib/specGeometries.cpp" "lib/specModelsEmulation.cpp" - "lib/specModelsSimulation.cpp" "lib/specProcess.cpp") + "lib/specModelsSimulation.cpp" "lib/specProcess.cpp" + "lib/MaterialRegistry.cpp") set_target_properties( ${LIB_NAME} diff --git a/examples/exampleProcess/velocityField.hpp b/examples/exampleProcess/velocityField.hpp index d1f04ed7..cf2207aa 100644 --- a/examples/exampleProcess/velocityField.hpp +++ b/examples/exampleProcess/velocityField.hpp @@ -1,7 +1,7 @@ #pragma once +#include #include -#include #include template diff --git a/gpu/models/PlasmaEtching.cuh b/gpu/models/PlasmaEtching.cuh index 87f2d468..a83343b9 100644 --- a/gpu/models/PlasmaEtching.cuh +++ b/gpu/models/PlasmaEtching.cuh @@ -6,9 +6,9 @@ #include "raygLaunchParams.hpp" #include "raygReflection.hpp" +#include "materials/psMaterials.hpp" #include "models/psPipelineParameters.hpp" #include "models/psPlasmaEtchingParameters.hpp" -#include "psMaterials.hpp" extern "C" __constant__ viennaray::gpu::LaunchParams launchParams; @@ -69,6 +69,8 @@ plasmaIonCollision(const void *sbtData, viennaray::gpu::PerRayData *prd) { for (int i = 0; i < prd->ISCount; ++i) { int id = launchParams.materialIds[prd->primIDs[i]]; // consecutive ID int material = launchParams.materialMap[id]; // mapped to enum + const int maskId = static_cast(viennaps::BuiltInMaterial::Mask); + const int polymerId = static_cast(viennaps::BuiltInMaterial::Polymer); auto geomNormal = viennaray::gpu::getNormal(sbtData, prd->primIDs[i]); auto cosTheta = __saturatef( -viennacore::DotProduct(prd->dir, geomNormal)); // clamp to [0,1] @@ -77,21 +79,18 @@ plasmaIonCollision(const void *sbtData, viennaray::gpu::PerRayData *prd) { float A_sp = params->Substrate.A_sp; float B_sp = params->Substrate.B_sp; float Eth_sp = params->Substrate.Eth_sp; - if (static_cast(material) == viennaps::Material::Mask) { + if (material == maskId) { A_sp = params->Mask.A_sp; B_sp = params->Mask.B_sp; Eth_sp = params->Mask.Eth_sp; - } else if (static_cast(material) == - viennaps::Material::Polymer) { + } else if (material == polymerId) { A_sp = params->Polymer.A_sp; B_sp = params->Polymer.B_sp; Eth_sp = params->Polymer.Eth_sp; } float f_sp_theta; - if (static_cast(material) == - viennaps::Material::Polymer && - params->Polymer.usePolyCosThetaYield) { + if (material == polymerId && params->Polymer.usePolyCosThetaYield) { const float c = cosTheta; const float sum = params->Polymer.a1 + params->Polymer.a2 + params->Polymer.a3 + params->Polymer.a4; diff --git a/include/viennaps/psMaterials.hpp b/include/viennaps/materials/psBuiltInMaterial.hpp similarity index 54% rename from include/viennaps/psMaterials.hpp rename to include/viennaps/materials/psBuiltInMaterial.hpp index 6e5ad6fc..c969e569 100644 --- a/include/viennaps/psMaterials.hpp +++ b/include/viennaps/materials/psBuiltInMaterial.hpp @@ -1,21 +1,12 @@ #pragma once -#ifndef __CUDACC__ -#include -#endif - -#include -#include - #include #include -#include +#include #include namespace viennaps { -using namespace viennacore; - enum class MaterialCategory : uint8_t { Generic, Silicon, @@ -29,7 +20,7 @@ enum class MaterialCategory : uint8_t { Misc }; -#define MATERIAL_LIST(X) \ +#define BUILTIN_MATERIAL_LIST(X) \ /* id, sym, cat, density_gcm3, conductive, color (hex) */ \ X(0, Mask, Hardmask, 500.0, false, 0x333333) \ X(1, Polymer, Generic, 1.2, false, 0xed3b13) \ @@ -129,252 +120,100 @@ enum class MaterialCategory : uint8_t { X(172, SiC_HM, Hardmask, 3.2, false, 0x4f4f4f) \ X(173, TiO, Misc, 4.9, false, 0x64b5f6) \ X(174, ZrO, Misc, 5.2, false, 0x5c6bc0) \ - X(175, SiO2_HM, Hardmask, 2.2, false, 0x66ccff) -enum class Material : uint16_t { + X(175, SiO2_HM, Hardmask, 2.2, false, 0x66ccff) \ + X(176, Custom, Generic, 0.0, false, 0xffffff) + +#ifndef MATERIAL_LIST +#define MATERIAL_LIST(X) BUILTIN_MATERIAL_LIST(X) +#endif + +enum class BuiltInMaterial : uint16_t { #define ENUM_ELEM(id, sym, cat, dens, cond, color) sym = id, - MATERIAL_LIST(ENUM_ELEM) + BUILTIN_MATERIAL_LIST(ENUM_ELEM) #undef ENUM_ELEM }; -struct MaterialInfo { +struct BuiltInMaterialInfo { std::string_view name; MaterialCategory category; - double density_gcm3; // nominal; tune per PDK + double density_gcm3; bool conductive; - uint32_t colorHex; // 0xRRGGBB + uint32_t colorHex; }; -constexpr uint16_t kMaterialMaxId = 175; +constexpr uint16_t kBuiltInMaterialMaxId = 176; -constexpr std::array kMaterialTable = [] { - std::array t{}; - // default fill - for (auto &e : t) - e = {"Undefined", MaterialCategory::Generic, 0.0, false, 0xcccccc}; - // populate +constexpr std::array + kBuiltInMaterialTable = [] { + std::array table{}; + for (auto &entry : table) { + entry = {"Undefined", MaterialCategory::Generic, 0.0, false, 0xcccccc}; + } #define FILL_ROW(id, sym, cat, dens, cond, color) \ - t[id] = {#sym, MaterialCategory::cat, dens, cond, color}; - MATERIAL_LIST(FILL_ROW) + table[id] = {#sym, MaterialCategory::cat, dens, cond, color}; + BUILTIN_MATERIAL_LIST(FILL_ROW) #undef FILL_ROW - return t; -}(); + return table; + }(); -constexpr const MaterialInfo &info(Material m) { - auto id = static_cast(m); - return kMaterialTable[id <= kMaterialMaxId ? id : 0]; // 0 == Mask or change - // to Undefined id +[[nodiscard]] constexpr bool isValidBuiltInMaterialId(uint16_t id) { + return id <= kBuiltInMaterialMaxId; } -constexpr std::string_view to_string_view(Material m) { return info(m).name; } -constexpr MaterialCategory categoryOf(Material m) { return info(m).category; } -constexpr double density(Material m) { return info(m).density_gcm3; } -constexpr bool isConductive(Material m) { return info(m).conductive; } -constexpr uint32_t color(Material m) { return info(m).colorHex; } - -template class MaterialValueMap { -public: - using Value = T; - - MaterialValueMap() = default; - - // generic constructor (works with map, vector, initializer_list, etc.) - template explicit MaterialValueMap(const MapLike &mapLike) { - for (const auto &[key, value] : mapLike) { - set(key, value); - } - } - - // set value for a material - void set(Material m, const T &value) { - auto idx = toIndex(m); - values_[idx] = value; - isSet_[idx] = true; - } - - // perfect-forwarding overload - template void emplace(Material m, Args &&...args) { - auto idx = toIndex(m); - values_[idx] = T(std::forward(args)...); - isSet_[idx] = true; - } - - // get value or default T{} - [[nodiscard]] T get(Material m) const { - auto idx = toIndex(m); - return isSet_[idx] ? values_[idx] : T{}; - } - - // get reference (no copy) - [[nodiscard]] const T &getRef(Material m) const { - auto idx = toIndex(m); - return isSet_[idx] ? values_[idx] : default_; - } - - void setDefault(const T &v) { default_ = v; } - - [[nodiscard]] const T &getDefault() const { return default_; } - - // check if user provided a value - [[nodiscard]] bool has(Material m) const { return isSet_[toIndex(m)]; } - - // remove value -> fallback to default - void clear(Material m) { isSet_[toIndex(m)] = false; } - - void clearAll() { - for (auto &b : isSet_) { - b = false; - } - } - - // ================= ITERATOR ================= - struct Entry { - Material material; - const T *value; - bool set; - - [[nodiscard]] bool isSet() const { return set; } - [[nodiscard]] const T &getValue() const { return *value; } - [[nodiscard]] Material getMaterial() const { return material; } - }; - - class Iterator { - public: - Iterator(const MaterialValueMap *map, std::size_t idx) - : map_(map), idx_(idx) { - advanceToValid(); - } - - Iterator &operator++() { - ++idx_; - advanceToValid(); - return *this; - } - - bool operator!=(const Iterator &other) const { return idx_ != other.idx_; } - - Entry operator*() const { - return Entry{static_cast(idx_), &map_->values_[idx_], - map_->isSet_[idx_]}; - } - - private: - void advanceToValid() { - while (idx_ < map_->values_.size() && !map_->isSet_[idx_]) { - ++idx_; - } - } - - const MaterialValueMap *map_; - std::size_t idx_; - }; - - Iterator begin() const { return Iterator(this, 0); } - - Iterator end() const { return Iterator(this, values_.size()); } - -private: - static constexpr std::size_t toIndex(Material m) { - return static_cast(m); - } - - std::array values_{}; - std::array isSet_{}; - - // default instance used for reference return - T default_{}; -}; - -#ifndef __CUDACC__ -/// A class that wraps the viennals MaterialMap class and provides a more user -/// friendly interface. It also provides a mapping from the integer material id -/// to the Material enum. -class MaterialMap { - SmartPointer map_; - -public: - MaterialMap() { map_ = SmartPointer::New(); }; - - void insertNextMaterial(Material material) { - map_->insertNextMaterial(static_cast(material)); - } - - void removeMaterial() { map_->removeLastMaterial(); } - - // Returns the material **ID** at the given index. If the index is out of - // bounds, it returns -1 - [[nodiscard]] int getMaterialIdAtIdx(std::size_t idx) const { - return map_->getMaterialId(idx); - } - - // Returns the material **enum** at the given index. If the index is out of - // bounds, it returns Material::Undefined - [[nodiscard]] Material getMaterialAtIdx(std::size_t idx) const { - if (int id = getMaterialIdAtIdx(idx); id < 0) { - VIENNACORE_LOG_WARNING("Getting material with out-of-bounds index."); - return Material::Undefined; - } else { - return mapToMaterial(id); - } - } - - void setMaterialAtIdx(std::size_t idx, const Material material) { - if (idx >= size()) { - VIENNACORE_LOG_ERROR("Setting material with out-of-bounds index."); - return; - } - map_->setMaterialId(idx, static_cast(material)); - } - - [[nodiscard]] SmartPointer getMaterialMap() const { - return map_; - } +[[nodiscard]] constexpr bool isValidBuiltInMaterial(BuiltInMaterial material) { + return isValidBuiltInMaterialId(static_cast(material)); +} - [[nodiscard]] inline std::size_t size() const { - return map_->getNumberOfLayers(); - } +[[nodiscard]] constexpr const BuiltInMaterialInfo & +getBuiltInMaterialInfo(BuiltInMaterial material) { + const auto id = static_cast(material); + return kBuiltInMaterialTable[isValidBuiltInMaterialId(id) + ? id + : static_cast( + BuiltInMaterial::Undefined)]; +} - static inline bool isValidMaterial(const Material mat) { - switch (mat) { -#define ENUM_ELEM(id, sym, cat, dens, cond, color) \ - case Material::sym: \ - return true; - MATERIAL_LIST(ENUM_ELEM) -#undef ENUM_ELEM - default: - return false; - } - } +[[nodiscard]] constexpr std::string_view +builtInMaterialToString(BuiltInMaterial material) { + return getBuiltInMaterialInfo(material).name; +} - static inline Material mapToMaterial(const int matId) { - auto mat = static_cast(matId); - if (Logger::hasDebug() && !isValidMaterial(mat)) { - VIENNACORE_LOG_DEBUG("mapToMaterial: Invalid material id " + - std::to_string(matId) + " mapped to Undefined."); - } - return mat; - } +[[nodiscard]] constexpr MaterialCategory categoryOf(BuiltInMaterial material) { + return getBuiltInMaterialInfo(material).category; +} - template static inline Material mapToMaterial(const T matId) { - return mapToMaterial(static_cast(matId)); - } +[[nodiscard]] constexpr double density(BuiltInMaterial material) { + return getBuiltInMaterialInfo(material).density_gcm3; +} - template - static inline bool isMaterial(const T matId, const Material material) { - return mapToMaterial(matId) == material; - } +[[nodiscard]] constexpr bool isConductive(BuiltInMaterial material) { + return getBuiltInMaterialInfo(material).conductive; +} - template static inline bool isHardmask(const T matId) { - return categoryOf(mapToMaterial(matId)) == MaterialCategory::Hardmask; - } +[[nodiscard]] constexpr uint32_t color(BuiltInMaterial material) { + return getBuiltInMaterialInfo(material).colorHex; +} - static inline std::string toString(const Material matId) { - return std::string(to_string_view(matId)); +[[nodiscard]] inline bool tryBuiltInMaterialFromString(std::string_view name, + BuiltInMaterial &out) { +#define MATCH_NAME(id, sym, cat, dens, cond, color) \ + if (name == std::string_view(#sym)) { \ + out = BuiltInMaterial::sym; \ + return true; \ } + BUILTIN_MATERIAL_LIST(MATCH_NAME) +#undef MATCH_NAME + return false; +} - static inline std::string toString(const int matId) { - return std::string(to_string_view(mapToMaterial(matId))); +[[nodiscard]] inline BuiltInMaterial +builtInMaterialFromString(std::string_view name) { + BuiltInMaterial material = BuiltInMaterial::Undefined; + if (!tryBuiltInMaterialFromString(name, material)) { + throw std::invalid_argument("Unknown built-in material name: " + + std::string(name)); } -}; -#endif + return material; +} } // namespace viennaps diff --git a/include/viennaps/materials/psMaterial.hpp b/include/viennaps/materials/psMaterial.hpp new file mode 100644 index 00000000..f51ff411 --- /dev/null +++ b/include/viennaps/materials/psMaterial.hpp @@ -0,0 +1,139 @@ +#pragma once + +#include "psBuiltInMaterial.hpp" + +#include +#include +#include +#include + +namespace viennaps { + +class Material { +public: + using ValueType = std::uint32_t; + + enum class Kind : std::uint8_t { BuiltIn = 0, Custom = 1 }; + + constexpr Material() noexcept = default; + + constexpr Material(BuiltInMaterial material) noexcept + : kind_(Kind::BuiltIn), value_(static_cast(material)) {} + + constexpr Material(int legacyId) noexcept { *this = fromLegacyId(legacyId); } + + [[nodiscard]] static constexpr Material custom(ValueType id) noexcept { + Material material; + material.kind_ = Kind::Custom; + material.value_ = id; + return material; + } + + [[nodiscard]] static constexpr Material fromLegacyId(int id) noexcept { + if (id < 0) { + return Material(BuiltInMaterial::Undefined); + } + + const auto raw = static_cast(id); + if (raw <= static_cast(kBuiltInMaterialMaxId)) { + return Material(static_cast(raw)); + } + + return Material::custom(raw - + static_cast(kBuiltInMaterialMaxId) - 1u); + } + + [[nodiscard]] constexpr Kind kind() const noexcept { return kind_; } + + [[nodiscard]] constexpr bool isBuiltIn() const noexcept { + return kind_ == Kind::BuiltIn; + } + + [[nodiscard]] constexpr bool isCustom() const noexcept { + return kind_ == Kind::Custom; + } + + [[nodiscard]] constexpr BuiltInMaterial builtIn() const { + if (!isBuiltIn()) { + throw std::logic_error("Material is not built-in."); + } + return static_cast(value_); + } + + [[nodiscard]] constexpr ValueType customId() const { + if (!isCustom()) { + throw std::logic_error("Material is not custom."); + } + return value_; + } + + [[nodiscard]] constexpr ValueType legacyId() const noexcept { + if (isBuiltIn()) { + return value_; + } + return static_cast(kBuiltInMaterialMaxId) + 1u + value_; + } + + constexpr operator ValueType() const noexcept { return legacyId(); } + + explicit constexpr operator int() const noexcept { + return static_cast(legacyId()); + } + + [[nodiscard]] constexpr ValueType value() const noexcept { return value_; } + + [[nodiscard]] constexpr bool + operator==(const Material &) const noexcept = default; + + [[nodiscard]] constexpr auto + operator<=>(const Material &) const noexcept = default; + +#define MATERIAL_CONST(id, sym, cat, dens, cond, color) \ + static const Material sym; + BUILTIN_MATERIAL_LIST(MATERIAL_CONST) +#undef MATERIAL_CONST + +private: + Kind kind_{Kind::BuiltIn}; + ValueType value_{static_cast(BuiltInMaterial::Undefined)}; +}; + +[[nodiscard]] inline constexpr bool isBuiltIn(const Material material) { + return material.isBuiltIn(); +} + +[[nodiscard]] inline constexpr bool isCustom(const Material material) { + return material.isCustom(); +} + +[[nodiscard]] inline BuiltInMaterial +toBuiltInMaterial(const Material material) { + return material.builtIn(); +} + +[[nodiscard]] inline constexpr Material toMaterial(BuiltInMaterial material) { + return Material(material); +} + +} // namespace viennaps + +namespace viennaps { + +#define MATERIAL_CONST_DEF(id, sym, cat, dens, cond, color) \ + inline constexpr Material Material::sym{BuiltInMaterial::sym}; +BUILTIN_MATERIAL_LIST(MATERIAL_CONST_DEF) +#undef MATERIAL_CONST_DEF + +} // namespace viennaps + +namespace std { + +template <> struct hash { + size_t operator()(const viennaps::Material &material) const noexcept { + const auto mix = (static_cast(material.value()) << 8u) | + static_cast(material.kind()); + return std::hash{}(mix); + } +}; + +} // namespace std diff --git a/include/viennaps/materials/psMaterialRegistry.hpp b/include/viennaps/materials/psMaterialRegistry.hpp new file mode 100644 index 00000000..f1e710c3 --- /dev/null +++ b/include/viennaps/materials/psMaterialRegistry.hpp @@ -0,0 +1,143 @@ +#pragma once + +#include "psMaterial.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace viennaps { + +struct CustomMaterialInfo { + std::string name; +}; + +class MaterialRegistry { +public: + MaterialRegistry() = default; + + MaterialRegistry(const MaterialRegistry &other) { + std::scoped_lock lock(other.mutex_); + nameToCustomId_ = other.nameToCustomId_; + materials_ = other.materials_; + } + + MaterialRegistry &operator=(const MaterialRegistry &other) { + if (this == &other) { + return *this; + } + + std::scoped_lock lock(mutex_, other.mutex_); + nameToCustomId_ = other.nameToCustomId_; + materials_ = other.materials_; + return *this; + } + + [[nodiscard]] Material registerMaterial(std::string name) { + std::lock_guard lock(mutex_); + + if (name.empty()) { + throw std::invalid_argument("Material name must not be empty."); + } + + BuiltInMaterial builtIn = BuiltInMaterial::Undefined; + if (tryBuiltInMaterialFromString(name, builtIn)) { + return Material(builtIn); + } + + const auto it = nameToCustomId_.find(name); + if (it != nameToCustomId_.end()) { + return Material::custom(it->second); + } + + const auto customId = static_cast(materials_.size()); + nameToCustomId_.emplace(name, customId); + materials_.push_back(CustomMaterialInfo{std::move(name)}); + return Material::custom(customId); + } + + [[nodiscard]] bool hasMaterial(std::string_view name) const { + std::lock_guard lock(mutex_); + + BuiltInMaterial builtIn = BuiltInMaterial::Undefined; + if (tryBuiltInMaterialFromString(name, builtIn)) { + return true; + } + + return nameToCustomId_.find(std::string(name)) != nameToCustomId_.end(); + } + + [[nodiscard]] std::optional + findMaterial(std::string_view name) const { + std::lock_guard lock(mutex_); + + BuiltInMaterial builtIn = BuiltInMaterial::Undefined; + if (tryBuiltInMaterialFromString(name, builtIn)) { + return Material(builtIn); + } + + const auto it = nameToCustomId_.find(std::string(name)); + if (it == nameToCustomId_.end()) { + return std::nullopt; + } + + return Material::custom(it->second); + } + + [[nodiscard]] Material getMaterial(std::string_view name) const { + const auto maybe = findMaterial(name); + if (!maybe.has_value()) { + throw std::invalid_argument("Unknown material: " + std::string(name)); + } + return maybe.value(); + } + + [[nodiscard]] std::string_view getName(Material material) const { + if (material.isBuiltIn()) { + return builtInMaterialToString(material.builtIn()); + } + + std::lock_guard lock(mutex_); + const auto customId = material.customId(); + if (customId >= materials_.size()) { + throw std::out_of_range("Unknown custom material id."); + } + + return materials_[customId].name; + } + + [[nodiscard]] bool isBuiltIn(Material material) const { + return material.isBuiltIn(); + } + + [[nodiscard]] const CustomMaterialInfo &getInfo(Material material) const { + if (material.isBuiltIn()) { + throw std::invalid_argument( + "Built-in materials do not have registry-owned metadata."); + } + + std::lock_guard lock(mutex_); + const auto customId = material.customId(); + if (customId >= materials_.size()) { + throw std::out_of_range("Unknown custom material id."); + } + + return materials_[customId]; + } + + [[nodiscard]] std::size_t customMaterialCount() const { + std::lock_guard lock(mutex_); + return materials_.size(); + } + +private: + mutable std::mutex mutex_; + std::unordered_map nameToCustomId_; + std::vector materials_; +}; + +} // namespace viennaps diff --git a/include/viennaps/materials/psMaterialValueMap.hpp b/include/viennaps/materials/psMaterialValueMap.hpp new file mode 100644 index 00000000..26478a5e --- /dev/null +++ b/include/viennaps/materials/psMaterialValueMap.hpp @@ -0,0 +1,161 @@ +#pragma once + +#include "psMaterials.hpp" + +namespace viennaps { + +template class MaterialValueMap { +public: + using Value = T; + + MaterialValueMap() = default; + + // generic constructor (works with map, vector, initializer_list, etc.) + template explicit MaterialValueMap(const MapLike &mapLike) { + for (const auto &[key, value] : mapLike) { + set(key, value); + } + } + + void set(Material material, const T &value) { + if (material.isBuiltIn()) { + auto idx = toIndex(material.builtIn()); + values_[idx] = value; + isSet_[idx] = true; + return; + } + customValues_[material.customId()] = value; + } + + // perfect-forwarding overload + template void emplace(BuiltInMaterial m, Args &&...args) { + auto idx = toIndex(m); + values_[idx] = T(std::forward(args)...); + isSet_[idx] = true; + } + + template void emplace(Material material, Args &&...args) { + set(material, T(std::forward(args)...)); + } + + // get value or default T{} + [[nodiscard]] T get(Material material) const { + if (material.isBuiltIn()) { + auto idx = toIndex(material.builtIn()); + return isSet_[idx] ? values_[idx] : default_; + } + + const auto it = customValues_.find(material.customId()); + return it == customValues_.end() ? default_ : it->second; + } + + [[nodiscard]] T get(BuiltInMaterial m) const { return get(Material(m)); } + + // get reference (no copy) + [[nodiscard]] const T &getRef(Material material) const { + if (material.isBuiltIn()) { + auto idx = toIndex(material.builtIn()); + return isSet_[idx] ? values_[idx] : default_; + } + + const auto it = customValues_.find(material.customId()); + return it == customValues_.end() ? default_ : it->second; + } + + [[nodiscard]] const T &getRef(BuiltInMaterial m) const { + return getRef(Material(m)); + } + + void setDefault(const T &v) { default_ = v; } + + [[nodiscard]] const T &getDefault() const { return default_; } + + // check if user provided a value + [[nodiscard]] bool has(Material material) const { + if (material.isBuiltIn()) { + return isSet_[toIndex(material.builtIn())]; + } + return customValues_.contains(material.customId()); + } + + [[nodiscard]] bool has(BuiltInMaterial m) const { return has(Material(m)); } + + // remove value -> fallback to default + void clear(Material material) { + if (material.isBuiltIn()) { + isSet_[toIndex(material.builtIn())] = false; + return; + } + customValues_.erase(material.customId()); + } + + void clear(BuiltInMaterial m) { clear(Material(m)); } + + void clearAll() { + for (auto &b : isSet_) { + b = false; + } + customValues_.clear(); + } + + // ================= ITERATOR ================= + struct Entry { + BuiltInMaterial material; + const T *value; + bool set; + + [[nodiscard]] bool isSet() const { return set; } + [[nodiscard]] const T &getValue() const { return *value; } + [[nodiscard]] BuiltInMaterial getMaterial() const { return material; } + }; + + class Iterator { + public: + Iterator(const MaterialValueMap *map, std::size_t idx) + : map_(map), idx_(idx) { + advanceToValid(); + } + + Iterator &operator++() { + ++idx_; + advanceToValid(); + return *this; + } + + bool operator!=(const Iterator &other) const { return idx_ != other.idx_; } + + Entry operator*() const { + return Entry{static_cast(idx_), &map_->values_[idx_], + map_->isSet_[idx_]}; + } + + private: + void advanceToValid() { + while (idx_ < map_->values_.size() && !map_->isSet_[idx_]) { + ++idx_; + } + } + + const MaterialValueMap *map_; + std::size_t idx_; + }; + + // iterators over built-in materials with user-provided values + Iterator begin() const { return Iterator(this, 0); } + + Iterator end() const { return Iterator(this, values_.size()); } + +private: + static constexpr std::size_t toIndex(BuiltInMaterial m) { + return static_cast(m); + } + + std::array values_{}; + std::array isSet_{}; + std::unordered_map customValues_; + + // default instance used for reference return + T default_{}; +}; + +} // namespace viennaps \ No newline at end of file diff --git a/include/viennaps/materials/psMaterials.hpp b/include/viennaps/materials/psMaterials.hpp new file mode 100644 index 00000000..97612c53 --- /dev/null +++ b/include/viennaps/materials/psMaterials.hpp @@ -0,0 +1,183 @@ +#pragma once + +#ifndef __CUDACC__ +#include +#endif + +#include "psBuiltInMaterial.hpp" +#include "psMaterial.hpp" +#include "psMaterialRegistry.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace viennaps { + +using namespace viennacore; + +struct MaterialInfo { + std::string_view name; + MaterialCategory category; + double density_gcm3; + bool conductive; + uint32_t colorHex; +}; + +[[nodiscard]] constexpr MaterialInfo info(const Material material) { + if (!material.isBuiltIn()) { + return {"Custom", MaterialCategory::Generic, 0.0, false, 0xffffff}; + } + const auto &builtIn = getBuiltInMaterialInfo(material.builtIn()); + return {builtIn.name, builtIn.category, builtIn.density_gcm3, + builtIn.conductive, builtIn.colorHex}; +} + +[[nodiscard]] constexpr MaterialCategory categoryOf(const Material material) { + return material.isBuiltIn() ? categoryOf(material.builtIn()) + : MaterialCategory::Generic; +} + +[[nodiscard]] constexpr double density(const Material material) { + return material.isBuiltIn() ? density(material.builtIn()) : 0.0; +} + +[[nodiscard]] constexpr bool isConductive(const Material material) { + return material.isBuiltIn() ? isConductive(material.builtIn()) : false; +} + +[[nodiscard]] constexpr uint32_t color(const Material material) { + return material.isBuiltIn() ? color(material.builtIn()) : 0xffffff; +} + +[[nodiscard]] constexpr std::string_view +to_string_view(const Material material) { + return material.isBuiltIn() ? builtInMaterialToString(material.builtIn()) + : std::string_view("Custom"); +} + +[[nodiscard]] inline std::string to_string(const Material material) { + if (material.isBuiltIn()) { + return std::string(builtInMaterialToString(material.builtIn())); + } + return "Custom#" + std::to_string(material.customId()); +} + +[[nodiscard]] inline std::string to_string(const Material material, + const MaterialRegistry ®istry) { + if (material.isBuiltIn()) { + return std::string(builtInMaterialToString(material.builtIn())); + } + return std::string(registry.getName(material)); +} + +#ifndef __CUDACC__ +/// A class that wraps the viennals MaterialMap class and provides a more user +/// friendly interface. It also provides a mapping from the integer material id +/// to the Material enum. +class MaterialMap { + SmartPointer map_; + +public: + MaterialMap() { map_ = SmartPointer::New(); }; + + void insertNextMaterial(Material material) { + map_->insertNextMaterial(static_cast(material)); + } + + void removeMaterial() { map_->removeLastMaterial(); } + + // Returns the material **ID** at the given index. If the index is out of + // bounds, it returns -1 + [[nodiscard]] int getMaterialIdAtIdx(std::size_t idx) const { + return map_->getMaterialId(idx); + } + + // Returns the material **enum** at the given index. If the index is out of + // bounds, it returns Material::Undefined + [[nodiscard]] Material getMaterialAtIdx(std::size_t idx) const { + if (int id = getMaterialIdAtIdx(idx); id < 0) { + VIENNACORE_LOG_WARNING("Getting material with out-of-bounds index."); + return Material::Undefined; + } else { + return mapToMaterial(id); + } + } + + void setMaterialAtIdx(std::size_t idx, const Material material) { + if (idx >= size()) { + VIENNACORE_LOG_ERROR("Setting material with out-of-bounds index."); + return; + } + map_->setMaterialId(idx, static_cast(material)); + } + + [[nodiscard]] SmartPointer getMaterialMap() const { + return map_; + } + + [[nodiscard]] inline std::size_t size() const { + return map_->getNumberOfLayers(); + } + + static inline bool isValidMaterial(const Material mat) { + if (mat.isBuiltIn()) { + return isValidBuiltInMaterial(mat.builtIn()); + } + return true; + } + + static inline Material mapToMaterial(const int matId) { + auto mat = Material::fromLegacyId(matId); + return mat; + } + + template static inline Material mapToMaterial(const T matId) { + return mapToMaterial(static_cast(matId)); + } + + template + static inline bool isMaterial(const T matId, const Material material) { + return mapToMaterial(matId) == material; + } + + template static inline bool isHardmask(const T matId) { + const auto material = mapToMaterial(matId); + if (!material.isBuiltIn()) { + return false; + } + return categoryOf(material.builtIn()) == MaterialCategory::Hardmask; + } + + static inline std::string toString(const Material matId) { + return to_string(matId); + } + + static inline std::string toString(const Material matId, + const MaterialRegistry ®istry) { + return to_string(matId, registry); + } + + static inline std::string toString(const int matId) { + return toString(mapToMaterial(matId)); + } + + static inline Material fromString(std::string_view name, + MaterialRegistry ®istry) { + BuiltInMaterial builtIn = BuiltInMaterial::Undefined; + if (tryBuiltInMaterialFromString(name, builtIn)) { + return Material(builtIn); + } + return registry.registerMaterial(std::string(name)); + } +}; +#endif + +} // namespace viennaps diff --git a/include/viennaps/models/psCF4O2Parameters.hpp b/include/viennaps/models/psCF4O2Parameters.hpp index fea4ccb8..e021c0aa 100644 --- a/include/viennaps/models/psCF4O2Parameters.hpp +++ b/include/viennaps/models/psCF4O2Parameters.hpp @@ -1,7 +1,7 @@ #pragma once +#include "../materials/psMaterials.hpp" #include "../psConstants.hpp" -#include "../psMaterials.hpp" #include namespace viennaps { diff --git a/include/viennaps/models/psCSVFileProcess.hpp b/include/viennaps/models/psCSVFileProcess.hpp index 09991caf..82c8d44a 100644 --- a/include/viennaps/models/psCSVFileProcess.hpp +++ b/include/viennaps/models/psCSVFileProcess.hpp @@ -1,9 +1,9 @@ #pragma once +#include "../materials/psMaterials.hpp" #include "../process/psProcessModel.hpp" #include "../process/psSurfaceModel.hpp" #include "../psDomain.hpp" -#include "../psMaterials.hpp" #include "../psRateGrid.hpp" #include diff --git a/include/viennaps/models/psDirectionalProcess.hpp b/include/viennaps/models/psDirectionalProcess.hpp index a616159a..fc6817f6 100644 --- a/include/viennaps/models/psDirectionalProcess.hpp +++ b/include/viennaps/models/psDirectionalProcess.hpp @@ -1,7 +1,7 @@ #pragma once +#include "../materials/psMaterialValueMap.hpp" #include "../process/psProcessModel.hpp" -#include "../psMaterials.hpp" #include #include diff --git a/include/viennaps/models/psFaradayCageEtching.hpp b/include/viennaps/models/psFaradayCageEtching.hpp index 3a2418f9..8bcf8bf3 100644 --- a/include/viennaps/models/psFaradayCageEtching.hpp +++ b/include/viennaps/models/psFaradayCageEtching.hpp @@ -3,8 +3,8 @@ #include "psIonBeamEtching.hpp" #include "psPipelineParameters.hpp" +#include "../materials/psMaterials.hpp" #include "../process/psProcessModel.hpp" -#include "../psMaterials.hpp" #include "../psUtil.hpp" #include diff --git a/include/viennaps/models/psFluorocarbonEtching.hpp b/include/viennaps/models/psFluorocarbonEtching.hpp index d9c4061b..434ce512 100644 --- a/include/viennaps/models/psFluorocarbonEtching.hpp +++ b/include/viennaps/models/psFluorocarbonEtching.hpp @@ -2,11 +2,12 @@ #include +#include "../materials/psMaterials.hpp" #include "../process/psProcessModel.hpp" #include "../psConstants.hpp" -#include "../psMaterials.hpp" #include "../psUnits.hpp" + #include "psIonModelUtil.hpp" #include diff --git a/include/viennaps/models/psIonBeamEtching.hpp b/include/viennaps/models/psIonBeamEtching.hpp index 039cb1cc..25bd0387 100644 --- a/include/viennaps/models/psIonBeamEtching.hpp +++ b/include/viennaps/models/psIonBeamEtching.hpp @@ -1,8 +1,8 @@ #pragma once +#include "../materials/psMaterials.hpp" #include "../process/psProcessModel.hpp" #include "../psConstants.hpp" -#include "../psMaterials.hpp" #include "psIonBeamParameters.hpp" #include "psIonModelUtil.hpp" #include "psPipelineParameters.hpp" diff --git a/include/viennaps/models/psIonBeamParameters.hpp b/include/viennaps/models/psIonBeamParameters.hpp index ff54dc2a..4b9a377a 100644 --- a/include/viennaps/models/psIonBeamParameters.hpp +++ b/include/viennaps/models/psIonBeamParameters.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../psMaterials.hpp" +#include "../materials/psMaterials.hpp" #include #include diff --git a/include/viennaps/models/psIsotropicProcess.hpp b/include/viennaps/models/psIsotropicProcess.hpp index 552b97f7..cd77fc90 100644 --- a/include/viennaps/models/psIsotropicProcess.hpp +++ b/include/viennaps/models/psIsotropicProcess.hpp @@ -1,9 +1,9 @@ #pragma once +#include "../materials/psMaterialValueMap.hpp" #include "../process/psProcessModel.hpp" #include "../process/psSurfaceModel.hpp" #include "../process/psVelocityField.hpp" -#include "../psMaterials.hpp" #include diff --git a/include/viennaps/models/psMultiParticleProcess.hpp b/include/viennaps/models/psMultiParticleProcess.hpp index 8586ebb5..298c812d 100644 --- a/include/viennaps/models/psMultiParticleProcess.hpp +++ b/include/viennaps/models/psMultiParticleProcess.hpp @@ -1,8 +1,8 @@ #pragma once +#include "../materials/psMaterials.hpp" #include "../process/psProcessModel.hpp" #include "../psConstants.hpp" -#include "../psMaterials.hpp" #include "../psUtil.hpp" #include "psIonModelUtil.hpp" #include "psPipelineParameters.hpp" diff --git a/include/viennaps/models/psSelectiveEpitaxy.hpp b/include/viennaps/models/psSelectiveEpitaxy.hpp index 42610bdb..3e617744 100644 --- a/include/viennaps/models/psSelectiveEpitaxy.hpp +++ b/include/viennaps/models/psSelectiveEpitaxy.hpp @@ -1,7 +1,7 @@ #pragma once +#include "../materials/psMaterials.hpp" #include "../process/psProcessModel.hpp" -#include "../psMaterials.hpp" namespace viennaps { diff --git a/include/viennaps/models/psSingleParticleProcess.hpp b/include/viennaps/models/psSingleParticleProcess.hpp index a9335c4c..ba6c34e4 100644 --- a/include/viennaps/models/psSingleParticleProcess.hpp +++ b/include/viennaps/models/psSingleParticleProcess.hpp @@ -1,7 +1,7 @@ #pragma once +#include "../materials/psMaterialValueMap.hpp" #include "../process/psProcessModel.hpp" -#include "../psMaterials.hpp" #include #include diff --git a/include/viennaps/models/psWetEtching.hpp b/include/viennaps/models/psWetEtching.hpp index 92c484ce..33528fdc 100644 --- a/include/viennaps/models/psWetEtching.hpp +++ b/include/viennaps/models/psWetEtching.hpp @@ -1,7 +1,7 @@ #pragma once +#include "../materials/psMaterials.hpp" #include "../process/psProcessModel.hpp" -#include "../psMaterials.hpp" #include diff --git a/include/viennaps/process/psTranslationField.hpp b/include/viennaps/process/psTranslationField.hpp index bb782003..a0219c81 100644 --- a/include/viennaps/process/psTranslationField.hpp +++ b/include/viennaps/process/psTranslationField.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../psMaterials.hpp" +#include "../materials/psMaterials.hpp" #include "psVelocityField.hpp" #include diff --git a/include/viennaps/psDomain.hpp b/include/viennaps/psDomain.hpp index 10377406..4ca95eea 100644 --- a/include/viennaps/psDomain.hpp +++ b/include/viennaps/psDomain.hpp @@ -1,7 +1,7 @@ #pragma once +#include "materials/psMaterials.hpp" #include "psDomainSetup.hpp" -#include "psMaterials.hpp" #include "psPreCompileMacros.hpp" #include "psSurfacePointValuesToLevelSet.hpp" #include "psUtil.hpp" @@ -64,6 +64,7 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Domain { lsDomainsType levelSets_; csDomainType cellSet_ = nullptr; MaterialMapType materialMap_ = nullptr; + MaterialRegistry materialRegistry_; MetaDataLevel metaDataLevel_ = MetaDataLevel::NONE; MetaDataType metaData_; @@ -143,6 +144,7 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Domain { clear(); setup_ = domain->setup_; metaData_ = domain->metaData_; + materialRegistry_ = domain->materialRegistry_; // Copy all Level-Sets. for (auto &ls : domain->levelSets_) { @@ -402,6 +404,14 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Domain { // Level-Set in the domain. [[nodiscard]] auto &getMaterialMap() const { return materialMap_; } + [[nodiscard]] MaterialRegistry &getMaterialRegistry() { + return materialRegistry_; + } + + [[nodiscard]] const MaterialRegistry &getMaterialRegistry() const { + return materialRegistry_; + } + [[nodiscard]] auto &getCellSet() const { return cellSet_; } // Returns the underlying HRLE grid of the top Level-Set in the domain. diff --git a/include/viennaps/psReader.hpp b/include/viennaps/psReader.hpp index 6d1210dd..91812996 100644 --- a/include/viennaps/psReader.hpp +++ b/include/viennaps/psReader.hpp @@ -76,10 +76,10 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Reader { // Check format version char formatVersion; fin.read(&formatVersion, 1); - if (formatVersion > 0) { // Update this when version changes + if (formatVersion > 1) { VIENNACORE_LOG_ERROR("Reading domain of version " + std::to_string(formatVersion) + - " with reader of version 0 failed."); + " with reader of version 1 failed."); return; } @@ -114,12 +114,25 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Reader { fin.read(reinterpret_cast(&numMaterials), sizeof(uint32_t)); assert(numMaterials == numLevelSets); - // Read each material ID and insert corresponding level set + // Read each material and insert corresponding level set. for (uint32_t i = 0; i < numMaterials; i++) { - int materialId; - fin.read(reinterpret_cast(&materialId), sizeof(int)); - domain->insertNextLevelSetAsMaterial( - levelSets[i], static_cast(materialId), false); + Material material = Material::Undefined; + + if (formatVersion == 0) { + int materialId; + fin.read(reinterpret_cast(&materialId), sizeof(int)); + material = Material::fromLegacyId(materialId); + } else { + uint32_t nameLength = 0; + fin.read(reinterpret_cast(&nameLength), sizeof(uint32_t)); + std::string materialName(nameLength, '\0'); + fin.read(materialName.data(), + static_cast(nameLength)); + material = MaterialMap::fromString(materialName, + domain->getMaterialRegistry()); + } + + domain->insertNextLevelSetAsMaterial(levelSets[i], material, false); } } else { diff --git a/include/viennaps/psUtil.hpp b/include/viennaps/psUtil.hpp index ab443c77..51c4a84b 100644 --- a/include/viennaps/psUtil.hpp +++ b/include/viennaps/psUtil.hpp @@ -10,7 +10,7 @@ #include #include -#include "psMaterials.hpp" +#include "materials/psMaterials.hpp" namespace viennaps { enum class FluxEngineType { diff --git a/include/viennaps/psWriter.hpp b/include/viennaps/psWriter.hpp index 58918a89..bc196b94 100644 --- a/include/viennaps/psWriter.hpp +++ b/include/viennaps/psWriter.hpp @@ -74,8 +74,8 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Writer { fout << "ForMesher"; } - // Write version number (starting with 0) - char formatVersion = 0; + // Version 1 serializes materials by stable names. + char formatVersion = 1; fout.write(&formatVersion, 1); // Write domain setup @@ -105,10 +105,22 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Writer { fout.write(reinterpret_cast(&numMaterials), sizeof(uint32_t)); - // Write each material ID + // Write each material by canonical name for stable cross-run I/O. for (size_t i = 0; i < numMaterials; i++) { - int materialId = static_cast(materialMap->getMaterialAtIdx(i)); - fout.write(reinterpret_cast(&materialId), sizeof(int)); + const auto material = materialMap->getMaterialAtIdx(i); + std::string materialName; + if (material.isBuiltIn()) { + materialName = MaterialMap::toString(material); + } else { + materialName = + std::string(domain->getMaterialRegistry().getName(material)); + } + + const auto nameLength = static_cast(materialName.size()); + fout.write(reinterpret_cast(&nameLength), + sizeof(uint32_t)); + fout.write(materialName.data(), + static_cast(nameLength)); } } diff --git a/python/pyWrap.cpp b/python/pyWrap.cpp index b9a3f18b..ee659538 100644 --- a/python/pyWrap.cpp +++ b/python/pyWrap.cpp @@ -57,15 +57,65 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { py::arg("shouldAbort") = true) .def("print", [](Logger &instance) { instance.print(std::cout); }); - // Material enum - auto matEnum = - py::native_enum(module, "Material", "enum.IntEnum", - "Material types for domain and level sets"); + // Built-in material enum + auto builtInMatEnum = py::native_enum( + module, "BuiltInMaterial", "enum.IntEnum", + "Fixed built-in material types for domain and level sets"); #define ENUM_BIND(id, sym, cat, dens, cond, color) \ - matEnum.value(#sym, Material::sym); - MATERIAL_LIST(ENUM_BIND) + builtInMatEnum.value(#sym, BuiltInMaterial::sym); + BUILTIN_MATERIAL_LIST(ENUM_BIND) #undef ENUM_BIND - matEnum.finalize(); + builtInMatEnum.finalize(); + + // Unified material handle + auto materialClass = py::class_(module, "Material"); + materialClass.def(py::init<>()) + .def(py::init()) + .def(py::init()) + .def_static("custom", &Material::custom, py::arg("id")) + .def("isBuiltIn", &Material::isBuiltIn) + .def("isCustom", &Material::isCustom) + .def("builtIn", &Material::builtIn) + .def("customId", &Material::customId) + .def("legacyId", &Material::legacyId) + .def("kind", &Material::kind) + .def("__int__", [](const Material &m) { return static_cast(m); }) + .def("__hash__", + [](const Material &m) { return std::hash{}(m); }) + .def("__repr__", + [](const Material &m) { + return "Material('" + MaterialMap::toString(m) + "')"; + }) + .def(py::self == py::self) + .def(py::self != py::self); + +#define MATERIAL_CONST(id, sym, cat, dens, cond, color) \ + materialClass.attr(#sym) = py::cast(Material::sym); + BUILTIN_MATERIAL_LIST(MATERIAL_CONST) +#undef MATERIAL_CONST + + py::implicitly_convertible(); + + py::native_enum(module, "MaterialKind", "enum.IntEnum") + .value("BuiltIn", Material::Kind::BuiltIn) + .value("Custom", Material::Kind::Custom) + .finalize(); + + py::class_(module, "MaterialRegistry") + .def(py::init<>()) + .def("registerMaterial", &MaterialRegistry::registerMaterial, + py::arg("name")) + .def("hasMaterial", &MaterialRegistry::hasMaterial, py::arg("name")) + .def("findMaterial", &MaterialRegistry::findMaterial, py::arg("name")) + .def("getMaterial", &MaterialRegistry::getMaterial, py::arg("name")) + .def( + "getName", + [](const MaterialRegistry ®istry, const Material material) { + return std::string(registry.getName(material)); + }, + py::arg("material")) + .def("isBuiltIn", &MaterialRegistry::isBuiltIn, py::arg("material")) + .def("customMaterialCount", &MaterialRegistry::customMaterialCount); // Material category enum py::native_enum(module, "MaterialCategory", "enum.IntEnum") @@ -109,6 +159,9 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { .def_static("mapToMaterial", &MaterialMap::mapToMaterial, "Map a float to a material.") .def_static("isMaterial", &MaterialMap::isMaterial) + .def_static("fromString", &MaterialMap::fromString, py::arg("name"), + py::arg("registry"), + "Resolve built-in or register custom material by name.") .def_static("toString", py::overload_cast(&MaterialMap::toString), "Get the name of a material."); diff --git a/python/pyWrapDimension.hpp b/python/pyWrapDimension.hpp index d513885d..a688f47c 100644 --- a/python/pyWrapDimension.hpp +++ b/python/pyWrapDimension.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -516,6 +517,37 @@ template void bindApi(py::module &module) { "Get the number of level sets in the domain.") .def("setMaterialMap", &Domain::setMaterialMap) .def("getMaterialMap", &Domain::getMaterialMap) + .def("getMaterialRegistry", + py::overload_cast<>(&Domain::getMaterialRegistry), + py::return_value_policy::reference_internal, + "Get the domain-local material registry.") + .def( + "registerMaterial", + [](Domain &self, const std::string &name) { + return self.getMaterialRegistry().registerMaterial(name); + }, + py::arg("name"), + "Register a material by name in this domain and return handle.") + .def( + "hasMaterial", + [](const Domain &self, const std::string &name) { + return self.getMaterialRegistry().hasMaterial(name); + }, + py::arg("name"), + "Check whether material name resolves in this domain.") + .def( + "getMaterialByName", + [](const Domain &self, const std::string &name) { + return self.getMaterialRegistry().getMaterial(name); + }, + py::arg("name"), "Resolve material by name in this domain.") + .def( + "getMaterialName", + [](const Domain &self, const Material material) { + return std::string(self.getMaterialRegistry().getName(material)); + }, + py::arg("material"), + "Resolve canonical material name in this domain.") .def("generateCellSet", &Domain::generateCellSet, "Generate the cell set.") .def("getLevelSets", &Domain::getLevelSets) diff --git a/python/viennaps/__init__.pyi b/python/viennaps/__init__.pyi index 373c6b29..44c9d5b3 100644 --- a/python/viennaps/__init__.pyi +++ b/python/viennaps/__init__.pyi @@ -17,6 +17,7 @@ from viennals._core import SpatialSchemeEnum as SpatialScheme from viennals._core import TemporalSchemeEnum as TemporalScheme from viennaps._core import AdvectionParameters from viennaps._core import AtomicLayerProcessParameters +from viennaps._core import BuiltInMaterial from viennaps._core import CF4O2Parameters from viennaps._core import CF4O2ParametersIons from viennaps._core import CF4O2ParametersMask @@ -39,7 +40,9 @@ from viennaps._core import Logger from viennaps._core import Material from viennaps._core import MaterialCategory from viennaps._core import MaterialInfo +from viennaps._core import MaterialKind from viennaps._core import MaterialMap +from viennaps._core import MaterialRegistry from viennaps._core import MetaDataLevel from viennaps._core import NormalizationType from viennaps._core import PlasmaEtchingParameters @@ -107,7 +110,7 @@ from viennaps.d2 import Writer from . import _core from . import d2 from . import d3 -__all__: list[str] = ['AdvectionCallback', 'AdvectionParameters', 'AtomicLayerProcessParameters', 'BoundaryType', 'BoxDistribution', 'CF4O2Etching', 'CF4O2Parameters', 'CF4O2ParametersIons', 'CF4O2ParametersMask', 'CF4O2ParametersPassivation', 'CF4O2ParametersSi', 'CF4O2ParametersSiGe', 'CSVFileProcess', 'CoverageParameters', 'DenseCellSet', 'DirectionalProcess', 'Domain', 'DomainSetup', 'Extrude', 'FaradayCageEtching', 'FaradayCageParameters', 'FluorocarbonEtching', 'FluorocarbonMaterialParameters', 'FluorocarbonParameters', 'FluorocarbonParametersIons', 'FluxEngineType', 'GDSGeometry', 'GDSReader', 'GeometricTrenchDeposition', 'GeometryFactory', 'HBrO2Etching', 'HoleShape', 'IBEParameters', 'IBEParametersCos4Yield', 'Interpolation', 'IonBeamEtching', 'IsotropicProcess', 'Length', 'LengthUnit', 'LogLevel', 'Logger', 'MakeFin', 'MakeHole', 'MakePlane', 'MakeStack', 'MakeTrench', 'Material', 'MaterialCategory', 'MaterialInfo', 'MaterialMap', 'MetaDataLevel', 'MultiParticleProcess', 'NormalizationType', 'OxideRegrowth', 'PROXY_DIM', 'Planarize', 'PlasmaEtchingParameters', 'PlasmaEtchingParametersIons', 'PlasmaEtchingParametersMask', 'PlasmaEtchingParametersPassivation', 'PlasmaEtchingParametersPolymer', 'PlasmaEtchingParametersSubstrate', 'Process', 'ProcessModel', 'ProcessModelBase', 'ProcessParams', 'RateGrid', 'RateSet', 'RayTracingParameters', 'Reader', 'RenderMode', 'SF6C4F8Etching', 'SF6O2Etching', 'SelectiveEpitaxy', 'SingleParticleALD', 'SingleParticleProcess', 'Slice', 'SpatialScheme', 'SphereDistribution', 'StencilLocalLaxFriedrichsScalar', 'TEOSDeposition', 'TEOSPECVD', 'TemporalScheme', 'Time', 'TimeUnit', 'ToDiskMesh', 'VTKRenderWindow', 'WetEtching', 'Writer', 'constants', 'd2', 'd3', 'gpu', 'gpuAvailable', 'ls', 'readConfigFile', 'setDimension', 'setNumThreads', 'util', 'version'] +__all__: list[str] = ['AdvectionCallback', 'AdvectionParameters', 'AtomicLayerProcessParameters', 'BoundaryType', 'BoxDistribution', 'BuiltInMaterial', 'CF4O2Etching', 'CF4O2Parameters', 'CF4O2ParametersIons', 'CF4O2ParametersMask', 'CF4O2ParametersPassivation', 'CF4O2ParametersSi', 'CF4O2ParametersSiGe', 'CSVFileProcess', 'CoverageParameters', 'DenseCellSet', 'DirectionalProcess', 'Domain', 'DomainSetup', 'Extrude', 'FaradayCageEtching', 'FaradayCageParameters', 'FluorocarbonEtching', 'FluorocarbonMaterialParameters', 'FluorocarbonParameters', 'FluorocarbonParametersIons', 'FluxEngineType', 'GDSGeometry', 'GDSReader', 'GeometricTrenchDeposition', 'GeometryFactory', 'HBrO2Etching', 'HoleShape', 'IBEParameters', 'IBEParametersCos4Yield', 'Interpolation', 'IonBeamEtching', 'IsotropicProcess', 'Length', 'LengthUnit', 'LogLevel', 'Logger', 'MakeFin', 'MakeHole', 'MakePlane', 'MakeStack', 'MakeTrench', 'Material', 'MaterialCategory', 'MaterialInfo', 'MaterialKind', 'MaterialMap', 'MaterialRegistry', 'MetaDataLevel', 'MultiParticleProcess', 'NormalizationType', 'OxideRegrowth', 'PROXY_DIM', 'Planarize', 'PlasmaEtchingParameters', 'PlasmaEtchingParametersIons', 'PlasmaEtchingParametersMask', 'PlasmaEtchingParametersPassivation', 'PlasmaEtchingParametersPolymer', 'PlasmaEtchingParametersSubstrate', 'Process', 'ProcessModel', 'ProcessModelBase', 'ProcessParams', 'RateGrid', 'RateSet', 'RayTracingParameters', 'Reader', 'RenderMode', 'SF6C4F8Etching', 'SF6O2Etching', 'SelectiveEpitaxy', 'SingleParticleALD', 'SingleParticleProcess', 'Slice', 'SpatialScheme', 'SphereDistribution', 'StencilLocalLaxFriedrichsScalar', 'TEOSDeposition', 'TEOSPECVD', 'TemporalScheme', 'Time', 'TimeUnit', 'ToDiskMesh', 'VTKRenderWindow', 'WetEtching', 'Writer', 'constants', 'd2', 'd3', 'gpu', 'gpuAvailable', 'ls', 'readConfigFile', 'setDimension', 'setNumThreads', 'util', 'version'] def __dir__(): ... def __getattr__(name): diff --git a/python/viennaps/_core/__init__.pyi b/python/viennaps/_core/__init__.pyi index 8e81da1c..accdd7b3 100644 --- a/python/viennaps/_core/__init__.pyi +++ b/python/viennaps/_core/__init__.pyi @@ -6,14 +6,14 @@ import collections.abc import enum import typing import viennals._core -from viennaps import d2 import viennaps.d2 -from viennaps import d3 +from viennaps import d2 import viennaps.d3 +from viennaps import d3 from . import constants from . import gpu from . import util -__all__: list[str] = ['AdvectionParameters', 'AtomicLayerProcessParameters', 'CF4O2Parameters', 'CF4O2ParametersIons', 'CF4O2ParametersMask', 'CF4O2ParametersPassivation', 'CF4O2ParametersSi', 'CF4O2ParametersSiGe', 'CoverageParameters', 'Extrude', 'FaradayCageParameters', 'FluorocarbonMaterialParameters', 'FluorocarbonParameters', 'FluorocarbonParametersIons', 'FluxEngineType', 'HoleShape', 'IBEParameters', 'IBEParametersCos4Yield', 'Length', 'LengthUnit', 'Logger', 'Material', 'MaterialCategory', 'MaterialInfo', 'MaterialMap', 'MetaDataLevel', 'NormalizationType', 'PlasmaEtchingParameters', 'PlasmaEtchingParametersIons', 'PlasmaEtchingParametersMask', 'PlasmaEtchingParametersPassivation', 'PlasmaEtchingParametersPolymer', 'PlasmaEtchingParametersSubstrate', 'ProcessParams', 'RateSet', 'RayTracingParameters', 'RenderMode', 'Slice', 'Time', 'TimeUnit', 'constants', 'd2', 'd3', 'gpu', 'gpuAvailable', 'setNumThreads', 'util', 'version'] +__all__: list[str] = ['AdvectionParameters', 'AtomicLayerProcessParameters', 'BuiltInMaterial', 'CF4O2Parameters', 'CF4O2ParametersIons', 'CF4O2ParametersMask', 'CF4O2ParametersPassivation', 'CF4O2ParametersSi', 'CF4O2ParametersSiGe', 'CoverageParameters', 'Extrude', 'FaradayCageParameters', 'FluorocarbonMaterialParameters', 'FluorocarbonParameters', 'FluorocarbonParametersIons', 'FluxEngineType', 'HoleShape', 'IBEParameters', 'IBEParametersCos4Yield', 'Length', 'LengthUnit', 'Logger', 'Material', 'MaterialCategory', 'MaterialInfo', 'MaterialKind', 'MaterialMap', 'MaterialRegistry', 'MetaDataLevel', 'NormalizationType', 'PlasmaEtchingParameters', 'PlasmaEtchingParametersIons', 'PlasmaEtchingParametersMask', 'PlasmaEtchingParametersPassivation', 'PlasmaEtchingParametersPolymer', 'PlasmaEtchingParametersSubstrate', 'ProcessParams', 'RateSet', 'RayTracingParameters', 'RenderMode', 'Slice', 'Time', 'TimeUnit', 'constants', 'd2', 'd3', 'gpu', 'gpuAvailable', 'setNumThreads', 'util', 'version'] class AdvectionParameters: adaptiveTimeStepping: bool calculateIntermediateVelocities: bool @@ -86,6 +86,108 @@ class AtomicLayerProcessParameters: @purgePulseTime.setter def purgePulseTime(self, arg0: typing.SupportsFloat | typing.SupportsIndex) -> None: ... +class BuiltInMaterial(enum.IntEnum): + """ + Fixed built-in material types for domain and level sets + """ + ARC: typing.ClassVar[BuiltInMaterial] # value = + AZO: typing.ClassVar[BuiltInMaterial] # value = + Air: typing.ClassVar[BuiltInMaterial] # value = + Al2O3: typing.ClassVar[BuiltInMaterial] # value = + AlN: typing.ClassVar[BuiltInMaterial] # value = + Au: typing.ClassVar[BuiltInMaterial] # value = + BN: typing.ClassVar[BuiltInMaterial] # value = + BPSG: typing.ClassVar[BuiltInMaterial] # value = + BulkSi: typing.ClassVar[BuiltInMaterial] # value = + C: typing.ClassVar[BuiltInMaterial] # value = + Co: typing.ClassVar[BuiltInMaterial] # value = + CoW: typing.ClassVar[BuiltInMaterial] # value = + Cr: typing.ClassVar[BuiltInMaterial] # value = + Cu: typing.ClassVar[BuiltInMaterial] # value = + Custom: typing.ClassVar[BuiltInMaterial] # value = + Dielectric: typing.ClassVar[BuiltInMaterial] # value = + GAS: typing.ClassVar[BuiltInMaterial] # value = + GST: typing.ClassVar[BuiltInMaterial] # value = + GaAs: typing.ClassVar[BuiltInMaterial] # value = + GaN: typing.ClassVar[BuiltInMaterial] # value = + Ge: typing.ClassVar[BuiltInMaterial] # value = + Graphene: typing.ClassVar[BuiltInMaterial] # value = + HSQ: typing.ClassVar[BuiltInMaterial] # value = + HfO2: typing.ClassVar[BuiltInMaterial] # value = + ITO: typing.ClassVar[BuiltInMaterial] # value = + InGaAs: typing.ClassVar[BuiltInMaterial] # value = + InP: typing.ClassVar[BuiltInMaterial] # value = + Ir: typing.ClassVar[BuiltInMaterial] # value = + La2O3: typing.ClassVar[BuiltInMaterial] # value = + Mask: typing.ClassVar[BuiltInMaterial] # value = + Metal: typing.ClassVar[BuiltInMaterial] # value = + Mn: typing.ClassVar[BuiltInMaterial] # value = + MnN: typing.ClassVar[BuiltInMaterial] # value = + MnO: typing.ClassVar[BuiltInMaterial] # value = + Mo: typing.ClassVar[BuiltInMaterial] # value = + MoS2: typing.ClassVar[BuiltInMaterial] # value = + MoSi2: typing.ClassVar[BuiltInMaterial] # value = + Ni: typing.ClassVar[BuiltInMaterial] # value = + NiW: typing.ClassVar[BuiltInMaterial] # value = + PHS: typing.ClassVar[BuiltInMaterial] # value = + PMMA: typing.ClassVar[BuiltInMaterial] # value = + PSG: typing.ClassVar[BuiltInMaterial] # value = + Pd: typing.ClassVar[BuiltInMaterial] # value = + PolySi: typing.ClassVar[BuiltInMaterial] # value = + Polymer: typing.ClassVar[BuiltInMaterial] # value = + Pt: typing.ClassVar[BuiltInMaterial] # value = + Rh: typing.ClassVar[BuiltInMaterial] # value = + Ru: typing.ClassVar[BuiltInMaterial] # value = + RuTa: typing.ClassVar[BuiltInMaterial] # value = + SOC: typing.ClassVar[BuiltInMaterial] # value = + SOG: typing.ClassVar[BuiltInMaterial] # value = + Si: typing.ClassVar[BuiltInMaterial] # value = + Si3N4: typing.ClassVar[BuiltInMaterial] # value = + SiBCN: typing.ClassVar[BuiltInMaterial] # value = + SiC: typing.ClassVar[BuiltInMaterial] # value = + SiCN: typing.ClassVar[BuiltInMaterial] # value = + SiCOH: typing.ClassVar[BuiltInMaterial] # value = + SiC_HM: typing.ClassVar[BuiltInMaterial] # value = + SiGaN: typing.ClassVar[BuiltInMaterial] # value = + SiGe: typing.ClassVar[BuiltInMaterial] # value = + SiLK: typing.ClassVar[BuiltInMaterial] # value = + SiN: typing.ClassVar[BuiltInMaterial] # value = + SiN_HM: typing.ClassVar[BuiltInMaterial] # value = + SiO2: typing.ClassVar[BuiltInMaterial] # value = + SiO2_HM: typing.ClassVar[BuiltInMaterial] # value = + SiOCH: typing.ClassVar[BuiltInMaterial] # value = + SiOCN: typing.ClassVar[BuiltInMaterial] # value = + SiON: typing.ClassVar[BuiltInMaterial] # value = + SiON_HM: typing.ClassVar[BuiltInMaterial] # value = + Ta: typing.ClassVar[BuiltInMaterial] # value = + Ta2O5: typing.ClassVar[BuiltInMaterial] # value = + TaN: typing.ClassVar[BuiltInMaterial] # value = + Ti: typing.ClassVar[BuiltInMaterial] # value = + TiAlN: typing.ClassVar[BuiltInMaterial] # value = + TiN: typing.ClassVar[BuiltInMaterial] # value = + TiO: typing.ClassVar[BuiltInMaterial] # value = + TiO2: typing.ClassVar[BuiltInMaterial] # value = + TiSi2: typing.ClassVar[BuiltInMaterial] # value = + Undefined: typing.ClassVar[BuiltInMaterial] # value = + VO2: typing.ClassVar[BuiltInMaterial] # value = + W: typing.ClassVar[BuiltInMaterial] # value = + WS2: typing.ClassVar[BuiltInMaterial] # value = + WSe2: typing.ClassVar[BuiltInMaterial] # value = + WSi2: typing.ClassVar[BuiltInMaterial] # value = + Y2O3: typing.ClassVar[BuiltInMaterial] # value = + ZnO: typing.ClassVar[BuiltInMaterial] # value = + ZrO: typing.ClassVar[BuiltInMaterial] # value = + ZrO2: typing.ClassVar[BuiltInMaterial] # value = + aC: typing.ClassVar[BuiltInMaterial] # value = + aSi: typing.ClassVar[BuiltInMaterial] # value = + hBN: typing.ClassVar[BuiltInMaterial] # value = + @classmethod + def __new__(cls, value): + ... + def __format__(self, format_spec): + """ + Convert to a string according to format_spec. + """ class CF4O2Parameters: Ions: CF4O2ParametersIons Mask: CF4O2ParametersMask @@ -798,107 +900,132 @@ class Logger: ... def print(self) -> None: ... -class Material(enum.IntEnum): - """ - Material types for domain and level sets - """ - ARC: typing.ClassVar[Material] # value = - AZO: typing.ClassVar[Material] # value = - Air: typing.ClassVar[Material] # value = - Al2O3: typing.ClassVar[Material] # value = - AlN: typing.ClassVar[Material] # value = - Au: typing.ClassVar[Material] # value = - BN: typing.ClassVar[Material] # value = - BPSG: typing.ClassVar[Material] # value = - BulkSi: typing.ClassVar[Material] # value = - C: typing.ClassVar[Material] # value = - Co: typing.ClassVar[Material] # value = - CoW: typing.ClassVar[Material] # value = - Cr: typing.ClassVar[Material] # value = - Cu: typing.ClassVar[Material] # value = - Dielectric: typing.ClassVar[Material] # value = - GAS: typing.ClassVar[Material] # value = - GST: typing.ClassVar[Material] # value = - GaAs: typing.ClassVar[Material] # value = - GaN: typing.ClassVar[Material] # value = - Ge: typing.ClassVar[Material] # value = - Graphene: typing.ClassVar[Material] # value = - HSQ: typing.ClassVar[Material] # value = - HfO2: typing.ClassVar[Material] # value = - ITO: typing.ClassVar[Material] # value = - InGaAs: typing.ClassVar[Material] # value = - InP: typing.ClassVar[Material] # value = - Ir: typing.ClassVar[Material] # value = - La2O3: typing.ClassVar[Material] # value = - Mask: typing.ClassVar[Material] # value = - Metal: typing.ClassVar[Material] # value = - Mn: typing.ClassVar[Material] # value = - MnN: typing.ClassVar[Material] # value = - MnO: typing.ClassVar[Material] # value = - Mo: typing.ClassVar[Material] # value = - MoS2: typing.ClassVar[Material] # value = - MoSi2: typing.ClassVar[Material] # value = - Ni: typing.ClassVar[Material] # value = - NiW: typing.ClassVar[Material] # value = - PHS: typing.ClassVar[Material] # value = - PMMA: typing.ClassVar[Material] # value = - PSG: typing.ClassVar[Material] # value = - Pd: typing.ClassVar[Material] # value = - PolySi: typing.ClassVar[Material] # value = - Polymer: typing.ClassVar[Material] # value = - Pt: typing.ClassVar[Material] # value = - Rh: typing.ClassVar[Material] # value = - Ru: typing.ClassVar[Material] # value = - RuTa: typing.ClassVar[Material] # value = - SOC: typing.ClassVar[Material] # value = - SOG: typing.ClassVar[Material] # value = - Si: typing.ClassVar[Material] # value = - Si3N4: typing.ClassVar[Material] # value = - SiBCN: typing.ClassVar[Material] # value = - SiC: typing.ClassVar[Material] # value = - SiCN: typing.ClassVar[Material] # value = - SiCOH: typing.ClassVar[Material] # value = - SiC_HM: typing.ClassVar[Material] # value = - SiGaN: typing.ClassVar[Material] # value = - SiGe: typing.ClassVar[Material] # value = - SiLK: typing.ClassVar[Material] # value = - SiN: typing.ClassVar[Material] # value = - SiN_HM: typing.ClassVar[Material] # value = - SiO2: typing.ClassVar[Material] # value = - SiO2_HM: typing.ClassVar[Material] # value = - SiOCH: typing.ClassVar[Material] # value = - SiOCN: typing.ClassVar[Material] # value = - SiON: typing.ClassVar[Material] # value = - SiON_HM: typing.ClassVar[Material] # value = - Ta: typing.ClassVar[Material] # value = - Ta2O5: typing.ClassVar[Material] # value = - TaN: typing.ClassVar[Material] # value = - Ti: typing.ClassVar[Material] # value = - TiAlN: typing.ClassVar[Material] # value = - TiN: typing.ClassVar[Material] # value = - TiO: typing.ClassVar[Material] # value = - TiO2: typing.ClassVar[Material] # value = - TiSi2: typing.ClassVar[Material] # value = - Undefined: typing.ClassVar[Material] # value = - VO2: typing.ClassVar[Material] # value = - W: typing.ClassVar[Material] # value = - WS2: typing.ClassVar[Material] # value = - WSe2: typing.ClassVar[Material] # value = - WSi2: typing.ClassVar[Material] # value = - Y2O3: typing.ClassVar[Material] # value = - ZnO: typing.ClassVar[Material] # value = - ZrO: typing.ClassVar[Material] # value = - ZrO2: typing.ClassVar[Material] # value = - aC: typing.ClassVar[Material] # value = - aSi: typing.ClassVar[Material] # value = - hBN: typing.ClassVar[Material] # value = - @classmethod - def __new__(cls, value): +class Material: + ARC: typing.ClassVar[Material] # value = Material('ARC') + AZO: typing.ClassVar[Material] # value = Material('AZO') + Air: typing.ClassVar[Material] # value = Material('Air') + Al2O3: typing.ClassVar[Material] # value = Material('Al2O3') + AlN: typing.ClassVar[Material] # value = Material('AlN') + Au: typing.ClassVar[Material] # value = Material('Au') + BN: typing.ClassVar[Material] # value = Material('BN') + BPSG: typing.ClassVar[Material] # value = Material('BPSG') + BulkSi: typing.ClassVar[Material] # value = Material('BulkSi') + C: typing.ClassVar[Material] # value = Material('C') + Co: typing.ClassVar[Material] # value = Material('Co') + CoW: typing.ClassVar[Material] # value = Material('CoW') + Cr: typing.ClassVar[Material] # value = Material('Cr') + Cu: typing.ClassVar[Material] # value = Material('Cu') + Custom: typing.ClassVar[Material] # value = Material('Custom') + Dielectric: typing.ClassVar[Material] # value = Material('Dielectric') + GAS: typing.ClassVar[Material] # value = Material('GAS') + GST: typing.ClassVar[Material] # value = Material('GST') + GaAs: typing.ClassVar[Material] # value = Material('GaAs') + GaN: typing.ClassVar[Material] # value = Material('GaN') + Ge: typing.ClassVar[Material] # value = Material('Ge') + Graphene: typing.ClassVar[Material] # value = Material('Graphene') + HSQ: typing.ClassVar[Material] # value = Material('HSQ') + HfO2: typing.ClassVar[Material] # value = Material('HfO2') + ITO: typing.ClassVar[Material] # value = Material('ITO') + InGaAs: typing.ClassVar[Material] # value = Material('InGaAs') + InP: typing.ClassVar[Material] # value = Material('InP') + Ir: typing.ClassVar[Material] # value = Material('Ir') + La2O3: typing.ClassVar[Material] # value = Material('La2O3') + Mask: typing.ClassVar[Material] # value = Material('Mask') + Metal: typing.ClassVar[Material] # value = Material('Metal') + Mn: typing.ClassVar[Material] # value = Material('Mn') + MnN: typing.ClassVar[Material] # value = Material('MnN') + MnO: typing.ClassVar[Material] # value = Material('MnO') + Mo: typing.ClassVar[Material] # value = Material('Mo') + MoS2: typing.ClassVar[Material] # value = Material('MoS2') + MoSi2: typing.ClassVar[Material] # value = Material('MoSi2') + Ni: typing.ClassVar[Material] # value = Material('Ni') + NiW: typing.ClassVar[Material] # value = Material('NiW') + PHS: typing.ClassVar[Material] # value = Material('PHS') + PMMA: typing.ClassVar[Material] # value = Material('PMMA') + PSG: typing.ClassVar[Material] # value = Material('PSG') + Pd: typing.ClassVar[Material] # value = Material('Pd') + PolySi: typing.ClassVar[Material] # value = Material('PolySi') + Polymer: typing.ClassVar[Material] # value = Material('Polymer') + Pt: typing.ClassVar[Material] # value = Material('Pt') + Rh: typing.ClassVar[Material] # value = Material('Rh') + Ru: typing.ClassVar[Material] # value = Material('Ru') + RuTa: typing.ClassVar[Material] # value = Material('RuTa') + SOC: typing.ClassVar[Material] # value = Material('SOC') + SOG: typing.ClassVar[Material] # value = Material('SOG') + Si: typing.ClassVar[Material] # value = Material('Si') + Si3N4: typing.ClassVar[Material] # value = Material('Si3N4') + SiBCN: typing.ClassVar[Material] # value = Material('SiBCN') + SiC: typing.ClassVar[Material] # value = Material('SiC') + SiCN: typing.ClassVar[Material] # value = Material('SiCN') + SiCOH: typing.ClassVar[Material] # value = Material('SiCOH') + SiC_HM: typing.ClassVar[Material] # value = Material('SiC_HM') + SiGaN: typing.ClassVar[Material] # value = Material('SiGaN') + SiGe: typing.ClassVar[Material] # value = Material('SiGe') + SiLK: typing.ClassVar[Material] # value = Material('SiLK') + SiN: typing.ClassVar[Material] # value = Material('SiN') + SiN_HM: typing.ClassVar[Material] # value = Material('SiN_HM') + SiO2: typing.ClassVar[Material] # value = Material('SiO2') + SiO2_HM: typing.ClassVar[Material] # value = Material('SiO2_HM') + SiOCH: typing.ClassVar[Material] # value = Material('SiOCH') + SiOCN: typing.ClassVar[Material] # value = Material('SiOCN') + SiON: typing.ClassVar[Material] # value = Material('SiON') + SiON_HM: typing.ClassVar[Material] # value = Material('SiON_HM') + Ta: typing.ClassVar[Material] # value = Material('Ta') + Ta2O5: typing.ClassVar[Material] # value = Material('Ta2O5') + TaN: typing.ClassVar[Material] # value = Material('TaN') + Ti: typing.ClassVar[Material] # value = Material('Ti') + TiAlN: typing.ClassVar[Material] # value = Material('TiAlN') + TiN: typing.ClassVar[Material] # value = Material('TiN') + TiO: typing.ClassVar[Material] # value = Material('TiO') + TiO2: typing.ClassVar[Material] # value = Material('TiO2') + TiSi2: typing.ClassVar[Material] # value = Material('TiSi2') + Undefined: typing.ClassVar[Material] # value = Material('Undefined') + VO2: typing.ClassVar[Material] # value = Material('VO2') + W: typing.ClassVar[Material] # value = Material('W') + WS2: typing.ClassVar[Material] # value = Material('WS2') + WSe2: typing.ClassVar[Material] # value = Material('WSe2') + WSi2: typing.ClassVar[Material] # value = Material('WSi2') + Y2O3: typing.ClassVar[Material] # value = Material('Y2O3') + ZnO: typing.ClassVar[Material] # value = Material('ZnO') + ZrO: typing.ClassVar[Material] # value = Material('ZrO') + ZrO2: typing.ClassVar[Material] # value = Material('ZrO2') + aC: typing.ClassVar[Material] # value = Material('aC') + aSi: typing.ClassVar[Material] # value = Material('aSi') + hBN: typing.ClassVar[Material] # value = Material('hBN') + @staticmethod + def custom(id: typing.SupportsInt | typing.SupportsIndex) -> Material: + ... + def __eq__(self, arg0: Material) -> bool: + ... + def __hash__(self) -> int: + ... + @typing.overload + def __init__(self) -> None: + ... + @typing.overload + def __init__(self, arg0: BuiltInMaterial) -> None: + ... + @typing.overload + def __init__(self, arg0: typing.SupportsInt | typing.SupportsIndex) -> None: + ... + def __int__(self) -> int: + ... + def __ne__(self, arg0: Material) -> bool: + ... + def __repr__(self) -> str: + ... + def builtIn(self) -> BuiltInMaterial: + ... + def customId(self) -> int: + ... + def isBuiltIn(self) -> bool: + ... + def isCustom(self) -> bool: + ... + def kind(self) -> ...: + ... + def legacyId(self) -> int: ... - def __format__(self, format_spec): - """ - Convert to a string according to format_spec. - """ class MaterialCategory(enum.IntEnum): Compound: typing.ClassVar[MaterialCategory] # value = Generic: typing.ClassVar[MaterialCategory] # value = @@ -938,8 +1065,23 @@ class MaterialInfo: @property def name(self) -> str: ... +class MaterialKind(enum.IntEnum): + BuiltIn: typing.ClassVar[MaterialKind] # value = + Custom: typing.ClassVar[MaterialKind] # value = + @classmethod + def __new__(cls, value): + ... + def __format__(self, format_spec): + """ + Convert to a string according to format_spec. + """ class MaterialMap: @staticmethod + def fromString(name: str, registry: MaterialRegistry) -> Material: + """ + Resolve built-in or register custom material by name. + """ + @staticmethod def isMaterial(arg0: typing.SupportsFloat | typing.SupportsIndex, arg1: Material) -> bool: ... @staticmethod @@ -964,6 +1106,23 @@ class MaterialMap: ... def size(self) -> int: ... +class MaterialRegistry: + def __init__(self) -> None: + ... + def customMaterialCount(self) -> int: + ... + def findMaterial(self, name: str) -> viennaps._core.Material | None: + ... + def getMaterial(self, name: str) -> Material: + ... + def getName(self, material: Material) -> str: + ... + def hasMaterial(self, name: str) -> bool: + ... + def isBuiltIn(self, material: Material) -> bool: + ... + def registerMaterial(self, name: str) -> Material: + ... class MetaDataLevel(enum.IntEnum): FULL: typing.ClassVar[MetaDataLevel] # value = GRID: typing.ClassVar[MetaDataLevel] # value = diff --git a/python/viennaps/d2/__init__.pyi b/python/viennaps/d2/__init__.pyi index 2b3641dd..dad9f692 100644 --- a/python/viennaps/d2/__init__.pyi +++ b/python/viennaps/d2/__init__.pyi @@ -305,8 +305,20 @@ class Domain: """ def getLevelSets(self) -> list[viennals.d2.Domain]: ... + def getMaterialByName(self, name: str) -> viennaps._core.Material: + """ + Resolve material by name in this domain. + """ def getMaterialMap(self) -> viennaps._core.MaterialMap: ... + def getMaterialName(self, material: viennaps._core.Material) -> str: + """ + Resolve canonical material name in this domain. + """ + def getMaterialRegistry(self) -> viennaps._core.MaterialRegistry: + """ + Get the domain-local material registry. + """ def getMaterialsInDomain(self) -> set[viennaps._core.Material]: """ Get the material IDs present in the domain. @@ -339,6 +351,10 @@ class Domain: """ Get the surface mesh of the domain """ + def hasMaterial(self, name: str) -> bool: + """ + Check whether material name resolves in this domain. + """ def insertMask(self, mask: viennals.d2.Domain, material: viennaps._core.Material = ...) -> None: """ Insert a mask level set to the domain. The mask is inserted at the front of the level set vector and can be used to exclude areas from processes. @@ -351,6 +367,10 @@ class Domain: """ Print the domain information. """ + def registerMaterial(self, name: str) -> viennaps._core.Material: + """ + Register a material by name in this domain and return handle. + """ def removeLevelSet(self, arg0: typing.SupportsInt | typing.SupportsIndex, arg1: bool) -> None: ... def removeMaterial(self, arg0: viennaps._core.Material) -> None: diff --git a/python/viennaps/d3/__init__.pyi b/python/viennaps/d3/__init__.pyi index 2fff87b5..450c2190 100644 --- a/python/viennaps/d3/__init__.pyi +++ b/python/viennaps/d3/__init__.pyi @@ -305,8 +305,20 @@ class Domain: """ def getLevelSets(self) -> list[viennals.d3.Domain]: ... + def getMaterialByName(self, name: str) -> viennaps._core.Material: + """ + Resolve material by name in this domain. + """ def getMaterialMap(self) -> viennaps._core.MaterialMap: ... + def getMaterialName(self, material: viennaps._core.Material) -> str: + """ + Resolve canonical material name in this domain. + """ + def getMaterialRegistry(self) -> viennaps._core.MaterialRegistry: + """ + Get the domain-local material registry. + """ def getMaterialsInDomain(self) -> set[viennaps._core.Material]: """ Get the material IDs present in the domain. @@ -339,6 +351,10 @@ class Domain: """ Get the surface mesh of the domain """ + def hasMaterial(self, name: str) -> bool: + """ + Check whether material name resolves in this domain. + """ def insertMask(self, mask: viennals.d3.Domain, material: viennaps._core.Material = ...) -> None: """ Insert a mask level set to the domain. The mask is inserted at the front of the level set vector and can be used to exclude areas from processes. @@ -351,6 +367,10 @@ class Domain: """ Print the domain information. """ + def registerMaterial(self, name: str) -> viennaps._core.Material: + """ + Register a material by name in this domain and return handle. + """ def removeLevelSet(self, arg0: typing.SupportsInt | typing.SupportsIndex, arg1: bool) -> None: ... def removeMaterial(self, arg0: viennaps._core.Material) -> None: diff --git a/tests/materialMap/materialMap.cpp b/tests/materialMap/materialMap.cpp index 18ed8458..879c8e82 100644 --- a/tests/materialMap/materialMap.cpp +++ b/tests/materialMap/materialMap.cpp @@ -1,4 +1,5 @@ -#include "psMaterials.hpp" +#include "materials/psMaterialValueMap.hpp" +#include "materials/psMaterials.hpp" #include @@ -27,8 +28,35 @@ int main() { materialMap.setMaterialAtIdx(0, Material::SiO2); VC_TEST_ASSERT(materialMap.getMaterialAtIdx(0) == Material::SiO2); + // Built-in mapping checks + VC_TEST_ASSERT(builtInMaterialToString(BuiltInMaterial::Si) == "Si"); + VC_TEST_ASSERT(builtInMaterialFromString("SiO2") == BuiltInMaterial::SiO2); + VC_TEST_ASSERT(Material::Si.isBuiltIn()); + + // Custom material registration checks + MaterialRegistry registry; + const auto customA = registry.registerMaterial("MyCustom"); + const auto customA2 = registry.registerMaterial("MyCustom"); + const auto customB = registry.registerMaterial("AnotherCustom"); + VC_TEST_ASSERT(customA.isCustom()); + VC_TEST_ASSERT(customA == customA2); + VC_TEST_ASSERT(customA != customB); + VC_TEST_ASSERT(registry.getName(customA) == "MyCustom"); + VC_TEST_ASSERT(registry.getMaterial("MyCustom") == customA); + + // MaterialMap supports custom IDs through legacy mapping. + materialMap.insertNextMaterial(customA); + VC_TEST_ASSERT(materialMap.getMaterialAtIdx(1) == customA); + + // Mixed MaterialValueMap support. + MaterialValueMap mixedRates; + mixedRates.set(Material::Si, 1.0); + mixedRates.set(customA, 2.0); + VC_TEST_ASSERT(mixedRates.get(Material::Si) == 1.0); + VC_TEST_ASSERT(mixedRates.get(customA) == 2.0); + VC_TEST_ASSERT(MaterialMap::isValidMaterial(Material::Si)); - VC_TEST_ASSERT(!MaterialMap::isValidMaterial(static_cast(999))); + VC_TEST_ASSERT(MaterialMap::isValidMaterial(customA)); return 0; } \ No newline at end of file diff --git a/tests/materialRegistry/CMakeLists.txt b/tests/materialRegistry/CMakeLists.txt new file mode 100644 index 00000000..9b99962d --- /dev/null +++ b/tests/materialRegistry/CMakeLists.txt @@ -0,0 +1,7 @@ +project(materialRegistry LANGUAGES CXX) + +add_executable(${PROJECT_NAME} "${PROJECT_NAME}.cpp") +target_link_libraries(${PROJECT_NAME} PRIVATE ViennaPS) + +add_dependencies(ViennaPS_Tests ${PROJECT_NAME}) +add_test(NAME ${PROJECT_NAME} COMMAND $) diff --git a/tests/materialRegistry/materialRegistry.cpp b/tests/materialRegistry/materialRegistry.cpp new file mode 100644 index 00000000..4f702f78 --- /dev/null +++ b/tests/materialRegistry/materialRegistry.cpp @@ -0,0 +1,34 @@ +#include "materials/psMaterials.hpp" + +#include + +using namespace viennaps; + +int main() { + MaterialRegistry registry; + + const auto builtIn = registry.registerMaterial("Si"); + VC_TEST_ASSERT(builtIn.isBuiltIn()); + VC_TEST_ASSERT(builtIn == Material::Si); + + const auto customA = registry.registerMaterial("CustomFoo"); + const auto customARepeat = registry.registerMaterial("CustomFoo"); + const auto customB = registry.registerMaterial("CustomBar"); + + VC_TEST_ASSERT(customA.isCustom()); + VC_TEST_ASSERT(customA == customARepeat); + VC_TEST_ASSERT(customA != customB); + VC_TEST_ASSERT(registry.hasMaterial("CustomFoo")); + VC_TEST_ASSERT(registry.getMaterial("CustomFoo") == customA); + VC_TEST_ASSERT(registry.getName(customA) == "CustomFoo"); + VC_TEST_ASSERT(registry.customMaterialCount() == 2); + + const auto maybeFound = registry.findMaterial("CustomBar"); + VC_TEST_ASSERT(maybeFound.has_value()); + VC_TEST_ASSERT(maybeFound.value() == customB); + + const auto maybeMissing = registry.findMaterial("DoesNotExist"); + VC_TEST_ASSERT(!maybeMissing.has_value()); + + return 0; +} diff --git a/tests/readAndWrite/readAndWrite.cpp b/tests/readAndWrite/readAndWrite.cpp index 2a923bbc..c363678a 100644 --- a/tests/readAndWrite/readAndWrite.cpp +++ b/tests/readAndWrite/readAndWrite.cpp @@ -10,8 +10,8 @@ #include #include +#include #include -#include #include #include From ba2cf1e486a52708048d598d6d89334bc911ed49 Mon Sep 17 00:00:00 2001 From: reiter Date: Thu, 26 Mar 2026 10:50:45 +0100 Subject: [PATCH 07/32] feat: materials on CUDA device code --- CMakeLists.txt | 1 + gpu/models/PlasmaEtching.cuh | 15 +++--- include/viennaps/materials/psMaterial.hpp | 60 ++++++++++++++-------- include/viennaps/materials/psMaterials.hpp | 35 ++++++++++--- 4 files changed, 76 insertions(+), 35 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4484358e..a9af4f66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,6 +107,7 @@ include("cmake/cpm.cmake") CPMAddPackage( NAME ViennaCore VERSION 2.1.1 + GIT_TAG main GIT_REPOSITORY "https://github.com/ViennaTools/ViennaCore" EXCLUDE_FROM_ALL ${VIENNAPS_BUILD_PYTHON} OPTIONS "VIENNACORE_USE_GPU ${VIENNAPS_USE_GPU}") diff --git a/gpu/models/PlasmaEtching.cuh b/gpu/models/PlasmaEtching.cuh index a83343b9..9de6efd2 100644 --- a/gpu/models/PlasmaEtching.cuh +++ b/gpu/models/PlasmaEtching.cuh @@ -63,14 +63,12 @@ plasmaNeutralReflectionNoPassivation(const void *sbtData, __forceinline__ __device__ void plasmaIonCollision(const void *sbtData, viennaray::gpu::PerRayData *prd) { - viennaps::PlasmaEtchingParametersGPU *params = - reinterpret_cast( - launchParams.customData); + using namespace viennaps; + PlasmaEtchingParametersGPU *params = + reinterpret_cast(launchParams.customData); for (int i = 0; i < prd->ISCount; ++i) { int id = launchParams.materialIds[prd->primIDs[i]]; // consecutive ID int material = launchParams.materialMap[id]; // mapped to enum - const int maskId = static_cast(viennaps::BuiltInMaterial::Mask); - const int polymerId = static_cast(viennaps::BuiltInMaterial::Polymer); auto geomNormal = viennaray::gpu::getNormal(sbtData, prd->primIDs[i]); auto cosTheta = __saturatef( -viennacore::DotProduct(prd->dir, geomNormal)); // clamp to [0,1] @@ -79,18 +77,19 @@ plasmaIonCollision(const void *sbtData, viennaray::gpu::PerRayData *prd) { float A_sp = params->Substrate.A_sp; float B_sp = params->Substrate.B_sp; float Eth_sp = params->Substrate.Eth_sp; - if (material == maskId) { + if (MaterialMap::isHardmask(material)) { A_sp = params->Mask.A_sp; B_sp = params->Mask.B_sp; Eth_sp = params->Mask.Eth_sp; - } else if (material == polymerId) { + } else if (MaterialMap::isMaterial(material, BuiltInMaterial::Polymer)) { A_sp = params->Polymer.A_sp; B_sp = params->Polymer.B_sp; Eth_sp = params->Polymer.Eth_sp; } float f_sp_theta; - if (material == polymerId && params->Polymer.usePolyCosThetaYield) { + if (MaterialMap::isMaterial(material, BuiltInMaterial::Polymer) && + params->Polymer.usePolyCosThetaYield) { const float c = cosTheta; const float sum = params->Polymer.a1 + params->Polymer.a2 + params->Polymer.a3 + params->Polymer.a4; diff --git a/include/viennaps/materials/psMaterial.hpp b/include/viennaps/materials/psMaterial.hpp index f51ff411..f05ef0ed 100644 --- a/include/viennaps/materials/psMaterial.hpp +++ b/include/viennaps/materials/psMaterial.hpp @@ -1,7 +1,9 @@ #pragma once #include "psBuiltInMaterial.hpp" +#include +#include #include #include #include @@ -15,21 +17,25 @@ class Material { enum class Kind : std::uint8_t { BuiltIn = 0, Custom = 1 }; - constexpr Material() noexcept = default; + __both__ constexpr Material() noexcept = default; - constexpr Material(BuiltInMaterial material) noexcept + __both__ constexpr Material(BuiltInMaterial material) noexcept : kind_(Kind::BuiltIn), value_(static_cast(material)) {} - constexpr Material(int legacyId) noexcept { *this = fromLegacyId(legacyId); } + __both__ constexpr Material(int legacyId) noexcept { + *this = fromLegacyId(legacyId); + } - [[nodiscard]] static constexpr Material custom(ValueType id) noexcept { + [[nodiscard]] __both__ static constexpr Material + custom(ValueType id) noexcept { Material material; material.kind_ = Kind::Custom; material.value_ = id; return material; } - [[nodiscard]] static constexpr Material fromLegacyId(int id) noexcept { + [[nodiscard]] __both__ static constexpr Material + fromLegacyId(int id) noexcept { if (id < 0) { return Material(BuiltInMaterial::Undefined); } @@ -43,49 +49,61 @@ class Material { static_cast(kBuiltInMaterialMaxId) - 1u); } - [[nodiscard]] constexpr Kind kind() const noexcept { return kind_; } + [[nodiscard]] __both__ constexpr Kind kind() const noexcept { return kind_; } - [[nodiscard]] constexpr bool isBuiltIn() const noexcept { + [[nodiscard]] __both__ constexpr bool isBuiltIn() const noexcept { return kind_ == Kind::BuiltIn; } - [[nodiscard]] constexpr bool isCustom() const noexcept { + [[nodiscard]] __both__ constexpr bool isCustom() const noexcept { return kind_ == Kind::Custom; } - [[nodiscard]] constexpr BuiltInMaterial builtIn() const { + [[nodiscard]] __both__ constexpr BuiltInMaterial builtIn() const { if (!isBuiltIn()) { +#ifdef __CUDA_ARCH__ + assert(false && "Material is not built-in."); + return BuiltInMaterial::Undefined; +#else throw std::logic_error("Material is not built-in."); +#endif } return static_cast(value_); } - [[nodiscard]] constexpr ValueType customId() const { + [[nodiscard]] __both__ constexpr ValueType customId() const { if (!isCustom()) { +#ifdef __CUDA_ARCH__ + assert(false && "Material is not custom."); + return 0; +#else throw std::logic_error("Material is not custom."); +#endif } return value_; } - [[nodiscard]] constexpr ValueType legacyId() const noexcept { + [[nodiscard]] __both__ constexpr ValueType legacyId() const noexcept { if (isBuiltIn()) { return value_; } return static_cast(kBuiltInMaterialMaxId) + 1u + value_; } - constexpr operator ValueType() const noexcept { return legacyId(); } + __both__ constexpr operator ValueType() const noexcept { return legacyId(); } - explicit constexpr operator int() const noexcept { + __both__ explicit constexpr operator int() const noexcept { return static_cast(legacyId()); } - [[nodiscard]] constexpr ValueType value() const noexcept { return value_; } + [[nodiscard]] __both__ constexpr ValueType value() const noexcept { + return value_; + } - [[nodiscard]] constexpr bool + [[nodiscard]] __both__ constexpr bool operator==(const Material &) const noexcept = default; - [[nodiscard]] constexpr auto + [[nodiscard]] __both__ constexpr auto operator<=>(const Material &) const noexcept = default; #define MATERIAL_CONST(id, sym, cat, dens, cond, color) \ @@ -98,20 +116,22 @@ class Material { ValueType value_{static_cast(BuiltInMaterial::Undefined)}; }; -[[nodiscard]] inline constexpr bool isBuiltIn(const Material material) { +[[nodiscard]] __both__ inline constexpr bool +isBuiltIn(const Material material) { return material.isBuiltIn(); } -[[nodiscard]] inline constexpr bool isCustom(const Material material) { +[[nodiscard]] __both__ inline constexpr bool isCustom(const Material material) { return material.isCustom(); } -[[nodiscard]] inline BuiltInMaterial +[[nodiscard]] __both__ inline BuiltInMaterial toBuiltInMaterial(const Material material) { return material.builtIn(); } -[[nodiscard]] inline constexpr Material toMaterial(BuiltInMaterial material) { +[[nodiscard]] __both__ inline constexpr Material +toMaterial(BuiltInMaterial material) { return Material(material); } diff --git a/include/viennaps/materials/psMaterials.hpp b/include/viennaps/materials/psMaterials.hpp index 97612c53..cfe17428 100644 --- a/include/viennaps/materials/psMaterials.hpp +++ b/include/viennaps/materials/psMaterials.hpp @@ -11,9 +11,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -78,11 +80,11 @@ to_string_view(const Material material) { return std::string(registry.getName(material)); } -#ifndef __CUDACC__ /// A class that wraps the viennals MaterialMap class and provides a more user /// friendly interface. It also provides a mapping from the integer material id /// to the Material enum. class MaterialMap { +#ifndef __CUDACC__ SmartPointer map_; public: @@ -126,29 +128,49 @@ class MaterialMap { [[nodiscard]] inline std::size_t size() const { return map_->getNumberOfLayers(); } +#endif - static inline bool isValidMaterial(const Material mat) { +public: + __both__ static inline bool isValidMaterial(const Material mat) { if (mat.isBuiltIn()) { return isValidBuiltInMaterial(mat.builtIn()); } return true; } - static inline Material mapToMaterial(const int matId) { + __both__ static inline Material mapToMaterial(const int matId) { auto mat = Material::fromLegacyId(matId); return mat; } - template static inline Material mapToMaterial(const T matId) { + template + __both__ static inline Material mapToMaterial(const T matId) { return mapToMaterial(static_cast(matId)); } template - static inline bool isMaterial(const T matId, const Material material) { + __both__ static inline bool isMaterial(const T matId, + const Material material) { return mapToMaterial(matId) == material; } - template static inline bool isHardmask(const T matId) { + template + __both__ static inline bool isMaterial(T matId, + std::span materials) { + const auto material = mapToMaterial(matId); + + return std::any_of(materials.begin(), materials.end(), + [&](const Material &m) { return material == m; }); + } + + template + __both__ static inline bool + isMaterial(T matId, std::initializer_list materials) { + return isMaterial( + matId, std::span(materials.begin(), materials.size())); + } + + template __both__ static inline bool isHardmask(const T matId) { const auto material = mapToMaterial(matId); if (!material.isBuiltIn()) { return false; @@ -178,6 +200,5 @@ class MaterialMap { return registry.registerMaterial(std::string(name)); } }; -#endif } // namespace viennaps From 6ee9e32257e36e51a5941d5791bc64d9fad9d186 Mon Sep 17 00:00:00 2001 From: reiter Date: Thu, 26 Mar 2026 17:10:17 +0100 Subject: [PATCH 08/32] fix: plasma etching models --- .../atomicLayerDeposition.py | 199 +++++++++++------- examples/atomicLayerDeposition/config.txt | 2 +- examples/holeEtching/config.txt | 4 +- examples/holeEtching/test_flux_engines.py | 85 ++++++++ .../trenchDeposition/test_flux_engines.py | 55 +++++ gpu/models/PlasmaEtching.cuh | 7 +- .../viennaps/materials/psMaterialValueMap.hpp | 2 + include/viennaps/materials/psMaterials.hpp | 2 +- include/viennaps/models/psHBrO2Etching.hpp | 27 ++- include/viennaps/models/psPlasmaEtching.hpp | 25 +-- .../models/psPlasmaEtchingParameters.hpp | 15 +- include/viennaps/models/psSF6C4F8Etching.hpp | 12 +- include/viennaps/models/psSF6O2Etching.hpp | 27 ++- python/pyWrap.cpp | 6 +- python/viennaps/_core/__init__.pyi | 21 +- 15 files changed, 353 insertions(+), 136 deletions(-) create mode 100644 examples/holeEtching/test_flux_engines.py create mode 100644 examples/trenchDeposition/test_flux_engines.py diff --git a/examples/atomicLayerDeposition/atomicLayerDeposition.py b/examples/atomicLayerDeposition/atomicLayerDeposition.py index 3af2a782..9fec9359 100644 --- a/examples/atomicLayerDeposition/atomicLayerDeposition.py +++ b/examples/atomicLayerDeposition/atomicLayerDeposition.py @@ -1,84 +1,127 @@ import viennaps as ps import viennals as ls +import matplotlib.pyplot as plt +import numpy as np ps.setDimension(2) ls.setDimension(2) -params = ps.readConfigFile("config.txt") -geometry = ps.Domain() - -# Create the geometry -boundaryCons = [ - ps.BoundaryType.REFLECTIVE_BOUNDARY, - ps.BoundaryType.INFINITE_BOUNDARY, -] -gridDelta = params["gridDelta"] -bounds = [ - 0.0, - params["openingWidth"] / 2.0 + params["xPad"] + params["gapLength"], - -gridDelta, - params["openingDepth"] + params["gapHeight"] + gridDelta, -] - -substrate = ls.Domain(bounds, boundaryCons, gridDelta) -normal = [0.0, 1.0] -origin = [0.0, params["openingDepth"] + params["gapHeight"]] -ls.MakeGeometry(substrate, ls.Plane(origin, normal)).apply() - -geometry.insertNextLevelSetAsMaterial(substrate, ps.Material.Si) - -vertBox = ls.Domain(bounds, boundaryCons, gridDelta) -minPoint = [-gridDelta, 0.0] -maxPoint = [ - params["openingWidth"] / 2.0, - params["gapHeight"] + params["openingDepth"] + gridDelta, -] -ls.MakeGeometry(vertBox, ls.Box(minPoint, maxPoint)).apply() - -geometry.applyBooleanOperation(vertBox, ls.BooleanOperationEnum.RELATIVE_COMPLEMENT) - -horiBox = ls.Domain(bounds, boundaryCons, gridDelta) -minPoint = [params["openingWidth"] / 2.0 - gridDelta, 0.0] -maxPoint = [params["openingWidth"] / 2.0 + params["gapLength"], params["gapHeight"]] -ls.MakeGeometry(horiBox, ls.Box(minPoint, maxPoint)).apply() -geometry.applyBooleanOperation(horiBox, ls.BooleanOperationEnum.RELATIVE_COMPLEMENT) - -geometry.saveVolumeMesh("SingleParticleALD_initial") - -geometry.duplicateTopLevelSet(ps.Material.Al2O3) - -gasMFP = ps.constants.gasMeanFreePath( - params["pressure"], params["temperature"], params["diameter"] -) -print("Mean free path: ", gasMFP, " um") - -model = ps.SingleParticleALD( - stickingProbability=params["stickingProbability"], - numCycles=int(params["numCycles"]), - growthPerCycle=params["growthPerCycle"], - totalCycles=int(params["totalCycles"]), - coverageTimeStep=params["coverageTimeStep"], - evFlux=params["evFlux"], - inFlux=params["inFlux"], - s0=params["s0"], - gasMFP=gasMFP, -) - -alpParams = ps.AtomicLayerProcessParameters() -alpParams.pulseTime = params["pulseTime"] -alpParams.coverageTimeStep = params["coverageTimeStep"] -alpParams.numCycles = int(params["numCycles"]) - -rayParams = ps.RayTracingParameters() -rayParams.raysPerPoint = int(params["numRaysPerPoint"]) - -ALP = ps.Process(geometry, model) -ALP.setParameters(rayParams) -ALP.setParameters(alpParams) -ALP.apply() - -## TODO: Implement MeasureProfile in Python -# MeasureProfile(domain, params.get("gapHeight") / 2.) -# .save(params.get("outputFile")); - -geometry.saveVolumeMesh("SingleParticleALD_final") +ps.Logger.setLogLevel(ps.LogLevel.DEBUG) + + +def run_simulation(gd, filename): + params = ps.readConfigFile("config.txt") + geometry = ps.Domain() + + # Create the geometry + boundaryCons = [ + ps.BoundaryType.REFLECTIVE_BOUNDARY, + ps.BoundaryType.INFINITE_BOUNDARY, + ] + gridDelta = gd + bounds = [ + 0.0, + params["openingWidth"] / 2.0 + params["xPad"] + params["gapLength"], + -gridDelta, + params["openingDepth"] + params["gapHeight"] + gridDelta, + ] + + substrate = ls.Domain(bounds, boundaryCons, gridDelta) + normal = [0.0, 1.0] + origin = [0.0, params["openingDepth"] + params["gapHeight"]] + ls.MakeGeometry(substrate, ls.Plane(origin, normal)).apply() + + geometry.insertNextLevelSetAsMaterial(substrate, ps.Material.Si) + + vertBox = ls.Domain(bounds, boundaryCons, gridDelta) + minPoint = [-gridDelta, 0.0] + maxPoint = [ + params["openingWidth"] / 2.0, + params["gapHeight"] + params["openingDepth"] + gridDelta, + ] + ls.MakeGeometry(vertBox, ls.Box(minPoint, maxPoint)).apply() + + geometry.applyBooleanOperation(vertBox, ls.BooleanOperationEnum.RELATIVE_COMPLEMENT) + + horiBox = ls.Domain(bounds, boundaryCons, gridDelta) + minPoint = [params["openingWidth"] / 2.0 - gridDelta, 0.0] + maxPoint = [params["openingWidth"] / 2.0 + params["gapLength"], params["gapHeight"]] + ls.MakeGeometry(horiBox, ls.Box(minPoint, maxPoint)).apply() + geometry.applyBooleanOperation(horiBox, ls.BooleanOperationEnum.RELATIVE_COMPLEMENT) + + geometry.saveSurfaceMesh("SingleParticleALD_initial") + + model = ps.SingleParticleProcess(rate=1.0, stickingProbability=1e-4) + + geometry.duplicateTopLevelSet(ps.Material.Al2O3) + + gasMFP = ps.constants.gasMeanFreePath( + params["pressure"], params["temperature"], params["diameter"] + ) + print("Mean free path: ", gasMFP, " um") + + model = ps.SingleParticleALD( + stickingProbability=params["stickingProbability"], + numCycles=int(params["numCycles"]), + growthPerCycle=params["growthPerCycle"], + totalCycles=int(params["totalCycles"]), + coverageTimeStep=params["coverageTimeStep"], + evFlux=params["evFlux"], + inFlux=params["inFlux"], + s0=params["s0"], + gasMFP=gasMFP, + ) + model.setProcessName(filename) + + alpParams = ps.AtomicLayerProcessParameters() + alpParams.pulseTime = params["pulseTime"] + alpParams.coverageTimeStep = params["coverageTimeStep"] + alpParams.numCycles = 1 + + p = ps.Process(geometry, model, 1.0) + p.setParameters(alpParams) + p.setFluxEngineType(ps.FluxEngineType.GPU_TRIANGLE) + p.apply() + + # flux = p.calculateFlux() + + # points = np.array(flux.getNodes()) + # fluxValues = np.array(flux.getCellData().getScalarData("particleFlux", True)) + + # cuts = points[:, 1] < 0.25 + # points = points[cuts] + # fluxValues = fluxValues[cuts] + + # ps.ls.VTKWriter(flux, filename).apply() + + mesh = ps.ls.Mesh() + ps.ToDiskMesh(geometry, mesh).apply() + points = np.array(mesh.getNodes()) + cuts = points[:, 1] < 0.25 + points = points[cuts] + return points + # return points, fluxValues + + +p = run_simulation(0.1, "flux_0p1") +plt.plot(p[:, 0], p[:, 1], "--") +p = run_simulation(0.01, "flux_0p01") +plt.plot(p[:, 0], p[:, 1], ".-") +p = run_simulation(0.05, "flux_0p05") +plt.plot(p[:, 0], p[:, 1], "-") +plt.show() + + +# rayParams = ps.RayTracingParameters() +# rayParams.raysPerPoint = int(params["numRaysPerPoint"]) + +# ALP = ps.Process(geometry, model) +# ALP.setParameters(rayParams) +# ALP.setParameters(alpParams) +# ALP.apply() + +# ## TODO: Implement MeasureProfile in Python +# # MeasureProfile(domain, params.get("gapHeight") / 2.) +# # .save(params.get("outputFile")); + +# geometry.saveVolumeMesh("SingleParticleALD_final") diff --git a/examples/atomicLayerDeposition/config.txt b/examples/atomicLayerDeposition/config.txt index 2e2b57c2..bb4b099f 100644 --- a/examples/atomicLayerDeposition/config.txt +++ b/examples/atomicLayerDeposition/config.txt @@ -1,7 +1,7 @@ # all length units in micrometers # Geometry -gridDelta = 0.2 +gridDelta = 0.1 openingDepth = 0.5 openingWidth = 90 gapLength = 800 diff --git a/examples/holeEtching/config.txt b/examples/holeEtching/config.txt index ceb92467..b6ea3a7a 100644 --- a/examples/holeEtching/config.txt +++ b/examples/holeEtching/config.txt @@ -30,7 +30,7 @@ A_Si=7 # Si yield constant etchStopDepth=-10 # maximum etching depth spatialScheme=EO_1 temporalScheme=RK3 -fluxEngine=GT +fluxEngine=CT raysPerPoint=1000 -outputFile=final.vtp +outputFile=final_CPU.vtp diff --git a/examples/holeEtching/test_flux_engines.py b/examples/holeEtching/test_flux_engines.py new file mode 100644 index 00000000..bfcdf993 --- /dev/null +++ b/examples/holeEtching/test_flux_engines.py @@ -0,0 +1,85 @@ +from argparse import ArgumentParser +import viennaps as ps + +# parse config file name and simulation dimension +parser = ArgumentParser(prog="holeEtching", description="Run a hole etching process.") +parser.add_argument("-D", "-DIM", dest="dim", type=int, default=2) +parser.add_argument("filename") +args = parser.parse_args() + +# switch between 2D and 3D mode +if args.dim == 2: + print("Running 2D simulation.") +else: + print("Running 3D simulation.") +ps.setDimension(args.dim) +ps.setNumThreads(16) + +params = ps.readConfigFile(args.filename) + +ps.Length.setUnit(params["lengthUnit"]) +ps.Time.setUnit(params["timeUnit"]) + + +def run_simulation(fluxEngine, suffix): + # geometry setup, all units in um + geometry = ps.Domain( + gridDelta=params["gridDelta"], + xExtent=params["xExtent"], + yExtent=params["yExtent"], + ) + + ps.MakeHole( + domain=geometry, + holeRadius=params["holeRadius"], + holeDepth=0.0, + maskHeight=params["maskHeight"], + maskTaperAngle=params["taperAngle"], + holeShape=ps.HoleShape.QUARTER, + ).apply() + + # use pre-defined model SF6O2 etching model + modelParams = ps.SF6O2Etching.defaultParameters() + modelParams.ionFlux = params["ionFlux"] + modelParams.etchantFlux = params["etchantFlux"] + modelParams.passivationFlux = params["oxygenFlux"] + modelParams.Ions.meanEnergy = params["meanEnergy"] + modelParams.Ions.sigmaEnergy = params["sigmaEnergy"] + modelParams.Ions.exponent = params["ionExponent"] + modelParams.Passivation.A_ie = params["A_O"] + modelParams.Substrate.A_ie = params["A_Si"] + modelParams.etchStopDepth = params["etchStopDepth"] + model = ps.SF6O2Etching(modelParams) + + covParams = ps.CoverageParameters() + covParams.tolerance = 1e-4 + + rayParams = ps.RayTracingParameters() + rayParams.raysPerPoint = int(params["raysPerPoint"]) + + advParams = ps.AdvectionParameters() + advParams.spatialScheme = ps.util.convertSpatialScheme(params["spatialScheme"]) + advParams.temporalScheme = ps.util.convertTemporalScheme(params["temporalScheme"]) + + # process setup + process = ps.Process(geometry, model) + process.setProcessDuration(params["processTime"]) # seconds + process.setParameters(covParams) + process.setParameters(rayParams) + process.setParameters(advParams) + + process.setFluxEngineType(fluxEngine) + + # run the process + process.apply() + + # print final surface + output_file = "hole_" + output_file += suffix + geometry.saveSurfaceMesh(filename=output_file, addInterfaces=True) + + +run_simulation(ps.FluxEngineType.CPU_DISK, "CPU_disk") +run_simulation(ps.FluxEngineType.GPU_DISK, "GPU_disk") +run_simulation(ps.FluxEngineType.CPU_TRIANGLE, "CPU_triangle") +run_simulation(ps.FluxEngineType.GPU_TRIANGLE, "GPU_triangle") diff --git a/examples/trenchDeposition/test_flux_engines.py b/examples/trenchDeposition/test_flux_engines.py new file mode 100644 index 00000000..04612909 --- /dev/null +++ b/examples/trenchDeposition/test_flux_engines.py @@ -0,0 +1,55 @@ +from argparse import ArgumentParser +import viennaps as ps + +# parse config file name and simulation dimension +parser = ArgumentParser( + prog="trenchDeposition", + description="Run a deposition process on a trench geometry.", +) +parser.add_argument("-D", "-DIM", dest="dim", type=int, default=2) +parser.add_argument("filename") +args = parser.parse_args() + +if args.dim == 2: + print("Running 2D simulation.") +else: + print("Running 3D simulation.") +ps.setDimension(args.dim) + +params = ps.readConfigFile(args.filename) + + +def run_simulation(fluxEngine, suffix): + geometry = ps.Domain( + gridDelta=params["gridDelta"] / 3.5, + xExtent=params["xExtent"], + yExtent=params["yExtent"], + ) + ps.MakeTrench( + domain=geometry, + trenchWidth=params["trenchWidth"], + trenchDepth=params["trenchHeight"] * 3, + trenchTaperAngle=params["taperAngle"], + ).apply() + + geometry.duplicateTopLevelSet(ps.Material.SiO2) + + model = ps.SingleParticleProcess( + stickingProbability=0.001, + sourceExponent=params["sourcePower"], + ) + + process = ps.Process(geometry, model) + process.setProcessDuration(params["processTime"]) + process.setFluxEngineType(fluxEngine) + + process.apply() + + filename = f"trench_{suffix}.vtp" + geometry.saveSurfaceMesh(filename=filename, addInterfaces=True) + + +run_simulation(ps.FluxEngineType.CPU_DISK, "CPU_disk") +run_simulation(ps.FluxEngineType.GPU_DISK, "GPU_disk") +run_simulation(ps.FluxEngineType.CPU_TRIANGLE, "CPU_triangle") +run_simulation(ps.FluxEngineType.GPU_TRIANGLE, "GPU_triangle") diff --git a/gpu/models/PlasmaEtching.cuh b/gpu/models/PlasmaEtching.cuh index 9de6efd2..79cc431b 100644 --- a/gpu/models/PlasmaEtching.cuh +++ b/gpu/models/PlasmaEtching.cuh @@ -77,18 +77,19 @@ plasmaIonCollision(const void *sbtData, viennaray::gpu::PerRayData *prd) { float A_sp = params->Substrate.A_sp; float B_sp = params->Substrate.B_sp; float Eth_sp = params->Substrate.Eth_sp; - if (MaterialMap::isHardmask(material)) { + if (static_cast(material) == BuiltInMaterial::Mask) { A_sp = params->Mask.A_sp; B_sp = params->Mask.B_sp; Eth_sp = params->Mask.Eth_sp; - } else if (MaterialMap::isMaterial(material, BuiltInMaterial::Polymer)) { + } else if (static_cast(material) == + BuiltInMaterial::Polymer) { A_sp = params->Polymer.A_sp; B_sp = params->Polymer.B_sp; Eth_sp = params->Polymer.Eth_sp; } float f_sp_theta; - if (MaterialMap::isMaterial(material, BuiltInMaterial::Polymer) && + if (static_cast(material) == BuiltInMaterial::Polymer && params->Polymer.usePolyCosThetaYield) { const float c = cosTheta; const float sum = params->Polymer.a1 + params->Polymer.a2 + diff --git a/include/viennaps/materials/psMaterialValueMap.hpp b/include/viennaps/materials/psMaterialValueMap.hpp index 26478a5e..6d5b145a 100644 --- a/include/viennaps/materials/psMaterialValueMap.hpp +++ b/include/viennaps/materials/psMaterialValueMap.hpp @@ -145,6 +145,8 @@ template class MaterialValueMap { Iterator end() const { return Iterator(this, values_.size()); } + auto getCustomMaterialValues() const { return customValues_; } + private: static constexpr std::size_t toIndex(BuiltInMaterial m) { return static_cast(m); diff --git a/include/viennaps/materials/psMaterials.hpp b/include/viennaps/materials/psMaterials.hpp index cfe17428..17eb05e3 100644 --- a/include/viennaps/materials/psMaterials.hpp +++ b/include/viennaps/materials/psMaterials.hpp @@ -170,7 +170,7 @@ class MaterialMap { matId, std::span(materials.begin(), materials.size())); } - template __both__ static inline bool isHardmask(const T matId) { + template static inline bool isHardmask(const T matId) { const auto material = mapToMaterial(matId); if (!material.isBuiltIn()) { return false; diff --git a/include/viennaps/models/psHBrO2Etching.hpp b/include/viennaps/models/psHBrO2Etching.hpp index 417482c4..6e3a4d6a 100644 --- a/include/viennaps/models/psHBrO2Etching.hpp +++ b/include/viennaps/models/psHBrO2Etching.hpp @@ -43,13 +43,25 @@ class HBrO2Etching final : public ProcessModelGPU { etchant.name = "Etchant"; etchant.dataLabels.push_back("etchantFlux"); etchant.cosineExponent = 1.f; - etchant.materialSticking = params.beta_E; + for (auto entry : params.beta_E) { + etchant.materialSticking[static_cast(entry.getMaterial())] = + entry.getValue(); + } + for (auto &entry : params.beta_E.getCustomMaterialValues()) { + etchant.materialSticking[entry.first] = entry.second; + } viennaray::gpu::Particle oxygen; oxygen.name = "Oxygen"; oxygen.dataLabels.push_back("passivationFlux"); oxygen.cosineExponent = 1.f; - oxygen.materialSticking = params.beta_P; + for (auto entry : params.beta_P) { + oxygen.materialSticking[static_cast(entry.getMaterial())] = + entry.getValue(); + } + for (auto &entry : params.beta_P.getCustomMaterialValues()) { + oxygen.materialSticking[entry.first] = entry.second; + } // surface model auto surfModel = SmartPointer< @@ -176,10 +188,13 @@ class HBrO2Etching : public ProcessModelCPU { defParams.passivationFlux = 1.0e2; // sticking probabilities - defParams.beta_E = {{static_cast(Material::Si), 0.1}, - {static_cast(Material::Mask), 0.1}}; - defParams.beta_P = {{static_cast(Material::Si), 1.}, - {static_cast(Material::Mask), 1.}}; + defParams.beta_E.set(Material::Si, 0.1); + defParams.beta_E.set(Material::Mask, 0.1); + defParams.beta_E.setDefault(1.0); + + defParams.beta_P.set(Material::Si, 1.); + defParams.beta_P.set(Material::Mask, 1.); + defParams.beta_P.setDefault(1.0); defParams.etchStopDepth = std::numeric_limits::lowest(); diff --git a/include/viennaps/models/psPlasmaEtching.hpp b/include/viennaps/models/psPlasmaEtching.hpp index 93131f62..ded1a62e 100644 --- a/include/viennaps/models/psPlasmaEtching.hpp +++ b/include/viennaps/models/psPlasmaEtching.hpp @@ -4,6 +4,7 @@ #include #include +#include "../materials/psMaterialValueMap.hpp" #include "../process/psSurfaceModel.hpp" #include "../psUnits.hpp" @@ -222,7 +223,7 @@ class PlasmaEtchingIon auto cosTheta = getCosTheta(rayDir, geomNormal); NumericType angle = std::acos(cosTheta); - assert(cosTheta >= 0 && "Hit backside of disc"); + assert(cosTheta >= 0 && "Hit backside of element"); assert(cosTheta <= 1 + 1e6 && "Error in calculating cos theta"); assert(rayWeight > 0. && "Invalid ray weight"); @@ -243,11 +244,11 @@ class PlasmaEtchingIon if (MaterialMap::isMaterial(materialId, Material::Polymer) && params.Polymer.usePolyCosThetaYield) { const auto c = cosTheta; + const auto c2 = c * c; const auto &p = params.Polymer; const auto sum = p.a1 + p.a2 + p.a3 + p.a4; f_sp_theta = - (p.a1 * c + p.a2 * c * c + p.a3 * c * c * c + p.a4 * c * c * c * c) / - sum; + (p.a1 * c + p.a2 * c2 + p.a3 * c2 * c + p.a4 * c2 * c2) / sum; f_sp_theta = std::max(f_sp_theta, NumericType(0)); } else { f_sp_theta = @@ -346,14 +347,14 @@ class PlasmaEtchingNeutral : public viennaray::Particle, NumericType> { const std::string fluxLabel; - const std::unordered_map &beta_map; + const MaterialValueMap &stickingMap; const int numCoverages; public: PlasmaEtchingNeutral(std::string pFluxLabel, - std::unordered_map &pBetaMap, + const MaterialValueMap &pBetaMap, const int pNumCoverages) - : fluxLabel(std::move(pFluxLabel)), beta_map(pBetaMap), + : fluxLabel(std::move(pFluxLabel)), stickingMap(pBetaMap), numCoverages(pNumCoverages) {} void surfaceCollision(NumericType rayWeight, const Vec3D &, @@ -378,7 +379,7 @@ class PlasmaEtchingNeutral if (S_eff < 0.) { S_eff = 0.; } else { - S_eff *= sticking(materialId); + S_eff *= stickingMap.get(Material::fromLegacyId(materialId)); } auto direction = @@ -389,15 +390,5 @@ class PlasmaEtchingNeutral std::vector getLocalDataLabels() const override final { return {fluxLabel}; } - -private: - NumericType sticking(const int matieralId) const { - auto beta = beta_map.find(matieralId); - if (beta != beta_map.end()) - return beta->second; - - // default value - return 1.0; - } }; } // namespace viennaps::impl diff --git a/include/viennaps/models/psPlasmaEtchingParameters.hpp b/include/viennaps/models/psPlasmaEtchingParameters.hpp index 1ae65a32..4dbe6864 100644 --- a/include/viennaps/models/psPlasmaEtchingParameters.hpp +++ b/include/viennaps/models/psPlasmaEtchingParameters.hpp @@ -1,5 +1,6 @@ #pragma once +#include "../materials/psMaterialValueMap.hpp" #include "../psConstants.hpp" #include @@ -13,8 +14,8 @@ template struct PlasmaEtchingParameters { NumericType passivationFlux = 1.0e2; // sticking probabilities - std::unordered_map beta_E; - std::unordered_map beta_P; + MaterialValueMap beta_E; + MaterialValueMap beta_P; NumericType etchStopDepth = std::numeric_limits::lowest(); @@ -98,11 +99,13 @@ template struct PlasmaEtchingParameters { processData["Etchant Flux"] = {etchantFlux}; processData["Passivation Flux"] = {passivationFlux}; - for (const auto &pair : beta_E) { - processData["Beta_E " + std::to_string(pair.first)] = {pair.second}; + for (auto entry : beta_E) { + processData["Beta_E " + to_string(entry.getMaterial())] = + std::vector{entry.getValue()}; } - for (const auto &pair : beta_P) { - processData["Beta_P " + std::to_string(pair.first)] = {pair.second}; + for (auto entry : beta_P) { + processData["Beta_P " + to_string(entry.getMaterial())] = + std::vector{entry.getValue()}; } if (etchStopDepth != std::numeric_limits::lowest()) diff --git a/include/viennaps/models/psSF6C4F8Etching.hpp b/include/viennaps/models/psSF6C4F8Etching.hpp index 68b588cc..7016f63e 100644 --- a/include/viennaps/models/psSF6C4F8Etching.hpp +++ b/include/viennaps/models/psSF6C4F8Etching.hpp @@ -42,7 +42,13 @@ class SF6C4F8Etching final : public ProcessModelGPU { etchant.name = "Etchant"; etchant.dataLabels.push_back("etchantFlux"); etchant.cosineExponent = 1.f; - etchant.materialSticking = params.beta_E; + for (auto entry : params.beta_E) { + etchant.materialSticking[static_cast(entry.getMaterial())] = + entry.getValue(); + } + for (auto &entry : params.beta_E.getCustomMaterialValues()) { + etchant.materialSticking[entry.first] = entry.second; + } // No oxygen/passivation particle for SF6C4F8 @@ -166,7 +172,9 @@ class SF6C4F8Etching : public ProcessModelCPU { defParams.passivationFlux = 0.; // No passivation // sticking probabilities - defParams.beta_E = {{1, 0.7}, {0, 0.7}}; + defParams.beta_E.set(Material::Si, 0.7); + defParams.beta_E.set(Material::Mask, 0.7); + defParams.beta_E.setDefault(1.0); // No beta_P needed since passivationFlux = 0 defParams.etchStopDepth = std::numeric_limits::lowest(); diff --git a/include/viennaps/models/psSF6O2Etching.hpp b/include/viennaps/models/psSF6O2Etching.hpp index 8d587376..4f2102ac 100644 --- a/include/viennaps/models/psSF6O2Etching.hpp +++ b/include/viennaps/models/psSF6O2Etching.hpp @@ -42,13 +42,25 @@ class SF6O2Etching final : public ProcessModelGPU { etchant.name = "Etchant"; etchant.dataLabels.push_back("etchantFlux"); etchant.cosineExponent = 1.f; - etchant.materialSticking = params.beta_E; + for (auto entry : params.beta_E) { + etchant.materialSticking[static_cast(entry.getMaterial())] = + entry.getValue(); + } + for (auto &entry : params.beta_E.getCustomMaterialValues()) { + etchant.materialSticking[entry.first] = entry.second; + } viennaray::gpu::Particle oxygen; oxygen.name = "Oxygen"; oxygen.dataLabels.push_back("passivationFlux"); oxygen.cosineExponent = 1.f; - oxygen.materialSticking = params.beta_P; + for (auto entry : params.beta_P) { + oxygen.materialSticking[static_cast(entry.getMaterial())] = + entry.getValue(); + } + for (auto &entry : params.beta_P.getCustomMaterialValues()) { + oxygen.materialSticking[entry.first] = entry.second; + } // surface model auto surfModel = SmartPointer< @@ -176,10 +188,13 @@ class SF6O2Etching : public ProcessModelCPU { defParams.passivationFlux = 1.0e2; // sticking probabilities - defParams.beta_E = {{static_cast(Material::Si), 0.7}, - {static_cast(Material::Mask), 0.7}}; - defParams.beta_P = {{static_cast(Material::Si), 1.}, - {static_cast(Material::Mask), 1.}}; + defParams.beta_E.set(Material::Si, 0.7); + defParams.beta_E.set(Material::Mask, 0.7); + defParams.beta_E.setDefault(1.0); + + defParams.beta_P.set(Material::Si, 1.); + defParams.beta_P.set(Material::Mask, 1.); + defParams.beta_P.setDefault(1.0); defParams.etchStopDepth = std::numeric_limits::lowest(); diff --git a/python/pyWrap.cpp b/python/pyWrap.cpp index ee659538..28b7fa95 100644 --- a/python/pyWrap.cpp +++ b/python/pyWrap.cpp @@ -158,7 +158,11 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { .def("size", &MaterialMap::size) .def_static("mapToMaterial", &MaterialMap::mapToMaterial, "Map a float to a material.") - .def_static("isMaterial", &MaterialMap::isMaterial) + .def_static("isMaterial", py::overload_cast( + &MaterialMap::isMaterial)) + .def_static("isMaterial", + py::overload_cast>( + &MaterialMap::isMaterial)) .def_static("fromString", &MaterialMap::fromString, py::arg("name"), py::arg("registry"), "Resolve built-in or register custom material by name.") diff --git a/python/viennaps/_core/__init__.pyi b/python/viennaps/_core/__init__.pyi index accdd7b3..a97d0122 100644 --- a/python/viennaps/_core/__init__.pyi +++ b/python/viennaps/_core/__init__.pyi @@ -6,8 +6,8 @@ import collections.abc import enum import typing import viennals._core -import viennaps.d2 from viennaps import d2 +import viennaps.d2 import viennaps.d3 from viennaps import d3 from . import constants @@ -1082,9 +1082,14 @@ class MaterialMap: Resolve built-in or register custom material by name. """ @staticmethod + @typing.overload def isMaterial(arg0: typing.SupportsFloat | typing.SupportsIndex, arg1: Material) -> bool: ... @staticmethod + @typing.overload + def isMaterial(*args, **kwargs) -> bool: + ... + @staticmethod def mapToMaterial(arg0: typing.SupportsFloat | typing.SupportsIndex) -> Material: """ Map a float to a material. @@ -1151,21 +1156,11 @@ class PlasmaEtchingParameters: Passivation: PlasmaEtchingParametersPassivation Polymer: PlasmaEtchingParametersPolymer Substrate: PlasmaEtchingParametersSubstrate + beta_E: ... + beta_P: ... def __init__(self) -> None: ... @property - def beta_E(self) -> dict[int, float]: - ... - @beta_E.setter - def beta_E(self, arg0: collections.abc.Mapping[typing.SupportsInt | typing.SupportsIndex, typing.SupportsFloat | typing.SupportsIndex]) -> None: - ... - @property - def beta_P(self) -> dict[int, float]: - ... - @beta_P.setter - def beta_P(self, arg0: collections.abc.Mapping[typing.SupportsInt | typing.SupportsIndex, typing.SupportsFloat | typing.SupportsIndex]) -> None: - ... - @property def etchStopDepth(self) -> float: ... @etchStopDepth.setter From 0e92b9084f3eaea88d7220f5f969d0289734bc3e Mon Sep 17 00:00:00 2001 From: reiter Date: Thu, 26 Mar 2026 17:24:29 +0100 Subject: [PATCH 09/32] feat: add material factors in plasma etching model --- include/viennaps/models/psPlasmaEtching.hpp | 3 +++ .../models/psPlasmaEtchingParameters.hpp | 1 + python/pyWrap.cpp | 13 +++++++++++ python/viennaps/__init__.pyi | 3 ++- python/viennaps/_core/__init__.pyi | 22 +++++++++++++++---- python/viennaps/d2/gpu.pyi | 2 +- python/viennaps/d3/gpu.pyi | 2 +- 7 files changed, 39 insertions(+), 7 deletions(-) diff --git a/include/viennaps/models/psPlasmaEtching.hpp b/include/viennaps/models/psPlasmaEtching.hpp index ded1a62e..0d69a503 100644 --- a/include/viennaps/models/psPlasmaEtching.hpp +++ b/include/viennaps/models/psPlasmaEtching.hpp @@ -126,6 +126,9 @@ class PlasmaEtchingSurfaceModel : public SurfaceModel { chRate->at(i) = chemicalRate; } } + + etchRate[i] *= + params.rateFactors.get(Material::fromLegacyId(materialIds[i])); } if (stop) { diff --git a/include/viennaps/models/psPlasmaEtchingParameters.hpp b/include/viennaps/models/psPlasmaEtchingParameters.hpp index 4dbe6864..b3c72eb6 100644 --- a/include/viennaps/models/psPlasmaEtchingParameters.hpp +++ b/include/viennaps/models/psPlasmaEtchingParameters.hpp @@ -17,6 +17,7 @@ template struct PlasmaEtchingParameters { MaterialValueMap beta_E; MaterialValueMap beta_P; + MaterialValueMap rateFactors; NumericType etchStopDepth = std::numeric_limits::lowest(); // Mask diff --git a/python/pyWrap.cpp b/python/pyWrap.cpp index 28b7fa95..67ca52ee 100644 --- a/python/pyWrap.cpp +++ b/python/pyWrap.cpp @@ -243,6 +243,18 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { .def("toString", &units::Time::toString) .def("toShortString", &units::Time::toShortString); + // MaterialValueMap + py::class_>(module, "MaterialValueMap") + .def(py::init<>()) + .def("set", &MaterialValueMap::set, py::arg("material"), + py::arg("value")) + .def("get", + py::overload_cast(&MaterialValueMap::get, py::const_), + py::arg("material")) + .def("getDefault", &MaterialValueMap::getDefault) + .def("setDefault", &MaterialValueMap::setDefault, py::arg("value")) + .def("clearAll", &MaterialValueMap::clearAll); + // ProcessParams py::class_, SmartPointer>>(module, "ProcessParams") @@ -340,6 +352,7 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { &PlasmaEtchingParameters::passivationFlux) .def_readwrite("etchStopDepth", &PlasmaEtchingParameters::etchStopDepth) + .def_readwrite("rateFactors", &PlasmaEtchingParameters::rateFactors) .def_readwrite("beta_E", &PlasmaEtchingParameters::beta_E) .def_readwrite("beta_P", &PlasmaEtchingParameters::beta_P) .def_readwrite("Mask", &PlasmaEtchingParameters::Mask) diff --git a/python/viennaps/__init__.pyi b/python/viennaps/__init__.pyi index 44c9d5b3..12f5a132 100644 --- a/python/viennaps/__init__.pyi +++ b/python/viennaps/__init__.pyi @@ -43,6 +43,7 @@ from viennaps._core import MaterialInfo from viennaps._core import MaterialKind from viennaps._core import MaterialMap from viennaps._core import MaterialRegistry +from viennaps._core import MaterialValueMap from viennaps._core import MetaDataLevel from viennaps._core import NormalizationType from viennaps._core import PlasmaEtchingParameters @@ -110,7 +111,7 @@ from viennaps.d2 import Writer from . import _core from . import d2 from . import d3 -__all__: list[str] = ['AdvectionCallback', 'AdvectionParameters', 'AtomicLayerProcessParameters', 'BoundaryType', 'BoxDistribution', 'BuiltInMaterial', 'CF4O2Etching', 'CF4O2Parameters', 'CF4O2ParametersIons', 'CF4O2ParametersMask', 'CF4O2ParametersPassivation', 'CF4O2ParametersSi', 'CF4O2ParametersSiGe', 'CSVFileProcess', 'CoverageParameters', 'DenseCellSet', 'DirectionalProcess', 'Domain', 'DomainSetup', 'Extrude', 'FaradayCageEtching', 'FaradayCageParameters', 'FluorocarbonEtching', 'FluorocarbonMaterialParameters', 'FluorocarbonParameters', 'FluorocarbonParametersIons', 'FluxEngineType', 'GDSGeometry', 'GDSReader', 'GeometricTrenchDeposition', 'GeometryFactory', 'HBrO2Etching', 'HoleShape', 'IBEParameters', 'IBEParametersCos4Yield', 'Interpolation', 'IonBeamEtching', 'IsotropicProcess', 'Length', 'LengthUnit', 'LogLevel', 'Logger', 'MakeFin', 'MakeHole', 'MakePlane', 'MakeStack', 'MakeTrench', 'Material', 'MaterialCategory', 'MaterialInfo', 'MaterialKind', 'MaterialMap', 'MaterialRegistry', 'MetaDataLevel', 'MultiParticleProcess', 'NormalizationType', 'OxideRegrowth', 'PROXY_DIM', 'Planarize', 'PlasmaEtchingParameters', 'PlasmaEtchingParametersIons', 'PlasmaEtchingParametersMask', 'PlasmaEtchingParametersPassivation', 'PlasmaEtchingParametersPolymer', 'PlasmaEtchingParametersSubstrate', 'Process', 'ProcessModel', 'ProcessModelBase', 'ProcessParams', 'RateGrid', 'RateSet', 'RayTracingParameters', 'Reader', 'RenderMode', 'SF6C4F8Etching', 'SF6O2Etching', 'SelectiveEpitaxy', 'SingleParticleALD', 'SingleParticleProcess', 'Slice', 'SpatialScheme', 'SphereDistribution', 'StencilLocalLaxFriedrichsScalar', 'TEOSDeposition', 'TEOSPECVD', 'TemporalScheme', 'Time', 'TimeUnit', 'ToDiskMesh', 'VTKRenderWindow', 'WetEtching', 'Writer', 'constants', 'd2', 'd3', 'gpu', 'gpuAvailable', 'ls', 'readConfigFile', 'setDimension', 'setNumThreads', 'util', 'version'] +__all__: list[str] = ['AdvectionCallback', 'AdvectionParameters', 'AtomicLayerProcessParameters', 'BoundaryType', 'BoxDistribution', 'BuiltInMaterial', 'CF4O2Etching', 'CF4O2Parameters', 'CF4O2ParametersIons', 'CF4O2ParametersMask', 'CF4O2ParametersPassivation', 'CF4O2ParametersSi', 'CF4O2ParametersSiGe', 'CSVFileProcess', 'CoverageParameters', 'DenseCellSet', 'DirectionalProcess', 'Domain', 'DomainSetup', 'Extrude', 'FaradayCageEtching', 'FaradayCageParameters', 'FluorocarbonEtching', 'FluorocarbonMaterialParameters', 'FluorocarbonParameters', 'FluorocarbonParametersIons', 'FluxEngineType', 'GDSGeometry', 'GDSReader', 'GeometricTrenchDeposition', 'GeometryFactory', 'HBrO2Etching', 'HoleShape', 'IBEParameters', 'IBEParametersCos4Yield', 'Interpolation', 'IonBeamEtching', 'IsotropicProcess', 'Length', 'LengthUnit', 'LogLevel', 'Logger', 'MakeFin', 'MakeHole', 'MakePlane', 'MakeStack', 'MakeTrench', 'Material', 'MaterialCategory', 'MaterialInfo', 'MaterialKind', 'MaterialMap', 'MaterialRegistry', 'MaterialValueMap', 'MetaDataLevel', 'MultiParticleProcess', 'NormalizationType', 'OxideRegrowth', 'PROXY_DIM', 'Planarize', 'PlasmaEtchingParameters', 'PlasmaEtchingParametersIons', 'PlasmaEtchingParametersMask', 'PlasmaEtchingParametersPassivation', 'PlasmaEtchingParametersPolymer', 'PlasmaEtchingParametersSubstrate', 'Process', 'ProcessModel', 'ProcessModelBase', 'ProcessParams', 'RateGrid', 'RateSet', 'RayTracingParameters', 'Reader', 'RenderMode', 'SF6C4F8Etching', 'SF6O2Etching', 'SelectiveEpitaxy', 'SingleParticleALD', 'SingleParticleProcess', 'Slice', 'SpatialScheme', 'SphereDistribution', 'StencilLocalLaxFriedrichsScalar', 'TEOSDeposition', 'TEOSPECVD', 'TemporalScheme', 'Time', 'TimeUnit', 'ToDiskMesh', 'VTKRenderWindow', 'WetEtching', 'Writer', 'constants', 'd2', 'd3', 'gpu', 'gpuAvailable', 'ls', 'readConfigFile', 'setDimension', 'setNumThreads', 'util', 'version'] def __dir__(): ... def __getattr__(name): diff --git a/python/viennaps/_core/__init__.pyi b/python/viennaps/_core/__init__.pyi index a97d0122..de1efc65 100644 --- a/python/viennaps/_core/__init__.pyi +++ b/python/viennaps/_core/__init__.pyi @@ -6,14 +6,14 @@ import collections.abc import enum import typing import viennals._core -from viennaps import d2 import viennaps.d2 +from viennaps import d2 import viennaps.d3 from viennaps import d3 from . import constants from . import gpu from . import util -__all__: list[str] = ['AdvectionParameters', 'AtomicLayerProcessParameters', 'BuiltInMaterial', 'CF4O2Parameters', 'CF4O2ParametersIons', 'CF4O2ParametersMask', 'CF4O2ParametersPassivation', 'CF4O2ParametersSi', 'CF4O2ParametersSiGe', 'CoverageParameters', 'Extrude', 'FaradayCageParameters', 'FluorocarbonMaterialParameters', 'FluorocarbonParameters', 'FluorocarbonParametersIons', 'FluxEngineType', 'HoleShape', 'IBEParameters', 'IBEParametersCos4Yield', 'Length', 'LengthUnit', 'Logger', 'Material', 'MaterialCategory', 'MaterialInfo', 'MaterialKind', 'MaterialMap', 'MaterialRegistry', 'MetaDataLevel', 'NormalizationType', 'PlasmaEtchingParameters', 'PlasmaEtchingParametersIons', 'PlasmaEtchingParametersMask', 'PlasmaEtchingParametersPassivation', 'PlasmaEtchingParametersPolymer', 'PlasmaEtchingParametersSubstrate', 'ProcessParams', 'RateSet', 'RayTracingParameters', 'RenderMode', 'Slice', 'Time', 'TimeUnit', 'constants', 'd2', 'd3', 'gpu', 'gpuAvailable', 'setNumThreads', 'util', 'version'] +__all__: list[str] = ['AdvectionParameters', 'AtomicLayerProcessParameters', 'BuiltInMaterial', 'CF4O2Parameters', 'CF4O2ParametersIons', 'CF4O2ParametersMask', 'CF4O2ParametersPassivation', 'CF4O2ParametersSi', 'CF4O2ParametersSiGe', 'CoverageParameters', 'Extrude', 'FaradayCageParameters', 'FluorocarbonMaterialParameters', 'FluorocarbonParameters', 'FluorocarbonParametersIons', 'FluxEngineType', 'HoleShape', 'IBEParameters', 'IBEParametersCos4Yield', 'Length', 'LengthUnit', 'Logger', 'Material', 'MaterialCategory', 'MaterialInfo', 'MaterialKind', 'MaterialMap', 'MaterialRegistry', 'MaterialValueMap', 'MetaDataLevel', 'NormalizationType', 'PlasmaEtchingParameters', 'PlasmaEtchingParametersIons', 'PlasmaEtchingParametersMask', 'PlasmaEtchingParametersPassivation', 'PlasmaEtchingParametersPolymer', 'PlasmaEtchingParametersSubstrate', 'ProcessParams', 'RateSet', 'RayTracingParameters', 'RenderMode', 'Slice', 'Time', 'TimeUnit', 'constants', 'd2', 'd3', 'gpu', 'gpuAvailable', 'setNumThreads', 'util', 'version'] class AdvectionParameters: adaptiveTimeStepping: bool calculateIntermediateVelocities: bool @@ -1128,6 +1128,19 @@ class MaterialRegistry: ... def registerMaterial(self, name: str) -> Material: ... +class MaterialValueMap: + def __init__(self) -> None: + ... + def clearAll(self) -> None: + ... + def get(self, material: Material) -> float: + ... + def getDefault(self) -> float: + ... + def set(self, material: Material, value: typing.SupportsFloat | typing.SupportsIndex) -> None: + ... + def setDefault(self, value: typing.SupportsFloat | typing.SupportsIndex) -> None: + ... class MetaDataLevel(enum.IntEnum): FULL: typing.ClassVar[MetaDataLevel] # value = GRID: typing.ClassVar[MetaDataLevel] # value = @@ -1156,8 +1169,9 @@ class PlasmaEtchingParameters: Passivation: PlasmaEtchingParametersPassivation Polymer: PlasmaEtchingParametersPolymer Substrate: PlasmaEtchingParametersSubstrate - beta_E: ... - beta_P: ... + beta_E: MaterialValueMap + beta_P: MaterialValueMap + rateFactors: MaterialValueMap def __init__(self) -> None: ... @property diff --git a/python/viennaps/d2/gpu.pyi b/python/viennaps/d2/gpu.pyi index 628672ef..2291e866 100644 --- a/python/viennaps/d2/gpu.pyi +++ b/python/viennaps/d2/gpu.pyi @@ -35,5 +35,5 @@ class SF6O2Etching(ProcessModelGPU): def __init__(self, parameters: viennaps._core.PlasmaEtchingParameters) -> None: ... class SingleParticleProcess(ProcessModelGPU): - def __init__(self, materialRates: ..., stickingProbability: typing.SupportsFloat | typing.SupportsIndex, sourceExponent: typing.SupportsFloat | typing.SupportsIndex) -> None: + def __init__(self, materialRates: viennaps._core.MaterialValueMap, stickingProbability: typing.SupportsFloat | typing.SupportsIndex, sourceExponent: typing.SupportsFloat | typing.SupportsIndex) -> None: ... diff --git a/python/viennaps/d3/gpu.pyi b/python/viennaps/d3/gpu.pyi index 7e0d7d2a..8186265f 100644 --- a/python/viennaps/d3/gpu.pyi +++ b/python/viennaps/d3/gpu.pyi @@ -35,5 +35,5 @@ class SF6O2Etching(ProcessModelGPU): def __init__(self, parameters: viennaps._core.PlasmaEtchingParameters) -> None: ... class SingleParticleProcess(ProcessModelGPU): - def __init__(self, materialRates: ..., stickingProbability: typing.SupportsFloat | typing.SupportsIndex, sourceExponent: typing.SupportsFloat | typing.SupportsIndex) -> None: + def __init__(self, materialRates: viennaps._core.MaterialValueMap, stickingProbability: typing.SupportsFloat | typing.SupportsIndex, sourceExponent: typing.SupportsFloat | typing.SupportsIndex) -> None: ... From 3d1de566c0db2f6d5f0bb1e64c424093b985522b Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Mon, 30 Mar 2026 15:11:52 +0200 Subject: [PATCH 10/32] test adding custom material to domain --- .../viennaps/materials/psBuiltInMaterial.hpp | 2 +- .../viennaps/materials/psMaterialValueMap.hpp | 8 ++- include/viennaps/psDomain.hpp | 12 +++++ python/pyWrapDimension.hpp | 53 ++++++++----------- python/scripts/install_ViennaPS.py | 2 +- tests/materialRegistry/materialRegistry.cpp | 48 ++++++++++++++++- 6 files changed, 87 insertions(+), 38 deletions(-) diff --git a/include/viennaps/materials/psBuiltInMaterial.hpp b/include/viennaps/materials/psBuiltInMaterial.hpp index c969e569..f6e7fdc7 100644 --- a/include/viennaps/materials/psBuiltInMaterial.hpp +++ b/include/viennaps/materials/psBuiltInMaterial.hpp @@ -141,7 +141,7 @@ struct BuiltInMaterialInfo { uint32_t colorHex; }; -constexpr uint16_t kBuiltInMaterialMaxId = 176; +inline constexpr uint16_t kBuiltInMaterialMaxId = 176; constexpr std::array kBuiltInMaterialTable = [] { diff --git a/include/viennaps/materials/psMaterialValueMap.hpp b/include/viennaps/materials/psMaterialValueMap.hpp index 6d5b145a..d0f151b1 100644 --- a/include/viennaps/materials/psMaterialValueMap.hpp +++ b/include/viennaps/materials/psMaterialValueMap.hpp @@ -49,7 +49,10 @@ template class MaterialValueMap { return it == customValues_.end() ? default_ : it->second; } - [[nodiscard]] T get(BuiltInMaterial m) const { return get(Material(m)); } + [[nodiscard]] T get(BuiltInMaterial m) const { + auto idx = toIndex(m); + return isSet_[idx] ? values_[idx] : default_; + } // get reference (no copy) [[nodiscard]] const T &getRef(Material material) const { @@ -63,7 +66,8 @@ template class MaterialValueMap { } [[nodiscard]] const T &getRef(BuiltInMaterial m) const { - return getRef(Material(m)); + auto idx = toIndex(m); + return isSet_[idx] ? values_[idx] : default_; } void setDefault(const T &v) { default_ = v; } diff --git a/include/viennaps/psDomain.hpp b/include/viennaps/psDomain.hpp index 4ca95eea..fc08ae27 100644 --- a/include/viennaps/psDomain.hpp +++ b/include/viennaps/psDomain.hpp @@ -168,6 +168,13 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Domain { } } + void insertNextLevelSetAsMaterial(lsDomainType levelSet, + std::string materialName, + bool wrapLowerLevelSet = true) { + auto material = materialRegistry_.registerMaterial(std::move(materialName)); + insertNextLevelSetAsMaterial(levelSet, material, wrapLowerLevelSet); + } + void insertNextLevelSetAsMaterial(lsDomainType levelSet, const Material material, bool wrapLowerLevelSet = true) { @@ -225,6 +232,11 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Domain { insertNextLevelSetAsMaterial(copy, material, false); } + void duplicateTopLevelSet(const std::string &materialName) { + auto material = materialRegistry_.registerMaterial(materialName); + duplicateTopLevelSet(material); + } + // Remove the top (last inserted) Level-Set. void removeTopLevelSet() { if (levelSets_.empty()) { diff --git a/python/pyWrapDimension.hpp b/python/pyWrapDimension.hpp index a688f47c..969e2489 100644 --- a/python/pyWrapDimension.hpp +++ b/python/pyWrapDimension.hpp @@ -434,6 +434,7 @@ template void bindApi(py::module &module) { * DOMAIN * ****************************************************************************/ using DomainType = SmartPointer>; + using lsDomainType = SmartPointer<::viennals::Domain>; // Domain Setup py::class_>(module, "DomainSetup") @@ -491,15 +492,30 @@ template void bindApi(py::module &module) { .def("getSetup", &Domain::getSetup, "Get the domain setup.") .def("deepCopy", &Domain::deepCopy) .def("insertNextLevelSetAsMaterial", - &Domain::insertNextLevelSetAsMaterial, py::arg("levelSet"), - py::arg("material"), py::arg("wrapLowerLevelSet") = true, + py::overload_cast( + &Domain::insertNextLevelSetAsMaterial), + py::arg("levelSet"), py::arg("material"), + py::arg("wrapLowerLevelSet") = true, + "Insert a level set to domain as a material.") + .def("insertNextLevelSetAsMaterial", + py::overload_cast( + &Domain::insertNextLevelSetAsMaterial), + py::arg("levelSet"), py::arg("material"), + py::arg("wrapLowerLevelSet") = true, "Insert a level set to domain as a material.") .def("insertMask", &Domain::insertMask, py::arg("mask"), py::arg("material") = Material::Mask, "Insert a mask level set to the domain. The mask is inserted at the " "front of the level set vector and can be used to exclude areas " "from processes.") - .def("duplicateTopLevelSet", &Domain::duplicateTopLevelSet, + .def("duplicateTopLevelSet", + py::overload_cast( + &Domain::duplicateTopLevelSet), + "Duplicate the top level set. Should be used before a deposition " + "process.") + .def("duplicateTopLevelSet", + py::overload_cast( + &Domain::duplicateTopLevelSet), "Duplicate the top level set. Should be used before a deposition " "process.") .def("removeTopLevelSet", &Domain::removeTopLevelSet) @@ -521,33 +537,6 @@ template void bindApi(py::module &module) { py::overload_cast<>(&Domain::getMaterialRegistry), py::return_value_policy::reference_internal, "Get the domain-local material registry.") - .def( - "registerMaterial", - [](Domain &self, const std::string &name) { - return self.getMaterialRegistry().registerMaterial(name); - }, - py::arg("name"), - "Register a material by name in this domain and return handle.") - .def( - "hasMaterial", - [](const Domain &self, const std::string &name) { - return self.getMaterialRegistry().hasMaterial(name); - }, - py::arg("name"), - "Check whether material name resolves in this domain.") - .def( - "getMaterialByName", - [](const Domain &self, const std::string &name) { - return self.getMaterialRegistry().getMaterial(name); - }, - py::arg("name"), "Resolve material by name in this domain.") - .def( - "getMaterialName", - [](const Domain &self, const Material material) { - return std::string(self.getMaterialRegistry().getName(material)); - }, - py::arg("material"), - "Resolve canonical material name in this domain.") .def("generateCellSet", &Domain::generateCellSet, "Generate the cell set.") .def("getLevelSets", &Domain::getLevelSets) @@ -574,7 +563,7 @@ template void bindApi(py::module &module) { py::arg("width") = 1, "Get the level set grids of layers in the domain.") .def("getSurfaceMesh", &Domain::getSurfaceMesh, - py::arg("addInterfaces") = false, py::arg("sharpCorners") = false, + py::arg("addInterfaces") = true, py::arg("sharpCorners") = false, py::arg("minNodeDistanceFactor") = 0.01, "Get the surface mesh of the domain") .def("getHullMesh", &Domain::getHullMesh, @@ -584,7 +573,7 @@ template void bindApi(py::module &module) { py::arg("filename"), py::arg("width") = 1, "Save the level set grids of layers in the domain.") .def("saveSurfaceMesh", &Domain::saveSurfaceMesh, - py::arg("filename"), py::arg("addInterfaces") = false, + py::arg("filename"), py::arg("addInterfaces") = true, py::arg("sharpCorners") = false, py::arg("minNodeDistanceFactor") = 0.01, "Save the surface of the domain.") diff --git a/python/scripts/install_ViennaPS.py b/python/scripts/install_ViennaPS.py index 703b0d96..3f776987 100644 --- a/python/scripts/install_ViennaPS.py +++ b/python/scripts/install_ViennaPS.py @@ -9,7 +9,7 @@ from pathlib import Path REQUIRED_NVCC_MAJOR = 12 -DEFAULT_VIENNALS_VERSION = "5.5.0" +DEFAULT_VIENNALS_VERSION = "5.7.0" # Detect OS IS_WINDOWS = sys.platform == "win32" or os.name == "nt" diff --git a/tests/materialRegistry/materialRegistry.cpp b/tests/materialRegistry/materialRegistry.cpp index 4f702f78..7614f598 100644 --- a/tests/materialRegistry/materialRegistry.cpp +++ b/tests/materialRegistry/materialRegistry.cpp @@ -1,10 +1,14 @@ -#include "materials/psMaterials.hpp" +#include +#include +#include +#include +#include #include using namespace viennaps; -int main() { +void testMaterialRegistry() { MaterialRegistry registry; const auto builtIn = registry.registerMaterial("Si"); @@ -29,6 +33,46 @@ int main() { const auto maybeMissing = registry.findMaterial("DoesNotExist"); VC_TEST_ASSERT(!maybeMissing.has_value()); +} + +void testCustomMaterialMap() { + auto domain = + Domain::New(0.5, 50.0, BoundaryType::REFLECTIVE_BOUNDARY); + MakeTrench(domain, 15.0, 0.0, 0.0, 3.0).apply(); + + auto isoEtch = + SmartPointer>::New(-1.0, Material::Mask); + Process(domain, isoEtch, 10.0).apply(); + + domain->saveSurfaceMesh("testCustomMaterialMap_0"); + + domain->duplicateTopLevelSet("CustomMaterial"); + + auto isoDep = SmartPointer>::New(1.0); + Process(domain, isoDep, 2.0).apply(); + + domain->saveSurfaceMesh("testCustomMaterialMap_1"); + + auto customMaterial = + domain->getMaterialRegistry().getMaterial("CustomMaterial"); + std::unordered_map> materialRates; + materialRates[Material::Mask] = {0.0, 0.0}; + materialRates[customMaterial] = {1.0, 0.0}; + materialRates[Material::Si] = {0.5, -0.5}; + + Logger::setLogLevel(LogLevel::DEBUG); + + auto directionalProcess = SmartPointer>::New( + Vec3Dd{0.0, -1.0, 0.0}, materialRates); + Process(domain, directionalProcess, 5.0).apply(); + + domain->saveSurfaceMesh("testCustomMaterialMap_2"); +} + +int main() { + + testMaterialRegistry(); + testCustomMaterialMap(); return 0; } From 696bf6ac530823c71a2bdc32bc051daabab90290 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Mon, 30 Mar 2026 15:52:56 +0200 Subject: [PATCH 11/32] refactor: use singleton MaterialRegistry --- .../viennaps/materials/psMaterialRegistry.hpp | 33 ++++++------------- include/viennaps/materials/psMaterials.hpp | 5 ++- include/viennaps/psDomain.hpp | 15 ++------- include/viennaps/psReader.hpp | 3 +- include/viennaps/psWriter.hpp | 2 +- python/pyWrap.cpp | 6 ++-- python/pyWrapDimension.hpp | 4 --- tests/materialMap/materialMap.cpp | 2 +- tests/materialRegistry/materialRegistry.cpp | 4 +-- 9 files changed, 24 insertions(+), 50 deletions(-) diff --git a/include/viennaps/materials/psMaterialRegistry.hpp b/include/viennaps/materials/psMaterialRegistry.hpp index f1e710c3..49967a16 100644 --- a/include/viennaps/materials/psMaterialRegistry.hpp +++ b/include/viennaps/materials/psMaterialRegistry.hpp @@ -14,31 +14,22 @@ namespace viennaps { struct CustomMaterialInfo { std::string name; + double density = 0.0; }; class MaterialRegistry { public: - MaterialRegistry() = default; - - MaterialRegistry(const MaterialRegistry &other) { - std::scoped_lock lock(other.mutex_); - nameToCustomId_ = other.nameToCustomId_; - materials_ = other.materials_; + [[nodiscard]] static MaterialRegistry &instance() { + static MaterialRegistry registry; + return registry; } - MaterialRegistry &operator=(const MaterialRegistry &other) { - if (this == &other) { - return *this; - } - - std::scoped_lock lock(mutex_, other.mutex_); - nameToCustomId_ = other.nameToCustomId_; - materials_ = other.materials_; - return *this; - } + MaterialRegistry(const MaterialRegistry &) = delete; + MaterialRegistry &operator=(const MaterialRegistry &) = delete; + MaterialRegistry(MaterialRegistry &&) = delete; + MaterialRegistry &operator=(MaterialRegistry &&) = delete; [[nodiscard]] Material registerMaterial(std::string name) { - std::lock_guard lock(mutex_); if (name.empty()) { throw std::invalid_argument("Material name must not be empty."); @@ -61,7 +52,6 @@ class MaterialRegistry { } [[nodiscard]] bool hasMaterial(std::string_view name) const { - std::lock_guard lock(mutex_); BuiltInMaterial builtIn = BuiltInMaterial::Undefined; if (tryBuiltInMaterialFromString(name, builtIn)) { @@ -73,7 +63,6 @@ class MaterialRegistry { [[nodiscard]] std::optional findMaterial(std::string_view name) const { - std::lock_guard lock(mutex_); BuiltInMaterial builtIn = BuiltInMaterial::Undefined; if (tryBuiltInMaterialFromString(name, builtIn)) { @@ -101,7 +90,6 @@ class MaterialRegistry { return builtInMaterialToString(material.builtIn()); } - std::lock_guard lock(mutex_); const auto customId = material.customId(); if (customId >= materials_.size()) { throw std::out_of_range("Unknown custom material id."); @@ -120,7 +108,6 @@ class MaterialRegistry { "Built-in materials do not have registry-owned metadata."); } - std::lock_guard lock(mutex_); const auto customId = material.customId(); if (customId >= materials_.size()) { throw std::out_of_range("Unknown custom material id."); @@ -130,12 +117,12 @@ class MaterialRegistry { } [[nodiscard]] std::size_t customMaterialCount() const { - std::lock_guard lock(mutex_); return materials_.size(); } private: - mutable std::mutex mutex_; + MaterialRegistry() = default; + std::unordered_map nameToCustomId_; std::vector materials_; }; diff --git a/include/viennaps/materials/psMaterials.hpp b/include/viennaps/materials/psMaterials.hpp index 17eb05e3..69e0acd7 100644 --- a/include/viennaps/materials/psMaterials.hpp +++ b/include/viennaps/materials/psMaterials.hpp @@ -191,13 +191,12 @@ class MaterialMap { return toString(mapToMaterial(matId)); } - static inline Material fromString(std::string_view name, - MaterialRegistry ®istry) { + static inline Material fromString(std::string_view name) { BuiltInMaterial builtIn = BuiltInMaterial::Undefined; if (tryBuiltInMaterialFromString(name, builtIn)) { return Material(builtIn); } - return registry.registerMaterial(std::string(name)); + return MaterialRegistry::instance().registerMaterial(std::string(name)); } }; diff --git a/include/viennaps/psDomain.hpp b/include/viennaps/psDomain.hpp index fc08ae27..e7430bf0 100644 --- a/include/viennaps/psDomain.hpp +++ b/include/viennaps/psDomain.hpp @@ -64,7 +64,6 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Domain { lsDomainsType levelSets_; csDomainType cellSet_ = nullptr; MaterialMapType materialMap_ = nullptr; - MaterialRegistry materialRegistry_; MetaDataLevel metaDataLevel_ = MetaDataLevel::NONE; MetaDataType metaData_; @@ -144,7 +143,6 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Domain { clear(); setup_ = domain->setup_; metaData_ = domain->metaData_; - materialRegistry_ = domain->materialRegistry_; // Copy all Level-Sets. for (auto &ls : domain->levelSets_) { @@ -171,7 +169,8 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Domain { void insertNextLevelSetAsMaterial(lsDomainType levelSet, std::string materialName, bool wrapLowerLevelSet = true) { - auto material = materialRegistry_.registerMaterial(std::move(materialName)); + auto material = + MaterialRegistry::instance().registerMaterial(std::move(materialName)); insertNextLevelSetAsMaterial(levelSet, material, wrapLowerLevelSet); } @@ -233,7 +232,7 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Domain { } void duplicateTopLevelSet(const std::string &materialName) { - auto material = materialRegistry_.registerMaterial(materialName); + auto material = MaterialRegistry::instance().registerMaterial(materialName); duplicateTopLevelSet(material); } @@ -416,14 +415,6 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Domain { // Level-Set in the domain. [[nodiscard]] auto &getMaterialMap() const { return materialMap_; } - [[nodiscard]] MaterialRegistry &getMaterialRegistry() { - return materialRegistry_; - } - - [[nodiscard]] const MaterialRegistry &getMaterialRegistry() const { - return materialRegistry_; - } - [[nodiscard]] auto &getCellSet() const { return cellSet_; } // Returns the underlying HRLE grid of the top Level-Set in the domain. diff --git a/include/viennaps/psReader.hpp b/include/viennaps/psReader.hpp index 91812996..f9f179d3 100644 --- a/include/viennaps/psReader.hpp +++ b/include/viennaps/psReader.hpp @@ -128,8 +128,7 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Reader { std::string materialName(nameLength, '\0'); fin.read(materialName.data(), static_cast(nameLength)); - material = MaterialMap::fromString(materialName, - domain->getMaterialRegistry()); + material = MaterialMap::fromString(materialName); } domain->insertNextLevelSetAsMaterial(levelSets[i], material, false); diff --git a/include/viennaps/psWriter.hpp b/include/viennaps/psWriter.hpp index bc196b94..bf68009c 100644 --- a/include/viennaps/psWriter.hpp +++ b/include/viennaps/psWriter.hpp @@ -113,7 +113,7 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class Writer { materialName = MaterialMap::toString(material); } else { materialName = - std::string(domain->getMaterialRegistry().getName(material)); + std::string(MaterialRegistry::instance().getName(material)); } const auto nameLength = static_cast(materialName.size()); diff --git a/python/pyWrap.cpp b/python/pyWrap.cpp index 67ca52ee..830bd81a 100644 --- a/python/pyWrap.cpp +++ b/python/pyWrap.cpp @@ -102,7 +102,10 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { .finalize(); py::class_(module, "MaterialRegistry") - .def(py::init<>()) + .def_static( + "instance", + []() -> MaterialRegistry & { return MaterialRegistry::instance(); }, + py::return_value_policy::reference) .def("registerMaterial", &MaterialRegistry::registerMaterial, py::arg("name")) .def("hasMaterial", &MaterialRegistry::hasMaterial, py::arg("name")) @@ -164,7 +167,6 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { py::overload_cast>( &MaterialMap::isMaterial)) .def_static("fromString", &MaterialMap::fromString, py::arg("name"), - py::arg("registry"), "Resolve built-in or register custom material by name.") .def_static("toString", py::overload_cast(&MaterialMap::toString), diff --git a/python/pyWrapDimension.hpp b/python/pyWrapDimension.hpp index 969e2489..8f621cd5 100644 --- a/python/pyWrapDimension.hpp +++ b/python/pyWrapDimension.hpp @@ -533,10 +533,6 @@ template void bindApi(py::module &module) { "Get the number of level sets in the domain.") .def("setMaterialMap", &Domain::setMaterialMap) .def("getMaterialMap", &Domain::getMaterialMap) - .def("getMaterialRegistry", - py::overload_cast<>(&Domain::getMaterialRegistry), - py::return_value_policy::reference_internal, - "Get the domain-local material registry.") .def("generateCellSet", &Domain::generateCellSet, "Generate the cell set.") .def("getLevelSets", &Domain::getLevelSets) diff --git a/tests/materialMap/materialMap.cpp b/tests/materialMap/materialMap.cpp index 879c8e82..3fda8ba5 100644 --- a/tests/materialMap/materialMap.cpp +++ b/tests/materialMap/materialMap.cpp @@ -34,7 +34,7 @@ int main() { VC_TEST_ASSERT(Material::Si.isBuiltIn()); // Custom material registration checks - MaterialRegistry registry; + auto ®istry = MaterialRegistry::instance(); const auto customA = registry.registerMaterial("MyCustom"); const auto customA2 = registry.registerMaterial("MyCustom"); const auto customB = registry.registerMaterial("AnotherCustom"); diff --git a/tests/materialRegistry/materialRegistry.cpp b/tests/materialRegistry/materialRegistry.cpp index 7614f598..932c7dd5 100644 --- a/tests/materialRegistry/materialRegistry.cpp +++ b/tests/materialRegistry/materialRegistry.cpp @@ -9,7 +9,7 @@ using namespace viennaps; void testMaterialRegistry() { - MaterialRegistry registry; + auto ®istry = MaterialRegistry::instance(); const auto builtIn = registry.registerMaterial("Si"); VC_TEST_ASSERT(builtIn.isBuiltIn()); @@ -54,7 +54,7 @@ void testCustomMaterialMap() { domain->saveSurfaceMesh("testCustomMaterialMap_1"); auto customMaterial = - domain->getMaterialRegistry().getMaterial("CustomMaterial"); + MaterialRegistry::instance().getMaterial("CustomMaterial"); std::unordered_map> materialRates; materialRates[Material::Mask] = {0.0, 0.0}; materialRates[customMaterial] = {1.0, 0.0}; From dfee75840b5ce8d807b6425ca96b3f573bcfff90 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Mon, 30 Mar 2026 17:46:02 +0200 Subject: [PATCH 12/32] material registry tests --- tests/materialRegistry/materialRegistry.cpp | 21 ++++++++------ tests/materialRegistry/materialRegistry.py | 31 +++++++++++++++++++++ 2 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 tests/materialRegistry/materialRegistry.py diff --git a/tests/materialRegistry/materialRegistry.cpp b/tests/materialRegistry/materialRegistry.cpp index 932c7dd5..44f7bc33 100644 --- a/tests/materialRegistry/materialRegistry.cpp +++ b/tests/materialRegistry/materialRegistry.cpp @@ -14,10 +14,12 @@ void testMaterialRegistry() { const auto builtIn = registry.registerMaterial("Si"); VC_TEST_ASSERT(builtIn.isBuiltIn()); VC_TEST_ASSERT(builtIn == Material::Si); + VC_TEST_ASSERT(registry.customMaterialCount() == 0); const auto customA = registry.registerMaterial("CustomFoo"); const auto customARepeat = registry.registerMaterial("CustomFoo"); const auto customB = registry.registerMaterial("CustomBar"); + VC_TEST_ASSERT(registry.customMaterialCount() == 2); VC_TEST_ASSERT(customA.isCustom()); VC_TEST_ASSERT(customA == customARepeat); @@ -36,37 +38,40 @@ void testMaterialRegistry() { } void testCustomMaterialMap() { + Logger::setLogLevel(LogLevel::ERROR); + auto domain = - Domain::New(0.5, 50.0, BoundaryType::REFLECTIVE_BOUNDARY); + Domain::New(0.97, 50.0, BoundaryType::REFLECTIVE_BOUNDARY); MakeTrench(domain, 15.0, 0.0, 0.0, 3.0).apply(); auto isoEtch = SmartPointer>::New(-1.0, Material::Mask); Process(domain, isoEtch, 10.0).apply(); - domain->saveSurfaceMesh("testCustomMaterialMap_0"); + // domain->saveSurfaceMesh("testCustomMaterialMap_0"); domain->duplicateTopLevelSet("CustomMaterial"); auto isoDep = SmartPointer>::New(1.0); Process(domain, isoDep, 2.0).apply(); - domain->saveSurfaceMesh("testCustomMaterialMap_1"); + // domain->saveSurfaceMesh("testCustomMaterialMap_1"); - auto customMaterial = - MaterialRegistry::instance().getMaterial("CustomMaterial"); + auto customMaterial = MaterialMap::fromString("CustomMaterial"); + VC_TEST_ASSERT(customMaterial == + MaterialRegistry::instance().getMaterial("CustomMaterial")); + VC_TEST_ASSERT(customMaterial == + domain->getMaterialMap()->getMaterialAtIdx(2)); std::unordered_map> materialRates; materialRates[Material::Mask] = {0.0, 0.0}; materialRates[customMaterial] = {1.0, 0.0}; materialRates[Material::Si] = {0.5, -0.5}; - Logger::setLogLevel(LogLevel::DEBUG); - auto directionalProcess = SmartPointer>::New( Vec3Dd{0.0, -1.0, 0.0}, materialRates); Process(domain, directionalProcess, 5.0).apply(); - domain->saveSurfaceMesh("testCustomMaterialMap_2"); + // domain->saveSurfaceMesh("testCustomMaterialMap_2"); } int main() { diff --git a/tests/materialRegistry/materialRegistry.py b/tests/materialRegistry/materialRegistry.py new file mode 100644 index 00000000..6c013fdd --- /dev/null +++ b/tests/materialRegistry/materialRegistry.py @@ -0,0 +1,31 @@ +import viennaps as ps + + +def test_material_registry(): + registry = ps.MaterialRegistry.instance() + + built_in = registry.registerMaterial("Si") + assert built_in.isBuiltIn() + assert built_in == ps.Material.Si + assert registry.customMaterialCount() == 0 + + custom_a = registry.registerMaterial("CustomFoo") + custom_a_repeat = registry.registerMaterial("CustomFoo") + custom_b = registry.registerMaterial("CustomBar") + assert registry.customMaterialCount() == 2 + + assert custom_a.isCustom() + assert custom_a == custom_a_repeat + assert custom_a != custom_b + + domain = ps.Domain(10.0, 10.0, 1.0) + ps.MakePlane(domain).apply() + domain.duplicateTopLevelSet("CustomMaterial") + + customMaterial = ps.MaterialMap.fromString("CustomMaterial") + assert customMaterial == ps.MaterialMap.fromString("CustomMaterial") + assert customMaterial != ps.MaterialMap.fromString("Si") + + +if __name__ == "__main__": + test_material_registry() From 777cc0cc61cf7544d2eeba827265948ffd63d6a6 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Mon, 30 Mar 2026 17:53:31 +0200 Subject: [PATCH 13/32] refactor: update directional process --- .../viennaps/models/psDirectionalProcess.hpp | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/include/viennaps/models/psDirectionalProcess.hpp b/include/viennaps/models/psDirectionalProcess.hpp index fc6817f6..2396201f 100644 --- a/include/viennaps/models/psDirectionalProcess.hpp +++ b/include/viennaps/models/psDirectionalProcess.hpp @@ -50,7 +50,8 @@ template struct RateSet { template class DirectionalVelocityField : public VelocityField { const std::vector> rateSets_; - std::unordered_map> visibilities_; + std::vector> + visibilities_; // Indexed by rateSetID and pointId public: DirectionalVelocityField(std::vector> &&rateSets) @@ -63,7 +64,7 @@ class DirectionalVelocityField : public VelocityField { NumericType scalarVelocity = 0.0; for (const auto &rateSet : rateSets_) { - if (isMaskMaterial(material, rateSet.maskMaterials)) { + if (MaterialMap::isMaterial(material, rateSet.maskMaterials)) { continue; // Skip this rate set if material is masked } // Accumulate isotropic velocities @@ -81,7 +82,7 @@ class DirectionalVelocityField : public VelocityField { for (unsigned rateSetID = 0; rateSetID < rateSets_.size(); ++rateSetID) { const auto &rateSet = rateSets_[rateSetID]; - if (isMaskMaterial(material, rateSet.maskMaterials)) { + if (MaterialMap::isMaterial(material, rateSet.maskMaterials)) { continue; // Skip this rate set if material is masked } @@ -108,6 +109,7 @@ class DirectionalVelocityField : public VelocityField { const NumericType processTime) override { visibilities_.clear(); + visibilities_.resize(rateSets_.size()); // Calculate visibilities for each rate set auto surfaceLS = domain->getLevelSets().back(); @@ -128,21 +130,9 @@ class DirectionalVelocityField : public VelocityField { const std::vector> &getRateSets() const { return rateSets_; } - const std::unordered_map> & - getVisibilities() const { + const std::vector> &getVisibilities() const { return visibilities_; } - -protected: - static bool isMaskMaterial(const int material, - const std::vector &maskMaterials) { - for (const auto &maskMaterial : maskMaterials) { - if (MaterialMap::isMaterial(material, maskMaterial)) { - return true; - } - } - return false; - } }; template From 12c2c535af6f1d29ef2ea0fdfc7dddb33fc0afbf Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Mon, 30 Mar 2026 19:27:46 +0200 Subject: [PATCH 14/32] refactor: udpate ion beam etching model --- .../viennaps/models/psDirectionalProcess.hpp | 1 - include/viennaps/models/psIonBeamEtching.hpp | 65 ++++++++----------- .../viennaps/models/psIonBeamParameters.hpp | 8 ++- 3 files changed, 31 insertions(+), 43 deletions(-) diff --git a/include/viennaps/models/psDirectionalProcess.hpp b/include/viennaps/models/psDirectionalProcess.hpp index 2396201f..7894f1f3 100644 --- a/include/viennaps/models/psDirectionalProcess.hpp +++ b/include/viennaps/models/psDirectionalProcess.hpp @@ -116,7 +116,6 @@ class DirectionalVelocityField : public VelocityField { for (unsigned rateSetID = 0; rateSetID < rateSets_.size(); ++rateSetID) { auto &rateSet = rateSets_[rateSetID]; if (rateSet.calculateVisibility) { - std::string label = "Visibilities_" + std::to_string(rateSetID); viennals::CalculateVisibilities( surfaceLS, rateSet.direction, label) diff --git a/include/viennaps/models/psIonBeamEtching.hpp b/include/viennaps/models/psIonBeamEtching.hpp index 25bd0387..33f94252 100644 --- a/include/viennaps/models/psIonBeamEtching.hpp +++ b/include/viennaps/models/psIonBeamEtching.hpp @@ -21,24 +21,21 @@ using namespace viennacore; namespace impl { template class IBESurfaceModel : public SurfaceModel { - const IBEParameters params_; - const std::vector maskMaterials_; + const IBEParameters ¶ms_; public: constexpr static const char *fluxLabel = "ionFlux"; constexpr static const char *redepositionLabel = "redepositionFlux"; - IBESurfaceModel(const IBEParameters ¶ms, - const std::vector &mask) - : params_(params), maskMaterials_(mask) {} + IBESurfaceModel(const IBEParameters ¶ms) : params_(params) {} SmartPointer> calculateVelocities(SmartPointer> rates, const std::vector> &coordinates, const std::vector &materialIds) override { - auto velocity = - SmartPointer>::New(materialIds.size(), 0.); + std::vector etchRate(materialIds.size(), 0.); + auto flux = rates->getScalarData(fluxLabel); assert(flux && flux->size() == materialIds.size()); std::vector redeposition(materialIds.size(), 0.); @@ -66,30 +63,18 @@ class IBESurfaceModel : public SurfaceModel { yield); #pragma omp parallel for - for (size_t i = 0; i < velocity->size(); i++) { - if (!isMaskMaterial(materialIds[i])) { - NumericType rate = params_.planeWaferRate; - if (auto material = MaterialMap::mapToMaterial(materialIds[i]); - params_.materialPlaneWaferRate.find(material) != - params_.materialPlaneWaferRate.end()) { - rate = params_.materialPlaneWaferRate.at(material); - } - - velocity->at(i) = -flux->at(i) * norm * rate; - velocity->at(i) += redeposition.at(i) * params_.redepositionRate; - } - } + for (size_t i = 0; i < etchRate.size(); i++) { + NumericType rate = params_.materialPlaneWaferRate.get( + Material::fromLegacyId(materialIds[i])); - return velocity; - } + if (rate == 0.) + continue; -private: - bool isMaskMaterial(const NumericType &material) const { - for (const auto &mat : maskMaterials_) { - if (MaterialMap::isMaterial(material, mat)) - return true; + etchRate[i] = -flux->at(i) * norm * rate; + etchRate[i] += redeposition.at(i) * params_.redepositionRate; } - return false; + + return SmartPointer>::New(std::move(etchRate)); } }; @@ -281,9 +266,7 @@ namespace gpu { template class IonBeamEtching : public ProcessModelGPU { public: - IonBeamEtching(const IBEParameters ¶ms, - const std::vector &maskMaterial) - : maskMaterials_(maskMaterial), params_(params) { + IonBeamEtching(const IBEParameters ¶ms) : params_(params) { // particles viennaray::gpu::Particle particle{ .name = "IBEIon", .cosineExponent = params_.exponent}; @@ -351,7 +334,7 @@ class IonBeamEtching : public ProcessModelGPU { // surface model auto surfModel = SmartPointer<::viennaps::impl::IBESurfaceModel>::New( - params_, maskMaterials_); + params_); // velocity field auto velField = SmartPointer>::New(); @@ -368,7 +351,6 @@ class IonBeamEtching : public ProcessModelGPU { ~IonBeamEtching() override { this->processData.free(); } private: - std::vector maskMaterials_; IBEParameters params_; }; @@ -383,15 +365,21 @@ class IonBeamEtching : public ProcessModelCPU { IonBeamEtching(const IBEParameters ¶ms, const std::vector &maskMaterial) - : maskMaterials_(maskMaterial), params_(params) { + : params_(params) { + + // material-specific plane wafer rates and mask + params_.materialPlaneWaferRate.setDefault(params_.planeWaferRate); + for (const auto &mask : maskMaterial) { + params_.materialPlaneWaferRate.set(mask, 0.); + } // particles auto particle = std::make_unique>(params_); // surface model - auto surfModel = SmartPointer>::New( - params_, maskMaterials_); + auto surfModel = + SmartPointer>::New(params_); // velocity field auto velField = SmartPointer>::New(); @@ -406,15 +394,14 @@ class IonBeamEtching : public ProcessModelCPU { #ifdef VIENNACORE_COMPILE_GPU SmartPointer> getGPUModel() final { - auto model = SmartPointer>::New( - params_, maskMaterials_); + auto model = + SmartPointer>::New(params_); model->setProcessName(this->getProcessName().value()); return model; } #endif private: - std::vector maskMaterials_; IBEParameters params_; }; diff --git a/include/viennaps/models/psIonBeamParameters.hpp b/include/viennaps/models/psIonBeamParameters.hpp index 4b9a377a..85e7420c 100644 --- a/include/viennaps/models/psIonBeamParameters.hpp +++ b/include/viennaps/models/psIonBeamParameters.hpp @@ -1,5 +1,6 @@ #pragma once +#include "../materials/psMaterialValueMap.hpp" #include "../materials/psMaterials.hpp" #include @@ -10,7 +11,7 @@ template struct IBEParameters { // Rates NumericType planeWaferRate = 1.; - std::unordered_map materialPlaneWaferRate; + MaterialValueMap materialPlaneWaferRate; NumericType meanEnergy = 250; // eV NumericType sigmaEnergy = 10; // eV @@ -49,8 +50,9 @@ template struct IBEParameters { std::unordered_map> processData; processData["Default PWR"] = {planeWaferRate}; - for (const auto &pair : materialPlaneWaferRate) { - processData[MaterialMap::toString(pair.first) + " PWR"] = {pair.second}; + for (const auto &entry : materialPlaneWaferRate) { + processData[MaterialMap::toString(entry.getMaterial()) + " PWR"] = { + entry.getValue()}; } processData["Mean Energy"] = {meanEnergy}; From d1f98f8da6089e927f2afaddd5520ba346bf01a9 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Mon, 30 Mar 2026 19:55:41 +0200 Subject: [PATCH 15/32] fix: faraday cage model, update python bindings --- examples/ionBeamEtching/ionBeamEtching.cpp | 2 +- examples/ionBeamEtching/ionBeamEtching.py | 2 +- .../viennaps/materials/psMaterialValueMap.hpp | 4 +- .../viennaps/models/psFaradayCageEtching.hpp | 37 ++++++++++------ include/viennaps/models/psIonBeamEtching.hpp | 2 + python/pyWrapDimension.hpp | 16 ++++--- python/viennaps/_core/__init__.pyi | 12 ++---- python/viennaps/d2/__init__.pyi | 43 +++++++++---------- python/viennaps/d2/gpu.pyi | 4 +- python/viennaps/d3/__init__.pyi | 43 +++++++++---------- python/viennaps/d3/gpu.pyi | 4 +- 11 files changed, 92 insertions(+), 77 deletions(-) diff --git a/examples/ionBeamEtching/ionBeamEtching.cpp b/examples/ionBeamEtching/ionBeamEtching.cpp index 9f8ddf1a..9c456711 100644 --- a/examples/ionBeamEtching/ionBeamEtching.cpp +++ b/examples/ionBeamEtching/ionBeamEtching.cpp @@ -35,7 +35,7 @@ int main(int argc, char *argv[]) { // copy top layer to capture deposition geometry->duplicateTopLevelSet(Material::Polymer); - IBEParameters ibeParams; + auto ibeParams = IonBeamEtching::defaultParameters(); ibeParams.tiltAngle = params.get("angle"); ibeParams.exponent = params.get("exponent"); ibeParams.thetaRMin = 0.; diff --git a/examples/ionBeamEtching/ionBeamEtching.py b/examples/ionBeamEtching/ionBeamEtching.py index 6f078590..60e5685c 100644 --- a/examples/ionBeamEtching/ionBeamEtching.py +++ b/examples/ionBeamEtching/ionBeamEtching.py @@ -35,7 +35,7 @@ # copy top layer to capture deposition geometry.duplicateTopLevelSet(ps.Material.Polymer) -ibeParams = ps.IBEParameters() +ibeParams = ps.IonBeamEtching.defaultParamters() ibeParams.tiltAngle = params["angle"] ibeParams.exponent = params["exponent"] ibeParams.thetaRMin = 0.0 diff --git a/include/viennaps/materials/psMaterialValueMap.hpp b/include/viennaps/materials/psMaterialValueMap.hpp index d0f151b1..379768d5 100644 --- a/include/viennaps/materials/psMaterialValueMap.hpp +++ b/include/viennaps/materials/psMaterialValueMap.hpp @@ -149,7 +149,9 @@ template class MaterialValueMap { Iterator end() const { return Iterator(this, values_.size()); } - auto getCustomMaterialValues() const { return customValues_; } + const auto &getCustomMaterialValues() const { return customValues_; } + + auto &customMaterialRates() { return customValues_; } private: static constexpr std::size_t toIndex(BuiltInMaterial m) { diff --git a/include/viennaps/models/psFaradayCageEtching.hpp b/include/viennaps/models/psFaradayCageEtching.hpp index 8bcf8bf3..2dff5220 100644 --- a/include/viennaps/models/psFaradayCageEtching.hpp +++ b/include/viennaps/models/psFaradayCageEtching.hpp @@ -181,9 +181,8 @@ template class FaradayCageEtching final : public ProcessModelGPU { public: // Angles in degrees - FaradayCageEtching(const FaradayCageParameters ¶ms, - const std::vector &maskMaterials) - : params_(params), maskMaterials_(maskMaterials) { + FaradayCageEtching(const FaradayCageParameters ¶ms) + : params_(params) { auto [direction1, direction2] = ::viennaps::impl::getFaradayCageSourceDirections( @@ -260,13 +259,21 @@ class FaradayCageEtching final : public ProcessModelGPU { // adjust rate here since we trace two particles per point auto surfaceModelParams = params_.ibeParams; surfaceModelParams.planeWaferRate *= 0.5; - for (auto &pair : surfaceModelParams.materialPlaneWaferRate) { - pair.second *= 0.5; + surfaceModelParams.materialPlaneWaferRate.setDefault( + surfaceModelParams.planeWaferRate); + for (auto entry : surfaceModelParams.materialPlaneWaferRate) { + surfaceModelParams.materialPlaneWaferRate.set(entry.getMaterial(), + entry.getValue() * 0.5); + } + auto &customMaterialRates = + surfaceModelParams.materialPlaneWaferRate.customMaterialRates(); + for (auto &mat : customMaterialRates) { + mat.second *= 0.5; } surfaceModelParams.redepositionRate *= 0.5; auto surfModel = SmartPointer<::viennaps::impl::IBESurfaceModel>::New( - surfaceModelParams, maskMaterials_); + surfaceModelParams); // velocity field auto velField = SmartPointer>::New(); @@ -282,7 +289,6 @@ class FaradayCageEtching final : public ProcessModelGPU { private: FaradayCageParameters params_; - std::vector maskMaterials_; }; } // namespace gpu #endif @@ -292,7 +298,15 @@ class FaradayCageEtching : public ProcessModelCPU { public: FaradayCageEtching(const FaradayCageParameters ¶ms, const std::vector &maskMaterials) - : maskMaterials_(maskMaterials), params_(params) { + : params_(params) { + + // material-specific plane wafer rates and mask + params_.ibeParams.materialPlaneWaferRate.setDefault( + params_.ibeParams.planeWaferRate); + for (const auto &mask : maskMaterials) { + params_.ibeParams.materialPlaneWaferRate.set(mask, 0.); + } + // particles auto particle = std::make_unique>( @@ -300,7 +314,7 @@ class FaradayCageEtching : public ProcessModelCPU { // surface model auto surfModel = SmartPointer>::New( - params_.ibeParams, maskMaterials_); + params_.ibeParams); // velocity field auto velField = SmartPointer>::New(); @@ -351,8 +365,8 @@ class FaradayCageEtching : public ProcessModelCPU { #ifdef VIENNACORE_COMPILE_GPU SmartPointer> getGPUModel() final { - auto model = SmartPointer>::New( - params_, maskMaterials_); + auto model = + SmartPointer>::New(params_); model->setProcessName(this->getProcessName().value()); return model; } @@ -360,7 +374,6 @@ class FaradayCageEtching : public ProcessModelCPU { private: bool firstInit = false; - std::vector maskMaterials_; FaradayCageParameters params_; }; diff --git a/include/viennaps/models/psIonBeamEtching.hpp b/include/viennaps/models/psIonBeamEtching.hpp index 33f94252..f42ab96d 100644 --- a/include/viennaps/models/psIonBeamEtching.hpp +++ b/include/viennaps/models/psIonBeamEtching.hpp @@ -392,6 +392,8 @@ class IonBeamEtching : public ProcessModelCPU { this->hasGPU = true; } + static auto defaultParameters() { return IBEParameters(); } + #ifdef VIENNACORE_COMPILE_GPU SmartPointer> getGPUModel() final { auto model = diff --git a/python/pyWrapDimension.hpp b/python/pyWrapDimension.hpp index 8f621cd5..f8e15b37 100644 --- a/python/pyWrapDimension.hpp +++ b/python/pyWrapDimension.hpp @@ -907,9 +907,14 @@ template void bindApi(py::module &module) { // Ion Beam Etching py::class_, SmartPointer>>( module, "IonBeamEtching", processModel) + .def(py::init(&SmartPointer>::template New< + const IBEParameters &>), + py::arg("parameters")) .def(py::init(&SmartPointer>::template New< const IBEParameters &, const std::vector &>), - py::arg("parameters"), py::arg("maskMaterials")); + py::arg("parameters"), py::arg("maskMaterials")) + .def_static("defaultParameters", + &IonBeamEtching::defaultParameters); // Faraday Cage Etching py::class_, SmartPointer>>( @@ -1571,16 +1576,15 @@ template void bindApi(py::module &module) { SmartPointer>>(m_gpu, "IonBeamEtching", processModel) .def(py::init(&SmartPointer>::template New< - const IBEParameters &, const std::vector &>), - py::arg("parameters"), py::arg("maskMaterials")); + const IBEParameters &>), + py::arg("parameters")); // Faraday Cage Etching py::class_, SmartPointer>>( m_gpu, "FaradayCageEtching", processModel) .def(py::init(&SmartPointer>::template New< - const FaradayCageParameters &, - const std::vector &>), - py::arg("parameters"), py::arg("maskMaterials")); + const FaradayCageParameters &>), + py::arg("parameters")); #endif } diff --git a/python/viennaps/_core/__init__.pyi b/python/viennaps/_core/__init__.pyi index de1efc65..eb9e44c0 100644 --- a/python/viennaps/_core/__init__.pyi +++ b/python/viennaps/_core/__init__.pyi @@ -699,6 +699,7 @@ class HoleShape(enum.IntEnum): """ class IBEParameters: cos4Yield: IBEParametersCos4Yield + materialPlaneWaferRate: MaterialValueMap def __init__(self) -> None: ... def toProcessMetaData(self) -> dict[str, list[float]]: @@ -718,12 +719,6 @@ class IBEParameters: def inflectAngle(self, arg0: typing.SupportsFloat | typing.SupportsIndex) -> None: ... @property - def materialPlaneWaferRate(self) -> dict[Material, float]: - ... - @materialPlaneWaferRate.setter - def materialPlaneWaferRate(self, arg0: collections.abc.Mapping[Material, typing.SupportsFloat | typing.SupportsIndex]) -> None: - ... - @property def meanEnergy(self) -> float: ... @meanEnergy.setter @@ -1077,7 +1072,7 @@ class MaterialKind(enum.IntEnum): """ class MaterialMap: @staticmethod - def fromString(name: str, registry: MaterialRegistry) -> Material: + def fromString(name: str) -> Material: """ Resolve built-in or register custom material by name. """ @@ -1112,7 +1107,8 @@ class MaterialMap: def size(self) -> int: ... class MaterialRegistry: - def __init__(self) -> None: + @staticmethod + def instance() -> MaterialRegistry: ... def customMaterialCount(self) -> int: ... diff --git a/python/viennaps/d2/__init__.pyi b/python/viennaps/d2/__init__.pyi index dad9f692..0177d786 100644 --- a/python/viennaps/d2/__init__.pyi +++ b/python/viennaps/d2/__init__.pyi @@ -265,10 +265,16 @@ class Domain: """ Disable adding meta data to domain. """ + @typing.overload def duplicateTopLevelSet(self, arg0: viennaps._core.Material) -> None: """ Duplicate the top level set. Should be used before a deposition process. """ + @typing.overload + def duplicateTopLevelSet(self, arg0: str) -> None: + """ + Duplicate the top level set. Should be used before a deposition process. + """ def enableMetaData(self, level: viennaps._core.MetaDataLevel = ...) -> None: """ Enable adding meta data from processes to domain. @@ -305,20 +311,8 @@ class Domain: """ def getLevelSets(self) -> list[viennals.d2.Domain]: ... - def getMaterialByName(self, name: str) -> viennaps._core.Material: - """ - Resolve material by name in this domain. - """ def getMaterialMap(self) -> viennaps._core.MaterialMap: ... - def getMaterialName(self, material: viennaps._core.Material) -> str: - """ - Resolve canonical material name in this domain. - """ - def getMaterialRegistry(self) -> viennaps._core.MaterialRegistry: - """ - Get the domain-local material registry. - """ def getMaterialsInDomain(self) -> set[viennaps._core.Material]: """ Get the material IDs present in the domain. @@ -347,18 +341,20 @@ class Domain: """ Get the surface level set. """ - def getSurfaceMesh(self, addInterfaces: bool = False, sharpCorners: bool = False, minNodeDistanceFactor: typing.SupportsFloat | typing.SupportsIndex = 0.01) -> viennals._core.Mesh: + def getSurfaceMesh(self, addInterfaces: bool = True, sharpCorners: bool = False, minNodeDistanceFactor: typing.SupportsFloat | typing.SupportsIndex = 0.01) -> viennals._core.Mesh: """ Get the surface mesh of the domain """ - def hasMaterial(self, name: str) -> bool: - """ - Check whether material name resolves in this domain. - """ def insertMask(self, mask: viennals.d2.Domain, material: viennaps._core.Material = ...) -> None: """ Insert a mask level set to the domain. The mask is inserted at the front of the level set vector and can be used to exclude areas from processes. """ + @typing.overload + def insertNextLevelSetAsMaterial(self, levelSet: viennals.d2.Domain, material: str, wrapLowerLevelSet: bool = True) -> None: + """ + Insert a level set to domain as a material. + """ + @typing.overload def insertNextLevelSetAsMaterial(self, levelSet: viennals.d2.Domain, material: viennaps._core.Material, wrapLowerLevelSet: bool = True) -> None: """ Insert a level set to domain as a material. @@ -367,10 +363,6 @@ class Domain: """ Print the domain information. """ - def registerMaterial(self, name: str) -> viennaps._core.Material: - """ - Register a material by name in this domain and return handle. - """ def removeLevelSet(self, arg0: typing.SupportsInt | typing.SupportsIndex, arg1: bool) -> None: ... def removeMaterial(self, arg0: viennaps._core.Material) -> None: @@ -389,7 +381,7 @@ class Domain: """ def saveLevelSets(self, filename: str) -> None: ... - def saveSurfaceMesh(self, filename: str, addInterfaces: bool = False, sharpCorners: bool = False, minNodeDistanceFactor: typing.SupportsFloat | typing.SupportsIndex = 0.01) -> None: + def saveSurfaceMesh(self, filename: str, addInterfaces: bool = True, sharpCorners: bool = False, minNodeDistanceFactor: typing.SupportsFloat | typing.SupportsIndex = 0.01) -> None: """ Save the surface of the domain. """ @@ -558,6 +550,13 @@ class Interpolation(enum.IntEnum): Convert to a string according to format_spec. """ class IonBeamEtching(ProcessModel): + @staticmethod + def defaultParameters() -> viennaps._core.IBEParameters: + ... + @typing.overload + def __init__(self, parameters: viennaps._core.IBEParameters) -> None: + ... + @typing.overload def __init__(self, parameters: viennaps._core.IBEParameters, maskMaterials: collections.abc.Sequence[viennaps._core.Material]) -> None: ... class IsotropicProcess(ProcessModel): diff --git a/python/viennaps/d2/gpu.pyi b/python/viennaps/d2/gpu.pyi index 2291e866..8c09ae89 100644 --- a/python/viennaps/d2/gpu.pyi +++ b/python/viennaps/d2/gpu.pyi @@ -8,13 +8,13 @@ import viennaps._core import viennaps.d2 __all__: list[str] = ['FaradayCageEtching', 'HBrO2Etching', 'IonBeamEtching', 'MultiParticleProcess', 'ProcessModelGPU', 'SF6O2Etching', 'SingleParticleProcess'] class FaradayCageEtching(viennaps.d2.ProcessModel): - def __init__(self, parameters: viennaps._core.FaradayCageParameters, maskMaterials: collections.abc.Sequence[viennaps._core.Material]) -> None: + def __init__(self, parameters: viennaps._core.FaradayCageParameters) -> None: ... class HBrO2Etching(ProcessModelGPU): def __init__(self, parameters: viennaps._core.PlasmaEtchingParameters) -> None: ... class IonBeamEtching(viennaps.d2.ProcessModel): - def __init__(self, parameters: viennaps._core.IBEParameters, maskMaterials: collections.abc.Sequence[viennaps._core.Material]) -> None: + def __init__(self, parameters: viennaps._core.IBEParameters) -> None: ... class MultiParticleProcess(ProcessModelGPU): def __init__(self) -> None: diff --git a/python/viennaps/d3/__init__.pyi b/python/viennaps/d3/__init__.pyi index 450c2190..476bfe78 100644 --- a/python/viennaps/d3/__init__.pyi +++ b/python/viennaps/d3/__init__.pyi @@ -265,10 +265,16 @@ class Domain: """ Disable adding meta data to domain. """ + @typing.overload def duplicateTopLevelSet(self, arg0: viennaps._core.Material) -> None: """ Duplicate the top level set. Should be used before a deposition process. """ + @typing.overload + def duplicateTopLevelSet(self, arg0: str) -> None: + """ + Duplicate the top level set. Should be used before a deposition process. + """ def enableMetaData(self, level: viennaps._core.MetaDataLevel = ...) -> None: """ Enable adding meta data from processes to domain. @@ -305,20 +311,8 @@ class Domain: """ def getLevelSets(self) -> list[viennals.d3.Domain]: ... - def getMaterialByName(self, name: str) -> viennaps._core.Material: - """ - Resolve material by name in this domain. - """ def getMaterialMap(self) -> viennaps._core.MaterialMap: ... - def getMaterialName(self, material: viennaps._core.Material) -> str: - """ - Resolve canonical material name in this domain. - """ - def getMaterialRegistry(self) -> viennaps._core.MaterialRegistry: - """ - Get the domain-local material registry. - """ def getMaterialsInDomain(self) -> set[viennaps._core.Material]: """ Get the material IDs present in the domain. @@ -347,18 +341,20 @@ class Domain: """ Get the surface level set. """ - def getSurfaceMesh(self, addInterfaces: bool = False, sharpCorners: bool = False, minNodeDistanceFactor: typing.SupportsFloat | typing.SupportsIndex = 0.01) -> viennals._core.Mesh: + def getSurfaceMesh(self, addInterfaces: bool = True, sharpCorners: bool = False, minNodeDistanceFactor: typing.SupportsFloat | typing.SupportsIndex = 0.01) -> viennals._core.Mesh: """ Get the surface mesh of the domain """ - def hasMaterial(self, name: str) -> bool: - """ - Check whether material name resolves in this domain. - """ def insertMask(self, mask: viennals.d3.Domain, material: viennaps._core.Material = ...) -> None: """ Insert a mask level set to the domain. The mask is inserted at the front of the level set vector and can be used to exclude areas from processes. """ + @typing.overload + def insertNextLevelSetAsMaterial(self, levelSet: viennals.d3.Domain, material: str, wrapLowerLevelSet: bool = True) -> None: + """ + Insert a level set to domain as a material. + """ + @typing.overload def insertNextLevelSetAsMaterial(self, levelSet: viennals.d3.Domain, material: viennaps._core.Material, wrapLowerLevelSet: bool = True) -> None: """ Insert a level set to domain as a material. @@ -367,10 +363,6 @@ class Domain: """ Print the domain information. """ - def registerMaterial(self, name: str) -> viennaps._core.Material: - """ - Register a material by name in this domain and return handle. - """ def removeLevelSet(self, arg0: typing.SupportsInt | typing.SupportsIndex, arg1: bool) -> None: ... def removeMaterial(self, arg0: viennaps._core.Material) -> None: @@ -389,7 +381,7 @@ class Domain: """ def saveLevelSets(self, filename: str) -> None: ... - def saveSurfaceMesh(self, filename: str, addInterfaces: bool = False, sharpCorners: bool = False, minNodeDistanceFactor: typing.SupportsFloat | typing.SupportsIndex = 0.01) -> None: + def saveSurfaceMesh(self, filename: str, addInterfaces: bool = True, sharpCorners: bool = False, minNodeDistanceFactor: typing.SupportsFloat | typing.SupportsIndex = 0.01) -> None: """ Save the surface of the domain. """ @@ -558,6 +550,13 @@ class Interpolation(enum.IntEnum): Convert to a string according to format_spec. """ class IonBeamEtching(ProcessModel): + @staticmethod + def defaultParameters() -> viennaps._core.IBEParameters: + ... + @typing.overload + def __init__(self, parameters: viennaps._core.IBEParameters) -> None: + ... + @typing.overload def __init__(self, parameters: viennaps._core.IBEParameters, maskMaterials: collections.abc.Sequence[viennaps._core.Material]) -> None: ... class IsotropicProcess(ProcessModel): diff --git a/python/viennaps/d3/gpu.pyi b/python/viennaps/d3/gpu.pyi index 8186265f..59ff81af 100644 --- a/python/viennaps/d3/gpu.pyi +++ b/python/viennaps/d3/gpu.pyi @@ -8,13 +8,13 @@ import viennaps._core import viennaps.d3 __all__: list[str] = ['FaradayCageEtching', 'HBrO2Etching', 'IonBeamEtching', 'MultiParticleProcess', 'ProcessModelGPU', 'SF6O2Etching', 'SingleParticleProcess'] class FaradayCageEtching(viennaps.d3.ProcessModel): - def __init__(self, parameters: viennaps._core.FaradayCageParameters, maskMaterials: collections.abc.Sequence[viennaps._core.Material]) -> None: + def __init__(self, parameters: viennaps._core.FaradayCageParameters) -> None: ... class HBrO2Etching(ProcessModelGPU): def __init__(self, parameters: viennaps._core.PlasmaEtchingParameters) -> None: ... class IonBeamEtching(viennaps.d3.ProcessModel): - def __init__(self, parameters: viennaps._core.IBEParameters, maskMaterials: collections.abc.Sequence[viennaps._core.Material]) -> None: + def __init__(self, parameters: viennaps._core.IBEParameters) -> None: ... class MultiParticleProcess(ProcessModelGPU): def __init__(self) -> None: From 35eb16a3d7b5433578942e0c7f12f5aafcb8d7c8 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Tue, 31 Mar 2026 09:09:55 +0200 Subject: [PATCH 16/32] small map refactor --- .../viennaps/materials/psMaterialValueMap.hpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/include/viennaps/materials/psMaterialValueMap.hpp b/include/viennaps/materials/psMaterialValueMap.hpp index 379768d5..2aed0af1 100644 --- a/include/viennaps/materials/psMaterialValueMap.hpp +++ b/include/viennaps/materials/psMaterialValueMap.hpp @@ -22,9 +22,9 @@ template class MaterialValueMap { auto idx = toIndex(material.builtIn()); values_[idx] = value; isSet_[idx] = true; - return; + } else { + customValues_[material.customId()] = value; } - customValues_[material.customId()] = value; } // perfect-forwarding overload @@ -42,7 +42,7 @@ template class MaterialValueMap { [[nodiscard]] T get(Material material) const { if (material.isBuiltIn()) { auto idx = toIndex(material.builtIn()); - return isSet_[idx] ? values_[idx] : default_; + return getBuiltInValue(idx); } const auto it = customValues_.find(material.customId()); @@ -51,14 +51,14 @@ template class MaterialValueMap { [[nodiscard]] T get(BuiltInMaterial m) const { auto idx = toIndex(m); - return isSet_[idx] ? values_[idx] : default_; + return getBuiltInValue(idx); } // get reference (no copy) [[nodiscard]] const T &getRef(Material material) const { if (material.isBuiltIn()) { auto idx = toIndex(material.builtIn()); - return isSet_[idx] ? values_[idx] : default_; + return getBuiltInValue(idx); } const auto it = customValues_.find(material.customId()); @@ -67,7 +67,7 @@ template class MaterialValueMap { [[nodiscard]] const T &getRef(BuiltInMaterial m) const { auto idx = toIndex(m); - return isSet_[idx] ? values_[idx] : default_; + return getBuiltInValue(idx); } void setDefault(const T &v) { default_ = v; } @@ -158,6 +158,10 @@ template class MaterialValueMap { return static_cast(m); } + const T &getBuiltInValue(std::size_t idx) const { + return isSet_[idx] ? values_[idx] : default_; + } + std::array values_{}; std::array isSet_{}; std::unordered_map customValues_; From 0d37a97c322c4504cdf21fdb3728bf417cae4b60 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Tue, 31 Mar 2026 12:53:19 +0200 Subject: [PATCH 17/32] fix: bug in mask blur GDS --- include/viennaps/gds/psGDSGeometry.hpp | 121 ++++++++++++++----------- 1 file changed, 69 insertions(+), 52 deletions(-) diff --git a/include/viennaps/gds/psGDSGeometry.hpp b/include/viennaps/gds/psGDSGeometry.hpp index 272b276a..e879b86c 100644 --- a/include/viennaps/gds/psGDSGeometry.hpp +++ b/include/viennaps/gds/psGDSGeometry.hpp @@ -146,17 +146,16 @@ template class GDSGeometry { weights); proximity.apply(); - // exposureGrid is the final blurred grid to be used for SDF calculation - // auto exposureGrid = proximity.getExposedGrid(); - if (Logger::hasDebug()) - proximity.saveGridToCSV("finalGrid.csv"); - PointType pointData; - constexpr std::array, 4> directions = {{ - {-1, 0}, {1, 0}, {0, -1}, {0, 1} // 4-neighbor stencil + PointType pointDataAll; + constexpr std::array, 8> directions = {{ + {-1, 0}, {1, 0}, {0, -1}, {0, 1}, {-1, -1}, {-1, 1}, {1, 1}, {1, -1} + // 8-neighbor stencil }}; // Calculate grid bounds + const double xOffset = boundaryPadding[0] - minBounds[0]; + const double yOffset = boundaryPadding[1] - minBounds[1]; const int xStart = std::floor((minBounds[0] - boundaryPadding[0]) / gridDelta_); const int xEnd = @@ -169,77 +168,82 @@ template class GDSGeometry { for (int y = yStart; y <= yEnd; ++y) { for (int x = xStart; x <= xEnd; ++x) { - double xReal = x * gridDelta_ + (boundaryPadding[0] - minBounds[0]); - double yReal = y * gridDelta_ + (boundaryPadding[1] - minBounds[1]); + const double xReal = x * gridDelta_ + xOffset; + const double yReal = y * gridDelta_ + yOffset; + const double current = proximity.exposureAt(xReal, yReal); + + IndexType pos(x, y); + if (Logger::hasDebug()) + pointDataAll.emplace_back(pos, current); - double current = proximity.exposureAt(xReal, yReal); // Check if current is on the contour if (std::abs(current - threshold) < thresholdEps) { - IndexType pos; - pos[0] = x; - pos[1] = y; pointData.emplace_back(pos, 0.); - break; + continue; } double minDist = std::numeric_limits::max(); - int bestNx = -1, bestNy = -1; - bool found = false; - for (auto [dy, dx] : directions) { int nx = x + dx; int ny = y + dy; - double nxReal = nx * gridDelta_ + (boundaryPadding[0] - minBounds[0]); - double nyReal = ny * gridDelta_ + (boundaryPadding[1] - minBounds[1]); + double nxReal = nx * gridDelta_ + xOffset; + double nyReal = ny * gridDelta_ + yOffset; double neighbor = proximity.exposureAt(nxReal, nyReal); - // Check if neighbor is on the contour - // If so, skip checks and add neighbor when it becomes "current" - if (std::abs(neighbor - threshold) < thresholdEps) - break; - // Check if neighbor is on opposite side of the contour if ((current - threshold) * (neighbor - threshold) < 0) { // Interpolate sub-cell distance double dist = std::abs((threshold - current) / (neighbor - current)); - if (dist < minDist) { - minDist = dist; - bestNx = nx; - bestNy = ny; - found = true; - } + minDist = std::min(minDist, dist); } } - if ((minDist < 1.0) && found) { - double sdfCurrent = minDist; // * gridDelta_; - IndexType pos; - pos[0] = x; - pos[1] = y; + + if (minDist < 1.0) { double sign = (current < threshold) ? 1.0 : -1.0; - pointData.emplace_back(pos, sign * sdfCurrent); + pointData.emplace_back(pos, sign * minDist); } } } - auto mesh = viennals::Mesh::New(); - mesh->nodes.reserve(pointData.size()); - mesh->getCellData().insertNextScalarData( - std::vector(pointData.size(), 0.0), "SDF"); - auto data = mesh->getCellData().getScalarData(0); - int ii = 0; - for (const auto &p : pointData) { - - mesh->nodes.push_back(Vec3D{p.first[0] * gridDelta_, - p.first[1] * gridDelta_, 0}); - - mesh->insertNextVertex( - {static_cast(mesh->nodes.size() - 1)}); - data->at(ii) = p.second; - ii++; + if (Logger::hasDebug()) { + auto mesh = viennals::Mesh::New(); + mesh->nodes.reserve(pointData.size()); + mesh->getCellData().insertNextScalarData( + std::vector(pointData.size(), 0.0), "SDF"); + auto data = mesh->getCellData().getScalarData(0); + int ii = 0; + for (const auto &p : pointData) { + mesh->nodes.push_back(Vec3D{p.first[0] * gridDelta_, + p.first[1] * gridDelta_, 0}); + + mesh->insertNextVertex( + {static_cast(mesh->nodes.size() - 1)}); + data->at(ii) = p.second; + ii++; + } + + viennals::VTKWriter(mesh, "blurredLSGrid.vtp").apply(); + + mesh->clear(); + mesh->nodes.reserve(pointDataAll.size()); + mesh->getCellData().insertNextScalarData( + std::vector(pointDataAll.size(), 0.0), "Exposure"); + data = mesh->getCellData().getScalarData(0); + ii = 0; + for (const auto &p : pointDataAll) { + mesh->nodes.push_back(Vec3D{p.first[0] * gridDelta_, + p.first[1] * gridDelta_, 0}); + + mesh->insertNextVertex( + {static_cast(mesh->nodes.size() - 1)}); + data->at(ii) = p.second; + ii++; + } + + viennals::VTKWriter(mesh, "exposureGrid.vtp").apply(); } - viennals::VTKWriter(mesh, "blurredLSGrid.vtp").apply(); return pointData; } @@ -630,6 +634,14 @@ template class GDSGeometry { } if (blurring) { + if (Logger::hasDebug()) { + auto mesh = viennals::Mesh::New(); + viennals::ToMesh(GDSLevelSet, mesh).apply(); + viennals::VTKWriter( + mesh, "GDS_layer_no_blur_" + std::to_string(layer) + ".vtp") + .apply(); + } + lsDomainType2D maskLS = lsDomainType2D::New(bounds_, boundaryConds_.data(), gridDelta_); PointType pointData = applyBlur(GDSLevelSet); @@ -642,6 +654,11 @@ template class GDSGeometry { viennals::VTKWriter( mesh, "GDS_layer_blurred_" + std::to_string(layer) + ".vtp") .apply(); + + viennals::ToSurfaceMesh(maskLS, mesh).apply(); + viennals::VTKWriter( + mesh, "GDS_layer_blurred_contour_" + std::to_string(layer) + ".vtp") + .apply(); } // viennals::Expand(maskLS, 2).apply(); return maskLS; From 0b77abc4579830f51d0708d5c989bc79f65439cc Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Tue, 31 Mar 2026 16:10:25 +0200 Subject: [PATCH 18/32] feat: parallel blur loop --- include/viennaps/gds/psGDSGeometry.hpp | 109 +++++++++++++++++-------- 1 file changed, 73 insertions(+), 36 deletions(-) diff --git a/include/viennaps/gds/psGDSGeometry.hpp b/include/viennaps/gds/psGDSGeometry.hpp index e879b86c..af1125e4 100644 --- a/include/viennaps/gds/psGDSGeometry.hpp +++ b/include/viennaps/gds/psGDSGeometry.hpp @@ -146,8 +146,6 @@ template class GDSGeometry { weights); proximity.apply(); - PointType pointData; - PointType pointDataAll; constexpr std::array, 8> directions = {{ {-1, 0}, {1, 0}, {0, -1}, {0, 1}, {-1, -1}, {-1, 1}, {1, 1}, {1, -1} // 8-neighbor stencil @@ -165,46 +163,85 @@ template class GDSGeometry { const int yEnd = std::ceil((maxBounds[1] + boundaryPadding[1]) / gridDelta_); - for (int y = yStart; y <= yEnd; ++y) { - for (int x = xStart; x <= xEnd; ++x) { - - const double xReal = x * gridDelta_ + xOffset; - const double yReal = y * gridDelta_ + yOffset; - const double current = proximity.exposureAt(xReal, yReal); - - IndexType pos(x, y); - if (Logger::hasDebug()) - pointDataAll.emplace_back(pos, current); + PointType pointDataAll; + int width = xEnd - xStart + 1; + if (Logger::hasDebug()) + pointDataAll.resize((yEnd - yStart + 1) * width); + +#ifdef _OPENMP + const int numThreads = omp_get_max_threads(); +#else + const int numThreads = 1; +#endif + + std::vector threadPointData(numThreads); + +#pragma omp parallel + { +#ifdef _OPENMP + const int threadId = omp_get_thread_num(); +#else + const int threadId = 0; +#endif + + auto &pointData = threadPointData[threadId]; + +#pragma omp for collapse(2) + for (int y = yStart; y <= yEnd; ++y) { + for (int x = xStart; x <= xEnd; ++x) { + + const double xReal = x * gridDelta_ + xOffset; + const double yReal = y * gridDelta_ + yOffset; + const double current = proximity.exposureAt(xReal, yReal); + + IndexType pos(x, y); + if (Logger::hasDebug()) { + pointDataAll[(y - yStart) * width + (x - xStart)] = + std::make_pair(pos, current); + } - // Check if current is on the contour - if (std::abs(current - threshold) < thresholdEps) { - pointData.emplace_back(pos, 0.); - continue; - } + // Check if current is on the contour + if (std::abs(current - threshold) < thresholdEps) { + pointData.emplace_back(pos, 0.); + continue; + } - double minDist = std::numeric_limits::max(); - for (auto [dy, dx] : directions) { - int nx = x + dx; - int ny = y + dy; - double nxReal = nx * gridDelta_ + xOffset; - double nyReal = ny * gridDelta_ + yOffset; - - double neighbor = proximity.exposureAt(nxReal, nyReal); - - // Check if neighbor is on opposite side of the contour - if ((current - threshold) * (neighbor - threshold) < 0) { - // Interpolate sub-cell distance - double dist = - std::abs((threshold - current) / (neighbor - current)); - minDist = std::min(minDist, dist); + double minDist = std::numeric_limits::max(); + for (auto [dy, dx] : directions) { + int nx = x + dx; + int ny = y + dy; + double nxReal = nx * gridDelta_ + xOffset; + double nyReal = ny * gridDelta_ + yOffset; + + double neighbor = proximity.exposureAt(nxReal, nyReal); + + // Check if neighbor is on opposite side of the contour + if ((current - threshold) * (neighbor - threshold) < 0) { + // Interpolate sub-cell distance + double dist = + std::abs((threshold - current) / (neighbor - current)); + minDist = std::min(minDist, dist); + } } - } - if (minDist < 1.0) { - double sign = (current < threshold) ? 1.0 : -1.0; - pointData.emplace_back(pos, sign * minDist); + if (minDist < 1.0) { + double sign = (current < threshold) ? 1.0 : -1.0; + pointData.emplace_back(pos, sign * minDist); + } } } + } // end parallel region + + PointType pointData; + // reduce + size_t totalSize = 0; + for (const auto &vec : threadPointData) { + totalSize += vec.size(); + } + pointData.reserve(totalSize); + for (auto &vec : threadPointData) { + pointData.insert(pointData.end(), std::make_move_iterator(vec.begin()), + std::make_move_iterator(vec.end())); } if (Logger::hasDebug()) { From 8d55eb796814e0c6fdd2b30f26289575904db46a Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Tue, 31 Mar 2026 16:14:28 +0200 Subject: [PATCH 19/32] fix: correct material check function in CSV file process --- include/viennaps/models/psCSVFileProcess.hpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/include/viennaps/models/psCSVFileProcess.hpp b/include/viennaps/models/psCSVFileProcess.hpp index 82c8d44a..790295af 100644 --- a/include/viennaps/models/psCSVFileProcess.hpp +++ b/include/viennaps/models/psCSVFileProcess.hpp @@ -49,7 +49,7 @@ class VelocityFieldFromFile : public VelocityField { int material, const Vec3D &normalVector, unsigned long pointId) override { - if (isMaskMaterial(material)) + if (MaterialMap::isMaterial(material, maskMaterials)) return 0.; if (calculateVisibility && @@ -63,7 +63,7 @@ class VelocityFieldFromFile : public VelocityField { int material, const Vec3D &normalVector, unsigned long pointId) override { - if (isMaskMaterial(material)) + if (MaterialMap::isMaterial(material, maskMaterials)) return {0., 0., 0.}; if (calculateVisibility && @@ -127,13 +127,6 @@ class VelocityFieldFromFile : public VelocityField { NumericType directionalScale; RateGrid rateGrid; - - bool isMaskMaterial(const int material) const { - for (const auto &mask : maskMaterials) - if (MaterialMap::isMaterial(material, mask)) - return true; - return false; - } }; } // namespace impl From dafdb1868203691ca2b8c772ee66dfcf9055717f Mon Sep 17 00:00:00 2001 From: reiter Date: Wed, 1 Apr 2026 11:17:15 +0200 Subject: [PATCH 20/32] refactor: material info --- .../viennaps/materials/psBuiltInMaterial.hpp | 22 +++--- .../viennaps/materials/psMaterialRegistry.hpp | 37 +++++++--- include/viennaps/materials/psMaterials.hpp | 70 ++++++------------- .../models/psPlasmaEtchingParameters.hpp | 4 +- include/viennaps/psUtil.hpp | 3 +- include/viennaps/psVTKRenderWindow.hpp | 2 +- python/pyWrap.cpp | 61 +++++++--------- python/viennaps/_core/__init__.pyi | 39 +++++++---- 8 files changed, 116 insertions(+), 122 deletions(-) diff --git a/include/viennaps/materials/psBuiltInMaterial.hpp b/include/viennaps/materials/psBuiltInMaterial.hpp index f6e7fdc7..637de56b 100644 --- a/include/viennaps/materials/psBuiltInMaterial.hpp +++ b/include/viennaps/materials/psBuiltInMaterial.hpp @@ -20,6 +20,14 @@ enum class MaterialCategory : uint8_t { Misc }; +struct MaterialInfo { + std::string_view name; + MaterialCategory category; + double density_gcm3; + bool conductive; + uint32_t colorHex; +}; + #define BUILTIN_MATERIAL_LIST(X) \ /* id, sym, cat, density_gcm3, conductive, color (hex) */ \ X(0, Mask, Hardmask, 500.0, false, 0x333333) \ @@ -133,19 +141,11 @@ enum class BuiltInMaterial : uint16_t { #undef ENUM_ELEM }; -struct BuiltInMaterialInfo { - std::string_view name; - MaterialCategory category; - double density_gcm3; - bool conductive; - uint32_t colorHex; -}; - inline constexpr uint16_t kBuiltInMaterialMaxId = 176; -constexpr std::array +constexpr std::array kBuiltInMaterialTable = [] { - std::array table{}; + std::array table{}; for (auto &entry : table) { entry = {"Undefined", MaterialCategory::Generic, 0.0, false, 0xcccccc}; } @@ -164,7 +164,7 @@ constexpr std::array return isValidBuiltInMaterialId(static_cast(material)); } -[[nodiscard]] constexpr const BuiltInMaterialInfo & +[[nodiscard]] constexpr const MaterialInfo & getBuiltInMaterialInfo(BuiltInMaterial material) { const auto id = static_cast(material); return kBuiltInMaterialTable[isValidBuiltInMaterialId(id) diff --git a/include/viennaps/materials/psMaterialRegistry.hpp b/include/viennaps/materials/psMaterialRegistry.hpp index 49967a16..09c91be6 100644 --- a/include/viennaps/materials/psMaterialRegistry.hpp +++ b/include/viennaps/materials/psMaterialRegistry.hpp @@ -12,11 +12,6 @@ namespace viennaps { -struct CustomMaterialInfo { - std::string name; - double density = 0.0; -}; - class MaterialRegistry { public: [[nodiscard]] static MaterialRegistry &instance() { @@ -47,7 +42,8 @@ class MaterialRegistry { const auto customId = static_cast(materials_.size()); nameToCustomId_.emplace(name, customId); - materials_.push_back(CustomMaterialInfo{std::move(name)}); + materials_.push_back(MaterialInfo{ + std::move(name), MaterialCategory::Generic, 0.0, false, 0xffffff}); return Material::custom(customId); } @@ -61,6 +57,14 @@ class MaterialRegistry { return nameToCustomId_.find(std::string(name)) != nameToCustomId_.end(); } + [[nodiscard]] bool hasMaterial(Material material) const { + if (material.isBuiltIn()) { + return true; + } + const auto customId = material.customId(); + return customId < materials_.size(); + } + [[nodiscard]] std::optional findMaterial(std::string_view name) const { @@ -102,10 +106,9 @@ class MaterialRegistry { return material.isBuiltIn(); } - [[nodiscard]] const CustomMaterialInfo &getInfo(Material material) const { + [[nodiscard]] const MaterialInfo &getInfo(Material material) const { if (material.isBuiltIn()) { - throw std::invalid_argument( - "Built-in materials do not have registry-owned metadata."); + return getBuiltInMaterialInfo(material.builtIn()); } const auto customId = material.customId(); @@ -116,6 +119,20 @@ class MaterialRegistry { return materials_[customId]; } + void setInfo(Material material, const MaterialInfo &info) { + if (material.isBuiltIn()) { + throw std::invalid_argument( + "Cannot set metadata for built-in materials."); + } + + const auto customId = material.customId(); + if (customId >= materials_.size()) { + throw std::out_of_range("Unknown custom material id."); + } + + materials_[customId] = info; + } + [[nodiscard]] std::size_t customMaterialCount() const { return materials_.size(); } @@ -124,7 +141,7 @@ class MaterialRegistry { MaterialRegistry() = default; std::unordered_map nameToCustomId_; - std::vector materials_; + std::vector materials_; }; } // namespace viennaps diff --git a/include/viennaps/materials/psMaterials.hpp b/include/viennaps/materials/psMaterials.hpp index 69e0acd7..88bb968b 100644 --- a/include/viennaps/materials/psMaterials.hpp +++ b/include/viennaps/materials/psMaterials.hpp @@ -14,28 +14,18 @@ #include #include #include -#include #include #include #include -#include #include namespace viennaps { using namespace viennacore; -struct MaterialInfo { - std::string_view name; - MaterialCategory category; - double density_gcm3; - bool conductive; - uint32_t colorHex; -}; - [[nodiscard]] constexpr MaterialInfo info(const Material material) { if (!material.isBuiltIn()) { - return {"Custom", MaterialCategory::Generic, 0.0, false, 0xffffff}; + return MaterialRegistry::instance().getInfo(material); } const auto &builtIn = getBuiltInMaterialInfo(material.builtIn()); return {builtIn.name, builtIn.category, builtIn.density_gcm3, @@ -44,40 +34,22 @@ struct MaterialInfo { [[nodiscard]] constexpr MaterialCategory categoryOf(const Material material) { return material.isBuiltIn() ? categoryOf(material.builtIn()) - : MaterialCategory::Generic; + : info(material).category; } [[nodiscard]] constexpr double density(const Material material) { - return material.isBuiltIn() ? density(material.builtIn()) : 0.0; + return material.isBuiltIn() ? density(material.builtIn()) + : info(material).density_gcm3; } [[nodiscard]] constexpr bool isConductive(const Material material) { - return material.isBuiltIn() ? isConductive(material.builtIn()) : false; + return material.isBuiltIn() ? isConductive(material.builtIn()) + : info(material).conductive; } [[nodiscard]] constexpr uint32_t color(const Material material) { - return material.isBuiltIn() ? color(material.builtIn()) : 0xffffff; -} - -[[nodiscard]] constexpr std::string_view -to_string_view(const Material material) { - return material.isBuiltIn() ? builtInMaterialToString(material.builtIn()) - : std::string_view("Custom"); -} - -[[nodiscard]] inline std::string to_string(const Material material) { - if (material.isBuiltIn()) { - return std::string(builtInMaterialToString(material.builtIn())); - } - return "Custom#" + std::to_string(material.customId()); -} - -[[nodiscard]] inline std::string to_string(const Material material, - const MaterialRegistry ®istry) { - if (material.isBuiltIn()) { - return std::string(builtInMaterialToString(material.builtIn())); - } - return std::string(registry.getName(material)); + return material.isBuiltIn() ? color(material.builtIn()) + : info(material).colorHex; } /// A class that wraps the viennals MaterialMap class and provides a more user @@ -158,7 +130,6 @@ class MaterialMap { __both__ static inline bool isMaterial(T matId, std::span materials) { const auto material = mapToMaterial(matId); - return std::any_of(materials.begin(), materials.end(), [&](const Material &m) { return material == m; }); } @@ -170,21 +141,24 @@ class MaterialMap { matId, std::span(materials.begin(), materials.size())); } - template static inline bool isHardmask(const T matId) { - const auto material = mapToMaterial(matId); - if (!material.isBuiltIn()) { - return false; - } - return categoryOf(material.builtIn()) == MaterialCategory::Hardmask; + static inline bool isHardmask(const Material material) { + return categoryOf(material) == MaterialCategory::Hardmask; } - static inline std::string toString(const Material matId) { - return to_string(matId); + template static inline bool isHardmask(const T matId) { + const auto material = mapToMaterial(matId); + return isHardmask(material); } - static inline std::string toString(const Material matId, - const MaterialRegistry ®istry) { - return to_string(matId, registry); + static inline std::string toString(const Material material) { + if (material.isBuiltIn()) { + return std::string(builtInMaterialToString(material.builtIn())); + } + auto ®istry = MaterialRegistry::instance(); + if (registry.hasMaterial(material)) { + return std::string(registry.getName(material)); + } + return "Custom#" + std::to_string(material.customId()); } static inline std::string toString(const int matId) { diff --git a/include/viennaps/models/psPlasmaEtchingParameters.hpp b/include/viennaps/models/psPlasmaEtchingParameters.hpp index b3c72eb6..20634f95 100644 --- a/include/viennaps/models/psPlasmaEtchingParameters.hpp +++ b/include/viennaps/models/psPlasmaEtchingParameters.hpp @@ -101,11 +101,11 @@ template struct PlasmaEtchingParameters { processData["Passivation Flux"] = {passivationFlux}; for (auto entry : beta_E) { - processData["Beta_E " + to_string(entry.getMaterial())] = + processData["Beta_E " + MaterialMap::toString(entry.getMaterial())] = std::vector{entry.getValue()}; } for (auto entry : beta_P) { - processData["Beta_P " + to_string(entry.getMaterial())] = + processData["Beta_P " + MaterialMap::toString(entry.getMaterial())] = std::vector{entry.getValue()}; } diff --git a/include/viennaps/psUtil.hpp b/include/viennaps/psUtil.hpp index 51c4a84b..10472f8d 100644 --- a/include/viennaps/psUtil.hpp +++ b/include/viennaps/psUtil.hpp @@ -200,8 +200,7 @@ template [[nodiscard]] std::string toString(const T &value) { else if constexpr (std::is_same_v) return convertTemporalSchemeToString(value); else if constexpr (std::is_same_v) { - std::string mat = viennaps::to_string_view(value); - return mat; + return viennaps::MaterialMap::toString(value); } else if constexpr (std::is_same_v) { return convertFluxEngineTypeToString(value); } else if constexpr (std::is_same_v) diff --git a/include/viennaps/psVTKRenderWindow.hpp b/include/viennaps/psVTKRenderWindow.hpp index 323efef7..c092b509 100644 --- a/include/viennaps/psVTKRenderWindow.hpp +++ b/include/viennaps/psVTKRenderWindow.hpp @@ -284,7 +284,7 @@ template class VTKRenderWindow { auto colorHex = color(materialId); auto [r, g, b] = util::hexToRGBArray(colorHex); lut->SetTableValue(index, r, g, b, 1.0); - auto label = to_string_view(materialId); + auto label = MaterialMap::toString(materialId); int id = static_cast(materialId); lut->SetAnnotation(id, label.data()); ++index; diff --git a/python/pyWrap.cpp b/python/pyWrap.cpp index 830bd81a..869b04f0 100644 --- a/python/pyWrap.cpp +++ b/python/pyWrap.cpp @@ -1,22 +1,5 @@ #include "pyWrapDimension.hpp" -struct MaterialInfoPy { - MaterialInfoPy(Material m) - : name(info(m).name), category(info(m).category), - density_gcm3(info(m).density_gcm3), conductive(info(m).conductive), - colorHex(info(m).colorHex) {} - MaterialInfoPy(MaterialInfo info) - : name(info.name), category(info.category), - density_gcm3(info.density_gcm3), conductive(info.conductive), - colorHex(info.colorHex) {} - - std::string name; - MaterialCategory category; - double density_gcm3; - bool conductive; - uint32_t colorHex; -}; - PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { module.doc() = "ViennaPS is a topography simulation library for microelectronic " @@ -101,6 +84,21 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { .value("Custom", Material::Kind::Custom) .finalize(); + // MaterialInfo + py::class_(module, "MaterialInfo") + .def_readwrite("name", &MaterialInfo::name) + .def_readwrite("category", &MaterialInfo::category) + .def_readwrite("density_gcm3", &MaterialInfo::density_gcm3) + .def_readwrite("conductive", &MaterialInfo::conductive) + .def_readwrite("color_hex", &MaterialInfo::colorHex) + // convenience: "#RRGGBB" + .def_property_readonly("color_rgb", [](const MaterialInfo &x) { + char buf[8]; + std::snprintf(buf, sizeof(buf), "#%06x", + (unsigned)(x.colorHex & 0xFFFFFFu)); + return std::string(buf); + }); + py::class_(module, "MaterialRegistry") .def_static( "instance", @@ -108,7 +106,10 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { py::return_value_policy::reference) .def("registerMaterial", &MaterialRegistry::registerMaterial, py::arg("name")) - .def("hasMaterial", &MaterialRegistry::hasMaterial, py::arg("name")) + .def("hasMaterial", (bool(MaterialRegistry::*)(std::string_view) const) & + MaterialRegistry::hasMaterial) + .def("hasMaterial", (bool(MaterialRegistry::*)(Material) const) & + MaterialRegistry::hasMaterial) .def("findMaterial", &MaterialRegistry::findMaterial, py::arg("name")) .def("getMaterial", &MaterialRegistry::getMaterial, py::arg("name")) .def( @@ -118,6 +119,8 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { }, py::arg("material")) .def("isBuiltIn", &MaterialRegistry::isBuiltIn, py::arg("material")) + .def("getInfo", &MaterialRegistry::getInfo) + .def("setInfo", &MaterialRegistry::setInfo) .def("customMaterialCount", &MaterialRegistry::customMaterialCount); // Material category enum @@ -134,22 +137,6 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { .value("Misc", MaterialCategory::Misc) .finalize(); - // MaterialInfo (immutable/read-only) - py::class_(module, "MaterialInfo") - .def(py::init()) - .def_readonly("name", &MaterialInfoPy::name) - .def_readonly("category", &MaterialInfoPy::category) - .def_readonly("density_gcm3", &MaterialInfoPy::density_gcm3) - .def_readonly("conductive", &MaterialInfoPy::conductive) - .def_readonly("color_hex", &MaterialInfoPy::colorHex) - // convenience: "#RRGGBB" - .def_property_readonly("color_rgb", [](const MaterialInfoPy &x) { - char buf[8]; - std::snprintf(buf, sizeof(buf), "#%06x", - (unsigned)(x.colorHex & 0xFFFFFFu)); - return std::string(buf); - }); - // MaterialMap py::class_>(module, "MaterialMap") .def(py::init<>()) @@ -170,6 +157,9 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { "Resolve built-in or register custom material by name.") .def_static("toString", py::overload_cast(&MaterialMap::toString), + "Get the name of a material.") + .def_static("toString", + py::overload_cast(&MaterialMap::toString), "Get the name of a material."); // Meta Data Enum @@ -251,7 +241,8 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { .def("set", &MaterialValueMap::set, py::arg("material"), py::arg("value")) .def("get", - py::overload_cast(&MaterialValueMap::get, py::const_), + (T(MaterialValueMap::*)(Material) const) & + MaterialValueMap::get, py::arg("material")) .def("getDefault", &MaterialValueMap::getDefault) .def("setDefault", &MaterialValueMap::setDefault, py::arg("value")) diff --git a/python/viennaps/_core/__init__.pyi b/python/viennaps/_core/__init__.pyi index eb9e44c0..8e9fbc82 100644 --- a/python/viennaps/_core/__init__.pyi +++ b/python/viennaps/_core/__init__.pyi @@ -6,10 +6,10 @@ import collections.abc import enum import typing import viennals._core -import viennaps.d2 from viennaps import d2 -import viennaps.d3 +import viennaps.d2 from viennaps import d3 +import viennaps.d3 from . import constants from . import gpu from . import util @@ -1040,25 +1040,23 @@ class MaterialCategory(enum.IntEnum): Convert to a string according to format_spec. """ class MaterialInfo: - def __init__(self, arg0: Material) -> None: - ... - @property - def category(self) -> MaterialCategory: - ... + category: ... + conductive: bool + name: str @property def color_hex(self) -> int: ... - @property - def color_rgb(self) -> str: + @color_hex.setter + def color_hex(self, arg0: typing.SupportsInt | typing.SupportsIndex) -> None: ... @property - def conductive(self) -> bool: + def color_rgb(self) -> str: ... @property def density_gcm3(self) -> float: ... - @property - def name(self) -> str: + @density_gcm3.setter + def density_gcm3(self, arg0: typing.SupportsFloat | typing.SupportsIndex) -> None: ... class MaterialKind(enum.IntEnum): BuiltIn: typing.ClassVar[MaterialKind] # value = @@ -1090,10 +1088,17 @@ class MaterialMap: Map a float to a material. """ @staticmethod + @typing.overload def toString(arg0: Material) -> str: """ Get the name of a material. """ + @staticmethod + @typing.overload + def toString(arg0: typing.SupportsInt | typing.SupportsIndex) -> str: + """ + Get the name of a material. + """ def __init__(self) -> None: ... def getMaterialAtIdx(self, arg0: typing.SupportsInt | typing.SupportsIndex) -> Material: @@ -1114,16 +1119,24 @@ class MaterialRegistry: ... def findMaterial(self, name: str) -> viennaps._core.Material | None: ... + def getInfo(self, arg0: Material) -> MaterialInfo: + ... def getMaterial(self, name: str) -> Material: ... def getName(self, material: Material) -> str: ... - def hasMaterial(self, name: str) -> bool: + @typing.overload + def hasMaterial(self, arg0: str) -> bool: + ... + @typing.overload + def hasMaterial(self, arg0: Material) -> bool: ... def isBuiltIn(self, material: Material) -> bool: ... def registerMaterial(self, name: str) -> Material: ... + def setInfo(self, arg0: Material, arg1: MaterialInfo) -> None: + ... class MaterialValueMap: def __init__(self) -> None: ... From f7b10934d035f1bfed9c3ddc849704c5bb4e2fde Mon Sep 17 00:00:00 2001 From: reiter Date: Wed, 1 Apr 2026 14:31:43 +0200 Subject: [PATCH 21/32] feat: fix build --- gpu/benchmark/holeCompare.cpp | 4 ++-- gpu/benchmark/holeCompareGrid.cpp | 4 ++-- include/viennaps/materials/psMaterialValueMap.hpp | 9 +++++++++ tests/fluxEngineBenchmarks/plasmaEtching.cpp | 8 ++++---- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/gpu/benchmark/holeCompare.cpp b/gpu/benchmark/holeCompare.cpp index e2e73e8d..cda9f180 100644 --- a/gpu/benchmark/holeCompare.cpp +++ b/gpu/benchmark/holeCompare.cpp @@ -83,8 +83,8 @@ int main(int argc, char *argv[]) { ibeParams.cos4Yield.a1 = 1.075; ibeParams.cos4Yield.a2 = -1.55; ibeParams.cos4Yield.a3 = 0.65; - ibeParams.materialPlaneWaferRate[Material::Si] = 2.0; - ibeParams.materialPlaneWaferRate[Material::Mask] = 0.15; + ibeParams.materialPlaneWaferRate.set(Material::Si, 2.0); + ibeParams.materialPlaneWaferRate.set(Material::Mask, 0.15); ibeParams.thetaRMin = 60; auto model = SmartPointer>::New(ibeParams); diff --git a/gpu/benchmark/holeCompareGrid.cpp b/gpu/benchmark/holeCompareGrid.cpp index be4052db..1f5cfb5e 100644 --- a/gpu/benchmark/holeCompareGrid.cpp +++ b/gpu/benchmark/holeCompareGrid.cpp @@ -78,8 +78,8 @@ int main(int argc, char *argv[]) { ibeParams.cos4Yield.a1 = 1.075; ibeParams.cos4Yield.a2 = -1.55; ibeParams.cos4Yield.a3 = 0.65; - ibeParams.materialPlaneWaferRate[Material::Si] = 2.0; - ibeParams.materialPlaneWaferRate[Material::Mask] = 0.15; + ibeParams.materialPlaneWaferRate.set(Material::Si, 2.0); + ibeParams.materialPlaneWaferRate.set(Material::Mask, 0.15); ibeParams.thetaRMin = 60; auto model = SmartPointer>::New(ibeParams); diff --git a/include/viennaps/materials/psMaterialValueMap.hpp b/include/viennaps/materials/psMaterialValueMap.hpp index 2aed0af1..2699724d 100644 --- a/include/viennaps/materials/psMaterialValueMap.hpp +++ b/include/viennaps/materials/psMaterialValueMap.hpp @@ -38,6 +38,15 @@ template class MaterialValueMap { set(material, T(std::forward(args)...)); } + T &operator[](Material material) { + if (material.isBuiltIn()) { + auto idx = toIndex(material.builtIn()); + isSet_[idx] = true; + return values_[idx]; + } + return customValues_[material.customId()]; + } + // get value or default T{} [[nodiscard]] T get(Material material) const { if (material.isBuiltIn()) { diff --git a/tests/fluxEngineBenchmarks/plasmaEtching.cpp b/tests/fluxEngineBenchmarks/plasmaEtching.cpp index c1a53496..714a858b 100644 --- a/tests/fluxEngineBenchmarks/plasmaEtching.cpp +++ b/tests/fluxEngineBenchmarks/plasmaEtching.cpp @@ -28,10 +28,10 @@ int main(int argc, char *argv[]) { // use pre-defined model SF6O2 etching model auto modelParams = SF6O2Etching::defaultParameters(); - modelParams.beta_E[static_cast(Material::Si)] = 1.0; - modelParams.beta_E[static_cast(Material::Mask)] = 1.0; - modelParams.beta_P[static_cast(Material::Si)] = 1.0; - modelParams.beta_P[static_cast(Material::Mask)] = 1.0; + modelParams.beta_E[Material::Si] = 1.0; + modelParams.beta_E[Material::Mask] = 1.0; + modelParams.beta_P[Material::Si] = 1.0; + modelParams.beta_P[Material::Mask] = 1.0; modelParams.ionFlux = 1.; modelParams.etchantFlux = 4.5e3; From a841c14fd971bd4475582415776bfe7c0315955d Mon Sep 17 00:00:00 2001 From: reiter Date: Wed, 1 Apr 2026 14:34:52 +0200 Subject: [PATCH 22/32] chore: format --- CMakeLists.txt | 6 +++--- include/viennaps/models/psFluorocarbonEtching.hpp | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fc59935..09af9638 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -181,9 +181,9 @@ if(VIENNAPS_PRECOMPILE_HEADERS) target_compile_definitions(${LIB_NAME} PRIVATE VIENNAPS_USE_PRECOMPILED=1 VIENNACORE_COMPILE_SHARED_LIB=1) target_sources( - ${LIB_NAME} PRIVATE "lib/specMain.cpp" "lib/specGeometries.cpp" "lib/specModelsEmulation.cpp" - "lib/specModelsSimulation.cpp" "lib/specProcess.cpp" - "lib/MaterialRegistry.cpp") + ${LIB_NAME} + PRIVATE "lib/specMain.cpp" "lib/specGeometries.cpp" "lib/specModelsEmulation.cpp" + "lib/specModelsSimulation.cpp" "lib/specProcess.cpp" "lib/MaterialRegistry.cpp") set_target_properties( ${LIB_NAME} diff --git a/include/viennaps/models/psFluorocarbonEtching.hpp b/include/viennaps/models/psFluorocarbonEtching.hpp index 434ce512..9f3a5b73 100644 --- a/include/viennaps/models/psFluorocarbonEtching.hpp +++ b/include/viennaps/models/psFluorocarbonEtching.hpp @@ -7,7 +7,6 @@ #include "../psConstants.hpp" #include "../psUnits.hpp" - #include "psIonModelUtil.hpp" #include From 5e5509824f86047b1a7b98691ba5fdb474d3e1a6 Mon Sep 17 00:00:00 2001 From: reiter Date: Wed, 1 Apr 2026 15:40:17 +0200 Subject: [PATCH 23/32] fix: material info requires string for custom values --- .../viennaps/materials/psBuiltInMaterial.hpp | 15 +++++++++----- .../viennaps/materials/psMaterialRegistry.hpp | 7 +++++-- include/viennaps/materials/psMaterials.hpp | 20 ++++--------------- include/viennaps/psVTKRenderWindow.hpp | 7 ++++--- 4 files changed, 23 insertions(+), 26 deletions(-) diff --git a/include/viennaps/materials/psBuiltInMaterial.hpp b/include/viennaps/materials/psBuiltInMaterial.hpp index 637de56b..3df867f4 100644 --- a/include/viennaps/materials/psBuiltInMaterial.hpp +++ b/include/viennaps/materials/psBuiltInMaterial.hpp @@ -20,14 +20,19 @@ enum class MaterialCategory : uint8_t { Misc }; -struct MaterialInfo { - std::string_view name; +template struct MaterialInfoType { + using StringType = + std::conditional_t; + StringType name; MaterialCategory category; double density_gcm3; bool conductive; uint32_t colorHex; }; +using BuiltInMaterialInfo = MaterialInfoType; +using MaterialInfo = MaterialInfoType; + #define BUILTIN_MATERIAL_LIST(X) \ /* id, sym, cat, density_gcm3, conductive, color (hex) */ \ X(0, Mask, Hardmask, 500.0, false, 0x333333) \ @@ -143,9 +148,9 @@ enum class BuiltInMaterial : uint16_t { inline constexpr uint16_t kBuiltInMaterialMaxId = 176; -constexpr std::array +constexpr std::array kBuiltInMaterialTable = [] { - std::array table{}; + std::array table{}; for (auto &entry : table) { entry = {"Undefined", MaterialCategory::Generic, 0.0, false, 0xcccccc}; } @@ -164,7 +169,7 @@ constexpr std::array return isValidBuiltInMaterialId(static_cast(material)); } -[[nodiscard]] constexpr const MaterialInfo & +[[nodiscard]] constexpr const BuiltInMaterialInfo & getBuiltInMaterialInfo(BuiltInMaterial material) { const auto id = static_cast(material); return kBuiltInMaterialTable[isValidBuiltInMaterialId(id) diff --git a/include/viennaps/materials/psMaterialRegistry.hpp b/include/viennaps/materials/psMaterialRegistry.hpp index 09c91be6..a98aa209 100644 --- a/include/viennaps/materials/psMaterialRegistry.hpp +++ b/include/viennaps/materials/psMaterialRegistry.hpp @@ -40,6 +40,7 @@ class MaterialRegistry { return Material::custom(it->second); } + // register new custom material const auto customId = static_cast(materials_.size()); nameToCustomId_.emplace(name, customId); materials_.push_back(MaterialInfo{ @@ -106,9 +107,11 @@ class MaterialRegistry { return material.isBuiltIn(); } - [[nodiscard]] const MaterialInfo &getInfo(Material material) const { + [[nodiscard]] MaterialInfo getInfo(Material material) const { if (material.isBuiltIn()) { - return getBuiltInMaterialInfo(material.builtIn()); + auto info = getBuiltInMaterialInfo(material.builtIn()); + return MaterialInfo{std::string(info.name), info.category, + info.density_gcm3, info.conductive, info.colorHex}; } const auto customId = material.customId(); diff --git a/include/viennaps/materials/psMaterials.hpp b/include/viennaps/materials/psMaterials.hpp index 88bb968b..f7aed671 100644 --- a/include/viennaps/materials/psMaterials.hpp +++ b/include/viennaps/materials/psMaterials.hpp @@ -23,33 +23,21 @@ namespace viennaps { using namespace viennacore; -[[nodiscard]] constexpr MaterialInfo info(const Material material) { - if (!material.isBuiltIn()) { - return MaterialRegistry::instance().getInfo(material); - } - const auto &builtIn = getBuiltInMaterialInfo(material.builtIn()); - return {builtIn.name, builtIn.category, builtIn.density_gcm3, - builtIn.conductive, builtIn.colorHex}; -} - [[nodiscard]] constexpr MaterialCategory categoryOf(const Material material) { return material.isBuiltIn() ? categoryOf(material.builtIn()) - : info(material).category; + : MaterialCategory::Generic; } [[nodiscard]] constexpr double density(const Material material) { - return material.isBuiltIn() ? density(material.builtIn()) - : info(material).density_gcm3; + return material.isBuiltIn() ? density(material.builtIn()) : 0.0; } [[nodiscard]] constexpr bool isConductive(const Material material) { - return material.isBuiltIn() ? isConductive(material.builtIn()) - : info(material).conductive; + return material.isBuiltIn() ? isConductive(material.builtIn()) : false; } [[nodiscard]] constexpr uint32_t color(const Material material) { - return material.isBuiltIn() ? color(material.builtIn()) - : info(material).colorHex; + return material.isBuiltIn() ? color(material.builtIn()) : 0xffffff; } /// A class that wraps the viennals MaterialMap class and provides a more user diff --git a/include/viennaps/psVTKRenderWindow.hpp b/include/viennaps/psVTKRenderWindow.hpp index c092b509..1e2a04fd 100644 --- a/include/viennaps/psVTKRenderWindow.hpp +++ b/include/viennaps/psVTKRenderWindow.hpp @@ -280,11 +280,12 @@ template class VTKRenderWindow { materialMinId = std::numeric_limits::max(); materialMaxId = std::numeric_limits::min(); int index = 0; + auto ®istry = MaterialRegistry::instance(); for (const auto &materialId : uniqueMaterialIds) { - auto colorHex = color(materialId); - auto [r, g, b] = util::hexToRGBArray(colorHex); + auto info = registry.getInfo(materialId); + auto [r, g, b] = util::hexToRGBArray(info.colorHex); lut->SetTableValue(index, r, g, b, 1.0); - auto label = MaterialMap::toString(materialId); + auto &label = info.name; int id = static_cast(materialId); lut->SetAnnotation(id, label.data()); ++index; From 2a4179f6b4b9359f39c6bd385102a0fe22cff21c Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Wed, 1 Apr 2026 20:32:56 +0200 Subject: [PATCH 24/32] feat: MaterialValueMap iterator includes custom values --- .../viennaps/materials/psMaterialValueMap.hpp | 75 ++++++++++++++----- .../viennaps/models/psFaradayCageEtching.hpp | 9 +-- include/viennaps/models/psHBrO2Etching.hpp | 12 +-- .../viennaps/models/psIonBeamParameters.hpp | 4 +- .../viennaps/models/psIsotropicProcess.hpp | 6 +- .../models/psPlasmaEtchingParameters.hpp | 8 +- include/viennaps/models/psSF6C4F8Etching.hpp | 6 +- include/viennaps/models/psSF6O2Etching.hpp | 12 +-- .../models/psSingleParticleProcess.hpp | 8 +- tests/materialValueMap/CMakeLists.txt | 7 ++ tests/materialValueMap/materialValueMap.cpp | 64 ++++++++++++++++ 11 files changed, 146 insertions(+), 65 deletions(-) create mode 100644 tests/materialValueMap/CMakeLists.txt create mode 100644 tests/materialValueMap/materialValueMap.cpp diff --git a/include/viennaps/materials/psMaterialValueMap.hpp b/include/viennaps/materials/psMaterialValueMap.hpp index 2699724d..864b3647 100644 --- a/include/viennaps/materials/psMaterialValueMap.hpp +++ b/include/viennaps/materials/psMaterialValueMap.hpp @@ -113,55 +113,90 @@ template class MaterialValueMap { // ================= ITERATOR ================= struct Entry { - BuiltInMaterial material; - const T *value; - bool set; - - [[nodiscard]] bool isSet() const { return set; } - [[nodiscard]] const T &getValue() const { return *value; } - [[nodiscard]] BuiltInMaterial getMaterial() const { return material; } + Material material; + const T &value; }; class Iterator { public: - Iterator(const MaterialValueMap *map, std::size_t idx) - : map_(map), idx_(idx) { - advanceToValid(); + using CustomIterator = + typename std::unordered_map::const_iterator; + + Iterator(const MaterialValueMap *map, bool isEnd) + : map_(map), phase_(isEnd ? Phase::End : Phase::BuiltIn), idx_(0) { + if (!isEnd) { + advanceToValid(); + } } Iterator &operator++() { - ++idx_; - advanceToValid(); + if (phase_ == Phase::BuiltIn) { + ++idx_; + advanceToValid(); + } else if (phase_ == Phase::Custom) { + ++customIt_; + if (customIt_ == map_->customValues_.cend()) { + phase_ = Phase::End; + } + } return *this; } - bool operator!=(const Iterator &other) const { return idx_ != other.idx_; } + bool operator!=(const Iterator &other) const { + if (phase_ != other.phase_) { + return true; + } + + switch (phase_) { + case Phase::BuiltIn: + return idx_ != other.idx_; + case Phase::Custom: + return customIt_ != other.customIt_; + case Phase::End: + return false; + } + return false; + } Entry operator*() const { - return Entry{static_cast(idx_), &map_->values_[idx_], - map_->isSet_[idx_]}; + if (phase_ == Phase::BuiltIn) { + return Entry{Material(static_cast(idx_)), + map_->values_[idx_]}; + } + + return Entry{Material::custom(customIt_->first), customIt_->second}; } private: + enum class Phase { BuiltIn, Custom, End }; + void advanceToValid() { while (idx_ < map_->values_.size() && !map_->isSet_[idx_]) { ++idx_; } + + if (idx_ >= map_->values_.size()) { + phase_ = Phase::Custom; + customIt_ = map_->customValues_.cbegin(); + if (customIt_ == map_->customValues_.cend()) { + phase_ = Phase::End; + } + } } const MaterialValueMap *map_; + Phase phase_; std::size_t idx_; + CustomIterator customIt_{}; }; - // iterators over built-in materials with user-provided values - Iterator begin() const { return Iterator(this, 0); } + // iterators over set built-in materials and custom materials + Iterator begin() const { return Iterator(this, false); } - Iterator end() const { return Iterator(this, values_.size()); } + Iterator end() const { return Iterator(this, true); } const auto &getCustomMaterialValues() const { return customValues_; } - auto &customMaterialRates() { return customValues_; } - private: static constexpr std::size_t toIndex(BuiltInMaterial m) { return static_cast(m); diff --git a/include/viennaps/models/psFaradayCageEtching.hpp b/include/viennaps/models/psFaradayCageEtching.hpp index 2dff5220..78cafa1e 100644 --- a/include/viennaps/models/psFaradayCageEtching.hpp +++ b/include/viennaps/models/psFaradayCageEtching.hpp @@ -262,13 +262,8 @@ class FaradayCageEtching final : public ProcessModelGPU { surfaceModelParams.materialPlaneWaferRate.setDefault( surfaceModelParams.planeWaferRate); for (auto entry : surfaceModelParams.materialPlaneWaferRate) { - surfaceModelParams.materialPlaneWaferRate.set(entry.getMaterial(), - entry.getValue() * 0.5); - } - auto &customMaterialRates = - surfaceModelParams.materialPlaneWaferRate.customMaterialRates(); - for (auto &mat : customMaterialRates) { - mat.second *= 0.5; + surfaceModelParams.materialPlaneWaferRate.set(entry.material, + entry.value * 0.5); } surfaceModelParams.redepositionRate *= 0.5; auto surfModel = diff --git a/include/viennaps/models/psHBrO2Etching.hpp b/include/viennaps/models/psHBrO2Etching.hpp index 6e3a4d6a..6ecc2475 100644 --- a/include/viennaps/models/psHBrO2Etching.hpp +++ b/include/viennaps/models/psHBrO2Etching.hpp @@ -44,11 +44,7 @@ class HBrO2Etching final : public ProcessModelGPU { etchant.dataLabels.push_back("etchantFlux"); etchant.cosineExponent = 1.f; for (auto entry : params.beta_E) { - etchant.materialSticking[static_cast(entry.getMaterial())] = - entry.getValue(); - } - for (auto &entry : params.beta_E.getCustomMaterialValues()) { - etchant.materialSticking[entry.first] = entry.second; + etchant.materialSticking[static_cast(entry.material)] = entry.value; } viennaray::gpu::Particle oxygen; @@ -56,11 +52,7 @@ class HBrO2Etching final : public ProcessModelGPU { oxygen.dataLabels.push_back("passivationFlux"); oxygen.cosineExponent = 1.f; for (auto entry : params.beta_P) { - oxygen.materialSticking[static_cast(entry.getMaterial())] = - entry.getValue(); - } - for (auto &entry : params.beta_P.getCustomMaterialValues()) { - oxygen.materialSticking[entry.first] = entry.second; + oxygen.materialSticking[static_cast(entry.material)] = entry.value; } // surface model diff --git a/include/viennaps/models/psIonBeamParameters.hpp b/include/viennaps/models/psIonBeamParameters.hpp index 85e7420c..e21972d7 100644 --- a/include/viennaps/models/psIonBeamParameters.hpp +++ b/include/viennaps/models/psIonBeamParameters.hpp @@ -51,8 +51,8 @@ template struct IBEParameters { processData["Default PWR"] = {planeWaferRate}; for (const auto &entry : materialPlaneWaferRate) { - processData[MaterialMap::toString(entry.getMaterial()) + " PWR"] = { - entry.getValue()}; + processData[MaterialMap::toString(entry.material) + " PWR"] = { + entry.value}; } processData["Mean Energy"] = {meanEnergy}; diff --git a/include/viennaps/models/psIsotropicProcess.hpp b/include/viennaps/models/psIsotropicProcess.hpp index cd77fc90..b750bc1b 100644 --- a/include/viennaps/models/psIsotropicProcess.hpp +++ b/include/viennaps/models/psIsotropicProcess.hpp @@ -73,9 +73,9 @@ class IsotropicProcess : public ProcessModelCPU { // store process data (before moving materialRates) addMetaData("Isotropic Rate", materialRates.getDefault()); - for (const auto &materialRate : materialRates) { - addMetaData("Rate " + MaterialMap::toString(materialRate.getMaterial()), - materialRate.getValue()); + for (auto materialRate : materialRates) { + addMetaData("Rate " + MaterialMap::toString(materialRate.material), + materialRate.value); } // velocity field diff --git a/include/viennaps/models/psPlasmaEtchingParameters.hpp b/include/viennaps/models/psPlasmaEtchingParameters.hpp index 20634f95..631da864 100644 --- a/include/viennaps/models/psPlasmaEtchingParameters.hpp +++ b/include/viennaps/models/psPlasmaEtchingParameters.hpp @@ -101,12 +101,12 @@ template struct PlasmaEtchingParameters { processData["Passivation Flux"] = {passivationFlux}; for (auto entry : beta_E) { - processData["Beta_E " + MaterialMap::toString(entry.getMaterial())] = - std::vector{entry.getValue()}; + processData["Beta_E " + MaterialMap::toString(entry.material)] = + std::vector{entry.value}; } for (auto entry : beta_P) { - processData["Beta_P " + MaterialMap::toString(entry.getMaterial())] = - std::vector{entry.getValue()}; + processData["Beta_P " + MaterialMap::toString(entry.material)] = + std::vector{entry.value}; } if (etchStopDepth != std::numeric_limits::lowest()) diff --git a/include/viennaps/models/psSF6C4F8Etching.hpp b/include/viennaps/models/psSF6C4F8Etching.hpp index 7016f63e..9ec9d0c2 100644 --- a/include/viennaps/models/psSF6C4F8Etching.hpp +++ b/include/viennaps/models/psSF6C4F8Etching.hpp @@ -43,11 +43,7 @@ class SF6C4F8Etching final : public ProcessModelGPU { etchant.dataLabels.push_back("etchantFlux"); etchant.cosineExponent = 1.f; for (auto entry : params.beta_E) { - etchant.materialSticking[static_cast(entry.getMaterial())] = - entry.getValue(); - } - for (auto &entry : params.beta_E.getCustomMaterialValues()) { - etchant.materialSticking[entry.first] = entry.second; + etchant.materialSticking[static_cast(entry.material)] = entry.value; } // No oxygen/passivation particle for SF6C4F8 diff --git a/include/viennaps/models/psSF6O2Etching.hpp b/include/viennaps/models/psSF6O2Etching.hpp index 4f2102ac..4b947c17 100644 --- a/include/viennaps/models/psSF6O2Etching.hpp +++ b/include/viennaps/models/psSF6O2Etching.hpp @@ -43,11 +43,7 @@ class SF6O2Etching final : public ProcessModelGPU { etchant.dataLabels.push_back("etchantFlux"); etchant.cosineExponent = 1.f; for (auto entry : params.beta_E) { - etchant.materialSticking[static_cast(entry.getMaterial())] = - entry.getValue(); - } - for (auto &entry : params.beta_E.getCustomMaterialValues()) { - etchant.materialSticking[entry.first] = entry.second; + etchant.materialSticking[static_cast(entry.material)] = entry.value; } viennaray::gpu::Particle oxygen; @@ -55,11 +51,7 @@ class SF6O2Etching final : public ProcessModelGPU { oxygen.dataLabels.push_back("passivationFlux"); oxygen.cosineExponent = 1.f; for (auto entry : params.beta_P) { - oxygen.materialSticking[static_cast(entry.getMaterial())] = - entry.getValue(); - } - for (auto &entry : params.beta_P.getCustomMaterialValues()) { - oxygen.materialSticking[entry.first] = entry.second; + oxygen.materialSticking[static_cast(entry.material)] = entry.value; } // surface model diff --git a/include/viennaps/models/psSingleParticleProcess.hpp b/include/viennaps/models/psSingleParticleProcess.hpp index ba6c34e4..be2a23e9 100644 --- a/include/viennaps/models/psSingleParticleProcess.hpp +++ b/include/viennaps/models/psSingleParticleProcess.hpp @@ -129,8 +129,8 @@ class SingleParticleProcess final : public ProcessModelGPU { this->processMetaData["Source Exponent"] = std::vector{sourceDistributionPower_}; for (auto rate : materialRates_) { - this->processMetaData[MaterialMap::toString(rate.getMaterial()) + - " Rate"] = std::vector{rate.getValue()}; + this->processMetaData[MaterialMap::toString(rate.material) + " Rate"] = + std::vector{rate.value}; } } }; @@ -226,8 +226,8 @@ class SingleParticleProcess : public ProcessModelCPU { this->processMetaData["Source Exponent"] = std::vector{sourceDistributionPower_}; for (auto mrate : materialRates_) { - this->processMetaData[MaterialMap::toString(mrate.getMaterial()) + - " Rate"] = std::vector{mrate.getValue()}; + this->processMetaData[MaterialMap::toString(mrate.material) + " Rate"] = + std::vector{mrate.value}; } } }; diff --git a/tests/materialValueMap/CMakeLists.txt b/tests/materialValueMap/CMakeLists.txt new file mode 100644 index 00000000..21dbcfd9 --- /dev/null +++ b/tests/materialValueMap/CMakeLists.txt @@ -0,0 +1,7 @@ +project(materialValueMap LANGUAGES CXX) + +add_executable(${PROJECT_NAME} "${PROJECT_NAME}.cpp") +target_link_libraries(${PROJECT_NAME} PRIVATE ViennaPS) + +add_dependencies(ViennaPS_Tests ${PROJECT_NAME}) +add_test(NAME ${PROJECT_NAME} COMMAND $) diff --git a/tests/materialValueMap/materialValueMap.cpp b/tests/materialValueMap/materialValueMap.cpp new file mode 100644 index 00000000..58e41e17 --- /dev/null +++ b/tests/materialValueMap/materialValueMap.cpp @@ -0,0 +1,64 @@ +#include + +#include +#include + +using namespace viennaps; + +void testBuiltInMaterial() { + MaterialValueMap map; + map.setDefault(-1); + map.set(Material::Si, 1); + map.set(Material::SiO2, 2); + + assert(map.get(Material::Si) == 1); + assert(map.get(Material::SiO2) == 2); + assert(map.get(Material::Si3N4) == -1); // default +} + +void testCustomMaterial() { + MaterialValueMap map; + map.setDefault(0); + Material customMat = MaterialMap::fromString("custom"); + map.set(customMat, 3); + + assert(map.get(customMat) == 3); + assert(map.get(Material::Si) == 0); // default + + Material customMat2 = MaterialMap::fromString("custom2"); + assert(map.get(customMat2) == 0); // default + + // test operator[] + map[customMat2] = 4; + assert(map.get(customMat2) == 4); +} + +void testIteration() { + MaterialValueMap map; + map.set(Material::Si, 1); + map.set(Material::SiO2, 2); + map.set(Material::custom(42), 7); + + std::vector> expected = { + {Material::Si, 1}, + {Material::SiO2, 2}, + {Material::custom(42), 7}, + }; + + std::vector> actual; + for (auto entry : map) { + actual.emplace_back(entry.material, entry.value); + } + + assert(actual.size() == expected.size()); + for (const auto &item : expected) { + assert(std::find(actual.begin(), actual.end(), item) != actual.end()); + } +} + +int main() { + testBuiltInMaterial(); + testCustomMaterial(); + testIteration(); + return 0; +} \ No newline at end of file From 5130a41ff0749a78f4913650c6f175b52f0c3ad4 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Wed, 1 Apr 2026 21:00:25 +0200 Subject: [PATCH 25/32] feat(MaterialValueMap): get entries by index --- .../viennaps/materials/psMaterialValueMap.hpp | 21 ++++++++++ tests/materialValueMap/materialValueMap.cpp | 39 +++++++++++-------- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/include/viennaps/materials/psMaterialValueMap.hpp b/include/viennaps/materials/psMaterialValueMap.hpp index 864b3647..7357820e 100644 --- a/include/viennaps/materials/psMaterialValueMap.hpp +++ b/include/viennaps/materials/psMaterialValueMap.hpp @@ -79,6 +79,14 @@ template class MaterialValueMap { return getBuiltInValue(idx); } + const T getEntryByIndex(std::size_t idx) const { + if (auto it = begin().goToIndex(idx); it != end()) { + return (*it).value; + } else { + throw std::out_of_range("Index out of range in MaterialValueMap."); + } + } + void setDefault(const T &v) { default_ = v; } [[nodiscard]] const T &getDefault() const { return default_; } @@ -129,6 +137,19 @@ template class MaterialValueMap { } } + Iterator goToIndex(std::size_t targetIdx) { + idx_ = 0; + phase_ = Phase::BuiltIn; + advanceToValid(); + + size_t idx = 0; + while (phase_ != Phase::End && idx != targetIdx) { + ++(*this); + ++idx; + } + return *this; + } + Iterator &operator++() { if (phase_ == Phase::BuiltIn) { ++idx_; diff --git a/tests/materialValueMap/materialValueMap.cpp b/tests/materialValueMap/materialValueMap.cpp index 58e41e17..3883cfb4 100644 --- a/tests/materialValueMap/materialValueMap.cpp +++ b/tests/materialValueMap/materialValueMap.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -11,9 +12,9 @@ void testBuiltInMaterial() { map.set(Material::Si, 1); map.set(Material::SiO2, 2); - assert(map.get(Material::Si) == 1); - assert(map.get(Material::SiO2) == 2); - assert(map.get(Material::Si3N4) == -1); // default + VC_TEST_ASSERT(map.get(Material::Si) == 1); + VC_TEST_ASSERT(map.get(Material::SiO2) == 2); + VC_TEST_ASSERT(map.get(Material::Si3N4) == -1); // default } void testCustomMaterial() { @@ -22,38 +23,44 @@ void testCustomMaterial() { Material customMat = MaterialMap::fromString("custom"); map.set(customMat, 3); - assert(map.get(customMat) == 3); - assert(map.get(Material::Si) == 0); // default + VC_TEST_ASSERT(map.get(customMat) == 3); + VC_TEST_ASSERT(map.get(Material::Si) == 0); // default Material customMat2 = MaterialMap::fromString("custom2"); - assert(map.get(customMat2) == 0); // default + VC_TEST_ASSERT(map.get(customMat2) == 0); // default // test operator[] map[customMat2] = 4; - assert(map.get(customMat2) == 4); + VC_TEST_ASSERT(map.get(customMat2) == 4); } void testIteration() { MaterialValueMap map; + auto customMat = MaterialMap::fromString("custom"); map.set(Material::Si, 1); map.set(Material::SiO2, 2); - map.set(Material::custom(42), 7); + map.set(customMat, 7); std::vector> expected = { {Material::Si, 1}, {Material::SiO2, 2}, - {Material::custom(42), 7}, + {customMat, 7}, }; - std::vector> actual; - for (auto entry : map) { - actual.emplace_back(entry.material, entry.value); + size_t idx = 0; + for (const auto &[material, value] : map) { + VC_TEST_ASSERT(idx < expected.size()); + VC_TEST_ASSERT(material == expected[idx].first); + VC_TEST_ASSERT(value == expected[idx].second); + std::cout << "Material: " << MaterialMap::toString(material) << "(" + << material.legacyId() << ")" << ", Value: " << value + << std::endl; + ++idx; } - assert(actual.size() == expected.size()); - for (const auto &item : expected) { - assert(std::find(actual.begin(), actual.end(), item) != actual.end()); - } + VC_TEST_ASSERT(map.getEntryByIndex(0) == 1); + VC_TEST_ASSERT(map.getEntryByIndex(1) == 2); + VC_TEST_ASSERT(map.getEntryByIndex(2) == 7); } int main() { From d3f4298e2d66d4c332a9efdd276349b59d4d4f6a Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Wed, 1 Apr 2026 22:24:25 +0200 Subject: [PATCH 26/32] refactor: SelectiveEpitaxy material handling and emulation examples --- examples/emulation/FinFET.cpp | 60 +++++-------- examples/emulation/FinFET.py | 39 +++------ examples/emulation/stackedNanowire.py | 87 +++++++------------ .../viennaps/materials/psMaterialValueMap.hpp | 9 ++ .../viennaps/models/psDirectionalProcess.hpp | 5 ++ .../viennaps/models/psSelectiveEpitaxy.hpp | 67 +++++++------- python/pyWrapDimension.hpp | 6 +- 7 files changed, 121 insertions(+), 152 deletions(-) diff --git a/examples/emulation/FinFET.cpp b/examples/emulation/FinFET.cpp index e1798149..05d06515 100644 --- a/examples/emulation/FinFET.cpp +++ b/examples/emulation/FinFET.cpp @@ -11,19 +11,9 @@ using DomainType = SmartPointer>; using IsotropicProcessGeometric = SmartPointer>; constexpr bool volumeOutput = false; -void writeVolume(DomainType domain) { - if (!volumeOutput) - return; - static int volumeNum = 0; - std::cout << "Writing volume mesh ..." << std::flush; - domain->saveVolumeMesh("FinFET_" + std::to_string(volumeNum), 0.05); - std::cout << " done" << std::endl; - ++volumeNum; -} - void writeSurface(DomainType domain) { static int outputNum = 0; - domain->saveSurfaceMesh("FinFET_" + std::to_string(outputNum) + ".vtp", true); + domain->saveSurfaceMesh("FinFET_" + std::to_string(outputNum) + ".vtp"); ++outputNum; } @@ -60,7 +50,7 @@ int main() { { // DP-Depo std::cout << "DP-Depo ..." << std::flush; const NumericType thickness = 4; // nm - domain->duplicateTopLevelSet(Material::Metal); + domain->duplicateTopLevelSet(Material::SiO2_HM); auto dist = IsotropicProcessGeometric::New(thickness); Process(domain, dist).apply(); std::cout << " done" << std::endl; @@ -81,19 +71,21 @@ int main() { // Remove mask with boolean operation domain->removeMaterial(Material::Mask); writeSurface(domain); - writeVolume(domain); // pattern si { std::cout << "Si-Patterning ..." << std::flush; const NumericType etchDepth = 90.; // nm - Vec3D direction = {0, 0, 1}; - auto model = SmartPointer>::New( - direction, 1.1, 0.1, Material::Metal, false); + Vec3D direction = {0, 0, -1}; + DirectionalProcess::RateMap rates; + rates.set(Material::Si, + {1., 0.1}); // directional and isotropic rates for silicon + rates.setDefault({0., 0.}); // default rates for non-silicon materials + auto model = + SmartPointer>::New(direction, rates); Process(domain, model, etchDepth).apply(); std::cout << " done" << std::endl; } - writeVolume(domain); writeSurface(domain); // Remove DP mask (metal) @@ -113,7 +105,7 @@ int main() { // CMP at 80 Planarize(domain, 80.0).apply(); - writeVolume(domain); + writeSurface(domain); // pattern STI material { @@ -124,7 +116,6 @@ int main() { std::cout << " done" << std::endl; } writeSurface(domain); - writeVolume(domain); // deposit gate material { @@ -179,7 +170,6 @@ int main() { // Remove mask domain->removeTopLevelSet(); writeSurface(domain); - writeVolume(domain); // Spacer Deposition and Etch { // spacer depo @@ -194,22 +184,20 @@ int main() { { // spacer etch std::cout << "Spacer Etch ..." << std::flush; - auto ls = domain->getLevelSets()[domain->getLevelSets().size() - 2]; + auto mask = domain->getLevelSets()[domain->getLevelSets().size() - 2]; auto dist = SmartPointer>::New( - std::array{-gridDelta, -gridDelta, -50}, ls); + std::array{-gridDelta, -gridDelta, -50}, mask); Process(domain, dist).apply(); std::cout << " done" << std::endl; } writeSurface(domain); - writeVolume(domain); // isotropic etch (fin-release) { std::cout << "Fin-Release ..." << std::flush; - std::vector masks = {Material::PolySi, Material::SiO2, - Material::Si3N4}; - auto model = - SmartPointer>::New(-1., masks); + auto model = SmartPointer>::New(0.0); + model->setMaterialRate(Material::Si, -1.); + AdvectionParameters advectionParams; advectionParams.spatialScheme = viennals::SpatialSchemeEnum::LAX_FRIEDRICHS_2ND_ORDER; @@ -219,22 +207,21 @@ int main() { std::cout << " done" << std::endl; } writeSurface(domain); - writeVolume(domain); // source/drain epitaxy { std::cout << "S/D Epitaxy ..." << std::flush; domain->duplicateTopLevelSet(Material::SiGe); - Logger::setLogLevel(LogLevel::INFO); AdvectionParameters advectionParams; advectionParams.spatialScheme = viennals::SpatialSchemeEnum::STENCIL_LOCAL_LAX_FRIEDRICHS_1ST_ORDER; lsInternal::StencilLocalLaxFriedrichsScalar::setMaxDissipation(100); - std::vector> material = { - {Material::Si, 1.}, {Material::SiGe, 1.}}; - auto model = SmartPointer>::New(material); + auto model = SmartPointer>::New(); + model->setMaterialRate(Material::Si, 1.); + model->setMaterialRate(Material::SiGe, 1.); + Process proc(domain, model, 14.); proc.setParameters(advectionParams); proc.apply(); @@ -242,7 +229,6 @@ int main() { std::cout << " done" << std::endl; } writeSurface(domain); - writeVolume(domain); // deposit dielectric { @@ -257,12 +243,11 @@ int main() { // CMP at 90 Planarize(domain, 90.0).apply(); - writeVolume(domain); + writeSurface(domain); - // now remove gate and add new gate materials + // now remove dummy gate and add new gate materials domain->removeMaterial(Material::PolySi); writeSurface(domain); - writeVolume(domain); // now deposit TiN and PolySi as replacement gate { @@ -283,10 +268,11 @@ int main() { Process(domain, dist).apply(); std::cout << " done" << std::endl; } + writeSurface(domain); // CMP at 90 Planarize(domain, 90.0).apply(); - writeVolume(domain); + writeSurface(domain); domain->saveVolumeMesh("FinFET_Final", 0.05); } diff --git a/examples/emulation/FinFET.py b/examples/emulation/FinFET.py index 13c18761..7bd8affc 100644 --- a/examples/emulation/FinFET.py +++ b/examples/emulation/FinFET.py @@ -38,7 +38,6 @@ def writeSurface(domain): ps.MakePlane(domain, 70.0, ps.Material.Si).apply() writeSurface(domain) - # Add double patterning mask box = ps.ls.Domain(domain.getGrid()) minPoints = [30.0, -10.0, 69.9] @@ -60,7 +59,7 @@ def writeSurface(domain): print("DP-Patterning ...", end="", flush=True) etchDepth = 6.0 # nm dist = ps.BoxDistribution( - [-gridDelta, -gridDelta, -etchDepth], domain.getLevelSets()[0] + halfAxes=[-gridDelta, -gridDelta, -etchDepth], mask=domain.getLevelSets()[0] ) ps.Process(domain, dist, 0).apply() print(" done") @@ -69,22 +68,16 @@ def writeSurface(domain): # Remove mask with boolean operation domain.removeMaterial(ps.Material.Mask) writeSurface(domain) -writeVolume(domain) # pattern si print("Si-Patterning ...", end="", flush=True) etchDepth = 90.0 # nm -direction = [0.0, 0.0, 1.0] model = ps.DirectionalProcess( - direction=direction, - directionalVelocity=1.1, - isotropicVelocity=0.1, - maskMaterial=ps.Material.Metal, - calculateVisibility=False, + direction=[0.0, 0.0, -1.0], + materialRates={ps.Material.Si: [1.0, 0.1]}, ) ps.Process(domain, model, etchDepth).apply() print(" done") -writeVolume(domain) writeSurface(domain) # Remove DP mask (metal) @@ -102,7 +95,7 @@ def writeSurface(domain): # CMP at 80 ps.Planarize(domain, 80.0).apply() -writeVolume(domain) +writeSurface(domain) # pattern STI material print("STI Patterning ...", end="", flush=True) @@ -110,7 +103,6 @@ def writeSurface(domain): ps.Process(domain, dist, 0).apply() print(" done") writeSurface(domain) -writeVolume(domain) # deposit gate material print("Gate Deposition HfO2 ...", end="", flush=True) @@ -131,6 +123,7 @@ def writeSurface(domain): # CMP at 150 ps.Planarize(domain, 150.0).apply() +writeSurface(domain) # dummy gate mask addition box = ps.ls.Domain(domain.getGrid()) @@ -158,7 +151,6 @@ def writeSurface(domain): # Remove mask domain.removeTopLevelSet() writeSurface(domain) -writeVolume(domain) # Spacer Deposition and Etch print("Spacer Deposition and Etch ...", end="", flush=True) @@ -180,8 +172,8 @@ def writeSurface(domain): # isotropic etch (fin-release) print("Fin-Release ...", end="", flush=True) -masks = [ps.Material.PolySi, ps.Material.SiO2, ps.Material.Si3N4] -model = ps.IsotropicProcess(rate=-1.0, maskMaterial=masks) +model = ps.IsotropicProcess(rate=0.0) +model.setMaterialRate(ps.Material.Si, -1.0) advParams = ps.AdvectionParameters() advParams.spatialScheme = ps.SpatialScheme.LAX_FRIEDRICHS_2ND_ORDER process = ps.Process(domain, model, 5.0) @@ -196,18 +188,15 @@ def writeSurface(domain): domain.duplicateTopLevelSet(ps.Material.SiGe) advectionParams = ps.AdvectionParameters() advectionParams.spatialScheme = ps.SpatialScheme.STENCIL_LOCAL_LAX_FRIEDRICHS_1ST_ORDER -ps.StencilLocalLaxFriedrichsScalar.setMaxDissipation(1000) -material = [ - (ps.Material.Si, 1.0), - (ps.Material.SiGe, 1.0), -] -model = ps.SelectiveEpitaxy(materialRates=material) +ps.StencilLocalLaxFriedrichsScalar.setMaxDissipation(100) +model = ps.SelectiveEpitaxy() +model.setMaterialRate(ps.Material.SiGe, 1.0) +model.setMaterialRate(ps.Material.Si, 1.0) process = ps.Process(domain, model, 14.0) process.setParameters(advectionParams) process.apply() print(" done") writeSurface(domain) -writeVolume(domain) # deposit dielectric print("Dielectric Deposition ...", end="", flush=True) @@ -220,12 +209,11 @@ def writeSurface(domain): # CMP at 90 ps.Planarize(domain, 90.0).apply() -writeVolume(domain) +writeSurface(domain) # now remove gate and add new gate materials domain.removeMaterial(ps.Material.PolySi) writeSurface(domain) -writeVolume(domain) # now deposit TiN and PolySi as replacement gate print("Gate Deposition TiN ...", end="", flush=True) @@ -242,9 +230,10 @@ def writeSurface(domain): dist = ps.SphereDistribution(radius=thickness) ps.Process(domain, dist, 0).apply() print(" done") +writeSurface(domain) # CMP at 90 ps.Planarize(domain, 90.0).apply() -writeVolume(domain) +writeSurface(domain) domain.saveVolumeMesh("FinFET_Final", 0.05) diff --git a/examples/emulation/stackedNanowire.py b/examples/emulation/stackedNanowire.py index 8dedc603..5dfedeba 100644 --- a/examples/emulation/stackedNanowire.py +++ b/examples/emulation/stackedNanowire.py @@ -36,7 +36,7 @@ geo.apply() domain.insertNextLevelSetAsMaterial(mask, ps.Material.Mask) -domain.saveVolumeMesh("StackedNanowire_" + str(n)) +domain.saveSurfaceMesh("StackedNanowire_" + str(n)) n += 1 # Double patterning @@ -53,7 +53,7 @@ ps.Process(domain, directional, 20.0).apply() domain.removeMaterial(ps.Material.Mask) -domain.saveVolumeMesh("StackedNanowire_" + str(n)) +domain.saveSurfaceMesh("StackedNanowire_" + str(n)) n += 1 # Pattern Si/SiGe/Si @@ -68,7 +68,7 @@ ps.Process(domain, directional, 30.0).apply() domain.removeMaterial(ps.Material.Metal) -domain.saveVolumeMesh("StackedNanowire_" + str(n)) +domain.saveSurfaceMesh("StackedNanowire_" + str(n)) n += 1 # Deposit dummy gate material @@ -79,7 +79,7 @@ # CMP at 80nm height ps.Planarize(domain, 80.0).apply() -domain.saveVolumeMesh("StackedNanowire_" + str(n)) +domain.saveSurfaceMesh("StackedNanowire_" + str(n)) n += 1 # Dummy gate mask addition @@ -101,70 +101,52 @@ ps.Process(tmpDomain, geometricEtch, 1.0).apply() domain.removeMaterial(ps.Material.Mask) -domain.saveVolumeMesh("StackedNanowire_" + str(n)) +domain.saveSurfaceMesh("StackedNanowire_" + str(n)) n += 1 # Spacer Deposition print("Depositing spacer material ...") domain.duplicateTopLevelSet(ps.Material.Si3N4) ps.Process(domain, growth, 12.0).apply() +domain.saveSurfaceMesh("StackedNanowire_" + str(n)) +n += 1 # Spacer patterning print("Patterning spacer ...") directional = ps.DirectionalProcess( - direction=[0.0, 0.0, 1.0], - directionalVelocity=1.0, - isotropicVelocity=-0.05, - maskMaterial=[ - ps.Material.PolySi, - ps.Material.Si, - ps.Material.SiGe, - ps.Material.SiO2, - ], - calculateVisibility=False, + direction=[0.0, 0.0, -1.0], materialRates={ps.Material.Si3N4: [1.0, 0.0]} ) ps.Process(domain, directional, 40.0).apply() -domain.saveVolumeMesh("StackedNanowire_" + str(n)) +domain.saveSurfaceMesh("StackedNanowire_" + str(n)) n += 1 # Fin patterning print("Patterning Fin ...") -rateSet1 = ps.RateSet( - direction=[0.0, 0.0, 1.0], - directionalVelocity=1.0, - isotropicVelocity=0.0, - maskMaterials=[ps.Material.PolySi, ps.Material.SiO2, ps.Material.Si3N4], - calculateVisibility=False, -) -rateSet2 = ps.RateSet( - direction=[0.0, 0.0, 1.0], - directionalVelocity=0.1, - isotropicVelocity=0.0, - maskMaterials=[ - ps.Material.SiO2, - ps.Material.Si3N4, - ps.Material.Si, - ps.Material.SiGe, - ], - calculateVisibility=False, +directional = ps.DirectionalProcess( + direction=[0.0, 0.0, -1.0], + materialRates={ + ps.Material.PolySi: [0.1, 0.0], + ps.Material.Si: [1.0, 0.0], + ps.Material.SiGe: [1.0, 0.0], + }, + defaultIsotropicRate=-0.005, ) -directional = ps.DirectionalProcess([rateSet1, rateSet2]) ps.Process(domain, directional, 21.0).apply() -domain.saveVolumeMesh("StackedNanowire_" + str(n)) +domain.saveSurfaceMesh("StackedNanowire_" + str(n)) n += 1 # SD Epitaxy print("SD Epitaxy ...") -domain.duplicateTopLevelSet(ps.Material.Metal) -epitaxy = ps.IsotropicProcess( - rate=1.0, - maskMaterial=[ps.Material.PolySi, ps.Material.SiO2, ps.Material.Si3N4], -) +epiMaterial = ps.MaterialMap.fromString("EpiSi") +domain.duplicateTopLevelSet(epiMaterial) +epitaxy = ps.IsotropicProcess(0.0) +epitaxy.setMaterialRate(ps.Material.Si, 1.0) +epitaxy.setMaterialRate(epiMaterial, 1.0) ps.Process(domain, epitaxy, 9.0).apply() -domain.saveVolumeMesh("StackedNanowire_" + str(n)) +domain.saveSurfaceMesh("StackedNanowire_" + str(n)) n += 1 # Dielectric deposition @@ -174,30 +156,22 @@ ps.Planarize(domain, 60).apply() -domain.saveVolumeMesh("StackedNanowire_" + str(n)) +domain.saveSurfaceMesh("StackedNanowire_" + str(n)) n += 1 # Remove dummy gate print("Removing dummy gate ...") domain.removeMaterial(ps.Material.PolySi) -domain.saveVolumeMesh("StackedNanowire_" + str(n)) +domain.saveSurfaceMesh("StackedNanowire_" + str(n)) n += 1 # remove SiGe interlayer print("Removing SiGe interlayer ...") -etch = ps.IsotropicProcess( - rate=-1.0, - maskMaterial=[ - ps.Material.Si, - ps.Material.SiO2, - ps.Material.Si3N4, - ps.Material.Dielectric, - ps.Material.Metal, - ], -) +etch = ps.IsotropicProcess(rate=0.0) # default rate is 0.0 +etch.setMaterialRate(ps.Material.SiGe, -1.0) ps.Process(domain, etch, 10.0).apply() -domain.saveVolumeMesh("StackedNanowire_" + str(n)) +domain.saveSurfaceMesh("StackedNanowire_" + str(n)) n += 1 # gate dielectric, gate metal, gate contact deposition @@ -213,4 +187,5 @@ ps.Planarize(domain, 47.5).apply() -domain.saveVolumeMesh("StackedNanowire_" + str(n)) +domain.saveSurfaceMesh("StackedNanowire_" + str(n)) +domain.saveVolumeMesh("StackedNanowire_Final") diff --git a/include/viennaps/materials/psMaterialValueMap.hpp b/include/viennaps/materials/psMaterialValueMap.hpp index 7357820e..f15adea2 100644 --- a/include/viennaps/materials/psMaterialValueMap.hpp +++ b/include/viennaps/materials/psMaterialValueMap.hpp @@ -101,6 +101,15 @@ template class MaterialValueMap { [[nodiscard]] bool has(BuiltInMaterial m) const { return has(Material(m)); } + bool empty() const { + for (const auto &b : isSet_) { + if (b) { + return false; + } + } + return customValues_.empty(); + } + // remove value -> fallback to default void clear(Material material) { if (material.isBuiltIn()) { diff --git a/include/viennaps/models/psDirectionalProcess.hpp b/include/viennaps/models/psDirectionalProcess.hpp index 7894f1f3..c35cefc7 100644 --- a/include/viennaps/models/psDirectionalProcess.hpp +++ b/include/viennaps/models/psDirectionalProcess.hpp @@ -173,6 +173,11 @@ class DirectionalProcess : public ProcessModelCPU { using RateSet = impl::RateSet; using RateMap = MaterialValueMap>; + DirectionalProcess(const Vec3D &direction, + RateMap materialRates) { + initialize(std::move(materialRates), direction); + } + DirectionalProcess( const Vec3D &direction, std::unordered_map> diff --git a/include/viennaps/models/psSelectiveEpitaxy.hpp b/include/viennaps/models/psSelectiveEpitaxy.hpp index 3e617744..6ad71dc0 100644 --- a/include/viennaps/models/psSelectiveEpitaxy.hpp +++ b/include/viennaps/models/psSelectiveEpitaxy.hpp @@ -1,5 +1,6 @@ #pragma once +#include "../materials/psMaterialValueMap.hpp" #include "../materials/psMaterials.hpp" #include "../process/psProcessModel.hpp" @@ -18,29 +19,28 @@ class EpitaxyVelocityField : public VelocityField { static constexpr double high = 1.0; const double factor; - const std::vector> &materials; + const MaterialValueMap &materialRates; public: - EpitaxyVelocityField( - const std::vector> &passedmaterials, - NumericType r111, NumericType r100) + EpitaxyVelocityField(const MaterialValueMap &materials, + NumericType r111, NumericType r100) : R111(r111), R100(r100), factor((R100 - R111) / (high - low)), - materials(passedmaterials) {} - - NumericType getScalarVelocity(const Vec3D &coordinate, - int material, const Vec3D &nv, - unsigned long pointID) override { - for (auto const &epitaxyMaterial : materials) { - if (MaterialMap::isMaterial(material, epitaxyMaterial.first)) { - double vel = std::max(std::abs(nv[0]), std::abs(nv[D - 1])); - vel = (vel - low) * factor + R111; - - if (std::abs(nv[0]) < std::abs(nv[D - 1])) { - vel *= 2.; - } + materialRates(materials) {} + + NumericType getScalarVelocity(const Vec3D &, int material, + const Vec3D &nv, + unsigned long) override { + + auto rate = materialRates.get(Material::fromLegacyId(material)); + if (rate > 0) { + double vel = std::max(std::abs(nv[0]), std::abs(nv[D - 1])); + vel = (vel - low) * factor + R111; - return -vel * epitaxyMaterial.second; + if (std::abs(nv[0]) < std::abs(nv[D - 1])) { + vel *= 2.; } + + return -vel * rate; } // not an epitaxy material @@ -53,25 +53,36 @@ class EpitaxyVelocityField : public VelocityField { template class SelectiveEpitaxy : public ProcessModelCPU { public: + SelectiveEpitaxy(NumericType rate111 = 0.5, NumericType rate100 = 1.) + : SelectiveEpitaxy(std::vector>{}, + rate111, rate100) {} + // The constructor expects the materials where epitaxy is allowed including // the corresponding rates. SelectiveEpitaxy( const std::vector> pMaterials, - NumericType rate111 = 0.5, NumericType rate100 = 1.) - : materials(pMaterials) { + NumericType rate111 = 0.5, NumericType rate100 = 1.) { + for (const auto &[material, rate] : pMaterials) { + materialRates.set(material, rate); + } + // default surface model auto surfModel = SmartPointer>::New(); // velocity field auto velField = SmartPointer>::New( - materials, rate111, rate100); + materialRates, rate111, rate100); this->setSurfaceModel(surfModel); this->setVelocityField(velField); this->setProcessName("SelectiveEpitaxy"); } + void setMaterialRate(Material material, NumericType rate) { + materialRates.set(material, rate); + } + void initialize(SmartPointer> domain, const NumericType processDuration) final { if (firstInit) { @@ -107,12 +118,6 @@ class SelectiveEpitaxy : public ProcessModelCPU { maskLayer, lsCopy, viennals::BooleanOperationEnum::RELATIVE_COMPLEMENT) .apply(); - - // auto mesh = viennals::Mesh::New(); - // viennals::ToSurfaceMesh(lsCopy, mesh).apply(); - // viennals::VTKWriter(mesh, "SelectiveEpitaxyMask_" + - // std::to_string(i)) - // .apply(); } } @@ -138,16 +143,12 @@ class SelectiveEpitaxy : public ProcessModelCPU { } private: - std::vector> materials; + MaterialValueMap materialRates; SmartPointer> domainCopy; bool firstInit = true; bool isEpitaxyMaterial(const Material &material) const { - for (const auto &epitaxyMaterial : materials) { - if (MaterialMap::isMaterial(material, epitaxyMaterial.first)) - return true; - } - return false; + return materialRates.get(material) != NumericType(0); } }; diff --git a/python/pyWrapDimension.hpp b/python/pyWrapDimension.hpp index f8e15b37..eab05f58 100644 --- a/python/pyWrapDimension.hpp +++ b/python/pyWrapDimension.hpp @@ -1106,10 +1106,14 @@ template void bindApi(py::module &module) { // Selective Epitaxy Process py::class_, SmartPointer>>( module, "SelectiveEpitaxy", processModel) + .def(py::init(&SmartPointer>::template New), + py::arg("rate111") = 0.5, py::arg("rate100") = 1.0) .def(py::init(&SmartPointer>::template New< const std::vector>, T, T>), py::arg("materialRates"), py::arg("rate111") = 0.5, - py::arg("rate100") = 1.0); + py::arg("rate100") = 1.0) + .def("setMaterialRate", &SelectiveEpitaxy::setMaterialRate, + py::arg("material"), py::arg("rate")); // Single Particle ALD py::class_, SmartPointer>>( From d3509dd31cf921388c58c89f7dede41b27ae49da Mon Sep 17 00:00:00 2001 From: reiter Date: Thu, 2 Apr 2026 10:41:39 +0200 Subject: [PATCH 27/32] refactor: rename header file with MaterialMap --- examples/exampleProcess/velocityField.hpp | 2 +- gpu/models/PlasmaEtching.cuh | 2 +- .../viennaps/materials/{psMaterials.hpp => psMaterialMap.hpp} | 0 include/viennaps/materials/psMaterialValueMap.hpp | 2 +- include/viennaps/models/psCF4O2Parameters.hpp | 2 +- include/viennaps/models/psCSVFileProcess.hpp | 2 +- include/viennaps/models/psFaradayCageEtching.hpp | 2 +- include/viennaps/models/psFluorocarbonEtching.hpp | 2 +- include/viennaps/models/psIonBeamEtching.hpp | 2 +- include/viennaps/models/psIonBeamParameters.hpp | 2 +- include/viennaps/models/psMultiParticleProcess.hpp | 2 +- include/viennaps/models/psSelectiveEpitaxy.hpp | 2 +- include/viennaps/models/psWetEtching.hpp | 2 +- include/viennaps/process/psTranslationField.hpp | 2 +- include/viennaps/psDomain.hpp | 2 +- include/viennaps/psUtil.hpp | 2 +- tests/materialMap/materialMap.cpp | 2 +- tests/readAndWrite/readAndWrite.cpp | 2 +- 18 files changed, 17 insertions(+), 17 deletions(-) rename include/viennaps/materials/{psMaterials.hpp => psMaterialMap.hpp} (100%) diff --git a/examples/exampleProcess/velocityField.hpp b/examples/exampleProcess/velocityField.hpp index cf2207aa..7b2ecef4 100644 --- a/examples/exampleProcess/velocityField.hpp +++ b/examples/exampleProcess/velocityField.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include diff --git a/gpu/models/PlasmaEtching.cuh b/gpu/models/PlasmaEtching.cuh index 79cc431b..f7cae010 100644 --- a/gpu/models/PlasmaEtching.cuh +++ b/gpu/models/PlasmaEtching.cuh @@ -6,7 +6,7 @@ #include "raygLaunchParams.hpp" #include "raygReflection.hpp" -#include "materials/psMaterials.hpp" +#include "materials/psMaterialMap.hpp" #include "models/psPipelineParameters.hpp" #include "models/psPlasmaEtchingParameters.hpp" diff --git a/include/viennaps/materials/psMaterials.hpp b/include/viennaps/materials/psMaterialMap.hpp similarity index 100% rename from include/viennaps/materials/psMaterials.hpp rename to include/viennaps/materials/psMaterialMap.hpp diff --git a/include/viennaps/materials/psMaterialValueMap.hpp b/include/viennaps/materials/psMaterialValueMap.hpp index f15adea2..83a60882 100644 --- a/include/viennaps/materials/psMaterialValueMap.hpp +++ b/include/viennaps/materials/psMaterialValueMap.hpp @@ -1,6 +1,6 @@ #pragma once -#include "psMaterials.hpp" +#include "psMaterial.hpp" namespace viennaps { diff --git a/include/viennaps/models/psCF4O2Parameters.hpp b/include/viennaps/models/psCF4O2Parameters.hpp index e021c0aa..095fc8f1 100644 --- a/include/viennaps/models/psCF4O2Parameters.hpp +++ b/include/viennaps/models/psCF4O2Parameters.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../materials/psMaterials.hpp" +#include "../materials/psMaterialMap.hpp" #include "../psConstants.hpp" #include diff --git a/include/viennaps/models/psCSVFileProcess.hpp b/include/viennaps/models/psCSVFileProcess.hpp index 790295af..9f2ae779 100644 --- a/include/viennaps/models/psCSVFileProcess.hpp +++ b/include/viennaps/models/psCSVFileProcess.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../materials/psMaterials.hpp" +#include "../materials/psMaterialMap.hpp" #include "../process/psProcessModel.hpp" #include "../process/psSurfaceModel.hpp" #include "../psDomain.hpp" diff --git a/include/viennaps/models/psFaradayCageEtching.hpp b/include/viennaps/models/psFaradayCageEtching.hpp index 78cafa1e..85759971 100644 --- a/include/viennaps/models/psFaradayCageEtching.hpp +++ b/include/viennaps/models/psFaradayCageEtching.hpp @@ -3,7 +3,7 @@ #include "psIonBeamEtching.hpp" #include "psPipelineParameters.hpp" -#include "../materials/psMaterials.hpp" +#include "../materials/psMaterialMap.hpp" #include "../process/psProcessModel.hpp" #include "../psUtil.hpp" diff --git a/include/viennaps/models/psFluorocarbonEtching.hpp b/include/viennaps/models/psFluorocarbonEtching.hpp index 9f3a5b73..6b83d82e 100644 --- a/include/viennaps/models/psFluorocarbonEtching.hpp +++ b/include/viennaps/models/psFluorocarbonEtching.hpp @@ -2,7 +2,7 @@ #include -#include "../materials/psMaterials.hpp" +#include "../materials/psMaterialMap.hpp" #include "../process/psProcessModel.hpp" #include "../psConstants.hpp" #include "../psUnits.hpp" diff --git a/include/viennaps/models/psIonBeamEtching.hpp b/include/viennaps/models/psIonBeamEtching.hpp index f42ab96d..9738a9cc 100644 --- a/include/viennaps/models/psIonBeamEtching.hpp +++ b/include/viennaps/models/psIonBeamEtching.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../materials/psMaterials.hpp" +#include "../materials/psMaterialMap.hpp" #include "../process/psProcessModel.hpp" #include "../psConstants.hpp" #include "psIonBeamParameters.hpp" diff --git a/include/viennaps/models/psIonBeamParameters.hpp b/include/viennaps/models/psIonBeamParameters.hpp index e21972d7..f4eea13a 100644 --- a/include/viennaps/models/psIonBeamParameters.hpp +++ b/include/viennaps/models/psIonBeamParameters.hpp @@ -1,7 +1,7 @@ #pragma once +#include "../materials/psMaterialMap.hpp" #include "../materials/psMaterialValueMap.hpp" -#include "../materials/psMaterials.hpp" #include #include diff --git a/include/viennaps/models/psMultiParticleProcess.hpp b/include/viennaps/models/psMultiParticleProcess.hpp index 298c812d..2a474ef4 100644 --- a/include/viennaps/models/psMultiParticleProcess.hpp +++ b/include/viennaps/models/psMultiParticleProcess.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../materials/psMaterials.hpp" +#include "../materials/psMaterialMap.hpp" #include "../process/psProcessModel.hpp" #include "../psConstants.hpp" #include "../psUtil.hpp" diff --git a/include/viennaps/models/psSelectiveEpitaxy.hpp b/include/viennaps/models/psSelectiveEpitaxy.hpp index 6ad71dc0..b6cd6e1b 100644 --- a/include/viennaps/models/psSelectiveEpitaxy.hpp +++ b/include/viennaps/models/psSelectiveEpitaxy.hpp @@ -1,7 +1,7 @@ #pragma once +#include "../materials/psMaterialMap.hpp" #include "../materials/psMaterialValueMap.hpp" -#include "../materials/psMaterials.hpp" #include "../process/psProcessModel.hpp" namespace viennaps { diff --git a/include/viennaps/models/psWetEtching.hpp b/include/viennaps/models/psWetEtching.hpp index 33528fdc..d25b9945 100644 --- a/include/viennaps/models/psWetEtching.hpp +++ b/include/viennaps/models/psWetEtching.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../materials/psMaterials.hpp" +#include "../materials/psMaterialMap.hpp" #include "../process/psProcessModel.hpp" #include diff --git a/include/viennaps/process/psTranslationField.hpp b/include/viennaps/process/psTranslationField.hpp index a0219c81..1e777153 100644 --- a/include/viennaps/process/psTranslationField.hpp +++ b/include/viennaps/process/psTranslationField.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../materials/psMaterials.hpp" +#include "../materials/psMaterialMap.hpp" #include "psVelocityField.hpp" #include diff --git a/include/viennaps/psDomain.hpp b/include/viennaps/psDomain.hpp index e7430bf0..d98f7e93 100644 --- a/include/viennaps/psDomain.hpp +++ b/include/viennaps/psDomain.hpp @@ -1,6 +1,6 @@ #pragma once -#include "materials/psMaterials.hpp" +#include "materials/psMaterialMap.hpp" #include "psDomainSetup.hpp" #include "psPreCompileMacros.hpp" #include "psSurfacePointValuesToLevelSet.hpp" diff --git a/include/viennaps/psUtil.hpp b/include/viennaps/psUtil.hpp index 10472f8d..7a9a7d42 100644 --- a/include/viennaps/psUtil.hpp +++ b/include/viennaps/psUtil.hpp @@ -10,7 +10,7 @@ #include #include -#include "materials/psMaterials.hpp" +#include "materials/psMaterialMap.hpp" namespace viennaps { enum class FluxEngineType { diff --git a/tests/materialMap/materialMap.cpp b/tests/materialMap/materialMap.cpp index 3fda8ba5..6ea10d06 100644 --- a/tests/materialMap/materialMap.cpp +++ b/tests/materialMap/materialMap.cpp @@ -1,5 +1,5 @@ +#include "materials/psMaterialMap.hpp" #include "materials/psMaterialValueMap.hpp" -#include "materials/psMaterials.hpp" #include diff --git a/tests/readAndWrite/readAndWrite.cpp b/tests/readAndWrite/readAndWrite.cpp index c363678a..1938c8fa 100644 --- a/tests/readAndWrite/readAndWrite.cpp +++ b/tests/readAndWrite/readAndWrite.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include #include From e8af7b0212f98faa4e2798b710ffa905855762b0 Mon Sep 17 00:00:00 2001 From: reiter Date: Thu, 2 Apr 2026 12:02:51 +0200 Subject: [PATCH 28/32] refactor: CF4O2 model and selective epitaxy --- examples/emulation/FinFET.cpp | 4 +- examples/emulation/FinFET.py | 4 +- examples/selectiveEpitaxy/config.txt | 2 +- .../viennaps/materials/psMaterialValueMap.hpp | 10 +- include/viennaps/models/psCF4O2Etching.hpp | 99 +++++-------------- include/viennaps/models/psCF4O2Parameters.hpp | 75 +++++++++----- include/viennaps/models/psHBrO2Etching.hpp | 4 - .../models/psPlasmaEtchingParameters.hpp | 12 ++- include/viennaps/models/psSF6C4F8Etching.hpp | 3 - include/viennaps/models/psSF6O2Etching.hpp | 4 - .../viennaps/models/psSelectiveEpitaxy.hpp | 57 ++++++----- python/pyWrapDimension.hpp | 4 + python/viennaps/_core/__init__.pyi | 44 ++------- python/viennaps/d2/__init__.pyi | 9 ++ python/viennaps/d3/__init__.pyi | 9 ++ tests/materialValueMap/materialValueMap.cpp | 1 + 16 files changed, 162 insertions(+), 179 deletions(-) diff --git a/examples/emulation/FinFET.cpp b/examples/emulation/FinFET.cpp index 05d06515..4f75306e 100644 --- a/examples/emulation/FinFET.cpp +++ b/examples/emulation/FinFET.cpp @@ -219,8 +219,8 @@ int main() { 1>::setMaxDissipation(100); auto model = SmartPointer>::New(); - model->setMaterialRate(Material::Si, 1.); - model->setMaterialRate(Material::SiGe, 1.); + model->setMaterialRate(Material::Si, 2.); + model->setMaterialRate(Material::SiGe, 2.); Process proc(domain, model, 14.); proc.setParameters(advectionParams); diff --git a/examples/emulation/FinFET.py b/examples/emulation/FinFET.py index 7bd8affc..4ad37157 100644 --- a/examples/emulation/FinFET.py +++ b/examples/emulation/FinFET.py @@ -190,8 +190,8 @@ def writeSurface(domain): advectionParams.spatialScheme = ps.SpatialScheme.STENCIL_LOCAL_LAX_FRIEDRICHS_1ST_ORDER ps.StencilLocalLaxFriedrichsScalar.setMaxDissipation(100) model = ps.SelectiveEpitaxy() -model.setMaterialRate(ps.Material.SiGe, 1.0) -model.setMaterialRate(ps.Material.Si, 1.0) +model.setMaterialRate(ps.Material.SiGe, 2.0) +model.setMaterialRate(ps.Material.Si, 2.0) process = ps.Process(domain, model, 14.0) process.setParameters(advectionParams) process.apply() diff --git a/examples/selectiveEpitaxy/config.txt b/examples/selectiveEpitaxy/config.txt index d7f121f4..73d9fdf0 100644 --- a/examples/selectiveEpitaxy/config.txt +++ b/examples/selectiveEpitaxy/config.txt @@ -12,6 +12,6 @@ etchTime = 4.0 # Process processTime=5. -epitaxyRate=1. +epitaxyRate=2. R111=0.4 R100=1.0 \ No newline at end of file diff --git a/include/viennaps/materials/psMaterialValueMap.hpp b/include/viennaps/materials/psMaterialValueMap.hpp index 83a60882..84fc0848 100644 --- a/include/viennaps/materials/psMaterialValueMap.hpp +++ b/include/viennaps/materials/psMaterialValueMap.hpp @@ -11,12 +11,20 @@ template class MaterialValueMap { MaterialValueMap() = default; // generic constructor (works with map, vector, initializer_list, etc.) - template explicit MaterialValueMap(const MapLike &mapLike) { + template + explicit MaterialValueMap(const MapLike &mapLike, T defaultValue = T{}) + : default_(defaultValue) { for (const auto &[key, value] : mapLike) { set(key, value); } } + static MaterialValueMap fromDefault(T defaultValue) { + MaterialValueMap map; + map.setDefault(defaultValue); + return map; + } + void set(Material material, const T &value) { if (material.isBuiltIn()) { auto idx = toIndex(material.builtIn()); diff --git a/include/viennaps/models/psCF4O2Etching.hpp b/include/viennaps/models/psCF4O2Etching.hpp index 5dc4cc9a..b62ec9df 100644 --- a/include/viennaps/models/psCF4O2Etching.hpp +++ b/include/viennaps/models/psCF4O2Etching.hpp @@ -370,8 +370,10 @@ class CF4O2Etchant final const auto &phi_O = globalData->getVectorData(1)[primID]; // F surface coverage on oxidized SiGe const auto &phi_C = globalData->getVectorData(2)[primID]; - NumericType gamma_F = sticking(materialId); - NumericType gamma_FO = stickingOxidized(materialId); + NumericType gamma_F = + params.gamma_F.get(Material::fromLegacyId(materialId)); + NumericType gamma_FO = + params.gamma_F_oxidized.get(Material::fromLegacyId(materialId)); S_eff = gamma_F * std::max(1. - phi_F - phi_O - phi_C, 0.) + gamma_FO * phi_O; } @@ -392,8 +394,10 @@ class CF4O2Etchant final // F surface coverage on oxidized SiGe const auto &phi_C = globalData->getVectorData(2)[primID]; // Obtain the sticking probability - NumericType gamma_F = sticking(materialId); - NumericType gamma_FO = stickingOxidized(materialId); + NumericType gamma_F = + params.gamma_F.get(Material::fromLegacyId(materialId)); + NumericType gamma_FO = + params.gamma_F_oxidized.get(Material::fromLegacyId(materialId)); NumericType S_eff = gamma_F * std::max(1. - phi_F - phi_O - phi_C, 0.) + gamma_FO * phi_O; @@ -405,27 +409,6 @@ class CF4O2Etchant final [[nodiscard]] std::vector getLocalDataLabels() const override { return {"etchantFlux"}; } - -private: - NumericType sticking(const int materialId) const { - auto material = static_cast(materialId); - auto gamma = params.gamma_F.find(material); - if (gamma != params.gamma_F.end()) - return gamma->second; - - // default value - return 1.0; - } - - // Sticking probability for oxidized SiGe or Si - NumericType stickingOxidized(const int materialId) const { - auto material = static_cast(materialId); - auto gamma = params.gamma_F_oxidized.find(material); - if (gamma != params.gamma_F_oxidized.end()) - return gamma->second; - - return 1.0; // Default - } }; template @@ -448,8 +431,10 @@ class CF4O2Oxygen final const auto &phi_F = globalData->getVectorData(0)[primID]; const auto &phi_O = globalData->getVectorData(1)[primID]; const auto &phi_C = globalData->getVectorData(2)[primID]; - NumericType gamma_O = sticking(materialId); - NumericType gamma_OC = stickingPassivated(materialId); + NumericType gamma_O = + params.gamma_O.get(Material::fromLegacyId(materialId)); + NumericType gamma_OC = + params.gamma_O_passivated.get(Material::fromLegacyId(materialId)); S_eff = gamma_O * std::max(1. - phi_O - phi_F - phi_C, 0.) + gamma_OC * phi_C; } @@ -466,8 +451,10 @@ class CF4O2Oxygen final const auto &phi_F = globalData->getVectorData(0)[primID]; const auto &phi_O = globalData->getVectorData(1)[primID]; const auto &phi_C = globalData->getVectorData(2)[primID]; - NumericType gamma_O = sticking(materialId); - NumericType gamma_OC = stickingPassivated(materialId); + NumericType gamma_O = + params.gamma_O.get(Material::fromLegacyId(materialId)); + NumericType gamma_OC = + params.gamma_O_passivated.get(Material::fromLegacyId(materialId)); NumericType S_eff = gamma_O * std::max(1. - phi_O - phi_F - phi_C, 0.) + gamma_OC * phi_C; @@ -479,27 +466,6 @@ class CF4O2Oxygen final [[nodiscard]] std::vector getLocalDataLabels() const override { return {"oxygenFlux"}; } - -private: - NumericType sticking(const int materialId) const { - auto material = static_cast(materialId); - auto gamma = params.gamma_O.find(material); - if (gamma != params.gamma_O.end()) - return gamma->second; - - // default value - return 1.0; - } - - NumericType stickingPassivated(const int materialId) const { - auto material = static_cast(materialId); - auto gamma = params.gamma_O_passivated.find(material); - if (gamma != params.gamma_O_passivated.end()) - return gamma->second; - - // default value - return 1.0; - } }; template @@ -522,8 +488,10 @@ class CF4O2Polymer final const auto &phi_F = globalData->getVectorData(0)[primID]; const auto &phi_O = globalData->getVectorData(1)[primID]; const auto &phi_C = globalData->getVectorData(2)[primID]; - NumericType gamma_C = sticking(materialId); - NumericType gamma_CO = stickingOxidized(materialId); + NumericType gamma_C = + params.gamma_C.get(Material::fromLegacyId(materialId)); + NumericType gamma_CO = + params.gamma_C_oxidized.get(Material::fromLegacyId(materialId)); S_eff = gamma_C * std::max(1. - phi_O - phi_F - phi_C, 0.) + gamma_CO * phi_O; } @@ -540,8 +508,10 @@ class CF4O2Polymer final const auto &phi_F = globalData->getVectorData(0)[primID]; const auto &phi_O = globalData->getVectorData(1)[primID]; const auto &phi_C = globalData->getVectorData(2)[primID]; - NumericType gamma_C = sticking(materialId); - NumericType gamma_CO = stickingOxidized(materialId); + NumericType gamma_C = + params.gamma_C.get(Material::fromLegacyId(materialId)); + NumericType gamma_CO = + params.gamma_C_oxidized.get(Material::fromLegacyId(materialId)); NumericType S_eff = gamma_C * std::max(1. - phi_O - phi_F - phi_C, 0.) + gamma_CO * phi_O; @@ -553,27 +523,6 @@ class CF4O2Polymer final [[nodiscard]] std::vector getLocalDataLabels() const override { return {"polymerFlux"}; } - -private: - NumericType sticking(const int materialId) const { - auto material = static_cast(materialId); - auto gamma = params.gamma_C.find(material); - if (gamma != params.gamma_C.end()) - return gamma->second; - - // default value - return 1.0; - } - - NumericType stickingOxidized(const int materialId) const { - auto material = static_cast(materialId); - auto gamma = params.gamma_C_oxidized.find(material); - if (gamma != params.gamma_C_oxidized.end()) - return gamma->second; - - // default value - return 1.0; - } }; } // namespace impl diff --git a/include/viennaps/models/psCF4O2Parameters.hpp b/include/viennaps/models/psCF4O2Parameters.hpp index 095fc8f1..d3e5a09a 100644 --- a/include/viennaps/models/psCF4O2Parameters.hpp +++ b/include/viennaps/models/psCF4O2Parameters.hpp @@ -1,6 +1,7 @@ #pragma once #include "../materials/psMaterialMap.hpp" +#include "../materials/psMaterialValueMap.hpp" #include "../psConstants.hpp" #include @@ -14,18 +15,44 @@ template struct CF4O2Parameters { NumericType polymerFlux = 1.0e2; // sticking probabilities - std::unordered_map gamma_F = { - {Material::Mask, 0.7}, {Material::Si, 0.7}, {Material::SiGe, 0.7}}; - std::unordered_map gamma_F_oxidized = { - {Material::Mask, 0.3}, {Material::Si, 0.3}, {Material::SiGe, 0.3}}; - std::unordered_map gamma_O = { - {Material::Mask, 1.0}, {Material::Si, 1.0}, {Material::SiGe, 1.0}}; - std::unordered_map gamma_O_passivated = { - {Material::Mask, 0.3}, {Material::Si, 0.3}, {Material::SiGe, 0.3}}; - std::unordered_map gamma_C = { - {Material::Mask, 1.0}, {Material::Si, 1.0}, {Material::SiGe, 1.0}}; - std::unordered_map gamma_C_oxidized = { - {Material::Mask, 0.3}, {Material::Si, 0.3}, {Material::SiGe, 0.3}}; + MaterialValueMap gamma_F = MaterialValueMap( + std::initializer_list>{ + {Material::Mask, 0.7}, {Material::Si, 0.7}, {Material::SiGe, 0.7}}, + 1.0); + + MaterialValueMap gamma_F_oxidized = + MaterialValueMap( + std::initializer_list>{ + {Material::Mask, 0.3}, + {Material::Si, 0.3}, + {Material::SiGe, 0.3}}, + 1.0); + + MaterialValueMap gamma_O = MaterialValueMap( + std::initializer_list>{ + {Material::Mask, 1.0}, {Material::Si, 1.0}, {Material::SiGe, 1.0}}, + 1.0); + + MaterialValueMap gamma_O_passivated = + MaterialValueMap( + std::initializer_list>{ + {Material::Mask, 0.3}, + {Material::Si, 0.3}, + {Material::SiGe, 0.3}}, + 1.0); + + MaterialValueMap gamma_C = MaterialValueMap( + std::initializer_list>{ + {Material::Mask, 1.0}, {Material::Si, 1.0}, {Material::SiGe, 1.0}}, + 1.0); + + MaterialValueMap gamma_C_oxidized = + MaterialValueMap( + std::initializer_list>{ + {Material::Mask, 0.3}, + {Material::Si, 0.3}, + {Material::SiGe, 0.3}}, + 1.0); NumericType etchStopDepth = std::numeric_limits::lowest(); bool fluxIncludeSticking = false; @@ -136,28 +163,28 @@ template struct CF4O2Parameters { // sticking probabilities for (const auto &gamma : gamma_F) { - metaData["gamma_F_" + MaterialMap::toString(gamma.first)] = { - gamma.second}; + metaData["gamma_F_" + MaterialMap::toString(gamma.material)] = { + gamma.value}; } for (const auto &gamma : gamma_F_oxidized) { - metaData["gamma_F_oxidized_" + MaterialMap::toString(gamma.first)] = { - gamma.second}; + metaData["gamma_F_oxidized_" + MaterialMap::toString(gamma.material)] = { + gamma.value}; } for (const auto &gamma : gamma_O) { - metaData["gamma_O_" + MaterialMap::toString(gamma.first)] = { - gamma.second}; + metaData["gamma_O_" + MaterialMap::toString(gamma.material)] = { + gamma.value}; } for (const auto &gamma : gamma_O_passivated) { - metaData["gamma_O_passivated_" + MaterialMap::toString(gamma.first)] = { - gamma.second}; + metaData["gamma_O_passivated_" + MaterialMap::toString(gamma.material)] = + {gamma.value}; } for (const auto &gamma : gamma_C) { - metaData["gamma_C_" + MaterialMap::toString(gamma.first)] = { - gamma.second}; + metaData["gamma_C_" + MaterialMap::toString(gamma.material)] = { + gamma.value}; } for (const auto &gamma : gamma_C_oxidized) { - metaData["gamma_C_oxidized_" + MaterialMap::toString(gamma.first)] = { - gamma.second}; + metaData["gamma_C_oxidized_" + MaterialMap::toString(gamma.material)] = { + gamma.value}; } return metaData; diff --git a/include/viennaps/models/psHBrO2Etching.hpp b/include/viennaps/models/psHBrO2Etching.hpp index db76850a..9edcc752 100644 --- a/include/viennaps/models/psHBrO2Etching.hpp +++ b/include/viennaps/models/psHBrO2Etching.hpp @@ -182,13 +182,9 @@ class HBrO2Etching : public ProcessModelCPU { // sticking probabilities defParams.beta_E.set(Material::Si, 0.1); defParams.beta_E.set(Material::Mask, 0.1); - defParams.beta_E.setDefault(1.0); defParams.beta_P.set(Material::Si, 1.); defParams.beta_P.set(Material::Mask, 1.); - defParams.beta_P.setDefault(1.0); - - defParams.rateFactors.setDefault(1.0); defParams.etchStopDepth = std::numeric_limits::lowest(); diff --git a/include/viennaps/models/psPlasmaEtchingParameters.hpp b/include/viennaps/models/psPlasmaEtchingParameters.hpp index 631da864..1ef043d5 100644 --- a/include/viennaps/models/psPlasmaEtchingParameters.hpp +++ b/include/viennaps/models/psPlasmaEtchingParameters.hpp @@ -14,10 +14,14 @@ template struct PlasmaEtchingParameters { NumericType passivationFlux = 1.0e2; // sticking probabilities - MaterialValueMap beta_E; - MaterialValueMap beta_P; - - MaterialValueMap rateFactors; + MaterialValueMap beta_E = + MaterialValueMap::fromDefault(1.0); + MaterialValueMap beta_P = + MaterialValueMap::fromDefault(1.0); + + MaterialValueMap rateFactors = + MaterialValueMap::fromDefault( + 1.0); // default to 1.0 for all materials NumericType etchStopDepth = std::numeric_limits::lowest(); // Mask diff --git a/include/viennaps/models/psSF6C4F8Etching.hpp b/include/viennaps/models/psSF6C4F8Etching.hpp index e3b9ec13..ebd8d4ca 100644 --- a/include/viennaps/models/psSF6C4F8Etching.hpp +++ b/include/viennaps/models/psSF6C4F8Etching.hpp @@ -171,11 +171,8 @@ class SF6C4F8Etching : public ProcessModelCPU { defParams.beta_E.set(Material::Si, 0.7); defParams.beta_E.set(Material::Mask, 0.02); defParams.beta_E.set(Material::Polymer, 0.02); - defParams.beta_E.setDefault(1.0); // No beta_P needed since passivationFlux = 0 - defParams.rateFactors.setDefault(1.0); - defParams.etchStopDepth = std::numeric_limits::lowest(); // Mask diff --git a/include/viennaps/models/psSF6O2Etching.hpp b/include/viennaps/models/psSF6O2Etching.hpp index a446e0de..a2ffd93a 100644 --- a/include/viennaps/models/psSF6O2Etching.hpp +++ b/include/viennaps/models/psSF6O2Etching.hpp @@ -182,13 +182,9 @@ class SF6O2Etching : public ProcessModelCPU { // sticking probabilities defParams.beta_E.set(Material::Si, 0.7); defParams.beta_E.set(Material::Mask, 0.7); - defParams.beta_E.setDefault(1.0); defParams.beta_P.set(Material::Si, 1.); defParams.beta_P.set(Material::Mask, 1.); - defParams.beta_P.setDefault(1.0); - - defParams.rateFactors.setDefault(1.0); defParams.etchStopDepth = std::numeric_limits::lowest(); diff --git a/include/viennaps/models/psSelectiveEpitaxy.hpp b/include/viennaps/models/psSelectiveEpitaxy.hpp index b6cd6e1b..336bc91a 100644 --- a/include/viennaps/models/psSelectiveEpitaxy.hpp +++ b/include/viennaps/models/psSelectiveEpitaxy.hpp @@ -18,14 +18,16 @@ class EpitaxyVelocityField : public VelocityField { (D > 2) ? 0.5773502691896257 : 0.7071067811865476; static constexpr double high = 1.0; const double factor; + const Vec3D nvFactors; const MaterialValueMap &materialRates; public: EpitaxyVelocityField(const MaterialValueMap &materials, - NumericType r111, NumericType r100) + NumericType r111, NumericType r100, + const Vec3D &rates) : R111(r111), R100(r100), factor((R100 - R111) / (high - low)), - materialRates(materials) {} + materialRates(materials), nvFactors(rates) {} NumericType getScalarVelocity(const Vec3D &, int material, const Vec3D &nv, @@ -33,14 +35,9 @@ class EpitaxyVelocityField : public VelocityField { auto rate = materialRates.get(Material::fromLegacyId(material)); if (rate > 0) { - double vel = std::max(std::abs(nv[0]), std::abs(nv[D - 1])); + double vel = MaxElement(Abs(nvFactors * nv)); vel = (vel - low) * factor + R111; - - if (std::abs(nv[0]) < std::abs(nv[D - 1])) { - vel *= 2.; - } - - return -vel * rate; + return std::min(-vel * rate, 0.0); } // not an epitaxy material @@ -63,24 +60,25 @@ class SelectiveEpitaxy : public ProcessModelCPU { const std::vector> pMaterials, NumericType rate111 = 0.5, NumericType rate100 = 1.) { for (const auto &[material, rate] : pMaterials) { - materialRates.set(material, rate); + materialRates_.set(material, rate); } - // default surface model - auto surfModel = SmartPointer>::New(); + // default nvFactors + nvFactors_.fill(0.); + nvFactors_[0] = 0.5; + nvFactors_[D - 1] = 1.0; - // velocity field - auto velField = - SmartPointer>::New( - materialRates, rate111, rate100); + setup(rate111, rate100); + } - this->setSurfaceModel(surfModel); - this->setVelocityField(velField); - this->setProcessName("SelectiveEpitaxy"); + SelectiveEpitaxy(const Vec3D &nvFactors, + NumericType rate111 = 0.5, NumericType rate100 = 1.) + : nvFactors_(nvFactors) { + setup(rate111, rate100); } void setMaterialRate(Material material, NumericType rate) { - materialRates.set(material, rate); + materialRates_.set(material, rate); } void initialize(SmartPointer> domain, @@ -143,12 +141,27 @@ class SelectiveEpitaxy : public ProcessModelCPU { } private: - MaterialValueMap materialRates; + MaterialValueMap materialRates_; + Vec3D nvFactors_; SmartPointer> domainCopy; bool firstInit = true; + void setup(const NumericType rate111, const NumericType rate100) { + // default surface model + auto surfModel = SmartPointer>::New(); + + // velocity field + auto velField = + SmartPointer>::New( + materialRates_, rate111, rate100, nvFactors_); + + this->setSurfaceModel(surfModel); + this->setVelocityField(velField); + this->setProcessName("SelectiveEpitaxy"); + } + bool isEpitaxyMaterial(const Material &material) const { - return materialRates.get(material) != NumericType(0); + return materialRates_.get(material) != NumericType(0); } }; diff --git a/python/pyWrapDimension.hpp b/python/pyWrapDimension.hpp index eab05f58..ed2fe981 100644 --- a/python/pyWrapDimension.hpp +++ b/python/pyWrapDimension.hpp @@ -1112,6 +1112,10 @@ template void bindApi(py::module &module) { const std::vector>, T, T>), py::arg("materialRates"), py::arg("rate111") = 0.5, py::arg("rate100") = 1.0) + .def(py::init(&SmartPointer>::template New< + const Vec3D &, T, T>), + py::arg("nvFactors"), py::arg("rate111") = 0.5, + py::arg("rate100") = 1.0) .def("setMaterialRate", &SelectiveEpitaxy::setMaterialRate, py::arg("material"), py::arg("rate")); diff --git a/python/viennaps/_core/__init__.pyi b/python/viennaps/_core/__init__.pyi index 8e9fbc82..6d941552 100644 --- a/python/viennaps/_core/__init__.pyi +++ b/python/viennaps/_core/__init__.pyi @@ -6,8 +6,8 @@ import collections.abc import enum import typing import viennals._core -from viennaps import d2 import viennaps.d2 +from viennaps import d2 from viennaps import d3 import viennaps.d3 from . import constants @@ -195,6 +195,12 @@ class CF4O2Parameters: Si: CF4O2ParametersSi SiGe: CF4O2ParametersSiGe fluxIncludeSticking: bool + gamma_C: MaterialValueMap + gamma_C_oxidized: MaterialValueMap + gamma_F: MaterialValueMap + gamma_F_oxidized: MaterialValueMap + gamma_O: MaterialValueMap + gamma_O_passivated: MaterialValueMap def __init__(self) -> None: ... @property @@ -210,42 +216,6 @@ class CF4O2Parameters: def etchantFlux(self, arg0: typing.SupportsFloat | typing.SupportsIndex) -> None: ... @property - def gamma_C(self) -> dict[Material, float]: - ... - @gamma_C.setter - def gamma_C(self, arg0: collections.abc.Mapping[Material, typing.SupportsFloat | typing.SupportsIndex]) -> None: - ... - @property - def gamma_C_oxidized(self) -> dict[Material, float]: - ... - @gamma_C_oxidized.setter - def gamma_C_oxidized(self, arg0: collections.abc.Mapping[Material, typing.SupportsFloat | typing.SupportsIndex]) -> None: - ... - @property - def gamma_F(self) -> dict[Material, float]: - ... - @gamma_F.setter - def gamma_F(self, arg0: collections.abc.Mapping[Material, typing.SupportsFloat | typing.SupportsIndex]) -> None: - ... - @property - def gamma_F_oxidized(self) -> dict[Material, float]: - ... - @gamma_F_oxidized.setter - def gamma_F_oxidized(self, arg0: collections.abc.Mapping[Material, typing.SupportsFloat | typing.SupportsIndex]) -> None: - ... - @property - def gamma_O(self) -> dict[Material, float]: - ... - @gamma_O.setter - def gamma_O(self, arg0: collections.abc.Mapping[Material, typing.SupportsFloat | typing.SupportsIndex]) -> None: - ... - @property - def gamma_O_passivated(self) -> dict[Material, float]: - ... - @gamma_O_passivated.setter - def gamma_O_passivated(self, arg0: collections.abc.Mapping[Material, typing.SupportsFloat | typing.SupportsIndex]) -> None: - ... - @property def ionFlux(self) -> float: ... @ionFlux.setter diff --git a/python/viennaps/d2/__init__.pyi b/python/viennaps/d2/__init__.pyi index 0177d786..f464981c 100644 --- a/python/viennaps/d2/__init__.pyi +++ b/python/viennaps/d2/__init__.pyi @@ -890,8 +890,17 @@ class SF6O2Etching(ProcessModel): def setParameters(self, arg0: viennaps._core.PlasmaEtchingParameters) -> None: ... class SelectiveEpitaxy(ProcessModel): + @typing.overload + def __init__(self, rate111: typing.SupportsFloat | typing.SupportsIndex = 0.5, rate100: typing.SupportsFloat | typing.SupportsIndex = 1.0) -> None: + ... + @typing.overload def __init__(self, materialRates: collections.abc.Sequence[tuple[viennaps._core.Material, typing.SupportsFloat | typing.SupportsIndex]], rate111: typing.SupportsFloat | typing.SupportsIndex = 0.5, rate100: typing.SupportsFloat | typing.SupportsIndex = 1.0) -> None: ... + @typing.overload + def __init__(self, nvFactors: typing.Annotated[collections.abc.Sequence[typing.SupportsFloat | typing.SupportsIndex], "FixedSize(3)"], rate111: typing.SupportsFloat | typing.SupportsIndex = 0.5, rate100: typing.SupportsFloat | typing.SupportsIndex = 1.0) -> None: + ... + def setMaterialRate(self, material: viennaps._core.Material, rate: typing.SupportsFloat | typing.SupportsIndex) -> None: + ... class SingleParticleALD(ProcessModel): def __init__(self, stickingProbability: typing.SupportsFloat | typing.SupportsIndex, numCycles: typing.SupportsInt | typing.SupportsIndex, growthPerCycle: typing.SupportsFloat | typing.SupportsIndex, totalCycles: typing.SupportsInt | typing.SupportsIndex, coverageTimeStep: typing.SupportsFloat | typing.SupportsIndex, evFlux: typing.SupportsFloat | typing.SupportsIndex, inFlux: typing.SupportsFloat | typing.SupportsIndex, s0: typing.SupportsFloat | typing.SupportsIndex, gasMFP: typing.SupportsFloat | typing.SupportsIndex) -> None: ... diff --git a/python/viennaps/d3/__init__.pyi b/python/viennaps/d3/__init__.pyi index 476bfe78..fc196ea6 100644 --- a/python/viennaps/d3/__init__.pyi +++ b/python/viennaps/d3/__init__.pyi @@ -890,8 +890,17 @@ class SF6O2Etching(ProcessModel): def setParameters(self, arg0: viennaps._core.PlasmaEtchingParameters) -> None: ... class SelectiveEpitaxy(ProcessModel): + @typing.overload + def __init__(self, rate111: typing.SupportsFloat | typing.SupportsIndex = 0.5, rate100: typing.SupportsFloat | typing.SupportsIndex = 1.0) -> None: + ... + @typing.overload def __init__(self, materialRates: collections.abc.Sequence[tuple[viennaps._core.Material, typing.SupportsFloat | typing.SupportsIndex]], rate111: typing.SupportsFloat | typing.SupportsIndex = 0.5, rate100: typing.SupportsFloat | typing.SupportsIndex = 1.0) -> None: ... + @typing.overload + def __init__(self, nvFactors: typing.Annotated[collections.abc.Sequence[typing.SupportsFloat | typing.SupportsIndex], "FixedSize(3)"], rate111: typing.SupportsFloat | typing.SupportsIndex = 0.5, rate100: typing.SupportsFloat | typing.SupportsIndex = 1.0) -> None: + ... + def setMaterialRate(self, material: viennaps._core.Material, rate: typing.SupportsFloat | typing.SupportsIndex) -> None: + ... class SingleParticleALD(ProcessModel): def __init__(self, stickingProbability: typing.SupportsFloat | typing.SupportsIndex, numCycles: typing.SupportsInt | typing.SupportsIndex, growthPerCycle: typing.SupportsFloat | typing.SupportsIndex, totalCycles: typing.SupportsInt | typing.SupportsIndex, coverageTimeStep: typing.SupportsFloat | typing.SupportsIndex, evFlux: typing.SupportsFloat | typing.SupportsIndex, inFlux: typing.SupportsFloat | typing.SupportsIndex, s0: typing.SupportsFloat | typing.SupportsIndex, gasMFP: typing.SupportsFloat | typing.SupportsIndex) -> None: ... diff --git a/tests/materialValueMap/materialValueMap.cpp b/tests/materialValueMap/materialValueMap.cpp index 3883cfb4..375a8210 100644 --- a/tests/materialValueMap/materialValueMap.cpp +++ b/tests/materialValueMap/materialValueMap.cpp @@ -1,3 +1,4 @@ +#include #include #include From 966ab6a5ec13bb23564f14a799fbc350be79837a Mon Sep 17 00:00:00 2001 From: reiter Date: Thu, 2 Apr 2026 15:07:00 +0200 Subject: [PATCH 29/32] fix: clean up finFET example --- examples/emulation/FinFET.cpp | 2 -- examples/emulation/FinFET.py | 14 -------------- 2 files changed, 16 deletions(-) diff --git a/examples/emulation/FinFET.cpp b/examples/emulation/FinFET.cpp index 4f75306e..b088bbb9 100644 --- a/examples/emulation/FinFET.cpp +++ b/examples/emulation/FinFET.cpp @@ -1,7 +1,5 @@ #include -#include - using namespace viennaps; constexpr int D = 3; diff --git a/examples/emulation/FinFET.py b/examples/emulation/FinFET.py index 4ad37157..fc4c000d 100644 --- a/examples/emulation/FinFET.py +++ b/examples/emulation/FinFET.py @@ -1,21 +1,9 @@ import viennaps as ps ps.setDimension(3) -volumeOutput = False -volumeNum = 0 surfaceNum = 0 -def writeVolume(domain): - if not volumeOutput: - return - global volumeNum - print("Writing volume mesh ...", end="", flush=True) - domain.saveVolumeMesh("FinFET_" + str(volumeNum)) - print(" done") - volumeNum += 1 - - def writeSurface(domain): global surfaceNum domain.saveSurfaceMesh("FinFET_" + str(surfaceNum) + ".vtp", True) @@ -168,7 +156,6 @@ def writeSurface(domain): ps.Process(domain, dist, 0).apply() print(" done") writeSurface(domain) -writeVolume(domain) # isotropic etch (fin-release) print("Fin-Release ...", end="", flush=True) @@ -181,7 +168,6 @@ def writeSurface(domain): process.apply() print(" done") writeSurface(domain) -writeVolume(domain) # source/drain epitaxy print("S/D Epitaxy ...", end="", flush=True) From b0f2b6441656a9fdf075a9e466bbe02ab6106442 Mon Sep 17 00:00:00 2001 From: reiter Date: Thu, 2 Apr 2026 15:46:42 +0200 Subject: [PATCH 30/32] refactor: MultiParticleProcess uses MaterialValueMap --- .../models/psMultiParticleProcess.hpp | 55 +++++++++---------- python/pyWrapDimension.hpp | 2 +- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/include/viennaps/models/psMultiParticleProcess.hpp b/include/viennaps/models/psMultiParticleProcess.hpp index 2a474ef4..192bdf76 100644 --- a/include/viennaps/models/psMultiParticleProcess.hpp +++ b/include/viennaps/models/psMultiParticleProcess.hpp @@ -1,6 +1,7 @@ #pragma once #include "../materials/psMaterialMap.hpp" +#include "../materials/psMaterialValueMap.hpp" #include "../process/psProcessModel.hpp" #include "../psConstants.hpp" #include "../psUtil.hpp" @@ -170,14 +171,12 @@ template class DiffuseParticle : public viennaray::Particle, NumericType> { public: - DiffuseParticle(NumericType stickingProbability, const std::string &dataLabel) - : stickingProbability_(stickingProbability), dataLabel_(dataLabel) {} - - DiffuseParticle(NumericType stickingProbability, - std::unordered_map materialSticking, - const std::string &dataLabel) - : materialSticking_(materialSticking), - stickingProbability_(stickingProbability), dataLabel_(dataLabel) {} + DiffuseParticle( + NumericType defaultStickingProbability, + const std::unordered_map &materialSticking, + const std::string &dataLabel) + : materialSticking_(materialSticking, defaultStickingProbability), + dataLabel_(dataLabel) {} void surfaceCollision(NumericType rayWeight, const Vec3D &rayDir, const Vec3D &geomNormal, @@ -193,13 +192,7 @@ class DiffuseParticle const int materialId, const viennaray::TracingData *, RNG &rngState) override final { - NumericType sticking = stickingProbability_; - if (auto mat = - materialSticking_.find(MaterialMap::mapToMaterial(materialId)); - mat != materialSticking_.end()) { - sticking = mat->second; - } - + auto sticking = materialSticking_.get(Material::fromLegacyId(materialId)); auto direction = viennaray::ReflectionDiffuse(geomNormal, rngState); return std::pair>{sticking, direction}; @@ -209,8 +202,7 @@ class DiffuseParticle } public: - const std::unordered_map materialSticking_; - const NumericType stickingProbability_; + const MaterialValueMap materialSticking_; const std::string dataLabel_; }; } // namespace impl @@ -310,11 +302,11 @@ class MultiParticleProcess final : public ProcessModelGPU { if (!neutralAdded[i] && label == neutrals[i].dataLabel_) { const auto &neutral = neutrals[i]; if (neutral.materialSticking_.empty()) { - addNeutralParticle(neutral.stickingProbability_, + addNeutralParticle(neutral.materialSticking_.getDefault(), neutral.dataLabel_); } else { addNeutralParticle(neutral.materialSticking_, - neutral.stickingProbability_, + neutral.materialSticking_.getDefault(), neutral.dataLabel_); } neutralAdded[i] = true; @@ -345,10 +337,12 @@ class MultiParticleProcess final : public ProcessModelGPU { "conversion. Appending neutral particle with label: " + neutral.dataLabel_); if (neutral.materialSticking_.empty()) { - addNeutralParticle(neutral.stickingProbability_, neutral.dataLabel_); + addNeutralParticle(neutral.materialSticking_.getDefault(), + neutral.dataLabel_); } else { addNeutralParticle(neutral.materialSticking_, - neutral.stickingProbability_, neutral.dataLabel_); + neutral.materialSticking_.getDefault(), + neutral.dataLabel_); } } @@ -378,10 +372,9 @@ class MultiParticleProcess final : public ProcessModelGPU { std::to_string(stickingProbability)); } - void addNeutralParticle( - const std::unordered_map &materialSticking, - NumericType defaultStickingProbability = 1., - const std::string &label = "neutralFlux") { + void addNeutralParticle(const MaterialValueMap &materialSticking, + NumericType defaultStickingProbability = 1., + const std::string &label = "neutralFlux") { std::string dataLabel = label + std::to_string(fluxDataLabels_.size()); fluxDataLabels_.push_back(dataLabel); @@ -389,8 +382,8 @@ class MultiParticleProcess final : public ProcessModelGPU { particle.name = "Neutral"; particle.sticking = defaultStickingProbability; particle.dataLabels.push_back(dataLabel); - for (auto &mat : materialSticking) { - particle.materialSticking[static_cast(mat.first)] = mat.second; + for (auto entry : materialSticking) { + particle.materialSticking[static_cast(entry.material)] = entry.value; } this->insertNextParticleType(particle); @@ -535,12 +528,16 @@ class MultiParticleProcess : public ProcessModelCPU { const std::string &label = "neutralFlux") { std::string dataLabel = label + std::to_string(fluxDataLabels_.size()); fluxDataLabels_.push_back(dataLabel); + std::unordered_map + materialSticking; // empty map will use default sticking for all + // materials auto particle = std::make_unique>( - stickingProbability, dataLabel); + stickingProbability, materialSticking, dataLabel); this->insertNextParticleType(particle); addStickingData(stickingProbability); - neutralParticles_.emplace_back(stickingProbability, dataLabel); + neutralParticles_.emplace_back(stickingProbability, materialSticking, + dataLabel); } void addNeutralParticle( diff --git a/python/pyWrapDimension.hpp b/python/pyWrapDimension.hpp index ed2fe981..3c1ad6b5 100644 --- a/python/pyWrapDimension.hpp +++ b/python/pyWrapDimension.hpp @@ -1549,7 +1549,7 @@ template void bindApi(py::module &module) { &gpu::MultiParticleProcess::addNeutralParticle), py::arg("stickingProbability"), py::arg("label") = "neutralFlux") .def("addNeutralParticle", - py::overload_cast &, T, + py::overload_cast &, T, const std::string &>( &gpu::MultiParticleProcess::addNeutralParticle), py::arg("materialSticking"), From e8a9efc2658a110cf46a9557b70ea53bf807dade Mon Sep 17 00:00:00 2001 From: reiter Date: Thu, 2 Apr 2026 17:03:41 +0200 Subject: [PATCH 31/32] fix: check all examples --- examples/SiGeSelectiveEtching/SiGeEtching.py | 53 +++-- .../atomicLayerDeposition.py | 191 +++++++----------- .../blazedGratingsEtching.py | 3 +- examples/holeEtching/holeEtching.py | 3 +- examples/ionBeamEtching/ionBeamEtching.py | 2 +- .../viennaps/models/psFaradayCageEtching.hpp | 19 +- python/scripts/run_examples_from_list.py | 181 +++++++++++++++++ python/viennaps/_core/__init__.pyi | 4 +- python/viennaps/d2/gpu.pyi | 2 +- python/viennaps/d3/gpu.pyi | 2 +- 10 files changed, 298 insertions(+), 162 deletions(-) create mode 100644 python/scripts/run_examples_from_list.py diff --git a/examples/SiGeSelectiveEtching/SiGeEtching.py b/examples/SiGeSelectiveEtching/SiGeEtching.py index 1c5a1029..5caeb64c 100644 --- a/examples/SiGeSelectiveEtching/SiGeEtching.py +++ b/examples/SiGeSelectiveEtching/SiGeEtching.py @@ -40,36 +40,29 @@ modelParams.Passivation.A_C_ie = params["A_C"] # Use Material enum -modelParams.gamma_F = { - ps.Material.Mask: 0.0, - ps.Material.Si: 0.1, - ps.Material.SiGe: 0.1, -} -modelParams.gamma_F_oxidized = { - ps.Material.Mask: 0.0, - ps.Material.Si: 0.1, - ps.Material.SiGe: 0.1, -} -modelParams.gamma_O = { - ps.Material.Mask: 0.0, - ps.Material.Si: 0.7, - ps.Material.SiGe: 0.7, -} -modelParams.gamma_O_passivated = { - ps.Material.Mask: 0.0, - ps.Material.Si: 0.7, - ps.Material.SiGe: 0.7, -} -modelParams.gamma_C = { - ps.Material.Mask: 0.0, - ps.Material.Si: 0.7, - ps.Material.SiGe: 0.7, -} -modelParams.gamma_C_oxidized = { - ps.Material.Mask: 0.0, - ps.Material.Si: 0.7, - ps.Material.SiGe: 0.7, -} +modelParams.gamma_F.set(ps.Material.Mask, 0.0) +modelParams.gamma_F.set(ps.Material.Si, 0.1) +modelParams.gamma_F.set(ps.Material.SiGe, 0.1) + +modelParams.gamma_F_oxidized.set(ps.Material.Mask, 0.0) +modelParams.gamma_F_oxidized.set(ps.Material.Si, 0.1) +modelParams.gamma_F_oxidized.set(ps.Material.SiGe, 0.1) + +modelParams.gamma_O.set(ps.Material.Mask, 0.0) +modelParams.gamma_O.set(ps.Material.Si, 0.7) +modelParams.gamma_O.set(ps.Material.SiGe, 0.7) + +modelParams.gamma_O_passivated.set(ps.Material.Mask, 0.0) +modelParams.gamma_O_passivated.set(ps.Material.Si, 0.7) +modelParams.gamma_O_passivated.set(ps.Material.SiGe, 0.7) + +modelParams.gamma_C.set(ps.Material.Mask, 0.0) +modelParams.gamma_C.set(ps.Material.Si, 0.7) +modelParams.gamma_C.set(ps.Material.SiGe, 0.7) + +modelParams.gamma_C_oxidized.set(ps.Material.Mask, 0.0) +modelParams.gamma_C_oxidized.set(ps.Material.Si, 0.7) +modelParams.gamma_C_oxidized.set(ps.Material.SiGe, 0.7) model = ps.CF4O2Etching(modelParams) parameters = model.getParameters() diff --git a/examples/atomicLayerDeposition/atomicLayerDeposition.py b/examples/atomicLayerDeposition/atomicLayerDeposition.py index 9fec9359..1626ecc9 100644 --- a/examples/atomicLayerDeposition/atomicLayerDeposition.py +++ b/examples/atomicLayerDeposition/atomicLayerDeposition.py @@ -1,127 +1,86 @@ import viennaps as ps import viennals as ls -import matplotlib.pyplot as plt -import numpy as np ps.setDimension(2) ls.setDimension(2) -ps.Logger.setLogLevel(ps.LogLevel.DEBUG) - - -def run_simulation(gd, filename): - params = ps.readConfigFile("config.txt") - geometry = ps.Domain() - - # Create the geometry - boundaryCons = [ - ps.BoundaryType.REFLECTIVE_BOUNDARY, - ps.BoundaryType.INFINITE_BOUNDARY, - ] - gridDelta = gd - bounds = [ - 0.0, - params["openingWidth"] / 2.0 + params["xPad"] + params["gapLength"], - -gridDelta, - params["openingDepth"] + params["gapHeight"] + gridDelta, - ] - - substrate = ls.Domain(bounds, boundaryCons, gridDelta) - normal = [0.0, 1.0] - origin = [0.0, params["openingDepth"] + params["gapHeight"]] - ls.MakeGeometry(substrate, ls.Plane(origin, normal)).apply() - - geometry.insertNextLevelSetAsMaterial(substrate, ps.Material.Si) - - vertBox = ls.Domain(bounds, boundaryCons, gridDelta) - minPoint = [-gridDelta, 0.0] - maxPoint = [ - params["openingWidth"] / 2.0, - params["gapHeight"] + params["openingDepth"] + gridDelta, - ] - ls.MakeGeometry(vertBox, ls.Box(minPoint, maxPoint)).apply() - - geometry.applyBooleanOperation(vertBox, ls.BooleanOperationEnum.RELATIVE_COMPLEMENT) - - horiBox = ls.Domain(bounds, boundaryCons, gridDelta) - minPoint = [params["openingWidth"] / 2.0 - gridDelta, 0.0] - maxPoint = [params["openingWidth"] / 2.0 + params["gapLength"], params["gapHeight"]] - ls.MakeGeometry(horiBox, ls.Box(minPoint, maxPoint)).apply() - geometry.applyBooleanOperation(horiBox, ls.BooleanOperationEnum.RELATIVE_COMPLEMENT) - - geometry.saveSurfaceMesh("SingleParticleALD_initial") - - model = ps.SingleParticleProcess(rate=1.0, stickingProbability=1e-4) - - geometry.duplicateTopLevelSet(ps.Material.Al2O3) - - gasMFP = ps.constants.gasMeanFreePath( - params["pressure"], params["temperature"], params["diameter"] - ) - print("Mean free path: ", gasMFP, " um") - - model = ps.SingleParticleALD( - stickingProbability=params["stickingProbability"], - numCycles=int(params["numCycles"]), - growthPerCycle=params["growthPerCycle"], - totalCycles=int(params["totalCycles"]), - coverageTimeStep=params["coverageTimeStep"], - evFlux=params["evFlux"], - inFlux=params["inFlux"], - s0=params["s0"], - gasMFP=gasMFP, - ) - model.setProcessName(filename) - - alpParams = ps.AtomicLayerProcessParameters() - alpParams.pulseTime = params["pulseTime"] - alpParams.coverageTimeStep = params["coverageTimeStep"] - alpParams.numCycles = 1 - - p = ps.Process(geometry, model, 1.0) - p.setParameters(alpParams) - p.setFluxEngineType(ps.FluxEngineType.GPU_TRIANGLE) - p.apply() - - # flux = p.calculateFlux() - - # points = np.array(flux.getNodes()) - # fluxValues = np.array(flux.getCellData().getScalarData("particleFlux", True)) - - # cuts = points[:, 1] < 0.25 - # points = points[cuts] - # fluxValues = fluxValues[cuts] - - # ps.ls.VTKWriter(flux, filename).apply() - - mesh = ps.ls.Mesh() - ps.ToDiskMesh(geometry, mesh).apply() - points = np.array(mesh.getNodes()) - cuts = points[:, 1] < 0.25 - points = points[cuts] - return points - # return points, fluxValues - - -p = run_simulation(0.1, "flux_0p1") -plt.plot(p[:, 0], p[:, 1], "--") -p = run_simulation(0.01, "flux_0p01") -plt.plot(p[:, 0], p[:, 1], ".-") -p = run_simulation(0.05, "flux_0p05") -plt.plot(p[:, 0], p[:, 1], "-") -plt.show() - - -# rayParams = ps.RayTracingParameters() -# rayParams.raysPerPoint = int(params["numRaysPerPoint"]) - -# ALP = ps.Process(geometry, model) -# ALP.setParameters(rayParams) -# ALP.setParameters(alpParams) -# ALP.apply() +params = ps.readConfigFile("config.txt") +geometry = ps.Domain() + +# Create the geometry +boundaryCons = [ + ps.BoundaryType.REFLECTIVE_BOUNDARY, + ps.BoundaryType.INFINITE_BOUNDARY, +] +gridDelta = params["gridDelta"] +bounds = [ + 0.0, + params["openingWidth"] / 2.0 + params["xPad"] + params["gapLength"], + -gridDelta, + params["openingDepth"] + params["gapHeight"] + gridDelta, +] + +substrate = ls.Domain(bounds, boundaryCons, gridDelta) +normal = [0.0, 1.0] +origin = [0.0, params["openingDepth"] + params["gapHeight"]] +ls.MakeGeometry(substrate, ls.Plane(origin, normal)).apply() + +geometry.insertNextLevelSetAsMaterial(substrate, ps.Material.Si) + +vertBox = ls.Domain(bounds, boundaryCons, gridDelta) +minPoint = [-gridDelta, 0.0] +maxPoint = [ + params["openingWidth"] / 2.0, + params["gapHeight"] + params["openingDepth"] + gridDelta, +] +ls.MakeGeometry(vertBox, ls.Box(minPoint, maxPoint)).apply() + +geometry.applyBooleanOperation(vertBox, ls.BooleanOperationEnum.RELATIVE_COMPLEMENT) + +horiBox = ls.Domain(bounds, boundaryCons, gridDelta) +minPoint = [params["openingWidth"] / 2.0 - gridDelta, 0.0] +maxPoint = [params["openingWidth"] / 2.0 + params["gapLength"], params["gapHeight"]] +ls.MakeGeometry(horiBox, ls.Box(minPoint, maxPoint)).apply() +geometry.applyBooleanOperation(horiBox, ls.BooleanOperationEnum.RELATIVE_COMPLEMENT) + +geometry.saveSurfaceMesh("SingleParticleALD_initial") + +model = ps.SingleParticleProcess(rate=1.0, stickingProbability=1e-4) + +geometry.duplicateTopLevelSet(ps.Material.Al2O3) + +gasMFP = ps.constants.gasMeanFreePath( + params["pressure"], params["temperature"], params["diameter"] +) +print("Mean free path: ", gasMFP, " um") + +model = ps.SingleParticleALD( + stickingProbability=params["stickingProbability"], + numCycles=int(params["numCycles"]), + growthPerCycle=params["growthPerCycle"], + totalCycles=int(params["totalCycles"]), + coverageTimeStep=params["coverageTimeStep"], + evFlux=params["evFlux"], + inFlux=params["inFlux"], + s0=params["s0"], + gasMFP=gasMFP, +) + +alpParams = ps.AtomicLayerProcessParameters() +alpParams.pulseTime = params["pulseTime"] +alpParams.coverageTimeStep = params["coverageTimeStep"] +alpParams.numCycles = int(params["numCycles"]) + +rayParams = ps.RayTracingParameters() +rayParams.raysPerPoint = int(params["numRaysPerPoint"]) + +ALP = ps.Process(geometry, model) +ALP.setParameters(rayParams) +ALP.setParameters(alpParams) +ALP.apply() # ## TODO: Implement MeasureProfile in Python # # MeasureProfile(domain, params.get("gapHeight") / 2.) # # .save(params.get("outputFile")); -# geometry.saveVolumeMesh("SingleParticleALD_final") +geometry.saveSurfaceMesh("SingleParticleALD_final") diff --git a/examples/blazedGratingsEtching/blazedGratingsEtching.py b/examples/blazedGratingsEtching/blazedGratingsEtching.py index fa09aff8..4f88ff91 100644 --- a/examples/blazedGratingsEtching/blazedGratingsEtching.py +++ b/examples/blazedGratingsEtching/blazedGratingsEtching.py @@ -64,7 +64,8 @@ ibeParams = ps.IBEParameters() ibeParams.exponent = params["exponent"] ibeParams.meanEnergy = params["meanEnergy"] -ibeParams.materialPlaneWaferRate = {ps.Material.SiO2: 1, ps.Material.Mask: 1 / 11} +ibeParams.materialPlaneWaferRate.set(ps.Material.SiO2, 1.0) +ibeParams.materialPlaneWaferRate.set(ps.Material.Mask, 1.0 / 11.0) ibeParams.cos4Yield.isDefined = True ibeParams.cos4Yield.a1 = params["yieldFactor"] ibeParams.cos4Yield.a2 = -1.55 diff --git a/examples/holeEtching/holeEtching.py b/examples/holeEtching/holeEtching.py index 31ad612b..30c60b24 100644 --- a/examples/holeEtching/holeEtching.py +++ b/examples/holeEtching/holeEtching.py @@ -89,7 +89,8 @@ def run_simulation(intermediate_velocities, suffix): output_file += suffix geometry.saveSurfaceMesh( - filename=output_file, addInterfaces=True, boolMaterials=True + filename=output_file, + addInterfaces=True, ) diff --git a/examples/ionBeamEtching/ionBeamEtching.py b/examples/ionBeamEtching/ionBeamEtching.py index 60e5685c..6ba8250c 100644 --- a/examples/ionBeamEtching/ionBeamEtching.py +++ b/examples/ionBeamEtching/ionBeamEtching.py @@ -35,7 +35,7 @@ # copy top layer to capture deposition geometry.duplicateTopLevelSet(ps.Material.Polymer) -ibeParams = ps.IonBeamEtching.defaultParamters() +ibeParams = ps.IonBeamEtching.defaultParameters() ibeParams.tiltAngle = params["angle"] ibeParams.exponent = params["exponent"] ibeParams.thetaRMin = 0.0 diff --git a/include/viennaps/models/psFaradayCageEtching.hpp b/include/viennaps/models/psFaradayCageEtching.hpp index 85759971..a4b03bcb 100644 --- a/include/viennaps/models/psFaradayCageEtching.hpp +++ b/include/viennaps/models/psFaradayCageEtching.hpp @@ -257,18 +257,18 @@ class FaradayCageEtching final : public ProcessModelGPU { // surface model // adjust rate here since we trace two particles per point - auto surfaceModelParams = params_.ibeParams; - surfaceModelParams.planeWaferRate *= 0.5; - surfaceModelParams.materialPlaneWaferRate.setDefault( - surfaceModelParams.planeWaferRate); - for (auto entry : surfaceModelParams.materialPlaneWaferRate) { - surfaceModelParams.materialPlaneWaferRate.set(entry.material, - entry.value * 0.5); + surfaceModelParams_ = params_.ibeParams; + surfaceModelParams_.planeWaferRate *= 0.5; + surfaceModelParams_.materialPlaneWaferRate.setDefault( + surfaceModelParams_.planeWaferRate); + for (auto entry : params_.ibeParams.materialPlaneWaferRate) { + surfaceModelParams_.materialPlaneWaferRate.set(entry.material, + entry.value * 0.5); } - surfaceModelParams.redepositionRate *= 0.5; + surfaceModelParams_.redepositionRate *= 0.5; auto surfModel = SmartPointer<::viennaps::impl::IBESurfaceModel>::New( - surfaceModelParams); + surfaceModelParams_); // velocity field auto velField = SmartPointer>::New(); @@ -284,6 +284,7 @@ class FaradayCageEtching final : public ProcessModelGPU { private: FaradayCageParameters params_; + IBEParameters surfaceModelParams_; }; } // namespace gpu #endif diff --git a/python/scripts/run_examples_from_list.py b/python/scripts/run_examples_from_list.py new file mode 100644 index 00000000..e3f86010 --- /dev/null +++ b/python/scripts/run_examples_from_list.py @@ -0,0 +1,181 @@ +import argparse +import os +import subprocess +import sys +from typing import Dict, List + + +# List of examples to run +EXAMPLES: List[Dict[str, object]] = [ + { + "name": "Trench_Deposition", + "dir": "examples/trenchDeposition", + "script": "trenchDeposition.py", + "args": ["-D", "2", "config.txt"], + }, + { + "name": "Hole_Etching", + "dir": "examples/holeEtching", + "script": "holeEtching.py", + "args": ["-D", "2", "config.txt"], + }, + { + "name": "Blazed_Gratings_Etching", + "dir": "examples/blazedGratingsEtching", + "script": "blazedGratingsEtching.py", + "args": ["config.txt"], + }, + { + "name": "Bosch_simulate", + "dir": "examples/boschProcess", + "script": "boschProcessSimulate.py", + "args": ["-D", "2", "config.txt"], + }, + { + "name": "Bosch_emulate", + "dir": "examples/boschProcess", + "script": "boschProcessEmulate.py", + "args": ["-D", "2", "config.txt"], + }, + { + "name": "Ion_Beam_Etching", + "dir": "examples/ionBeamEtching", + "script": "ionBeamEtching.py", + "args": ["-D", "2", "config.txt"], + }, + { + "name": "Faraday_Cage_Etching", + "dir": "examples/faradayCageEtching", + "script": "faradayCageEtching.py", + "args": ["-D", "2", "config.txt"], + }, + { + "name": "Selective_Epitaxy", + "dir": "examples/selectiveEpitaxy", + "script": "selectiveEpitaxy.py", + "args": ["-D", "3", "config.txt"], + }, + { + "name": "Simple_Etching", + "dir": "examples/simpleEtching", + "script": "simpleEtching.py", + "args": [], + }, + { + "name": "Stack_Etching", + "dir": "examples/stackEtching", + "script": "stackEtching.py", + "args": ["config.txt"], + }, + { + "name": "Single_TEOS", + "dir": "examples/TEOSTrenchDeposition", + "script": "singleTEOS.py", + "args": ["-D", "2", "singleTEOS_config.txt"], + }, + { + "name": "Multi_TEOS", + "dir": "examples/TEOSTrenchDeposition", + "script": "multiTEOS.py", + "args": ["-D", "2", "multiTEOS_config.txt"], + }, + { + "name": "Sputter_Deposition", + "dir": "examples/sputterDeposition", + "script": "sputterDeposition.py", + "args": ["-D", "3", "config3D.txt"], + }, + { + "name": "SiGe_Selective_Etching", + "dir": "examples/SiGeSelectiveEtching", + "script": "SiGeEtching.py", + "args": [], + }, + { + "name": "Cantilever_Wet_Etching", + "dir": "examples/cantileverWetEtching", + "script": "cantileverWetEtching.py", + "args": [], + }, +] + + +def run_example(source_dir: str, example: Dict[str, object], python_exe: str) -> bool: + example_name = str(example["name"]) + rel_dir = str(example["dir"]) + script = str(example["script"]) + args = [str(arg) for arg in example.get("args", [])] + + working_dir = os.path.join(source_dir, rel_dir) + script_path = os.path.join(working_dir, script) + + if not os.path.isdir(working_dir): + print(f"[FAIL] {example_name}: example directory not found: {working_dir}") + return False + + if not os.path.isfile(script_path): + print(f"[FAIL] {example_name}: script not found: {script_path}") + return False + + cmd = [python_exe, script] + args + print(f"[RUN ] {example_name}: {' '.join(cmd)}") + + try: + subprocess.run(cmd, cwd=working_dir, check=True) + print(f"[ OK ] {example_name}") + return True + except subprocess.CalledProcessError as error: + print(f"[FAIL] {example_name}: exited with code {error.returncode}") + return False + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Run configured ViennaPS examples from a source folder" + ) + parser.add_argument( + "source_dir", + nargs="?", + default=".", + help="Path to ViennaPS source folder containing .venv and examples/", + ) + parser.add_argument( + "--keep-going", + action="store_true", + help="Continue with remaining examples after a failure", + ) + args = parser.parse_args() + + source_dir = os.path.abspath(args.source_dir) + python_exe = os.path.join(source_dir, ".venv", "bin", "python") + + if not os.path.isfile(python_exe): + print(f"Python executable not found: {python_exe}") + return 2 + + print(f"Using Python: {python_exe}") + print(f"Source folder: {source_dir}") + print(f"Examples to run: {len(EXAMPLES)}") + + passed = 0 + failed = 0 + + for example in EXAMPLES: + success = run_example(source_dir, example, python_exe) + if success: + passed += 1 + else: + failed += 1 + if not args.keep_going: + print("Stopping after first failure. Use --keep-going to continue.") + break + + print("\nSummary") + print(f"Passed: {passed}") + print(f"Failed: {failed}") + + return 0 if failed == 0 else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/python/viennaps/_core/__init__.pyi b/python/viennaps/_core/__init__.pyi index 6d941552..9ef42d98 100644 --- a/python/viennaps/_core/__init__.pyi +++ b/python/viennaps/_core/__init__.pyi @@ -6,10 +6,10 @@ import collections.abc import enum import typing import viennals._core -import viennaps.d2 from viennaps import d2 -from viennaps import d3 +import viennaps.d2 import viennaps.d3 +from viennaps import d3 from . import constants from . import gpu from . import util diff --git a/python/viennaps/d2/gpu.pyi b/python/viennaps/d2/gpu.pyi index 8c09ae89..caa5a1c4 100644 --- a/python/viennaps/d2/gpu.pyi +++ b/python/viennaps/d2/gpu.pyi @@ -25,7 +25,7 @@ class MultiParticleProcess(ProcessModelGPU): def addNeutralParticle(self, stickingProbability: typing.SupportsFloat | typing.SupportsIndex, label: str = 'neutralFlux') -> None: ... @typing.overload - def addNeutralParticle(self, materialSticking: collections.abc.Mapping[viennaps._core.Material, typing.SupportsFloat | typing.SupportsIndex], defaultStickingProbability: typing.SupportsFloat | typing.SupportsIndex = 1.0, label: str = 'neutralFlux') -> None: + def addNeutralParticle(self, materialSticking: viennaps._core.MaterialValueMap, defaultStickingProbability: typing.SupportsFloat | typing.SupportsIndex = 1.0, label: str = 'neutralFlux') -> None: ... def setRateFunction(self, arg0: collections.abc.Callable[[collections.abc.Sequence[typing.SupportsFloat | typing.SupportsIndex], viennaps._core.Material], float]) -> None: ... diff --git a/python/viennaps/d3/gpu.pyi b/python/viennaps/d3/gpu.pyi index 59ff81af..dce1f224 100644 --- a/python/viennaps/d3/gpu.pyi +++ b/python/viennaps/d3/gpu.pyi @@ -25,7 +25,7 @@ class MultiParticleProcess(ProcessModelGPU): def addNeutralParticle(self, stickingProbability: typing.SupportsFloat | typing.SupportsIndex, label: str = 'neutralFlux') -> None: ... @typing.overload - def addNeutralParticle(self, materialSticking: collections.abc.Mapping[viennaps._core.Material, typing.SupportsFloat | typing.SupportsIndex], defaultStickingProbability: typing.SupportsFloat | typing.SupportsIndex = 1.0, label: str = 'neutralFlux') -> None: + def addNeutralParticle(self, materialSticking: viennaps._core.MaterialValueMap, defaultStickingProbability: typing.SupportsFloat | typing.SupportsIndex = 1.0, label: str = 'neutralFlux') -> None: ... def setRateFunction(self, arg0: collections.abc.Callable[[collections.abc.Sequence[typing.SupportsFloat | typing.SupportsIndex], viennaps._core.Material], float]) -> None: ... From f33ac445698861f266bf02dfd19ebb46088a00f4 Mon Sep 17 00:00:00 2001 From: reiter Date: Thu, 2 Apr 2026 17:18:13 +0200 Subject: [PATCH 32/32] format --- tests/materialValueMap/materialValueMap.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/materialValueMap/materialValueMap.cpp b/tests/materialValueMap/materialValueMap.cpp index 375a8210..a1bd79a9 100644 --- a/tests/materialValueMap/materialValueMap.cpp +++ b/tests/materialValueMap/materialValueMap.cpp @@ -53,9 +53,6 @@ void testIteration() { VC_TEST_ASSERT(idx < expected.size()); VC_TEST_ASSERT(material == expected[idx].first); VC_TEST_ASSERT(value == expected[idx].second); - std::cout << "Material: " << MaterialMap::toString(material) << "(" - << material.legacyId() << ")" << ", Value: " << value - << std::endl; ++idx; }