From 2bbee15184f4416ac43aaca0867b6a55bca85fd9 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Fri, 3 Jan 2025 13:01:07 +0100 Subject: [PATCH 01/25] Update Sf6O2 coverage equations --- examples/holeEtching/CMakeLists.txt | 8 +++--- include/viennaps/models/psSF6O2Etching.hpp | 29 +++++++++------------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/examples/holeEtching/CMakeLists.txt b/examples/holeEtching/CMakeLists.txt index 86176537..8772d6bc 100644 --- a/examples/holeEtching/CMakeLists.txt +++ b/examples/holeEtching/CMakeLists.txt @@ -1,10 +1,10 @@ project(holeEtching LANGUAGES CXX) -add_executable(${PROJECT_NAME} "${PROJECT_NAME}.cpp") -target_link_libraries(${PROJECT_NAME} PRIVATE ViennaPS) +add_executable("${PROJECT_NAME}.exe" "${PROJECT_NAME}.cpp") +target_link_libraries("${PROJECT_NAME}.exe" PRIVATE ViennaPS) configure_file(holeEtching.py ${CMAKE_CURRENT_BINARY_DIR}/holeEtching.py COPYONLY) configure_file(config.txt ${CMAKE_CURRENT_BINARY_DIR}/config.txt COPYONLY) -add_dependencies(ViennaPS_Examples ${PROJECT_NAME}) -viennacore_setup_bat(${PROJECT_NAME} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) +add_dependencies(ViennaPS_Examples "${PROJECT_NAME}.exe") +viennacore_setup_bat(${PROJECT_NAME}.exe ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) diff --git a/include/viennaps/models/psSF6O2Etching.hpp b/include/viennaps/models/psSF6O2Etching.hpp index 21aa7922..eb724020 100644 --- a/include/viennaps/models/psSF6O2Etching.hpp +++ b/include/viennaps/models/psSF6O2Etching.hpp @@ -151,30 +151,25 @@ class SF6O2SurfaceModel : public SurfaceModel { auto oCoverage = coverages->getScalarData("oCoverage"); oCoverage->resize(numPoints); for (size_t i = 0; i < numPoints; ++i) { + auto Gb_F = etchantRate->at(i) * params.etchantFlux * params.beta_F; + auto Gb_O = oxygenRate->at(i) * params.oxygenFlux * params.beta_O; + auto GY_ie = ionEnhancedRate->at(i) * params.ionFlux; + auto GY_o = oxygenSputteringRate->at(i) * params.ionFlux; + if (etchantRate->at(i) < 1e-6) { eCoverage->at(i) = 0; } else { - eCoverage->at(i) = - etchantRate->at(i) * params.etchantFlux * params.beta_F / - (etchantRate->at(i) * params.etchantFlux * params.beta_F + - (params.Si.k_sigma + 2 * ionEnhancedRate->at(i) * params.ionFlux) * - (1 + (oxygenRate->at(i) * params.oxygenFlux * params.beta_O) / - (params.Si.beta_sigma + - oxygenSputteringRate->at(i) * params.ionFlux))); + double tmp = 1 + ((params.Si.k_sigma + 2 * GY_ie) / Gb_F) * + (1 + Gb_O / (params.Si.beta_sigma + GY_o)); + eCoverage->at(i) = 1 / tmp; } if (oxygenRate->at(i) < 1e-6) { oCoverage->at(i) = 0; } else { - oCoverage->at(i) = - oxygenRate->at(i) * params.oxygenFlux * params.beta_O / - (oxygenRate->at(i) * params.oxygenFlux * params.beta_O + - (params.Si.beta_sigma + - oxygenSputteringRate->at(i) * params.ionFlux) * - (1 + - (etchantRate->at(i) * params.etchantFlux * params.beta_F) / - (params.Si.k_sigma + - 2 * ionEnhancedRate->at(i) * params.ionFlux))); + double tmp = 1 + ((params.Si.beta_sigma + GY_ie) / Gb_O) * + (1 + Gb_F / (params.Si.k_sigma + 2 * GY_ie)); + oCoverage->at(i) = 1 / tmp; } } } @@ -207,7 +202,7 @@ class SF6SurfaceModel : public SF6O2SurfaceModel { eCoverage->at(i) = etchantRate->at(i) * params.etchantFlux * params.beta_F / (etchantRate->at(i) * params.etchantFlux * params.beta_F + - (params.Si.k_sigma + 2 * ionEnhancedRate->at(i) * params.ionFlux)); + params.Si.k_sigma + 2 * ionEnhancedRate->at(i) * params.ionFlux); } } } From a8acfcb30cdb51b7321e8424b48cf2e9e127f27e Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Sat, 4 Jan 2025 16:10:03 +0100 Subject: [PATCH 02/25] Use cosine IE yield and monitor etching components --- include/viennaps/models/psSF6O2Etching.hpp | 26 +++++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/include/viennaps/models/psSF6O2Etching.hpp b/include/viennaps/models/psSF6O2Etching.hpp index eb724020..dc44500f 100644 --- a/include/viennaps/models/psSF6O2Etching.hpp +++ b/include/viennaps/models/psSF6O2Etching.hpp @@ -90,6 +90,9 @@ class SF6O2SurfaceModel : public SurfaceModel { std::vector cov(numGeometryPoints, 0.); coverages->insertNextScalarData(cov, "eCoverage"); coverages->insertNextScalarData(cov, "oCoverage"); + coverages->insertNextScalarData(cov, "ieComponent"); + coverages->insertNextScalarData(cov, "spComponent"); + coverages->insertNextScalarData(cov, "thComponent"); } SmartPointer> @@ -105,6 +108,10 @@ class SF6O2SurfaceModel : public SurfaceModel { const auto etchantRate = rates->getScalarData("etchantRate"); const auto eCoverage = coverages->getScalarData("eCoverage"); + const auto ieComponent = coverages->getScalarData("ieComponent"); + const auto spComponent = coverages->getScalarData("spComponent"); + const auto thComponent = coverages->getScalarData("thComponent"); + bool stop = false; for (size_t i = 0; i < numPoints; ++i) { @@ -116,12 +123,19 @@ class SF6O2SurfaceModel : public SurfaceModel { if (MaterialMap::isMaterial(materialIds[i], Material::Mask)) { etchRate[i] = -(1 / params.Mask.rho) * ionSputteringRate->at(i) * params.ionFlux; + spComponent->at(i) = ionSputteringRate->at(i) * params.ionFlux; + ieComponent->at(i) = 0.; + thComponent->at(i) = 0.; } else { etchRate[i] = -(1 / params.Si.rho) * (params.Si.k_sigma * eCoverage->at(i) / 4. + ionSputteringRate->at(i) * params.ionFlux + eCoverage->at(i) * ionEnhancedRate->at(i) * params.ionFlux); // in um / s + spComponent->at(i) = ionSputteringRate->at(i) * params.ionFlux; + ieComponent->at(i) = + eCoverage->at(i) * ionEnhancedRate->at(i) * params.ionFlux; + thComponent->at(i) = params.Si.k_sigma * eCoverage->at(i) / 4.; } } @@ -235,12 +249,12 @@ class SF6O2Ion const double angle = std::acos(std::max(std::min(cosTheta, 1.), 0.)); - NumericType f_ie_theta; - if (cosTheta > 0.5) { - f_ie_theta = 1.; - } else { - f_ie_theta = 3. - 6. * angle / M_PI; - } + NumericType f_ie_theta = cosTheta; + // if (cosTheta > 0.5) { + // f_ie_theta = 1.; + // } else { + // f_ie_theta = 3. - 6. * angle / M_PI; + // } NumericType A_sp = params.Si.A_sp; NumericType B_sp = params.Si.B_sp; NumericType Eth_sp = params.Si.Eth_sp; From 51d591f3bf8a0975b51baac18985e59dca191b41 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Sun, 5 Jan 2025 17:21:31 +0100 Subject: [PATCH 03/25] Adjust parameters and yield function --- examples/holeEtching/holeEtching.cpp | 4 +- include/viennaps/models/psSF6O2Etching.hpp | 89 +++++++++++++--------- include/viennaps/psConstants.hpp | 4 + 3 files changed, 59 insertions(+), 38 deletions(-) diff --git a/examples/holeEtching/holeEtching.cpp b/examples/holeEtching/holeEtching.cpp index f2bb3555..40693432 100644 --- a/examples/holeEtching/holeEtching.cpp +++ b/examples/holeEtching/holeEtching.cpp @@ -10,7 +10,7 @@ int main(int argc, char *argv[]) { using NumericType = double; constexpr int D = 3; - ps::Logger::setLogLevel(ps::LogLevel::INTERMEDIATE); + ps::Logger::setLogLevel(ps::LogLevel::INFO); omp_set_num_threads(16); // Parse the parameters @@ -60,5 +60,5 @@ int main(int argc, char *argv[]) { process.apply(); // print final surface - geometry->saveSurfaceMesh("final.vtp"); + geometry->saveSurfaceMesh(params.get("outputFile")); } diff --git a/include/viennaps/models/psSF6O2Etching.hpp b/include/viennaps/models/psSF6O2Etching.hpp index dc44500f..bc2648b6 100644 --- a/include/viennaps/models/psSF6O2Etching.hpp +++ b/include/viennaps/models/psSF6O2Etching.hpp @@ -4,6 +4,7 @@ #include #include +#include "../psConstants.hpp" #include "../psProcessModel.hpp" #include "../psSurfaceModel.hpp" #include "../psVelocityField.hpp" @@ -42,21 +43,25 @@ template struct SF6O2Parameters { // sputtering coefficients NumericType Eth_sp = 20.; // eV - NumericType Eth_ie = 4.; // eV + NumericType Eth_ie = 15.; // eV NumericType A_sp = 0.0337; NumericType B_sp = 9.3; - NumericType A_ie = 0.0361; + NumericType theta_g_sp = M_PI_2; // angle where yield is zero in rad + NumericType A_ie = 7.; + NumericType B_ie = 0.8; + NumericType theta_g_ie = + constants::degToRad(78); // angle where yield is zero in rad // chemical etching NumericType k_sigma = 3.0e2; // in (1e15 cm⁻²s⁻¹) - NumericType beta_sigma = 5.0e-2; // in (1e15 cm⁻²s⁻¹) + NumericType beta_sigma = 4.0e-2; // in (1e15 cm⁻²s⁻¹) } Si; // Passivation struct PassivationType { // sputtering coefficients - NumericType Eth_ie = 4.; // eV - NumericType A_ie = 0.0361; + NumericType Eth_ie = 10.; // eV + NumericType A_ie = 3; } Passivation; struct IonType { @@ -90,9 +95,11 @@ class SF6O2SurfaceModel : public SurfaceModel { std::vector cov(numGeometryPoints, 0.); coverages->insertNextScalarData(cov, "eCoverage"); coverages->insertNextScalarData(cov, "oCoverage"); - coverages->insertNextScalarData(cov, "ieComponent"); - coverages->insertNextScalarData(cov, "spComponent"); - coverages->insertNextScalarData(cov, "thComponent"); + if (Logger::getLogLevel() > 3) { + coverages->insertNextScalarData(cov, "ieComponent"); + coverages->insertNextScalarData(cov, "spComponent"); + coverages->insertNextScalarData(cov, "thComponent"); + } } SmartPointer> @@ -108,10 +115,14 @@ class SF6O2SurfaceModel : public SurfaceModel { const auto etchantRate = rates->getScalarData("etchantRate"); const auto eCoverage = coverages->getScalarData("eCoverage"); - const auto ieComponent = coverages->getScalarData("ieComponent"); - const auto spComponent = coverages->getScalarData("spComponent"); - const auto thComponent = coverages->getScalarData("thComponent"); - + // save the etch rate components for visualization + std::vector *ieComponent = nullptr, *spComponent = nullptr, + *thComponent = nullptr; + if (Logger::getLogLevel() > 3) { + ieComponent = coverages->getScalarData("ieComponent"); + spComponent = coverages->getScalarData("spComponent"); + thComponent = coverages->getScalarData("thComponent"); + } bool stop = false; for (size_t i = 0; i < numPoints; ++i) { @@ -123,19 +134,23 @@ class SF6O2SurfaceModel : public SurfaceModel { if (MaterialMap::isMaterial(materialIds[i], Material::Mask)) { etchRate[i] = -(1 / params.Mask.rho) * ionSputteringRate->at(i) * params.ionFlux; - spComponent->at(i) = ionSputteringRate->at(i) * params.ionFlux; - ieComponent->at(i) = 0.; - thComponent->at(i) = 0.; + if (Logger::getLogLevel() > 3) { + spComponent->at(i) = ionSputteringRate->at(i) * params.ionFlux; + ieComponent->at(i) = 0.; + thComponent->at(i) = 0.; + } } else { etchRate[i] = -(1 / params.Si.rho) * (params.Si.k_sigma * eCoverage->at(i) / 4. + ionSputteringRate->at(i) * params.ionFlux + eCoverage->at(i) * ionEnhancedRate->at(i) * params.ionFlux); // in um / s - spComponent->at(i) = ionSputteringRate->at(i) * params.ionFlux; - ieComponent->at(i) = - eCoverage->at(i) * ionEnhancedRate->at(i) * params.ionFlux; - thComponent->at(i) = params.Si.k_sigma * eCoverage->at(i) / 4.; + if (Logger::getLogLevel() > 3) { + spComponent->at(i) = ionSputteringRate->at(i) * params.ionFlux; + ieComponent->at(i) = + eCoverage->at(i) * ionEnhancedRate->at(i) * params.ionFlux; + thComponent->at(i) = params.Si.k_sigma * eCoverage->at(i) / 4.; + } } } @@ -164,26 +179,26 @@ class SF6O2SurfaceModel : public SurfaceModel { // oxygen coverage auto oCoverage = coverages->getScalarData("oCoverage"); oCoverage->resize(numPoints); + for (size_t i = 0; i < numPoints; ++i) { auto Gb_F = etchantRate->at(i) * params.etchantFlux * params.beta_F; auto Gb_O = oxygenRate->at(i) * params.oxygenFlux * params.beta_O; auto GY_ie = ionEnhancedRate->at(i) * params.ionFlux; auto GY_o = oxygenSputteringRate->at(i) * params.ionFlux; - if (etchantRate->at(i) < 1e-6) { + auto a = (params.Si.k_sigma + 2 * GY_ie) / Gb_F; + auto b = (params.Si.beta_sigma + GY_o) / Gb_O; + + if (Gb_F < 1e-6) { eCoverage->at(i) = 0; } else { - double tmp = 1 + ((params.Si.k_sigma + 2 * GY_ie) / Gb_F) * - (1 + Gb_O / (params.Si.beta_sigma + GY_o)); - eCoverage->at(i) = 1 / tmp; + eCoverage->at(i) = 1 / (1 + (a * (1 + 1 / b))); } - if (oxygenRate->at(i) < 1e-6) { + if (Gb_O < 1e-6) { oCoverage->at(i) = 0; } else { - double tmp = 1 + ((params.Si.beta_sigma + GY_ie) / Gb_O) * - (1 + Gb_F / (params.Si.k_sigma + 2 * GY_ie)); - oCoverage->at(i) = 1 / tmp; + oCoverage->at(i) = 1 / (1 + (b * (1 + 1 / a))); } } } @@ -242,19 +257,14 @@ class SF6O2Ion assert(primID < localData.getVectorData(0).size() && "id out of bounds"); const double cosTheta = -DotProduct(rayDir, geomNormal); + NumericType angle = + std::acos(std::max(std::min(cosTheta, static_cast(1.)), + static_cast(0.))); assert(cosTheta >= 0 && "Hit backside of disc"); assert(cosTheta <= 1 + 1e6 && "Error in calculating cos theta"); assert(rayWeight > 0. && "Invalid ray weight"); - const double angle = std::acos(std::max(std::min(cosTheta, 1.), 0.)); - - NumericType f_ie_theta = cosTheta; - // if (cosTheta > 0.5) { - // f_ie_theta = 1.; - // } else { - // f_ie_theta = 3. - 6. * angle / M_PI; - // } NumericType A_sp = params.Si.A_sp; NumericType B_sp = params.Si.B_sp; NumericType Eth_sp = params.Si.Eth_sp; @@ -264,7 +274,14 @@ class SF6O2Ion Eth_sp = params.Mask.Eth_sp; } - NumericType f_sp_theta = (1 + B_sp * (1 - cosTheta * cosTheta)) * cosTheta; + NumericType f_sp_theta = + std::max((1 + B_sp * (1 - cosTheta * cosTheta)) * + std::cos(angle / params.Si.theta_g_sp * M_PI_2), + 0.); + NumericType f_ie_theta = + std::max((1 + params.Si.B_ie * (1 - cosTheta * cosTheta)) * + std::cos(angle / params.Si.theta_g_ie * M_PI_2), + 0.); double sqrtE = std::sqrt(E); NumericType Y_sp = diff --git a/include/viennaps/psConstants.hpp b/include/viennaps/psConstants.hpp index 5ec315b0..ab978390 100644 --- a/include/viennaps/psConstants.hpp +++ b/include/viennaps/psConstants.hpp @@ -15,6 +15,10 @@ inline double torrToPascal(double torr) { return torr * 133.322; } inline double celsiusToKelvin(double celsius) { return celsius + 273.15; } +inline double radToDeg(double rad) { return rad * 180. / M_PI; } + +inline double degToRad(double deg) { return deg * M_PI / 180.; } + // p: pressure in torr // T: temperature in Celsius // d: diameter of the gas molecule in angstroms From 790fe44cadb3b6b9f7af7c372a64cb996d221bf5 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Mon, 6 Jan 2025 21:51:05 +0100 Subject: [PATCH 04/25] Add integration scheme conversion from parameters --- examples/holeEtching/config.txt | 1 + examples/holeEtching/holeEtching.cpp | 4 +- examples/holeEtching/holeEtching.py | 1 + include/viennaps/models/psSF6O2Etching.hpp | 31 ++++++-------- include/viennaps/psUtils.hpp | 47 ++++++++++++++++++++++ python/pyWrap.cpp | 12 +++++- 6 files changed, 75 insertions(+), 21 deletions(-) diff --git a/examples/holeEtching/config.txt b/examples/holeEtching/config.txt index 134b89e7..bec28a01 100644 --- a/examples/holeEtching/config.txt +++ b/examples/holeEtching/config.txt @@ -25,5 +25,6 @@ sigmaEnergy=10 # eV A_O=3 # passivation layer sputtering coefficient etchStopDepth=-1000 # maximum etching depth +integrationScheme=EO_1 raysPerPoint=1000 \ No newline at end of file diff --git a/examples/holeEtching/holeEtching.cpp b/examples/holeEtching/holeEtching.cpp index 40693432..72afe4ab 100644 --- a/examples/holeEtching/holeEtching.cpp +++ b/examples/holeEtching/holeEtching.cpp @@ -10,7 +10,7 @@ int main(int argc, char *argv[]) { using NumericType = double; constexpr int D = 3; - ps::Logger::setLogLevel(ps::LogLevel::INFO); + ps::Logger::setLogLevel(ps::LogLevel::INTERMEDIATE); omp_set_num_threads(16); // Parse the parameters @@ -52,6 +52,8 @@ int main(int argc, char *argv[]) { process.setMaxCoverageInitIterations(10); process.setNumberOfRaysPerPoint(params.get("raysPerPoint")); process.setProcessDuration(params.get("processTime")); + process.setIntegrationScheme( + params.get("integrationScheme")); // print initial surface geometry->saveSurfaceMesh("initial.vtp"); diff --git a/examples/holeEtching/holeEtching.py b/examples/holeEtching/holeEtching.py index 081b265b..4768e7aa 100644 --- a/examples/holeEtching/holeEtching.py +++ b/examples/holeEtching/holeEtching.py @@ -53,6 +53,7 @@ process.setMaxCoverageInitIterations(10) process.setNumberOfRaysPerPoint(int(params["raysPerPoint"])) process.setProcessDuration(params["processTime"]) # seconds +process.setIntegrationScheme(vps.util.convertIntegrationScheme(params["integrationScheme"])) # print initial surface geometry.saveSurfaceMesh(filename="initial.vtp", addMaterialIds=True) diff --git a/include/viennaps/models/psSF6O2Etching.hpp b/include/viennaps/models/psSF6O2Etching.hpp index bc2648b6..36942f81 100644 --- a/include/viennaps/models/psSF6O2Etching.hpp +++ b/include/viennaps/models/psSF6O2Etching.hpp @@ -189,17 +189,8 @@ class SF6O2SurfaceModel : public SurfaceModel { auto a = (params.Si.k_sigma + 2 * GY_ie) / Gb_F; auto b = (params.Si.beta_sigma + GY_o) / Gb_O; - if (Gb_F < 1e-6) { - eCoverage->at(i) = 0; - } else { - eCoverage->at(i) = 1 / (1 + (a * (1 + 1 / b))); - } - - if (Gb_O < 1e-6) { - oCoverage->at(i) = 0; - } else { - oCoverage->at(i) = 1 / (1 + (b * (1 + 1 / a))); - } + eCoverage->at(i) = Gb_F < 1e-6 ? 0. : 1 / (1 + (a * (1 + 1 / b))); + oCoverage->at(i) = Gb_O < 1e-6 ? 0. : 1 / (1 + (b * (1 + 1 / a))); } } }; @@ -244,7 +235,9 @@ class SF6O2Ion SF6O2Ion(const SF6O2Parameters &pParams) : params(pParams), A(1. / - (1. + params.Ions.n_l * (M_PI_2 / params.Ions.inflectAngle - 1.))) {} + (1. + params.Ions.n_l * (M_PI_2 / params.Ions.inflectAngle - 1.))), + sqrt_E_th_ie_O(std::sqrt(params.Passivation.Eth_ie)), + sqrt_E_th_ie_Si(std::sqrt(params.Si.Eth_ie)) {} void surfaceCollision(NumericType rayWeight, const Vec3D &rayDir, const Vec3D &geomNormal, @@ -286,12 +279,10 @@ class SF6O2Ion double sqrtE = std::sqrt(E); NumericType Y_sp = params.Si.A_sp * std::max(sqrtE - std::sqrt(Eth_sp), 0.) * f_sp_theta; - NumericType Y_Si = params.Si.A_ie * - std::max(sqrtE - std::sqrt(params.Si.Eth_ie), 0.) * - f_ie_theta; - NumericType Y_O = - params.Passivation.A_ie * - std::max(sqrtE - std::sqrt(params.Passivation.Eth_ie), 0.) * f_ie_theta; + NumericType Y_Si = + params.Si.A_ie * std::max(sqrtE - sqrt_E_th_ie_Si, 0.) * f_ie_theta; + NumericType Y_O = params.Passivation.A_ie * + std::max(sqrtE - sqrt_E_th_ie_O, 0.) * f_ie_theta; assert(Y_sp >= 0. && "Invalid yield"); assert(Y_Si >= 0. && "Invalid yield"); @@ -367,6 +358,10 @@ class SF6O2Ion private: const SF6O2Parameters ¶ms; const NumericType A; + // save precomputed square roots + const NumericType sqrt_E_th_ie_O; + const NumericType sqrt_E_th_ie_Si; + NumericType E; }; diff --git a/include/viennaps/psUtils.hpp b/include/viennaps/psUtils.hpp index 830e2afa..8eba174b 100644 --- a/include/viennaps/psUtils.hpp +++ b/include/viennaps/psUtils.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -27,6 +28,44 @@ namespace utils { return false; } +[[nodiscard]] viennals::IntegrationSchemeEnum +convertIntegrationScheme(const std::string &s) { + if (s == "ENGQUIST_OSHER_1ST_ORDER" || s == "EO_1") + return viennals::IntegrationSchemeEnum::ENGQUIST_OSHER_1ST_ORDER; + if (s == "ENGQUIST_OSHER_2ND_ORDER" || s == "EO_2") + return viennals::IntegrationSchemeEnum::ENGQUIST_OSHER_2ND_ORDER; + if (s == "LAX_FRIEDRICHS_1ST_ORDER" || s == "LF_1") + return viennals::IntegrationSchemeEnum::LAX_FRIEDRICHS_1ST_ORDER; + if (s == "LAX_FRIEDRICHS_2ND_ORDER" || s == "LF_2") + return viennals::IntegrationSchemeEnum::LAX_FRIEDRICHS_2ND_ORDER; + if (s == "LOCAL_LAX_FRIEDRICHS_ANALYTICAL_1ST_ORDER" || s == "LLFA_1") + return viennals::IntegrationSchemeEnum:: + LOCAL_LAX_FRIEDRICHS_ANALYTICAL_1ST_ORDER; + if (s == "LOCAL_LOCAL_LAX_FRIEDRICHS_1ST_ORDER" || s == "LLLF_1") + return viennals::IntegrationSchemeEnum:: + LOCAL_LOCAL_LAX_FRIEDRICHS_1ST_ORDER; + if (s == "LOCAL_LOCAL_LAX_FRIEDRICHS_2ND_ORDER" || s == "LLLF_2") + return viennals::IntegrationSchemeEnum:: + LOCAL_LOCAL_LAX_FRIEDRICHS_2ND_ORDER; + if (s == "LOCAL_LAX_FRIEDRICHS_1ST_ORDER" || s == "LLF_1") + return viennals::IntegrationSchemeEnum::LOCAL_LAX_FRIEDRICHS_1ST_ORDER; + if (s == "LOCAL_LAX_FRIEDRICHS_2ND_ORDER" || s == "LLF_2") + return viennals::IntegrationSchemeEnum::LOCAL_LAX_FRIEDRICHS_2ND_ORDER; + if (s == "STENCIL_LOCAL_LAX_FRIEDRICHS_1ST_ORDER" || s == "SLLF_1") + return viennals::IntegrationSchemeEnum:: + STENCIL_LOCAL_LAX_FRIEDRICHS_1ST_ORDER; + throw std::invalid_argument( + "The value must be one of the following: " + "ENGQUIST_OSHER_1ST_ORDER, ENGQUIST_OSHER_2ND_ORDER, " + "LAX_FRIEDRICHS_1ST_ORDER, LAX_FRIEDRICHS_2ND_ORDER, " + "LOCAL LAX_FRIEDRICHS ANALYTICAL 1ST ORDER, " + "LOCAL LOCAL LAX FRIEDRICHS 1ST ORDER, " + "LOCAL LOCAL LAX FRIEDRICHS 2ND ORDER, " + "LOCAL LAX FRIEDRICHS 1ST ORDER, " + "LOCAL LAX FRIEDRICHS 2ND ORDER, " + "STENCIL LOCAL LAX FRIEDRICHS 1ST ORDER"); +} + // Converts string to the given numeric datatype template [[nodiscard]] T convert(const std::string &s) { if constexpr (std::is_same_v) { @@ -60,6 +99,14 @@ template [[nodiscard]] T convert(const std::string &s) { return std::stold(s); } else if constexpr (std::is_same_v) { return s; + } else if constexpr (std::is_same_v) { + if (s == "true") + return true; + if (s == "false") + return false; + throw std::invalid_argument("The value must be either 'true' or 'false'"); + } else if constexpr (std::is_same_v) { + return convertIntegrationScheme(s); } else { // Throws a compile time error for all types but void return; diff --git a/python/pyWrap.cpp b/python/pyWrap.cpp index 131154d4..513085ce 100644 --- a/python/pyWrap.cpp +++ b/python/pyWrap.cpp @@ -642,11 +642,14 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { .def_readwrite("rho", &SF6O2Parameters::SiType::rho) .def_readwrite("k_sigma", &SF6O2Parameters::SiType::k_sigma) .def_readwrite("beta_sigma", &SF6O2Parameters::SiType::beta_sigma) + .def_readwrite("Eth_sp", &SF6O2Parameters::SiType::Eth_sp) .def_readwrite("A_sp", &SF6O2Parameters::SiType::A_sp) .def_readwrite("B_sp", &SF6O2Parameters::SiType::B_sp) + .def_readwrite("theta_g_sp", &SF6O2Parameters::SiType::theta_g_sp) .def_readwrite("Eth_ie", &SF6O2Parameters::SiType::Eth_ie) - .def_readwrite("Eth_sp", &SF6O2Parameters::SiType::Eth_sp) - .def_readwrite("A_ie", &SF6O2Parameters::SiType::A_ie); + .def_readwrite("A_ie", &SF6O2Parameters::SiType::A_ie) + .def_readwrite("B_ie", &SF6O2Parameters::SiType::B_ie) + .def_readwrite("theta_g_ie", &SF6O2Parameters::SiType::theta_g_ie); pybind11::class_::PassivationType>( module, "SF6O2ParametersPassivation") @@ -1261,6 +1264,11 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { m_constants.def("gasMeanThermalVelocity", &constants::gasMeanThermalVelocity, "Calculate the mean thermal velocity of a gas molecule."); + // Utility functions + auto m_util = module.def_submodule("util", "Utility functions."); + m_util.def("convertIntegrationScheme", &utils::convertIntegrationScheme, + "Convert a string to an integration scheme."); + // Planarize pybind11::class_>(module, "Planarize") .def(pybind11::init()) From 50ac174c9b0b57eb16099522b510727a37645523 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Tue, 7 Jan 2025 14:36:35 +0100 Subject: [PATCH 05/25] Fix numeric conversion error --- include/viennaps/models/psSF6O2Etching.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/viennaps/models/psSF6O2Etching.hpp b/include/viennaps/models/psSF6O2Etching.hpp index 36942f81..96955438 100644 --- a/include/viennaps/models/psSF6O2Etching.hpp +++ b/include/viennaps/models/psSF6O2Etching.hpp @@ -250,9 +250,7 @@ class SF6O2Ion assert(primID < localData.getVectorData(0).size() && "id out of bounds"); const double cosTheta = -DotProduct(rayDir, geomNormal); - NumericType angle = - std::acos(std::max(std::min(cosTheta, static_cast(1.)), - static_cast(0.))); + NumericType angle = std::acos(std::max(std::min(cosTheta, 1.), 0.)); assert(cosTheta >= 0 && "Hit backside of disc"); assert(cosTheta <= 1 + 1e6 && "Error in calculating cos theta"); From 75515e54234f12a94ab2e245ec25cdb3c46ec992 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Tue, 14 Jan 2025 14:04:06 +0100 Subject: [PATCH 06/25] Add ion sticking coefficient --- include/viennaps/models/psSF6O2Etching.hpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/include/viennaps/models/psSF6O2Etching.hpp b/include/viennaps/models/psSF6O2Etching.hpp index 96955438..094b2c68 100644 --- a/include/viennaps/models/psSF6O2Etching.hpp +++ b/include/viennaps/models/psSF6O2Etching.hpp @@ -72,6 +72,9 @@ template struct SF6O2Parameters { NumericType inflectAngle = 1.55334303; NumericType n_l = 10.; NumericType minAngle = 1.3962634; + + NumericType thetaRMin = constants::degToRad(70.); + NumericType thetaRMax = constants::degToRad(90.); } Ions; }; @@ -287,13 +290,13 @@ class SF6O2Ion assert(Y_O >= 0. && "Invalid yield"); // sputtering yield Y_sp ionSputteringRate - localData.getVectorData(0)[primID] += Y_sp; + localData.getVectorData(0)[primID] += Y_sp * rayWeight; // ion enhanced etching yield Y_Si ionEnhancedRate - localData.getVectorData(1)[primID] += Y_Si; + localData.getVectorData(1)[primID] += Y_Si * rayWeight; // ion enhanced O sputtering yield Y_O oxygenSputteringRate - localData.getVectorData(2)[primID] += Y_O; + localData.getVectorData(2)[primID] += Y_O * rayWeight; } std::pair> @@ -328,12 +331,19 @@ class SF6O2Ion NewEnergy = normalDist(Rng); } while (NewEnergy > E || NewEnergy < 0.); + NumericType sticking = 1.; + if (incAngle > params.Ions.thetaRMin) + sticking = + 1. - std::min((incAngle - params.Ions.thetaRMin) / + (params.Ions.thetaRMax - params.Ions.thetaRMin), + NumericType(1.)); + // Set the flag to stop tracing if the energy is below the threshold if (NewEnergy > params.Si.Eth_ie) { E = NewEnergy; auto direction = viennaray::ReflectionConedCosine( rayDir, geomNormal, Rng, std::max(incAngle, params.Ions.minAngle)); - return std::pair>{0., direction}; + return std::pair>{sticking, direction}; } else { return std::pair>{ 1., Vec3D{0., 0., 0.}}; From a89cdf832e9ecaf22cd0d559fb59771a6b8d586c Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Tue, 14 Jan 2025 14:05:16 +0100 Subject: [PATCH 07/25] Out file name in config --- examples/holeEtching/config.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/holeEtching/config.txt b/examples/holeEtching/config.txt index bec28a01..3fbe1e3f 100644 --- a/examples/holeEtching/config.txt +++ b/examples/holeEtching/config.txt @@ -27,4 +27,6 @@ A_O=3 # passivation layer sputtering coefficient etchStopDepth=-1000 # maximum etching depth integrationScheme=EO_1 -raysPerPoint=1000 \ No newline at end of file +raysPerPoint=1000 + +outputFile=final \ No newline at end of file From 514e714f5655549044322659ae5cf2770b7d9e4a Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Wed, 15 Jan 2025 11:27:53 +0100 Subject: [PATCH 08/25] Seperate ion flux and yield --- examples/holeEtching/config.txt | 13 +- examples/holeEtching/holeEtching.cpp | 20 +-- include/viennaps/models/psSF6O2Etching.hpp | 143 +++++++++++---------- 3 files changed, 96 insertions(+), 80 deletions(-) diff --git a/examples/holeEtching/config.txt b/examples/holeEtching/config.txt index 3fbe1e3f..e61abff9 100644 --- a/examples/holeEtching/config.txt +++ b/examples/holeEtching/config.txt @@ -8,21 +8,22 @@ yExtent=100.0 # um # Geometry holeRadius=20.0 # um -maskHeight=20.0 # um -taperAngle=0.0 # degree +maskHeight=65.0 # um +taperAngle=1.5 # degree # Process parameters -processTime=150 # seconds +processTime=50 # seconds # all flux values are units 1e16 / cm² -ionFlux=1. -etchantFlux=180. +ionFlux=10. +etchantFlux=500. oxygenFlux=30. ionExponent=200 meanEnergy=100 # eV sigmaEnergy=10 # eV -A_O=3 # passivation layer sputtering coefficient +A_O=3 # passivation layer yield constant +A_Si=7 # Si yield constant etchStopDepth=-1000 # maximum etching depth integrationScheme=EO_1 diff --git a/examples/holeEtching/holeEtching.cpp b/examples/holeEtching/holeEtching.cpp index 72afe4ab..67941aaf 100644 --- a/examples/holeEtching/holeEtching.cpp +++ b/examples/holeEtching/holeEtching.cpp @@ -35,15 +35,17 @@ int main(int argc, char *argv[]) { .apply(); // use pre-defined model SF6O2 etching model - auto model = ps::SmartPointer>::New( - params.get("ionFlux") /*ion flux*/, - params.get("etchantFlux") /*etchant flux*/, - params.get("oxygenFlux") /*oxygen flux*/, - params.get("meanEnergy") /*mean energy*/, - params.get("sigmaEnergy") /*energy sigma*/, - params.get("ionExponent") /*source power cosine distribution exponent*/, - params.get("A_O") /*oxy sputter yield*/, - params.get("etchStopDepth") /*max etch depth*/); + auto model = ps::SmartPointer>::New(); + auto &modelParams = model->getParameters(); + modelParams.ionFlux = params.get("ionFlux"); + modelParams.etchantFlux = params.get("etchantFlux"); + modelParams.oxygenFlux = params.get("oxygenFlux"); + modelParams.Ions.meanEnergy = params.get("meanEnergy"); + modelParams.Ions.sigmaEnergy = params.get("sigmaEnergy"); + modelParams.Ions.exponent = params.get("ionExponent"); + modelParams.Passivation.A_ie = params.get("A_O"); + modelParams.Si.A_ie = params.get("A_Si"); + modelParams.etchStopDepth = params.get("etchStopDepth"); // process setup ps::Process process; diff --git a/include/viennaps/models/psSF6O2Etching.hpp b/include/viennaps/models/psSF6O2Etching.hpp index 094b2c68..bea7ea94 100644 --- a/include/viennaps/models/psSF6O2Etching.hpp +++ b/include/viennaps/models/psSF6O2Etching.hpp @@ -19,12 +19,12 @@ template struct SF6O2Parameters { NumericType etchantFlux = 1.8e3; NumericType oxygenFlux = 1.0e2; - NumericType etchStopDepth = std::numeric_limits::lowest(); - // sticking probabilities NumericType beta_F = 0.7; NumericType beta_O = 1.; + NumericType etchStopDepth = std::numeric_limits::lowest(); + // Mask struct MaskType { NumericType rho = 500.; // 1e22 atoms/cm³ @@ -44,13 +44,15 @@ template struct SF6O2Parameters { // sputtering coefficients NumericType Eth_sp = 20.; // eV NumericType Eth_ie = 15.; // eV + NumericType A_sp = 0.0337; NumericType B_sp = 9.3; - NumericType theta_g_sp = M_PI_2; // angle where yield is zero in rad + NumericType theta_g_sp = M_PI_2; // angle where yield is zero [rad] + NumericType A_ie = 7.; NumericType B_ie = 0.8; NumericType theta_g_ie = - constants::degToRad(78); // angle where yield is zero in rad + constants::degToRad(78); // angle where yield is zero [rad] // chemical etching NumericType k_sigma = 3.0e2; // in (1e15 cm⁻²s⁻¹) @@ -99,9 +101,9 @@ class SF6O2SurfaceModel : public SurfaceModel { coverages->insertNextScalarData(cov, "eCoverage"); coverages->insertNextScalarData(cov, "oCoverage"); if (Logger::getLogLevel() > 3) { - coverages->insertNextScalarData(cov, "ieComponent"); - coverages->insertNextScalarData(cov, "spComponent"); - coverages->insertNextScalarData(cov, "thComponent"); + coverages->insertNextScalarData(cov, "ionEnhancedRate"); + coverages->insertNextScalarData(cov, "sputterRate"); + coverages->insertNextScalarData(cov, "chemicalRate"); } } @@ -113,18 +115,20 @@ class SF6O2SurfaceModel : public SurfaceModel { const auto numPoints = rates->getScalarData(0)->size(); std::vector etchRate(numPoints, 0.); - const auto ionEnhancedRate = rates->getScalarData("ionEnhancedRate"); - const auto ionSputteringRate = rates->getScalarData("ionSputteringRate"); - const auto etchantRate = rates->getScalarData("etchantRate"); + const auto ionEnhancedYield = rates->getScalarData("ionEnhancedYield"); + const auto ionSputterYield = rates->getScalarData("ionSputterYield"); + const auto ionFlux = rates->getScalarData("ionFlux"); + + const auto etchantFlux = rates->getScalarData("etchantFlux"); const auto eCoverage = coverages->getScalarData("eCoverage"); // save the etch rate components for visualization - std::vector *ieComponent = nullptr, *spComponent = nullptr, - *thComponent = nullptr; + std::vector *ieRate = nullptr, *spRate = nullptr, + *chRate = nullptr; if (Logger::getLogLevel() > 3) { - ieComponent = coverages->getScalarData("ieComponent"); - spComponent = coverages->getScalarData("spComponent"); - thComponent = coverages->getScalarData("thComponent"); + ieRate = coverages->getScalarData("ionEnhancedRate"); + spRate = coverages->getScalarData("sputterRate"); + chRate = coverages->getScalarData("chemicalRate"); } bool stop = false; @@ -134,25 +138,26 @@ class SF6O2SurfaceModel : public SurfaceModel { break; } + const auto sputterRate = + ionSputterYield->at(i) * ionFlux->at(i) * params.ionFlux; + const auto ionEnhancedRate = eCoverage->at(i) * ionEnhancedYield->at(i) * + ionFlux->at(i) * params.ionFlux; + const auto chemicalRate = params.Si.k_sigma * eCoverage->at(i) / 4.; + if (MaterialMap::isMaterial(materialIds[i], Material::Mask)) { - etchRate[i] = - -(1 / params.Mask.rho) * ionSputteringRate->at(i) * params.ionFlux; + etchRate[i] = -(1 / params.Mask.rho) * sputterRate; if (Logger::getLogLevel() > 3) { - spComponent->at(i) = ionSputteringRate->at(i) * params.ionFlux; - ieComponent->at(i) = 0.; - thComponent->at(i) = 0.; + spRate->at(i) = sputterRate; + ieRate->at(i) = 0.; + chRate->at(i) = 0.; } } else { - etchRate[i] = - -(1 / params.Si.rho) * (params.Si.k_sigma * eCoverage->at(i) / 4. + - ionSputteringRate->at(i) * params.ionFlux + - eCoverage->at(i) * ionEnhancedRate->at(i) * - params.ionFlux); // in um / s + etchRate[i] = -(1 / params.Si.rho) * (chemicalRate + sputterRate + + ionEnhancedRate); // in um / s if (Logger::getLogLevel() > 3) { - spComponent->at(i) = ionSputteringRate->at(i) * params.ionFlux; - ieComponent->at(i) = - eCoverage->at(i) * ionEnhancedRate->at(i) * params.ionFlux; - thComponent->at(i) = params.Si.k_sigma * eCoverage->at(i) / 4.; + spRate->at(i) = sputterRate; + ieRate->at(i) = ionEnhancedRate; + chRate->at(i) = chemicalRate; } } } @@ -170,11 +175,13 @@ class SF6O2SurfaceModel : public SurfaceModel { // update coverages based on fluxes const auto numPoints = rates->getScalarData(0)->size(); - const auto etchantRate = rates->getScalarData("etchantRate"); - const auto ionEnhancedRate = rates->getScalarData("ionEnhancedRate"); - const auto oxygenRate = rates->getScalarData("oxygenRate"); - const auto oxygenSputteringRate = - rates->getScalarData("oxygenSputteringRate"); + const auto etchantFlux = rates->getScalarData("etchantFlux"); + const auto oxygenFlux = rates->getScalarData("oxygenFlux"); + + const auto ionEnhancedYield = rates->getScalarData("ionEnhancedYield"); + const auto ionEnhancedPassivationYield = + rates->getScalarData("ionEnhancedPassivationYield"); + const auto ionFlux = rates->getScalarData("ionFlux"); // etchant fluorine coverage auto eCoverage = coverages->getScalarData("eCoverage"); @@ -184,10 +191,11 @@ class SF6O2SurfaceModel : public SurfaceModel { oCoverage->resize(numPoints); for (size_t i = 0; i < numPoints; ++i) { - auto Gb_F = etchantRate->at(i) * params.etchantFlux * params.beta_F; - auto Gb_O = oxygenRate->at(i) * params.oxygenFlux * params.beta_O; - auto GY_ie = ionEnhancedRate->at(i) * params.ionFlux; - auto GY_o = oxygenSputteringRate->at(i) * params.ionFlux; + auto Gb_F = etchantFlux->at(i) * params.etchantFlux * params.beta_F; + auto Gb_O = oxygenFlux->at(i) * params.oxygenFlux * params.beta_O; + auto GY_ie = ionEnhancedYield->at(i) * ionFlux->at(i) * params.ionFlux; + auto GY_o = + ionEnhancedPassivationYield->at(i) * ionFlux->at(i) * params.ionFlux; auto a = (params.Si.k_sigma + 2 * GY_ie) / Gb_F; auto b = (params.Si.beta_sigma + GY_o) / Gb_O; @@ -212,20 +220,23 @@ class SF6SurfaceModel : public SF6O2SurfaceModel { // update coverages based on fluxes const auto numPoints = rates->getScalarData(0)->size(); - const auto etchantRate = rates->getScalarData("etchantRate"); - const auto ionEnhancedRate = rates->getScalarData("ionEnhancedRate"); + const auto etchantFlux = rates->getScalarData("etchantFlux"); + const auto ionEnhancedYield = rates->getScalarData("ionEnhancedYield"); + const auto ionFlux = rates->getScalarData("ionFlux"); // etchant fluorine coverage auto eCoverage = coverages->getScalarData("eCoverage"); eCoverage->resize(numPoints); + for (size_t i = 0; i < numPoints; ++i) { - if (etchantRate->at(i) < 1e-6) { + if (etchantFlux->at(i) < 1e-6) { eCoverage->at(i) = 0; } else { - eCoverage->at(i) = - etchantRate->at(i) * params.etchantFlux * params.beta_F / - (etchantRate->at(i) * params.etchantFlux * params.beta_F + - params.Si.k_sigma + 2 * ionEnhancedRate->at(i) * params.ionFlux); + double tmp = + 1 + (params.Si.k_sigma + 2 * ionEnhancedYield->at(i) * + ionFlux->at(i) * params.ionFlux) / + (etchantFlux->at(i) * params.etchantFlux * params.beta_F); + eCoverage->at(i) = 1 / tmp; } } } @@ -237,8 +248,8 @@ class SF6O2Ion public: SF6O2Ion(const SF6O2Parameters &pParams) : params(pParams), - A(1. / - (1. + params.Ions.n_l * (M_PI_2 / params.Ions.inflectAngle - 1.))), + A_energy(1. / (1. + params.Ions.n_l * + (M_PI_2 / params.Ions.inflectAngle - 1.))), sqrt_E_th_ie_O(std::sqrt(params.Passivation.Eth_ie)), sqrt_E_th_ie_Si(std::sqrt(params.Si.Eth_ie)) {} @@ -248,10 +259,7 @@ class SF6O2Ion viennaray::TracingData &localData, const viennaray::TracingData *globalData, RNG &) override final { - // collect data for this hit - assert(primID < localData.getVectorData(0).size() && "id out of bounds"); - const double cosTheta = -DotProduct(rayDir, geomNormal); NumericType angle = std::acos(std::max(std::min(cosTheta, 1.), 0.)); @@ -277,7 +285,7 @@ class SF6O2Ion std::cos(angle / params.Si.theta_g_ie * M_PI_2), 0.); - double sqrtE = std::sqrt(E); + const double sqrtE = std::sqrt(E); NumericType Y_sp = params.Si.A_sp * std::max(sqrtE - std::sqrt(Eth_sp), 0.) * f_sp_theta; NumericType Y_Si = @@ -290,13 +298,16 @@ class SF6O2Ion assert(Y_O >= 0. && "Invalid yield"); // sputtering yield Y_sp ionSputteringRate - localData.getVectorData(0)[primID] += Y_sp * rayWeight; + localData.getVectorData(0)[primID] += Y_sp; // ion enhanced etching yield Y_Si ionEnhancedRate - localData.getVectorData(1)[primID] += Y_Si * rayWeight; + localData.getVectorData(1)[primID] += Y_Si; - // ion enhanced O sputtering yield Y_O oxygenSputteringRate - localData.getVectorData(2)[primID] += Y_O * rayWeight; + // ion enhanced O sputtering yield Y_O ionEnhancedPassivationYield + localData.getVectorData(2)[primID] += Y_O; + + // ion flux + localData.getVectorData(3)[primID] += rayWeight; } std::pair> @@ -318,11 +329,11 @@ class SF6O2Ion // 0 NumericType Eref_peak; if (incAngle >= params.Ions.inflectAngle) { - Eref_peak = (1 - (1 - A) * (M_PI_2 - incAngle) / + Eref_peak = (1 - (1 - A_energy) * (M_PI_2 - incAngle) / (M_PI_2 - params.Ions.inflectAngle)); } else { - Eref_peak = - A * std::pow(incAngle / params.Ions.inflectAngle, params.Ions.n_l); + Eref_peak = A_energy * std::pow(incAngle / params.Ions.inflectAngle, + params.Ions.n_l); } // Gaussian distribution around the Eref_peak scaled by the particle energy NumericType NewEnergy; @@ -332,11 +343,12 @@ class SF6O2Ion } while (NewEnergy > E || NewEnergy < 0.); NumericType sticking = 1.; - if (incAngle > params.Ions.thetaRMin) + if (incAngle > params.Ions.thetaRMin) { sticking = 1. - std::min((incAngle - params.Ions.thetaRMin) / (params.Ions.thetaRMax - params.Ions.thetaRMin), NumericType(1.)); + } // Set the flag to stop tracing if the energy is below the threshold if (NewEnergy > params.Si.Eth_ie) { @@ -360,12 +372,13 @@ class SF6O2Ion return params.Ions.exponent; } std::vector getLocalDataLabels() const override final { - return {"ionSputteringRate", "ionEnhancedRate", "oxygenSputteringRate"}; + return {"ionSputterYield", "ionEnhancedYield", + "ionEnhancedPassivationYield", "ionFlux"}; } private: const SF6O2Parameters ¶ms; - const NumericType A; + const NumericType A_energy; // save precomputed square roots const NumericType sqrt_E_th_ie_O; const NumericType sqrt_E_th_ie_Si; @@ -412,7 +425,7 @@ class SF6O2Etchant } NumericType getSourceDistributionPower() const override final { return 1.; } std::vector getLocalDataLabels() const override final { - return {"etchantRate"}; + return {"etchantFlux"}; } }; @@ -453,7 +466,7 @@ class SF6Etchant } NumericType getSourceDistributionPower() const override final { return 1.; } std::vector getLocalDataLabels() const override final { - return {"etchantRate"}; + return {"etchantFlux"}; } }; @@ -495,7 +508,7 @@ class SF6O2Oxygen } NumericType getSourceDistributionPower() const override final { return 1.; } std::vector getLocalDataLabels() const override final { - return {"oxygenRate"}; + return {"oxygenFlux"}; } }; } // namespace impl @@ -509,7 +522,7 @@ class SF6O2Etching : public ProcessModel { public: SF6O2Etching() { initializeModel(); } - // All flux values are in units 1e16 / cm² + // All flux values are in units 1e15 / cm² SF6O2Etching(const double ionFlux, const double etchantFlux, const double oxygenFlux, const NumericType meanEnergy /* eV */, const NumericType sigmaEnergy /* eV */, // 5 parameters From cfbbd9948f7bafa5688be99265cb09b447de778b Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Wed, 15 Jan 2025 16:50:51 +0100 Subject: [PATCH 09/25] Revert: seperate ion flux and yield --- examples/holeEtching/config.txt | 6 +-- include/viennaps/models/psSF6O2Etching.hpp | 49 +++++++++------------- 2 files changed, 23 insertions(+), 32 deletions(-) diff --git a/examples/holeEtching/config.txt b/examples/holeEtching/config.txt index e61abff9..65eb912c 100644 --- a/examples/holeEtching/config.txt +++ b/examples/holeEtching/config.txt @@ -14,10 +14,10 @@ taperAngle=1.5 # degree # Process parameters processTime=50 # seconds -# all flux values are units 1e16 / cm² +# all flux values are units 1e15 / cm² ionFlux=10. etchantFlux=500. -oxygenFlux=30. +oxygenFlux=200. ionExponent=200 meanEnergy=100 # eV @@ -30,4 +30,4 @@ integrationScheme=EO_1 raysPerPoint=1000 -outputFile=final \ No newline at end of file +outputFile=final diff --git a/include/viennaps/models/psSF6O2Etching.hpp b/include/viennaps/models/psSF6O2Etching.hpp index bea7ea94..d7d7ca28 100644 --- a/include/viennaps/models/psSF6O2Etching.hpp +++ b/include/viennaps/models/psSF6O2Etching.hpp @@ -115,9 +115,8 @@ class SF6O2SurfaceModel : public SurfaceModel { const auto numPoints = rates->getScalarData(0)->size(); std::vector etchRate(numPoints, 0.); - const auto ionEnhancedYield = rates->getScalarData("ionEnhancedYield"); - const auto ionSputterYield = rates->getScalarData("ionSputterYield"); - const auto ionFlux = rates->getScalarData("ionFlux"); + const auto ionEnhancedFlux = rates->getScalarData("ionEnhancedFlux"); + const auto ionSputterFlux = rates->getScalarData("ionSputterFlux"); const auto etchantFlux = rates->getScalarData("etchantFlux"); const auto eCoverage = coverages->getScalarData("eCoverage"); @@ -138,10 +137,9 @@ class SF6O2SurfaceModel : public SurfaceModel { break; } - const auto sputterRate = - ionSputterYield->at(i) * ionFlux->at(i) * params.ionFlux; - const auto ionEnhancedRate = eCoverage->at(i) * ionEnhancedYield->at(i) * - ionFlux->at(i) * params.ionFlux; + const auto sputterRate = ionSputterFlux->at(i) * params.ionFlux; + const auto ionEnhancedRate = + eCoverage->at(i) * ionEnhancedFlux->at(i) * params.ionFlux; const auto chemicalRate = params.Si.k_sigma * eCoverage->at(i) / 4.; if (MaterialMap::isMaterial(materialIds[i], Material::Mask)) { @@ -178,10 +176,9 @@ class SF6O2SurfaceModel : public SurfaceModel { const auto etchantFlux = rates->getScalarData("etchantFlux"); const auto oxygenFlux = rates->getScalarData("oxygenFlux"); - const auto ionEnhancedYield = rates->getScalarData("ionEnhancedYield"); - const auto ionEnhancedPassivationYield = - rates->getScalarData("ionEnhancedPassivationYield"); - const auto ionFlux = rates->getScalarData("ionFlux"); + const auto ionEnhancedFlux = rates->getScalarData("ionEnhancedFlux"); + const auto ionEnhancedPassivationFlux = + rates->getScalarData("ionEnhancedPassivationFlux"); // etchant fluorine coverage auto eCoverage = coverages->getScalarData("eCoverage"); @@ -193,9 +190,8 @@ class SF6O2SurfaceModel : public SurfaceModel { for (size_t i = 0; i < numPoints; ++i) { auto Gb_F = etchantFlux->at(i) * params.etchantFlux * params.beta_F; auto Gb_O = oxygenFlux->at(i) * params.oxygenFlux * params.beta_O; - auto GY_ie = ionEnhancedYield->at(i) * ionFlux->at(i) * params.ionFlux; - auto GY_o = - ionEnhancedPassivationYield->at(i) * ionFlux->at(i) * params.ionFlux; + auto GY_ie = ionEnhancedFlux->at(i) * params.ionFlux; + auto GY_o = ionEnhancedPassivationFlux->at(i) * params.ionFlux; auto a = (params.Si.k_sigma + 2 * GY_ie) / Gb_F; auto b = (params.Si.beta_sigma + GY_o) / Gb_O; @@ -222,7 +218,6 @@ class SF6SurfaceModel : public SF6O2SurfaceModel { const auto etchantFlux = rates->getScalarData("etchantFlux"); const auto ionEnhancedYield = rates->getScalarData("ionEnhancedYield"); - const auto ionFlux = rates->getScalarData("ionFlux"); // etchant fluorine coverage auto eCoverage = coverages->getScalarData("eCoverage"); @@ -233,9 +228,9 @@ class SF6SurfaceModel : public SF6O2SurfaceModel { eCoverage->at(i) = 0; } else { double tmp = - 1 + (params.Si.k_sigma + 2 * ionEnhancedYield->at(i) * - ionFlux->at(i) * params.ionFlux) / - (etchantFlux->at(i) * params.etchantFlux * params.beta_F); + 1 + + (params.Si.k_sigma + 2 * ionEnhancedYield->at(i) * params.ionFlux) / + (etchantFlux->at(i) * params.etchantFlux * params.beta_F); eCoverage->at(i) = 1 / tmp; } } @@ -297,17 +292,14 @@ class SF6O2Ion assert(Y_Si >= 0. && "Invalid yield"); assert(Y_O >= 0. && "Invalid yield"); - // sputtering yield Y_sp ionSputteringRate - localData.getVectorData(0)[primID] += Y_sp; - - // ion enhanced etching yield Y_Si ionEnhancedRate - localData.getVectorData(1)[primID] += Y_Si; + // sputtering yield Y_sp ionSputterFlux + localData.getVectorData(0)[primID] += Y_sp * rayWeight; - // ion enhanced O sputtering yield Y_O ionEnhancedPassivationYield - localData.getVectorData(2)[primID] += Y_O; + // ion enhanced etching yield Y_Si ionEnhancedFlux + localData.getVectorData(1)[primID] += Y_Si * rayWeight; - // ion flux - localData.getVectorData(3)[primID] += rayWeight; + // ion enhanced O sputtering yield Y_O ionEnhancedPassivationFlux + localData.getVectorData(2)[primID] += Y_O * rayWeight; } std::pair> @@ -372,8 +364,7 @@ class SF6O2Ion return params.Ions.exponent; } std::vector getLocalDataLabels() const override final { - return {"ionSputterYield", "ionEnhancedYield", - "ionEnhancedPassivationYield", "ionFlux"}; + return {"ionSputterFlux", "ionEnhancedFlux", "ionEnhancedPassivationFlux"}; } private: From 12f25763c99ef55518c93320ff1425b9a31510cf Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Thu, 16 Jan 2025 15:14:37 +0100 Subject: [PATCH 10/25] Fix ion beam etching init function --- include/viennaps/models/psIonBeamEtching.hpp | 26 +++++++++++--------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/include/viennaps/models/psIonBeamEtching.hpp b/include/viennaps/models/psIonBeamEtching.hpp index 45ea4230..fb5fecb2 100644 --- a/include/viennaps/models/psIonBeamEtching.hpp +++ b/include/viennaps/models/psIonBeamEtching.hpp @@ -18,13 +18,13 @@ template struct IBEParameters { NumericType meanEnergy = 250; // eV NumericType sigmaEnergy = 10; // eV NumericType thresholdEnergy = 20; // eV - NumericType sourcePower = 100; + NumericType exponent = 100; NumericType n = 10; NumericType inflectAngle = 89; // degree NumericType minAngle = 5; // degree NumericType tiltAngle = 0; // degree std::function yieldFunction = - [](NumericType cosTheta) { return 1.; }; + [](NumericType theta) { return 1.; }; }; namespace impl { @@ -87,11 +87,14 @@ class IBEIon : public viennaray::Particle, NumericType> { viennaray::TracingData &localData, const viennaray::TracingData *, RNG &) override final { - NumericType cosTheta = -DotProduct(rayDir, geomNormal); + auto cosTheta = -DotProduct(rayDir, geomNormal); + NumericType theta = + std::acos(std::max(std::min(cosTheta, static_cast(1.)), + static_cast(0.))); localData.getVectorData(0)[primID] += std::max(std::sqrt(energy_) - std::sqrt(params_.thresholdEnergy), 0.) * - params_.yieldFunction(cosTheta); + params_.yieldFunction(theta); } std::pair> @@ -137,7 +140,7 @@ class IBEIon : public viennaray::Particle, NumericType> { } NumericType getSourceDistributionPower() const override final { - return params_.sourcePower; + return params_.exponent; } std::vector getLocalDataLabels() const override final { @@ -158,23 +161,22 @@ class IBEIon : public viennaray::Particle, NumericType> { template class IonBeamEtching : public ProcessModel { public: - IonBeamEtching() { - std::vector maskMaterial; - initialize(std::move(maskMaterial)); - } + IonBeamEtching() { initialize(maskMaterials_); } IonBeamEtching(std::vector maskMaterial) { - initialize(std::move(maskMaterial)); + maskMaterials_ = std::move(maskMaterial); + initialize(maskMaterials_); } IBEParameters &getParameters() { return params_; } void setParameters(const IBEParameters ¶ms) { params_ = params; + initialize(maskMaterials_); } private: - void initialize(std::vector &&maskMaterial) { + void initialize(const std::vector &maskMaterial) { // particles auto particle = std::make_unique>(params_); @@ -187,12 +189,14 @@ class IonBeamEtching : public ProcessModel { this->setSurfaceModel(surfModel); this->setVelocityField(velField); + this->particles.clear(); this->insertNextParticleType(particle); this->setProcessName("IonBeamEtching"); } private: IBEParameters params_; + std::vector maskMaterials_; }; } // namespace viennaps From ac1b082ce1db06e394dc4b307df926e28db4e7fd Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Tue, 21 Jan 2025 13:01:56 +0100 Subject: [PATCH 11/25] Test flux script --- examples/holeEtching/testFluxes.py | 73 ++++++++++++++++++++++ include/viennaps/models/psSF6O2Etching.hpp | 6 ++ python/pyWrap.cpp | 2 +- python/stubs/viennaps2d/viennaps2d.pyi | 2 +- python/stubs/viennaps3d/viennaps3d.pyi | 2 +- 5 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 examples/holeEtching/testFluxes.py diff --git a/examples/holeEtching/testFluxes.py b/examples/holeEtching/testFluxes.py new file mode 100644 index 00000000..d1827d1e --- /dev/null +++ b/examples/holeEtching/testFluxes.py @@ -0,0 +1,73 @@ +from argparse import ArgumentParser +import numpy as np + +# 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) +args = parser.parse_args() + +# switch between 2D and 3D mode +if args.dim == 2: + print("Running 2D simulation.") + import viennaps2d as vps +else: + print("Running 3D simulation.") + import viennaps3d as vps + + +vps.Logger.setLogLevel(vps.LogLevel.INFO) + +# hole geometry parameters +gridDelta = 0.04 # um +xExtent = 1.0 +yExtent = 1.0 +holeRadius = 0.175 +maskHeight = 1.2 +taperAngle = 1.193 + +# fluxes +ionFlux = [10., 10., 10., 10., 10.] +etchantFlux = [5.5e3, 5e3, 4e3, 3e3, 2e3] +oxygenFlux = [2e2, 3e2, 1e3, 1.5e3, 0.] +yo2 = [0.44, 0.5, 0.56, 0.62, 0] + +# etching model parameters +params = vps.SF6O2Parameters() +params.Si.A_ie = 7.0 +params.Si.Eth_ie = 15.0 + +params.Si.A_sp = 0.0337 +params.Si.Eth_sp = 20.0 + +params.Passivation.A_ie = 3.0 + +params.Ions.exponent = 500 +params.Ions.meanEnergy = 100.0 +params.Ions.sigmaEnergy = 10.0 +params.Ions.minAngle = np.deg2rad(10.0) + +# simulation parameters +processDuration = 0.1 # s +integrationScheme = vps.ls.IntegrationSchemeEnum.ENGQUIST_OSHER_2ND_ORDER +numberOfRaysPerPoint = int(1000) + +for i in range(len(yo2)): + + geometry = vps.Domain() + vps.MakeHole(geometry, gridDelta, xExtent, yExtent, holeRadius, maskHeight, taperAngle, 0.0, False, True, vps.Material.Si).apply() + + process = vps.Process() + process.setDomain(geometry) + process.setProcessDuration(processDuration) + process.setIntegrationScheme(integrationScheme) + process.setNumberOfRaysPerPoint(numberOfRaysPerPoint) + + params.ionFlux = ionFlux[i] + params.etchantFlux = etchantFlux[i] + params.oxygenFlux = oxygenFlux[i] + model = vps.SF6O2Etching(params) + + process.setProcessModel(model) + process.apply() + + geometry.saveSurfaceMesh("hole_y{:.2f}_EO2.vtp".format(yo2[i])) \ No newline at end of file diff --git a/include/viennaps/models/psSF6O2Etching.hpp b/include/viennaps/models/psSF6O2Etching.hpp index d7d7ca28..62b9d077 100644 --- a/include/viennaps/models/psSF6O2Etching.hpp +++ b/include/viennaps/models/psSF6O2Etching.hpp @@ -279,6 +279,11 @@ class SF6O2Ion std::max((1 + params.Si.B_ie * (1 - cosTheta * cosTheta)) * std::cos(angle / params.Si.theta_g_ie * M_PI_2), 0.); + // NumericType f_sp_theta = 1.; + // NumericType f_ie_theta = 1.; + // if (cosTheta < 0.5) { + // f_ie_theta = std::max(3 - 6 * angle / M_PI, 0.); + // } const double sqrtE = std::sqrt(E); NumericType Y_sp = @@ -334,6 +339,7 @@ class SF6O2Ion NewEnergy = normalDist(Rng); } while (NewEnergy > E || NewEnergy < 0.); + // NumericType sticking = 0.; NumericType sticking = 1.; if (incAngle > params.Ions.thetaRMin) { sticking = diff --git a/python/pyWrap.cpp b/python/pyWrap.cpp index 513085ce..76ee67be 100644 --- a/python/pyWrap.cpp +++ b/python/pyWrap.cpp @@ -676,7 +676,7 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { .def_readwrite("beta_O", &SF6O2Parameters::beta_O) .def_readwrite("Mask", &SF6O2Parameters::Mask) .def_readwrite("Si", &SF6O2Parameters::Si) - .def_readwrite("Polymer", &SF6O2Parameters::Passivation) + .def_readwrite("Passivation", &SF6O2Parameters::Passivation) .def_readwrite("Ions", &SF6O2Parameters::Ions); // SF6O2 Etching diff --git a/python/stubs/viennaps2d/viennaps2d.pyi b/python/stubs/viennaps2d/viennaps2d.pyi index e0bcf891..06882c12 100644 --- a/python/stubs/viennaps2d/viennaps2d.pyi +++ b/python/stubs/viennaps2d/viennaps2d.pyi @@ -481,7 +481,7 @@ class SF6O2Etching(ProcessModel): class SF6O2Parameters: Ions: SF6O2ParametersIons Mask: SF6O2ParametersMask - Polymer: SF6O2ParametersPassivation + Passivation: SF6O2ParametersPassivation Si: SF6O2ParametersSi beta_F: float beta_O: float diff --git a/python/stubs/viennaps3d/viennaps3d.pyi b/python/stubs/viennaps3d/viennaps3d.pyi index 029dc7ca..34ee9068 100644 --- a/python/stubs/viennaps3d/viennaps3d.pyi +++ b/python/stubs/viennaps3d/viennaps3d.pyi @@ -471,7 +471,7 @@ class SF6O2Etching(ProcessModel): class SF6O2Parameters: Ions: SF6O2ParametersIons Mask: SF6O2ParametersMask - Polymer: SF6O2ParametersPassivation + Passivation: SF6O2ParametersPassivation Si: SF6O2ParametersSi beta_F: float beta_O: float From 4ac7ab2e7711182265f28f0b6a1325cae82bfbb0 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Mon, 3 Feb 2025 18:06:33 +0100 Subject: [PATCH 12/25] Add units --- examples/holeEtching/config.txt | 30 +- examples/holeEtching/holeEtching.cpp | 12 +- include/viennaps/models/psSF6O2Etching.hpp | 55 ++-- include/viennaps/psProcess.hpp | 4 +- include/viennaps/psUnits.hpp | 347 +++++++++++++++++++++ include/viennaps/psUtils.hpp | 16 +- 6 files changed, 414 insertions(+), 50 deletions(-) create mode 100644 include/viennaps/psUnits.hpp diff --git a/examples/holeEtching/config.txt b/examples/holeEtching/config.txt index 65eb912c..4502feda 100644 --- a/examples/holeEtching/config.txt +++ b/examples/holeEtching/config.txt @@ -2,32 +2,34 @@ # all length units are in micrometers (um) # Domain -gridDelta=4.0 # um -xExtent=100.0 # um -yExtent=100.0 # um +lengthUnit=um +gridDelta=0.03 # um +xExtent=1.0 # um +yExtent=1.0 # um # Geometry -holeRadius=20.0 # um -maskHeight=65.0 # um -taperAngle=1.5 # degree +holeRadius=0.175 # um +maskHeight=1.2 # um +taperAngle=1.193 # degree # Process parameters -processTime=50 # seconds +processTime=3 +timeUnit=min # all flux values are units 1e15 / cm² ionFlux=10. -etchantFlux=500. -oxygenFlux=200. +etchantFlux=3000. +oxygenFlux=1500. ionExponent=200 meanEnergy=100 # eV sigmaEnergy=10 # eV -A_O=3 # passivation layer yield constant -A_Si=7 # Si yield constant +A_O=2 # passivation layer yield constant +A_Si=7 # Si yield constant -etchStopDepth=-1000 # maximum etching depth -integrationScheme=EO_1 +etchStopDepth=-10 # maximum etching depth +integrationScheme=EO_1 raysPerPoint=1000 -outputFile=final +outputFile=final_y0p62 diff --git a/examples/holeEtching/holeEtching.cpp b/examples/holeEtching/holeEtching.cpp index 67941aaf..916c8d04 100644 --- a/examples/holeEtching/holeEtching.cpp +++ b/examples/holeEtching/holeEtching.cpp @@ -8,9 +8,9 @@ namespace ps = viennaps; int main(int argc, char *argv[]) { using NumericType = double; - constexpr int D = 3; + constexpr int D = 2; - ps::Logger::setLogLevel(ps::LogLevel::INTERMEDIATE); + ps::Logger::setLogLevel(ps::LogLevel::INFO); omp_set_num_threads(16); // Parse the parameters @@ -35,8 +35,7 @@ int main(int argc, char *argv[]) { .apply(); // use pre-defined model SF6O2 etching model - auto model = ps::SmartPointer>::New(); - auto &modelParams = model->getParameters(); + ps::SF6O2Parameters modelParams; modelParams.ionFlux = params.get("ionFlux"); modelParams.etchantFlux = params.get("etchantFlux"); modelParams.oxygenFlux = params.get("oxygenFlux"); @@ -46,6 +45,11 @@ int main(int argc, char *argv[]) { modelParams.Passivation.A_ie = params.get("A_O"); modelParams.Si.A_ie = params.get("A_Si"); modelParams.etchStopDepth = params.get("etchStopDepth"); + auto model = + ps::SmartPointer>::New(modelParams); + + ps::units::Length::setUnit(params.get("lengthUnit")); + ps::units::Time::setUnit(params.get("timeUnit")); // process setup ps::Process process; diff --git a/include/viennaps/models/psSF6O2Etching.hpp b/include/viennaps/models/psSF6O2Etching.hpp index 76a7b020..91ad79de 100644 --- a/include/viennaps/models/psSF6O2Etching.hpp +++ b/include/viennaps/models/psSF6O2Etching.hpp @@ -7,6 +7,7 @@ #include "../psConstants.hpp" #include "../psProcessModel.hpp" #include "../psSurfaceModel.hpp" +#include "../psUnits.hpp" #include "../psVelocityField.hpp" namespace viennaps { @@ -142,16 +143,22 @@ class SF6O2SurfaceModel : public SurfaceModel { eCoverage->at(i) * ionEnhancedFlux->at(i) * params.ionFlux; const auto chemicalRate = params.Si.k_sigma * eCoverage->at(i) / 4.; + // The etch rate is calculated in nm/s + const double unitConversion = + units::Time::getInstance().convertSecond() / + units::Length::getInstance().convertNanometer(); + if (MaterialMap::isMaterial(materialIds[i], Material::Mask)) { - etchRate[i] = -(1 / params.Mask.rho) * sputterRate; + etchRate[i] = -(1 / params.Mask.rho) * sputterRate * unitConversion; if (Logger::getLogLevel() > 3) { spRate->at(i) = sputterRate; ieRate->at(i) = 0.; chRate->at(i) = 0.; } } else { - etchRate[i] = -(1 / params.Si.rho) * (chemicalRate + sputterRate + - ionEnhancedRate); // in um / s + etchRate[i] = -(1 / params.Si.rho) * + (chemicalRate + sputterRate + ionEnhancedRate) * + unitConversion; if (Logger::getLogLevel() > 3) { spRate->at(i) = sputterRate; ieRate->at(i) = ionEnhancedRate; @@ -271,19 +278,19 @@ class SF6O2Ion Eth_sp = params.Mask.Eth_sp; } - NumericType f_sp_theta = - std::max((1 + B_sp * (1 - cosTheta * cosTheta)) * - std::cos(angle / params.Si.theta_g_sp * M_PI_2), - 0.); - NumericType f_ie_theta = - std::max((1 + params.Si.B_ie * (1 - cosTheta * cosTheta)) * - std::cos(angle / params.Si.theta_g_ie * M_PI_2), - 0.); - // NumericType f_sp_theta = 1.; - // NumericType f_ie_theta = 1.; - // if (cosTheta < 0.5) { - // f_ie_theta = std::max(3 - 6 * angle / M_PI, 0.); - // } + // NumericType f_sp_theta = + // std::max((1 + B_sp * (1 - cosTheta * cosTheta)) * + // std::cos(angle / params.Si.theta_g_sp * M_PI_2), + // 0.); + // NumericType f_ie_theta = + // std::max((1 + params.Si.B_ie * (1 - cosTheta * cosTheta)) * + // std::cos(angle / params.Si.theta_g_ie * M_PI_2), + // 0.); + NumericType f_sp_theta = 1.; + NumericType f_ie_theta = 1.; + if (cosTheta < 0.5) { + f_ie_theta = std::max(3 - 6 * angle / M_PI, 0.); + } const double sqrtE = std::sqrt(E); NumericType Y_sp = @@ -339,14 +346,14 @@ class SF6O2Ion NewEnergy = normalDist(Rng); } while (NewEnergy > E || NewEnergy < 0.); - // NumericType sticking = 0.; - NumericType sticking = 1.; - if (incAngle > params.Ions.thetaRMin) { - sticking = - 1. - std::min((incAngle - params.Ions.thetaRMin) / - (params.Ions.thetaRMax - params.Ions.thetaRMin), - NumericType(1.)); - } + NumericType sticking = 0.; + // NumericType sticking = 1.; + // if (incAngle > params.Ions.thetaRMin) { + // sticking = + // 1. - std::min((incAngle - params.Ions.thetaRMin) / + // (params.Ions.thetaRMax - params.Ions.thetaRMin), + // NumericType(1.)); + // } // Set the flag to stop tracing if the energy is below the threshold if (NewEnergy > params.Si.Eth_ie) { diff --git a/include/viennaps/psProcess.hpp b/include/viennaps/psProcess.hpp index 124404b6..a27d626e 100644 --- a/include/viennaps/psProcess.hpp +++ b/include/viennaps/psProcess.hpp @@ -2,6 +2,7 @@ #include "psProcessModel.hpp" #include "psTranslationField.hpp" +#include "psUnits.hpp" #include "psUtils.hpp" #include @@ -677,7 +678,8 @@ template class Process { std::stringstream stream; stream << std::fixed << std::setprecision(4) << "Process time: " << processDuration - remainingTime << " / " - << processDuration; + << processDuration << " " + << units::Time::getInstance().toStringShort(); Logger::getInstance().addInfo(stream.str()).print(); } } diff --git a/include/viennaps/psUnits.hpp b/include/viennaps/psUnits.hpp new file mode 100644 index 00000000..7b186314 --- /dev/null +++ b/include/viennaps/psUnits.hpp @@ -0,0 +1,347 @@ +#pragma once + +namespace viennaps { + +namespace units { + +class Length { + static int unit_; + +public: + enum : int { + METER, + CENTIMETER, + MILLIMETER, + MICROMETER, + NANOMETER, + ANGSTROM, + UNDEFINED + }; + + Length() {} + + static void setUnit(const int passedUnit) { unit_ = passedUnit; } + + static void setUnit(const std::string &unit) { + if (unit == "meter" || unit == "m") { + unit_ = METER; + } else if (unit == "centimeter" || unit == "cm") { + unit_ = CENTIMETER; + } else if (unit == "millimeter" || unit == "mm") { + unit_ = MILLIMETER; + } else if (unit == "micrometer" || unit == "um") { + unit_ = MICROMETER; + } else if (unit == "nanometer" || unit == "nm") { + unit_ = NANOMETER; + } else if (unit == "angstrom" || unit == "A") { + unit_ = ANGSTROM; + } else { + throw std::invalid_argument( + "The value must be one of the following: meter, centimeter, " + "millimeter, micrometer, nanometer, angstrom"); + } + } + + static int getUnit() { return unit_; } + + static Length &getInstance() { + static Length instance; + return instance; + } + + // delete constructors to result in better error messages by compilers + Length(const Length &) = delete; + void operator=(const Length &) = delete; + + double convertMeter() const { + switch (unit_) { + case METER: + return 1.; + case CENTIMETER: + return 1e-2; + case MILLIMETER: + return 1e-3; + case MICROMETER: + return 1e-6; + case NANOMETER: + return 1e-9; + case ANGSTROM: + return 1e-10; + case UNDEFINED: { + Logger::getInstance().addWarning("Length unit is not defined.").print(); + return 1.; + } + } + + Logger::getInstance().addError("Invalid length unit.").print(); + return 0.; + } + + double convertCentimeter() const { + switch (unit_) { + case METER: + return 1e2; + case CENTIMETER: + return 1.; + case MILLIMETER: + return 1e-1; + case MICROMETER: + return 1e-4; + case NANOMETER: + return 1e-7; + case ANGSTROM: + return 1e-8; + case UNDEFINED: { + Logger::getInstance().addWarning("Length unit is not defined.").print(); + return 1.; + } + } + + Logger::getInstance().addError("Invalid length unit.").print(); + return 0.; + } + + double convertMillimeter() const { + switch (unit_) { + case METER: + return 1e3; + case CENTIMETER: + return 1e1; + case MILLIMETER: + return 1.; + case MICROMETER: + return 1e-3; + case NANOMETER: + return 1e-6; + case ANGSTROM: + return 1e-7; + case UNDEFINED: { + Logger::getInstance().addWarning("Length unit is not defined.").print(); + return 1.; + } + } + + Logger::getInstance().addError("Invalid length unit.").print(); + return 0.; + } + + double convertMicrometer() const { + switch (unit_) { + case METER: + return 1e6; + case CENTIMETER: + return 1e4; + case MILLIMETER: + return 1e3; + case MICROMETER: + return 1.; + case NANOMETER: + return 1e-3; + case ANGSTROM: + return 1e-4; + case UNDEFINED: { + Logger::getInstance().addWarning("Length unit is not defined.").print(); + return 1.; + } + } + + Logger::getInstance().addError("Invalid length unit.").print(); + return 0.; + } + + double convertNanometer() const { + switch (unit_) { + case METER: + return 1e9; + case CENTIMETER: + return 1e7; + case MILLIMETER: + return 1e6; + case MICROMETER: + return 1e3; + case NANOMETER: + return 1.; + case ANGSTROM: + return 1e-1; + case UNDEFINED: { + Logger::getInstance().addWarning("Length unit is not defined.").print(); + return 1.; + } + } + + Logger::getInstance().addError("Invalid length unit.").print(); + return 0.; + } + + std::string toString() const { + switch (unit_) { + case METER: + return "meter"; + case CENTIMETER: + return "centimeter"; + case MILLIMETER: + return "millimeter"; + case MICROMETER: + return "micrometer"; + case NANOMETER: + return "nanometer"; + case ANGSTROM: + return "angstrom"; + case UNDEFINED: + return ""; + } + + Logger::getInstance().addError("Invalid length unit.").print(); + return "error-length-unit"; + } + + std::string toStringShort() const { + switch (unit_) { + case METER: + return "m"; + case CENTIMETER: + return "cm"; + case MILLIMETER: + return "mm"; + case MICROMETER: + return "um"; + case NANOMETER: + return "nm"; + case ANGSTROM: + return "A"; + case UNDEFINED: + return ""; + } + + Logger::getInstance().addError("Invalid length unit.").print(); + return "error-length-unit"; + } +}; + +inline int Length::unit_ = Length::UNDEFINED; + +class Time { + static int unit_; + +public: + enum : int { MINUTE, SECOND, MILLISECOND, UNDEFINED }; + + Time() {} + + static void setUnit(const int passedUnit) { unit_ = passedUnit; } + + static void setUnit(const std::string &unit) { + if (unit == "second" || unit == "s") { + unit_ = SECOND; + } else if (unit == "minute" || unit == "min") { + unit_ = MINUTE; + } else if (unit == "millisecond" || unit == "ms") { + unit_ = MILLISECOND; + } else { + throw std::invalid_argument("The value must be one of the following: " + "second, minute, millisecond"); + } + } + + static int getUnit() { return unit_; } + + static Time &getInstance() { + static Time instance; + return instance; + } + + // delete constructors to result in better error messages by compilers + Time(const Time &) = delete; + void operator=(const Time &) = delete; + + double convertMinute() const { + switch (unit_) { + case MINUTE: + return 1.; + case SECOND: + return 1. / 60.; + case MILLISECOND: + return 1. / 60000.; + case UNDEFINED: { + Logger::getInstance().addWarning("Time unit is not defined.").print(); + return 1.; + } + } + + Logger::getInstance().addError("Invalid time unit.").print(); + return 0.; + } + + double convertSecond() const { + switch (unit_) { + case MINUTE: + return 60.; + case SECOND: + return 1.; + case MILLISECOND: + return 1e-3; + case UNDEFINED: { + Logger::getInstance().addWarning("Time unit is not defined.").print(); + return 1.; + } + } + + Logger::getInstance().addError("Invalid time unit.").print(); + return 0.; + } + + double convertMillisecond() const { + switch (unit_) { + case MINUTE: + return 60000.; + case SECOND: + return 1e3; + case MILLISECOND: + return 1.; + case UNDEFINED: { + Logger::getInstance().addWarning("Time unit is not defined.").print(); + return 1.; + } + } + + Logger::getInstance().addError("Invalid time unit.").print(); + return 0.; + } + + std::string toString() const { + switch (unit_) { + case MINUTE: + return "minute"; + case SECOND: + return "second"; + case MILLISECOND: + return "millisecond"; + case UNDEFINED: + return ""; + } + + Logger::getInstance().addError("Invalid time unit.").print(); + return "error-time-unit"; + } + + std::string toStringShort() const { + switch (unit_) { + case MINUTE: + return "min"; + case SECOND: + return "s"; + case MILLISECOND: + return "ms"; + case UNDEFINED: + return ""; + } + + Logger::getInstance().addError("Invalid time unit.").print(); + return "error-time-unit"; + } +}; + +inline int Time::unit_ = Time::UNDEFINED; + +} // namespace units +}; // namespace viennaps \ No newline at end of file diff --git a/include/viennaps/psUtils.hpp b/include/viennaps/psUtils.hpp index 8eba174b..072af085 100644 --- a/include/viennaps/psUtils.hpp +++ b/include/viennaps/psUtils.hpp @@ -58,12 +58,12 @@ convertIntegrationScheme(const std::string &s) { "The value must be one of the following: " "ENGQUIST_OSHER_1ST_ORDER, ENGQUIST_OSHER_2ND_ORDER, " "LAX_FRIEDRICHS_1ST_ORDER, LAX_FRIEDRICHS_2ND_ORDER, " - "LOCAL LAX_FRIEDRICHS ANALYTICAL 1ST ORDER, " - "LOCAL LOCAL LAX FRIEDRICHS 1ST ORDER, " - "LOCAL LOCAL LAX FRIEDRICHS 2ND ORDER, " - "LOCAL LAX FRIEDRICHS 1ST ORDER, " - "LOCAL LAX FRIEDRICHS 2ND ORDER, " - "STENCIL LOCAL LAX FRIEDRICHS 1ST ORDER"); + "LOCAL_LAX_FRIEDRICHS_ANALYTICAL_1ST_ORDER, " + "LOCAL_LOCAL_LAX_FRIEDRICHS_1ST_ORDER, " + "LOCAL_LOCAL_LAX_FRIEDRICHS_2ND_ORDER, " + "LOCAL_LAX_FRIEDRICHS_1ST_ORDER, " + "LOCAL_LAX_FRIEDRICHS_2ND_ORDER, " + "STENCIL_LOCAL_LAX_FRIEDRICHS_1ST_ORDER"); } // Converts string to the given numeric datatype @@ -236,9 +236,11 @@ struct Parameters { void readConfigFile(const std::string &fileName) { m = readFile(fileName); } + bool exists(const std::string &key) const { return m.find(key) != m.end(); } + template [[nodiscard]] T get(const std::string &key) const { - if (m.find(key) == m.end()) { + if (!exists(key)) { std::cout << "Key not found in parameters: " << key << std::endl; exit(1); return T(); From 5efeb386dbcb621427038fe82e5c4dc6f9e5d1b1 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Mon, 3 Feb 2025 19:23:37 +0100 Subject: [PATCH 13/25] Add ion only test --- examples/holeEtching/CMakeLists.txt | 3 + examples/holeEtching/holeEtching.cpp | 1 + examples/holeEtching/ionOnly.cpp | 53 +++++++ include/viennaps/models/ion_only.hpp | 212 +++++++++++++++++++++++++++ 4 files changed, 269 insertions(+) create mode 100644 examples/holeEtching/ionOnly.cpp create mode 100644 include/viennaps/models/ion_only.hpp diff --git a/examples/holeEtching/CMakeLists.txt b/examples/holeEtching/CMakeLists.txt index d2399a0a..775a5d87 100644 --- a/examples/holeEtching/CMakeLists.txt +++ b/examples/holeEtching/CMakeLists.txt @@ -8,3 +8,6 @@ configure_file(config.txt ${CMAKE_CURRENT_BINARY_DIR}/config.txt COPYONLY) add_dependencies(ViennaPS_Examples ${PROJECT_NAME}) viennacore_setup_bat(${PROJECT_NAME} ${VIENNAPS_ARTIFACTS_DIRECTORY}) + +add_executable("ionOnly" "ionOnly.cpp") +target_link_libraries("ionOnly" PRIVATE ViennaPS) diff --git a/examples/holeEtching/holeEtching.cpp b/examples/holeEtching/holeEtching.cpp index 916c8d04..a1d8b648 100644 --- a/examples/holeEtching/holeEtching.cpp +++ b/examples/holeEtching/holeEtching.cpp @@ -48,6 +48,7 @@ int main(int argc, char *argv[]) { auto model = ps::SmartPointer>::New(modelParams); + // set parameter units ps::units::Length::setUnit(params.get("lengthUnit")); ps::units::Time::setUnit(params.get("timeUnit")); diff --git a/examples/holeEtching/ionOnly.cpp b/examples/holeEtching/ionOnly.cpp new file mode 100644 index 00000000..1b7574b0 --- /dev/null +++ b/examples/holeEtching/ionOnly.cpp @@ -0,0 +1,53 @@ +#include +#include + +#include +#include + +namespace ps = viennaps; + +int main(int argc, char *argv[]) { + using NumericType = double; + constexpr int D = 2; + + ps::Logger::setLogLevel(ps::LogLevel::INTERMEDIATE); + omp_set_num_threads(16); + + // geometry setup + auto geometry = ps::SmartPointer>::New(); + ps::MakeHole(geometry, 0.02, 1.0, 1.0, 0.175, 1.2, 1.193, 0, + false /* periodic boundary */, + true /*create mask*/, ps::Material::Si) + .apply(); + + // use pre-defined model SF6O2 etching model + ps::SF6O2Parameters modelParams; + modelParams.Ions.meanEnergy = 100.0; + modelParams.Ions.sigmaEnergy = 10.0; + modelParams.Ions.exponent = 500; + modelParams.Si.A_ie = 7.0; + + modelParams.Si.rho = 0.1; + modelParams.Mask.rho = 0.05; + + auto model = + ps::SmartPointer>::New(modelParams); + + // process setup + ps::Process process; + process.setDomain(geometry); + process.setProcessModel(model); + process.setMaxCoverageInitIterations(10); + process.setProcessDuration(10.0 / 60.0); + process.setIntegrationScheme( + viennals::IntegrationSchemeEnum::ENGQUIST_OSHER_1ST_ORDER); + + // print initial surface + geometry->saveSurfaceMesh("initial.vtp"); + + // run the process + process.apply(); + + // print final surface + geometry->saveSurfaceMesh("ionOnly"); +} diff --git a/include/viennaps/models/ion_only.hpp b/include/viennaps/models/ion_only.hpp new file mode 100644 index 00000000..7e64bec2 --- /dev/null +++ b/include/viennaps/models/ion_only.hpp @@ -0,0 +1,212 @@ +#pragma once + +#include +#include +#include + +#include "../psConstants.hpp" +#include "../psProcessModel.hpp" +#include "../psSurfaceModel.hpp" +#include "../psUnits.hpp" +#include "../psVelocityField.hpp" + +#include "psSF6O2Etching.hpp" + +namespace viennaps { + +using namespace viennacore; + +namespace impl { + +template +class IonOnlySurfModel : public SurfaceModel { +public: + const SF6O2Parameters ¶ms; + + IonOnlySurfModel(const SF6O2Parameters &pParams) + : params(pParams) {} + + SmartPointer> + calculateVelocities(SmartPointer> rates, + const std::vector> &coordinates, + const std::vector &materialIds) override { + const auto numPoints = rates->getScalarData(0)->size(); + std::vector etchRate(numPoints, 0.); + + const auto flux = rates->getScalarData("ionFlux"); + + for (size_t i = 0; i < numPoints; ++i) { + + if (MaterialMap::isMaterial(materialIds[i], Material::Mask)) { + etchRate[i] = -params.Mask.rho * flux->at(i); + } else { + etchRate[i] = -params.Si.rho * flux->at(i); + } + } + + return SmartPointer>::New(std::move(etchRate)); + } +}; + +template +class IonOnlyIon + : public viennaray::Particle, NumericType> { +public: + IonOnlyIon(const SF6O2Parameters &pParams) : params(pParams) {} + + void surfaceCollision(NumericType rayWeight, const Vec3D &rayDir, + const Vec3D &geomNormal, + const unsigned int primID, const int materialId, + viennaray::TracingData &localData, + const viennaray::TracingData *globalData, + RNG &) override final { + // collect data for this hit + const double cosTheta = -DotProduct(rayDir, geomNormal); + NumericType angle = std::acos(std::max(std::min(cosTheta, 1.), 0.)); + + NumericType f_ie_theta = 1.; + if (cosTheta < 0.5) { + f_ie_theta = std::max(3 - 6 * angle / M_PI, 0.); + } + + NumericType Y_Si = + params.Si.A_ie * + std::max(std::sqrt(E) - std::sqrt(params.Si.Eth_ie), 0.) * f_ie_theta; + + localData.getVectorData(0)[primID] += Y_Si * rayWeight; + } + + std::pair> + surfaceReflection(NumericType rayWeight, const Vec3D &rayDir, + const Vec3D &geomNormal, + const unsigned int primId, const int materialId, + const viennaray::TracingData *globalData, + RNG &Rng) override final { + auto cosTheta = -DotProduct(rayDir, geomNormal); + + assert(cosTheta >= 0 && "Hit backside of disc"); + assert(cosTheta <= 1 + 1e-6 && "Error in calculating cos theta"); + + NumericType incAngle = + std::acos(std::max(std::min(cosTheta, static_cast(1.)), + static_cast(0.))); + + // Small incident angles are reflected with the energy fraction centered at + // 0 + NumericType Eref_peak; + NumericType A_energy = + 1. / (1. + params.Ions.n_l * (M_PI_2 / params.Ions.inflectAngle - 1.)); + if (incAngle >= params.Ions.inflectAngle) { + Eref_peak = (1 - (1 - A_energy) * (M_PI_2 - incAngle) / + (M_PI_2 - params.Ions.inflectAngle)); + } else { + Eref_peak = A_energy * std::pow(incAngle / params.Ions.inflectAngle, + params.Ions.n_l); + } + // Gaussian distribution around the Eref_peak scaled by the particle energy + NumericType NewEnergy = Eref_peak * E; + // std::normal_distribution normalDist(E * Eref_peak, 0.1 * E); + // do { + // NewEnergy = normalDist(Rng); + // } while (NewEnergy > E || NewEnergy < 0.); + + NumericType sticking = 0.; + // NumericType sticking = 1.; + // if (incAngle > params.Ions.thetaRMin) { + // sticking = + // 1. - std::min((incAngle - params.Ions.thetaRMin) / + // (params.Ions.thetaRMax - params.Ions.thetaRMin), + // NumericType(1.)); + // } + + // Set the flag to stop tracing if the energy is below the threshold + if (NewEnergy > params.Si.Eth_ie) { + E = NewEnergy; + auto direction = viennaray::ReflectionConedCosine( + rayDir, geomNormal, Rng, + M_PI_2 - std::min(incAngle, params.Ions.minAngle)); + return std::pair>{sticking, direction}; + } else { + return std::pair>{ + 1., Vec3D{0., 0., 0.}}; + } + } + void initNew(RNG &rngState) override final { + std::normal_distribution normalDist{params.Ions.meanEnergy, + params.Ions.sigmaEnergy}; + do { + E = normalDist(rngState); + } while (E <= 0.); + } + NumericType getSourceDistributionPower() const override final { + return params.Ions.exponent; + } + std::vector getLocalDataLabels() const override final { + return {"ionFlux"}; + } + +private: + const SF6O2Parameters ¶ms; + + NumericType E; +}; +} // namespace impl + +template +class IonOnlyEtching : public ProcessModel { +public: + IonOnlyEtching() { initializeModel(); } + + // All flux values are in units 1e15 / cm² + IonOnlyEtching(const double ionFlux, const double etchantFlux, + const double oxygenFlux, const NumericType meanEnergy /* eV */, + const NumericType sigmaEnergy /* eV */, // 5 parameters + const NumericType ionExponent = 300., + const NumericType oxySputterYield = 2., + const NumericType etchStopDepth = + std::numeric_limits::lowest()) { + params.ionFlux = ionFlux; + params.etchantFlux = etchantFlux; + params.oxygenFlux = oxygenFlux; + params.Ions.meanEnergy = meanEnergy; + params.Ions.sigmaEnergy = sigmaEnergy; + params.Ions.exponent = ionExponent; + params.Passivation.A_ie = oxySputterYield; + params.etchStopDepth = etchStopDepth; + initializeModel(); + } + + IonOnlyEtching(const SF6O2Parameters &pParams) + : params(pParams) { + initializeModel(); + } + + void setParameters(const SF6O2Parameters &pParams) { + params = pParams; + } + + SF6O2Parameters &getParameters() { return params; } + +private: + void initializeModel() { + // particles + auto ion = std::make_unique>(params); + + // surface model + auto surfModel = + SmartPointer>::New(params); + + // velocity field + auto velField = SmartPointer>::New(2); + + this->setSurfaceModel(surfModel); + this->setVelocityField(velField); + this->setProcessName("ionOnly"); + this->particles.clear(); + this->insertNextParticleType(ion); + } + + SF6O2Parameters params; +}; + +} // namespace viennaps From c152907335dbf7695d1b3841b4015edaf7547552 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Mon, 3 Feb 2025 20:22:35 +0100 Subject: [PATCH 14/25] Add unit check in SF6O2 model --- include/viennaps/models/psSF6O2Etching.hpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/include/viennaps/models/psSF6O2Etching.hpp b/include/viennaps/models/psSF6O2Etching.hpp index 91ad79de..5242acee 100644 --- a/include/viennaps/models/psSF6O2Etching.hpp +++ b/include/viennaps/models/psSF6O2Etching.hpp @@ -552,12 +552,19 @@ class SF6O2Etching : public ProcessModel { void setParameters(const SF6O2Parameters &pParams) { params = pParams; + initializeModel(); } SF6O2Parameters &getParameters() { return params; } private: void initializeModel() { + // check if units have been set + if (units::Length::getInstance().getUnit() == units::Length::UNDEFINED || + units::Time::getInstance().getUnit() == units::Time::UNDEFINED) { + Logger::getInstance().addError("Units have not been set.").print(); + } + // particles auto ion = std::make_unique>(params); auto etchant = std::make_unique>(params); @@ -587,7 +594,7 @@ class SF6Etching : public ProcessModel { public: SF6Etching() { initializeModel(); } - // All flux values are in units 1e16 / cm² + // All flux values are in units 1e15 / cm² SF6Etching(const double ionFlux, const double etchantFlux, const NumericType meanEnergy /* eV */, const NumericType sigmaEnergy /* eV */, // 5 parameters @@ -609,12 +616,19 @@ class SF6Etching : public ProcessModel { void setParameters(const SF6O2Parameters &pParams) { params = pParams; + initializeModel(); } SF6O2Parameters &getParameters() { return params; } private: void initializeModel() { + // check if units have been set + if (units::Length::getInstance().getUnit() == units::Length::UNDEFINED || + units::Time::getInstance().getUnit() == units::Time::UNDEFINED) { + Logger::getInstance().addError("Units have not been set.").print(); + } + // particles auto ion = std::make_unique>(params); auto etchant = std::make_unique>(params); @@ -629,6 +643,7 @@ class SF6Etching : public ProcessModel { this->setSurfaceModel(surfModel); this->setVelocityField(velField); this->setProcessName("SF6Etching"); + this->particles.clear(); this->insertNextParticleType(ion); this->insertNextParticleType(etchant); } From cf54aa41e45f2927f61177072339f2e22644ba28 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Tue, 4 Feb 2025 10:16:30 +0100 Subject: [PATCH 15/25] Formatting --- .gitignore | 1 + examples/faradayCageEtching/faradayCageEtching.py | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index ea937a78..54e15872 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ dist/ dependencies/ *.ipynb old_* +*_old.* *a.out install* *.vtk diff --git a/examples/faradayCageEtching/faradayCageEtching.py b/examples/faradayCageEtching/faradayCageEtching.py index f68d659d..87b812c8 100644 --- a/examples/faradayCageEtching/faradayCageEtching.py +++ b/examples/faradayCageEtching/faradayCageEtching.py @@ -1,7 +1,9 @@ from argparse import ArgumentParser # parse config file name and simulation dimension -parser = ArgumentParser(prog="faradayCageEtching", description="Run a faraday cage etching process.") +parser = ArgumentParser( + prog="faradayCageEtching", description="Run a faraday cage etching process." +) parser.add_argument("-D", "-DIM", dest="dim", type=int, default=2) parser.add_argument("filename") args = parser.parse_args() @@ -33,7 +35,7 @@ material=vps.Material.Si, ).apply() -# use pre-defined model SF6O2 etching model +# use pre-defined etching model parameters = vps.FaradayCageParameters() parameters.cageAngle = params["cageAngle"] parameters.ibeParams.tiltAngle = params["tiltAngle"] From 8b38688628b875779211c4312695771a2d99e3d0 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Tue, 4 Feb 2025 11:12:27 +0100 Subject: [PATCH 16/25] Units python wrapping --- include/viennaps/models/psIonBeamEtching.hpp | 2 - include/viennaps/psProcess.hpp | 2 +- include/viennaps/psUnits.hpp | 28 +- python/pyWrap.cpp | 47 +++ python/stubs/viennaps2d/viennaps2d.pyi | 337 +++++++++++++++++-- python/stubs/viennaps3d/viennaps3d.pyi | 325 ++++++++++++++++-- 6 files changed, 669 insertions(+), 72 deletions(-) diff --git a/include/viennaps/models/psIonBeamEtching.hpp b/include/viennaps/models/psIonBeamEtching.hpp index 97a59be3..7c556bac 100644 --- a/include/viennaps/models/psIonBeamEtching.hpp +++ b/include/viennaps/models/psIonBeamEtching.hpp @@ -174,7 +174,6 @@ class IonBeamEtching : public ProcessModel { void setParameters(const IBEParameters ¶ms) { params_ = params; - initialize(maskMaterials_); } void initialize(SmartPointer> domain, @@ -207,7 +206,6 @@ class IonBeamEtching : public ProcessModel { bool firstInit = false; std::vector maskMaterials_; IBEParameters params_; - std::vector maskMaterials_; }; } // namespace viennaps diff --git a/include/viennaps/psProcess.hpp b/include/viennaps/psProcess.hpp index a27d626e..d2f559a1 100644 --- a/include/viennaps/psProcess.hpp +++ b/include/viennaps/psProcess.hpp @@ -679,7 +679,7 @@ template class Process { stream << std::fixed << std::setprecision(4) << "Process time: " << processDuration - remainingTime << " / " << processDuration << " " - << units::Time::getInstance().toStringShort(); + << units::Time::getInstance().toShortString(); Logger::getInstance().addInfo(stream.str()).print(); } } diff --git a/include/viennaps/psUnits.hpp b/include/viennaps/psUnits.hpp index 7b186314..36ff2948 100644 --- a/include/viennaps/psUnits.hpp +++ b/include/viennaps/psUnits.hpp @@ -173,6 +173,30 @@ class Length { return 0.; } + double convertAngstrom() const { + switch (unit_) { + case METER: + return 1e10; + case CENTIMETER: + return 1e8; + case MILLIMETER: + return 1e7; + case MICROMETER: + return 1e4; + case NANOMETER: + return 1e1; + case ANGSTROM: + return 1.; + case UNDEFINED: { + Logger::getInstance().addWarning("Length unit is not defined.").print(); + return 1.; + } + } + + Logger::getInstance().addError("Invalid length unit.").print(); + return 0.; + } + std::string toString() const { switch (unit_) { case METER: @@ -195,7 +219,7 @@ class Length { return "error-length-unit"; } - std::string toStringShort() const { + std::string toShortString() const { switch (unit_) { case METER: return "m"; @@ -324,7 +348,7 @@ class Time { return "error-time-unit"; } - std::string toStringShort() const { + std::string toShortString() const { switch (unit_) { case MINUTE: return "min"; diff --git a/python/pyWrap.cpp b/python/pyWrap.cpp index 3d9069de..59aebb8d 100644 --- a/python/pyWrap.cpp +++ b/python/pyWrap.cpp @@ -32,6 +32,7 @@ #include #include #include +#include // geometries #include @@ -351,6 +352,52 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { * MODEL FRAMEWORK * ****************************************************************************/ + // Units + // Length + // enum not necessary, as we can use a string to set the unit + // pybind11::enum_(module, "LengthUnit") + // .value("METER", units::Length::METER) + // .value("CENTIMETER", units::Length::CENTIMETER) + // .value("MILLIMETER", units::Length::MILLIMETER) + // .value("MICROMETER", units::Length::MICROMETER) + // .value("NANOMETER", units::Length::NANOMETER) + // .value("ANGSTROM", units::Length::ANGSTROM) + // .value("UNDEFINED", units::Length::UNDEFINED) + // .export_values(); + + pybind11::class_(module, "Length") + .def_static("setUnit", pybind11::overload_cast( + &units::Length::setUnit)) + .def_static("getInstance", &units::Length::getInstance, + pybind11::return_value_policy::reference) + .def("convertMeter", &units::Length::convertMeter) + .def("convertCentimeter", &units::Length::convertCentimeter) + .def("convertMillimeter", &units::Length::convertMillimeter) + .def("convertMicrometer", &units::Length::convertMicrometer) + .def("convertNanometer", &units::Length::convertNanometer) + .def("convertAngstrom", &units::Length::convertAngstrom) + .def("toString", &units::Length::toString) + .def("toShortString", &units::Length::toShortString); + + // Time + // pybind11::enum_(module, "TimeUnit") + // .value("MINUTE", units::Time::MINUTE) + // .value("SECOND", units::Time::SECOND) + // .value("MILLISECOND", units::Time::MILLISECOND) + // .value("UNDEFINED", units::Time::UNDEFINED) + // .export_values(); + + pybind11::class_(module, "Time") + .def_static("setUnit", pybind11::overload_cast( + &units::Time::setUnit)) + .def_static("getInstance", &units::Time::getInstance, + pybind11::return_value_policy::reference) + .def("convertMinute", &units::Time::convertMinute) + .def("convertSecond", &units::Time::convertSecond) + .def("convertMillisecond", &units::Time::convertMillisecond) + .def("toString", &units::Time::toString) + .def("toShortString", &units::Time::toShortString); + // ProcessModel pybind11::class_, SmartPointer>> processModel(module, "ProcessModel", pybind11::module_local()); diff --git a/python/stubs/viennaps2d/viennaps2d.pyi b/python/stubs/viennaps2d/viennaps2d.pyi index f508354e..8beb18c1 100644 --- a/python/stubs/viennaps2d/viennaps2d.pyi +++ b/python/stubs/viennaps2d/viennaps2d.pyi @@ -8,8 +8,10 @@ D: int DEBUG: LogLevel Dielectric: Material ERROR: LogLevel +Full: HoleShape GAS: Material GaN: Material +Half: HoleShape INFO: LogLevel INTERMEDIATE: LogLevel Mask: Material @@ -22,6 +24,7 @@ POS_Y: rayTraceDirection POS_Z: rayTraceDirection PolySi: Material Polymer: Material +Quarter: HoleShape Si: Material Si3N4: Material SiC: Material @@ -46,7 +49,16 @@ class AnisotropicProcess(ProcessModel): @overload def __init__(self, materials: list[tuple[Material, float]]) -> None: ... @overload - def __init__(self, direction100, direction010, rate100: float, rate110: float, rate111: float, rate311: float, materials: list[tuple[Material, float]]) -> None: ... + def __init__( + self, + direction100, + direction010, + rate100: float, + rate110: float, + rate111: float, + rate311: float, + materials: list[tuple[Material, float]], + ) -> None: ... class AtomicLayerProcess: def __init__(self) -> None: ... @@ -68,9 +80,23 @@ class BoxDistribution(ProcessModel): class DirectionalEtching(ProcessModel): @overload - def __init__(self, direction, directionalVelocity: float, isotropicVelocity: float = ..., maskMaterial: Material = ..., calculateVisibility: bool = ...) -> None: ... - @overload - def __init__(self, direction, directionalVelocity: float, isotropicVelocity: float = ..., maskMaterial: list[Material] = ..., calculateVisibility: bool = ...) -> None: ... + def __init__( + self, + direction, + directionalVelocity: float, + isotropicVelocity: float = ..., + maskMaterial: Material = ..., + calculateVisibility: bool = ..., + ) -> None: ... + @overload + def __init__( + self, + direction, + directionalVelocity: float, + isotropicVelocity: float = ..., + maskMaterial: list[Material] = ..., + calculateVisibility: bool = ..., + ) -> None: ... @overload def __init__(self, rateSets: list[RateSet]) -> None: ... @overload @@ -83,8 +109,11 @@ class Domain: def deepCopy(self, arg0: Domain) -> None: ... def duplicateTopLevelSet(self, arg0: Material) -> None: ... def generateCellSet(self, arg0: float, arg1: Material, arg2: bool) -> None: ... + def getBoundaryConditions(self, *args, **kwargs): ... + def getBoundingBox(self, *args, **kwargs): ... def getCellSet(self, *args, **kwargs): ... def getGrid(self, *args, **kwargs): ... + def getGridDelta(self) -> float: ... def getLevelSets(self, *args, **kwargs): ... def getMaterialMap(self, *args, **kwargs): ... def insertNextLevelSet(self, *args, **kwargs): ... @@ -106,8 +135,11 @@ class Domain3D: def deepCopy(self, arg0: Domain3D) -> None: ... def duplicateTopLevelSet(self, arg0: Material) -> None: ... def generateCellSet(self, arg0: float, arg1: Material, arg2: bool) -> None: ... + def getBoundaryConditions(self, *args, **kwargs): ... + def getBoundingBox(self, *args, **kwargs): ... def getCellSet(self, *args, **kwargs): ... def getGrid(self, *args, **kwargs): ... + def getGridDelta(self) -> float: ... def getLevelSets(self, *args, **kwargs): ... def getMaterialMap(self) -> MaterialMap: ... def insertNextLevelSet(self, *args, **kwargs): ... @@ -126,7 +158,14 @@ class Extrude: @overload def __init__(self) -> None: ... @overload - def __init__(self, inputDomain: Domain, outputDomain: Domain3D, extent, extrudeDimension: int, boundaryConditions) -> None: ... + def __init__( + self, + inputDomain: Domain, + outputDomain: Domain3D, + extent, + extrudeDimension: int, + boundaryConditions, + ) -> None: ... def apply(self) -> None: ... def setBoundaryConditions(self, arg0) -> None: ... def setExtent(self, arg0) -> None: ... @@ -140,7 +179,9 @@ class FaradayCageEtching(ProcessModel): @overload def __init__(self, maskMaterials: list[Material]) -> None: ... @overload - def __init__(self, maskMaterials: list[Material], parameters: FaradayCageParameters) -> None: ... + def __init__( + self, maskMaterials: list[Material], parameters: FaradayCageParameters + ) -> None: ... def getParameters(self) -> FaradayCageParameters: ... def setParameters(self, arg0: FaradayCageParameters) -> None: ... @@ -153,7 +194,17 @@ class FluorocarbonEtching(ProcessModel): @overload def __init__(self) -> None: ... @overload - def __init__(self, ionFlux: float, etchantFlux: float, polyFlux: float, meanIonEnergy: float = ..., sigmaIonEnergy: float = ..., ionExponent: float = ..., deltaP: float = ..., etchStopDepth: float = ...) -> None: ... + def __init__( + self, + ionFlux: float, + etchantFlux: float, + polyFlux: float, + meanIonEnergy: float = ..., + sigmaIonEnergy: float = ..., + ionExponent: float = ..., + deltaP: float = ..., + etchStopDepth: float = ..., + ) -> None: ... @overload def __init__(self, parameters: FluorocarbonParameters) -> None: ... def getParameters(self) -> FluorocarbonParameters: ... @@ -230,6 +281,23 @@ class FluorocarbonParametersSiO2: rho: float def __init__(self) -> None: ... +class HoleShape: + __members__: ClassVar[dict] = ... # read-only + Full: ClassVar[HoleShape] = ... + Half: ClassVar[HoleShape] = ... + Quarter: ClassVar[HoleShape] = ... + __entries: ClassVar[dict] = ... + def __init__(self, value: int) -> None: ... + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __int__(self) -> int: ... + def __ne__(self, other: object) -> bool: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... + class IBEParameters: exponent: float inflectAngle: float @@ -249,7 +317,9 @@ class IonBeamEtching(ProcessModel): @overload def __init__(self, maskMaterials: list[Material]) -> None: ... @overload - def __init__(self, maskMaterials: list[Material], parameters: IBEParameters) -> None: ... + def __init__( + self, maskMaterials: list[Material], parameters: IBEParameters + ) -> None: ... def getParameters(self) -> IBEParameters: ... def setParameters(self, arg0: IBEParameters) -> None: ... @@ -259,6 +329,21 @@ class IsotropicProcess(ProcessModel): @overload def __init__(self, rate: float, maskMaterial: list[Material]) -> None: ... +class Length: + def __init__(self, *args, **kwargs) -> None: ... + def convertAngstrom(self) -> float: ... + def convertCentimeter(self) -> float: ... + def convertMeter(self) -> float: ... + def convertMicrometer(self) -> float: ... + def convertMillimeter(self) -> float: ... + def convertNanometer(self) -> float: ... + @staticmethod + def getInstance() -> Length: ... + @staticmethod + def setUnit(arg0: str) -> None: ... + def toShortString(self) -> str: ... + def toString(self) -> str: ... + class LogLevel: __members__: ClassVar[dict] = ... # read-only DEBUG: ClassVar[LogLevel] = ... @@ -298,31 +383,93 @@ class Logger: def setLogLevel(arg0: LogLevel) -> None: ... class MakeFin: - def __init__(self, domain: Domain, gridDelta: float, xExtent: float, yExtent: float, finWidth: float, finHeight: float, taperAngle: float = 0.0, baseHeight: float = 0.0, periodicBoundary: bool = False, makeMask: bool = False, material: Material = Undefined) -> None: ... + def __init__( + self, + domain: Domain, + gridDelta: float, + xExtent: float, + yExtent: float, + finWidth: float, + finHeight: float, + taperAngle: float = 0.0, + baseHeight: float = 0.0, + periodicBoundary: bool = False, + makeMask: bool = False, + material: Material = Undefined, + ) -> None: ... def apply(self) -> None: ... class MakeHole: - def __init__(self, domain: Domain, gridDelta: float, xExtent: float, yExtent: float, holeRadius: float, holeDepth: float, taperingAngle: float = 0.0, baseHeight: float = 0.0, periodicBoundary: bool = False, makeMask: bool = False, material: Material = Undefined) -> None: ... + def __init__( + self, + domain: Domain, + gridDelta: float, + xExtent: float, + yExtent: float, + holeRadius: float, + holeDepth: float, + taperingAngle: float = 0.0, + baseHeight: float = 0.0, + periodicBoundary: bool = False, + makeMask: bool = False, + material: Material = Undefined, + ) -> None: ... def apply(self) -> None: ... class MakePlane: @overload - def __init__(self, domain: Domain, gridDelta: float, xExtent: float, yExtent: float, height: float = 0.0, periodicBoundary: bool = False, material: Material = Undefined) -> None: ... - @overload - def __init__(self, domain: Domain, height: float = 0.0, material: Material = Undefined) -> None: ... + def __init__( + self, + domain: Domain, + gridDelta: float, + xExtent: float, + yExtent: float, + height: float = 0.0, + periodicBoundary: bool = False, + material: Material = Undefined, + ) -> None: ... + @overload + def __init__( + self, domain: Domain, height: float = 0.0, material: Material = Undefined + ) -> None: ... def apply(self) -> None: ... class MakeStack: - def __init__(self, domain: Domain, gridDelta: float, xExtent: float, yExtent: float, numLayers: int, layerHeight: float, substrateHeight: float, holeRadius: float, trenchWidth: float, maskHeight: float, periodicBoundary: bool = False) -> None: ... + def __init__( + self, + domain: Domain, + gridDelta: float, + xExtent: float, + yExtent: float, + numLayers: int, + layerHeight: float, + substrateHeight: float, + holeRadius: float, + trenchWidth: float, + maskHeight: float, + periodicBoundary: bool = False, + ) -> None: ... def apply(self) -> None: ... def getHeight(self) -> float: ... def getTopLayer(self) -> int: ... class MakeTrench: - def __init__(self, domain: Domain, gridDelta: float, xExtent: float, yExtent: float, trenchWidth: float, trenchDepth: float, taperingAngle: float = 0.0, baseHeight: float = 0.0, periodicBoundary: bool = False, makeMask: bool = False, material: Material = Undefined) -> None: ... + def __init__( + self, + domain: Domain, + gridDelta: float, + xExtent: float, + yExtent: float, + trenchWidth: float, + trenchDepth: float, + taperingAngle: float = 0.0, + baseHeight: float = 0.0, + periodicBoundary: bool = False, + makeMask: bool = False, + material: Material = Undefined, + ) -> None: ... def apply(self) -> None: ... - class Material: __members__: ClassVar[dict] = ... # read-only Air: ClassVar[Material] = ... @@ -370,15 +517,51 @@ class MaterialMap: class MultiParticleProcess(ProcessModel): def __init__(self) -> None: ... - def addIonParticle(self, sourcePower: float, thetaRMin: float = ..., thetaRMax: float = ..., minAngle: float = ..., B_sp: float = ..., meanEnergy: float = ..., sigmaEnergy: float = ..., thresholdEnergy: float = ..., inflectAngle: float = ..., n: float = ..., label: str = ...) -> None: ... - @overload - def addNeutralParticle(self, stickingProbability: float, label: str = ...) -> None: ... - @overload - def addNeutralParticle(self, materialSticking: dict[Material, float], defaultStickingProbability: float = ..., label: str = ...) -> None: ... - def setRateFunction(self, arg0: Callable[[list[float], Material], float]) -> None: ... + def addIonParticle( + self, + sourcePower: float, + thetaRMin: float = ..., + thetaRMax: float = ..., + minAngle: float = ..., + B_sp: float = ..., + meanEnergy: float = ..., + sigmaEnergy: float = ..., + thresholdEnergy: float = ..., + inflectAngle: float = ..., + n: float = ..., + label: str = ..., + ) -> None: ... + @overload + def addNeutralParticle( + self, stickingProbability: float, label: str = ... + ) -> None: ... + @overload + def addNeutralParticle( + self, + materialSticking: dict[Material, float], + defaultStickingProbability: float = ..., + label: str = ..., + ) -> None: ... + def setRateFunction( + self, arg0: Callable[[list[float], Material], float] + ) -> None: ... class OxideRegrowth(ProcessModel): - def __init__(self, nitrideEtchRate: float, oxideEtchRate: float, redepositionRate: float, redepositionThreshold: float, redepositionTimeInt: float, diffusionCoefficient: float, sinkStrength: float, scallopVelocity: float, centerVelocity: float, topHeight: float, centerWidth: float, stabilityFactor: float) -> None: ... + def __init__( + self, + nitrideEtchRate: float, + oxideEtchRate: float, + redepositionRate: float, + redepositionThreshold: float, + redepositionTimeInt: float, + diffusionCoefficient: float, + sinkStrength: float, + scallopVelocity: float, + centerVelocity: float, + topHeight: float, + centerWidth: float, + stabilityFactor: float, + ) -> None: ... class Particle: def __init__(self, *args, **kwargs) -> None: ... @@ -459,13 +642,28 @@ class RateSet: directionalVelocity: float isotropicVelocity: float maskMaterials: list[Material] - def __init__(self, direction=..., directionalVelocity: float = ..., isotropicVelocity: float = ..., maskMaterials: list[Material] = ..., calculateVisibility: bool = ...) -> None: ... + def __init__( + self, + direction=..., + directionalVelocity: float = ..., + isotropicVelocity: float = ..., + maskMaterials: list[Material] = ..., + calculateVisibility: bool = ..., + ) -> None: ... class SF6Etching(ProcessModel): @overload def __init__(self) -> None: ... @overload - def __init__(self, ionFlux: float, etchantFlux: float, meanIonEnergy: float = ..., sigmaIonEnergy: float = ..., ionExponent: float = ..., etchStopDepth: float = ...) -> None: ... + def __init__( + self, + ionFlux: float, + etchantFlux: float, + meanIonEnergy: float = ..., + sigmaIonEnergy: float = ..., + ionExponent: float = ..., + etchStopDepth: float = ..., + ) -> None: ... @overload def __init__(self, parameters: SF6O2Parameters) -> None: ... def getParameters(self) -> SF6O2Parameters: ... @@ -475,7 +673,17 @@ class SF6O2Etching(ProcessModel): @overload def __init__(self) -> None: ... @overload - def __init__(self, ionFlux: float, etchantFlux: float, oxygenFlux: float, meanIonEnergy: float = ..., sigmaIonEnergy: float = ..., ionExponent: float = ..., oxySputterYield: float = ..., etchStopDepth: float = ...) -> None: ... + def __init__( + self, + ionFlux: float, + etchantFlux: float, + oxygenFlux: float, + meanIonEnergy: float = ..., + sigmaIonEnergy: float = ..., + ionExponent: float = ..., + oxySputterYield: float = ..., + etchStopDepth: float = ..., + ) -> None: ... @overload def __init__(self, parameters: SF6O2Parameters) -> None: ... def getParameters(self) -> SF6O2Parameters: ... @@ -520,33 +728,94 @@ class SF6O2ParametersPassivation: class SF6O2ParametersSi: A_ie: float A_sp: float + B_ie: float B_sp: float Eth_ie: float Eth_sp: float beta_sigma: float k_sigma: float rho: float + theta_g_ie: float + theta_g_sp: float def __init__(self) -> None: ... class SingleParticleALD(ProcessModel): - def __init__(self, stickingProbability: float, numCycles: float, growthPerCycle: float, totalCycles: float, coverageTimeStep: float, evFlux: float, inFlux: float, s0: float, gasMFP: float) -> None: ... + def __init__( + self, + stickingProbability: float, + numCycles: float, + growthPerCycle: float, + totalCycles: float, + coverageTimeStep: float, + evFlux: float, + inFlux: float, + s0: float, + gasMFP: float, + ) -> None: ... class SingleParticleProcess(ProcessModel): @overload - def __init__(self, rate: float = ..., stickingProbability: float = ..., sourceExponent: float = ..., maskMaterial: Material = ...) -> None: ... - @overload - def __init__(self, rate: float, stickingProbability: float, sourceExponent: float, maskMaterials: list[Material]) -> None: ... - @overload - def __init__(self, materialRates: dict[Material, float], stickingProbability: float, sourceExponent: float) -> None: ... + def __init__( + self, + rate: float = ..., + stickingProbability: float = ..., + sourceExponent: float = ..., + maskMaterial: Material = ..., + ) -> None: ... + @overload + def __init__( + self, + rate: float, + stickingProbability: float, + sourceExponent: float, + maskMaterials: list[Material], + ) -> None: ... + @overload + def __init__( + self, + materialRates: dict[Material, float], + stickingProbability: float, + sourceExponent: float, + ) -> None: ... class SphereDistribution(ProcessModel): def __init__(self, radius: float, gridDelta: float) -> None: ... class TEOSDeposition(ProcessModel): - def __init__(self, stickingProbabilityP1: float, rateP1: float, orderP1: float, stickingProbabilityP2: float = ..., rateP2: float = ..., orderP2: float = ...) -> None: ... + def __init__( + self, + stickingProbabilityP1: float, + rateP1: float, + orderP1: float, + stickingProbabilityP2: float = ..., + rateP2: float = ..., + orderP2: float = ..., + ) -> None: ... class TEOSPECVD(ProcessModel): - def __init__(self, stickingProbabilityRadical: float, depositionRateRadical: float, depositionRateIon: float, exponentIon: float, stickingProbabilityIon: float = ..., reactionOrderRadical: float = ..., reactionOrderIon: float = ..., minAngleIon: float = ...) -> None: ... + def __init__( + self, + stickingProbabilityRadical: float, + depositionRateRadical: float, + depositionRateIon: float, + exponentIon: float, + stickingProbabilityIon: float = ..., + reactionOrderRadical: float = ..., + reactionOrderIon: float = ..., + minAngleIon: float = ..., + ) -> None: ... + +class Time: + def __init__(self, *args, **kwargs) -> None: ... + def convertMillisecond(self) -> float: ... + def convertMinute(self) -> float: ... + def convertSecond(self) -> float: ... + @staticmethod + def getInstance() -> Time: ... + @staticmethod + def setUnit(arg0: str) -> None: ... + def toShortString(self) -> str: ... + def toString(self) -> str: ... class ToDiskMesh: @overload diff --git a/python/stubs/viennaps3d/viennaps3d.pyi b/python/stubs/viennaps3d/viennaps3d.pyi index c2d98c86..7c491a2a 100644 --- a/python/stubs/viennaps3d/viennaps3d.pyi +++ b/python/stubs/viennaps3d/viennaps3d.pyi @@ -8,8 +8,10 @@ D: int DEBUG: LogLevel Dielectric: Material ERROR: LogLevel +Full: HoleShape GAS: Material GaN: Material +Half: HoleShape INFO: LogLevel INTERMEDIATE: LogLevel Mask: Material @@ -22,6 +24,7 @@ POS_Y: rayTraceDirection POS_Z: rayTraceDirection PolySi: Material Polymer: Material +Quarter: HoleShape Si: Material Si3N4: Material SiC: Material @@ -46,7 +49,16 @@ class AnisotropicProcess(ProcessModel): @overload def __init__(self, materials: list[tuple[Material, float]]) -> None: ... @overload - def __init__(self, direction100, direction010, rate100: float, rate110: float, rate111: float, rate311: float, materials: list[tuple[Material, float]]) -> None: ... + def __init__( + self, + direction100, + direction010, + rate100: float, + rate110: float, + rate111: float, + rate311: float, + materials: list[tuple[Material, float]], + ) -> None: ... class AtomicLayerProcess: def __init__(self) -> None: ... @@ -68,9 +80,23 @@ class BoxDistribution(ProcessModel): class DirectionalEtching(ProcessModel): @overload - def __init__(self, direction, directionalVelocity: float, isotropicVelocity: float = ..., maskMaterial: Material = ..., calculateVisibility: bool = ...) -> None: ... - @overload - def __init__(self, direction, directionalVelocity: float, isotropicVelocity: float = ..., maskMaterial: list[Material] = ..., calculateVisibility: bool = ...) -> None: ... + def __init__( + self, + direction, + directionalVelocity: float, + isotropicVelocity: float = ..., + maskMaterial: Material = ..., + calculateVisibility: bool = ..., + ) -> None: ... + @overload + def __init__( + self, + direction, + directionalVelocity: float, + isotropicVelocity: float = ..., + maskMaterial: list[Material] = ..., + calculateVisibility: bool = ..., + ) -> None: ... @overload def __init__(self, rateSets: list[RateSet]) -> None: ... @overload @@ -83,8 +109,11 @@ class Domain: def deepCopy(self, arg0: Domain) -> None: ... def duplicateTopLevelSet(self, arg0: Material) -> None: ... def generateCellSet(self, arg0: float, arg1: Material, arg2: bool) -> None: ... + def getBoundaryConditions(self, *args, **kwargs): ... + def getBoundingBox(self, *args, **kwargs): ... def getCellSet(self, *args, **kwargs): ... def getGrid(self, *args, **kwargs): ... + def getGridDelta(self) -> float: ... def getLevelSets(self, *args, **kwargs): ... def getMaterialMap(self, *args, **kwargs): ... def insertNextLevelSet(self, *args, **kwargs): ... @@ -105,7 +134,9 @@ class FaradayCageEtching(ProcessModel): @overload def __init__(self, maskMaterials: list[Material]) -> None: ... @overload - def __init__(self, maskMaterials: list[Material], parameters: FaradayCageParameters) -> None: ... + def __init__( + self, maskMaterials: list[Material], parameters: FaradayCageParameters + ) -> None: ... def getParameters(self) -> FaradayCageParameters: ... def setParameters(self, arg0: FaradayCageParameters) -> None: ... @@ -118,7 +149,17 @@ class FluorocarbonEtching(ProcessModel): @overload def __init__(self) -> None: ... @overload - def __init__(self, ionFlux: float, etchantFlux: float, polyFlux: float, meanIonEnergy: float = ..., sigmaIonEnergy: float = ..., ionExponent: float = ..., deltaP: float = ..., etchStopDepth: float = ...) -> None: ... + def __init__( + self, + ionFlux: float, + etchantFlux: float, + polyFlux: float, + meanIonEnergy: float = ..., + sigmaIonEnergy: float = ..., + ionExponent: float = ..., + deltaP: float = ..., + etchStopDepth: float = ..., + ) -> None: ... @overload def __init__(self, parameters: FluorocarbonParameters) -> None: ... def getParameters(self) -> FluorocarbonParameters: ... @@ -216,6 +257,23 @@ class GDSReader: def setFileName(self, arg0: str) -> None: ... def setGeometry(self, arg0: GDSGeometry) -> None: ... +class HoleShape: + __members__: ClassVar[dict] = ... # read-only + Full: ClassVar[HoleShape] = ... + Half: ClassVar[HoleShape] = ... + Quarter: ClassVar[HoleShape] = ... + __entries: ClassVar[dict] = ... + def __init__(self, value: int) -> None: ... + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __int__(self) -> int: ... + def __ne__(self, other: object) -> bool: ... + @property + def name(self) -> str: ... + @property + def value(self) -> int: ... + class IBEParameters: exponent: float inflectAngle: float @@ -235,7 +293,9 @@ class IonBeamEtching(ProcessModel): @overload def __init__(self, maskMaterials: list[Material]) -> None: ... @overload - def __init__(self, maskMaterials: list[Material], parameters: IBEParameters) -> None: ... + def __init__( + self, maskMaterials: list[Material], parameters: IBEParameters + ) -> None: ... def getParameters(self) -> IBEParameters: ... def setParameters(self, arg0: IBEParameters) -> None: ... @@ -245,6 +305,21 @@ class IsotropicProcess(ProcessModel): @overload def __init__(self, rate: float, maskMaterial: list[Material]) -> None: ... +class Length: + def __init__(self, *args, **kwargs) -> None: ... + def convertAngstrom(self) -> float: ... + def convertCentimeter(self) -> float: ... + def convertMeter(self) -> float: ... + def convertMicrometer(self) -> float: ... + def convertMillimeter(self) -> float: ... + def convertNanometer(self) -> float: ... + @staticmethod + def getInstance() -> Length: ... + @staticmethod + def setUnit(arg0: str) -> None: ... + def toShortString(self) -> str: ... + def toString(self) -> str: ... + class LogLevel: __members__: ClassVar[dict] = ... # read-only DEBUG: ClassVar[LogLevel] = ... @@ -284,31 +359,93 @@ class Logger: def setLogLevel(arg0: LogLevel) -> None: ... class MakeFin: - def __init__(self, domain: Domain, gridDelta: float, xExtent: float, yExtent: float, finWidth: float, finHeight: float, taperAngle: float = 0.0, baseHeight: float = 0.0, periodicBoundary: bool = False, makeMask: bool = False, material: Material = Undefined) -> None: ... + def __init__( + self, + domain: Domain, + gridDelta: float, + xExtent: float, + yExtent: float, + finWidth: float, + finHeight: float, + taperAngle: float = 0.0, + baseHeight: float = 0.0, + periodicBoundary: bool = False, + makeMask: bool = False, + material: Material = Undefined, + ) -> None: ... def apply(self) -> None: ... class MakeHole: - def __init__(self, domain: Domain, gridDelta: float, xExtent: float, yExtent: float, holeRadius: float, holeDepth: float, taperingAngle: float = 0.0, baseHeight: float = 0.0, periodicBoundary: bool = False, makeMask: bool = False, material: Material = Undefined) -> None: ... + def __init__( + self, + domain: Domain, + gridDelta: float, + xExtent: float, + yExtent: float, + holeRadius: float, + holeDepth: float, + taperingAngle: float = 0.0, + baseHeight: float = 0.0, + periodicBoundary: bool = False, + makeMask: bool = False, + material: Material = Undefined, + ) -> None: ... def apply(self) -> None: ... class MakePlane: @overload - def __init__(self, domain: Domain, gridDelta: float, xExtent: float, yExtent: float, height: float = 0.0, periodicBoundary: bool = False, material: Material = Undefined) -> None: ... - @overload - def __init__(self, domain: Domain, height: float = 0.0, material: Material = Undefined) -> None: ... + def __init__( + self, + domain: Domain, + gridDelta: float, + xExtent: float, + yExtent: float, + height: float = 0.0, + periodicBoundary: bool = False, + material: Material = Undefined, + ) -> None: ... + @overload + def __init__( + self, domain: Domain, height: float = 0.0, material: Material = Undefined + ) -> None: ... def apply(self) -> None: ... class MakeStack: - def __init__(self, domain: Domain, gridDelta: float, xExtent: float, yExtent: float, numLayers: int, layerHeight: float, substrateHeight: float, holeRadius: float, trenchWidth: float, maskHeight: float, periodicBoundary: bool = False) -> None: ... + def __init__( + self, + domain: Domain, + gridDelta: float, + xExtent: float, + yExtent: float, + numLayers: int, + layerHeight: float, + substrateHeight: float, + holeRadius: float, + trenchWidth: float, + maskHeight: float, + periodicBoundary: bool = False, + ) -> None: ... def apply(self) -> None: ... def getHeight(self) -> float: ... def getTopLayer(self) -> int: ... class MakeTrench: - def __init__(self, domain: Domain, gridDelta: float, xExtent: float, yExtent: float, trenchWidth: float, trenchDepth: float, taperingAngle: float = 0.0, baseHeight: float = 0.0, periodicBoundary: bool = False, makeMask: bool = False, material: Material = Undefined) -> None: ... + def __init__( + self, + domain: Domain, + gridDelta: float, + xExtent: float, + yExtent: float, + trenchWidth: float, + trenchDepth: float, + taperingAngle: float = 0.0, + baseHeight: float = 0.0, + periodicBoundary: bool = False, + makeMask: bool = False, + material: Material = Undefined, + ) -> None: ... def apply(self) -> None: ... - class Material: __members__: ClassVar[dict] = ... # read-only Air: ClassVar[Material] = ... @@ -356,15 +493,51 @@ class MaterialMap: class MultiParticleProcess(ProcessModel): def __init__(self) -> None: ... - def addIonParticle(self, sourcePower: float, thetaRMin: float = ..., thetaRMax: float = ..., minAngle: float = ..., B_sp: float = ..., meanEnergy: float = ..., sigmaEnergy: float = ..., thresholdEnergy: float = ..., inflectAngle: float = ..., n: float = ..., label: str = ...) -> None: ... - @overload - def addNeutralParticle(self, stickingProbability: float, label: str = ...) -> None: ... - @overload - def addNeutralParticle(self, materialSticking: dict[Material, float], defaultStickingProbability: float = ..., label: str = ...) -> None: ... - def setRateFunction(self, arg0: Callable[[list[float], Material], float]) -> None: ... + def addIonParticle( + self, + sourcePower: float, + thetaRMin: float = ..., + thetaRMax: float = ..., + minAngle: float = ..., + B_sp: float = ..., + meanEnergy: float = ..., + sigmaEnergy: float = ..., + thresholdEnergy: float = ..., + inflectAngle: float = ..., + n: float = ..., + label: str = ..., + ) -> None: ... + @overload + def addNeutralParticle( + self, stickingProbability: float, label: str = ... + ) -> None: ... + @overload + def addNeutralParticle( + self, + materialSticking: dict[Material, float], + defaultStickingProbability: float = ..., + label: str = ..., + ) -> None: ... + def setRateFunction( + self, arg0: Callable[[list[float], Material], float] + ) -> None: ... class OxideRegrowth(ProcessModel): - def __init__(self, nitrideEtchRate: float, oxideEtchRate: float, redepositionRate: float, redepositionThreshold: float, redepositionTimeInt: float, diffusionCoefficient: float, sinkStrength: float, scallopVelocity: float, centerVelocity: float, topHeight: float, centerWidth: float, stabilityFactor: float) -> None: ... + def __init__( + self, + nitrideEtchRate: float, + oxideEtchRate: float, + redepositionRate: float, + redepositionThreshold: float, + redepositionTimeInt: float, + diffusionCoefficient: float, + sinkStrength: float, + scallopVelocity: float, + centerVelocity: float, + topHeight: float, + centerWidth: float, + stabilityFactor: float, + ) -> None: ... class Particle: def __init__(self, *args, **kwargs) -> None: ... @@ -445,13 +618,28 @@ class RateSet: directionalVelocity: float isotropicVelocity: float maskMaterials: list[Material] - def __init__(self, direction=..., directionalVelocity: float = ..., isotropicVelocity: float = ..., maskMaterials: list[Material] = ..., calculateVisibility: bool = ...) -> None: ... + def __init__( + self, + direction=..., + directionalVelocity: float = ..., + isotropicVelocity: float = ..., + maskMaterials: list[Material] = ..., + calculateVisibility: bool = ..., + ) -> None: ... class SF6Etching(ProcessModel): @overload def __init__(self) -> None: ... @overload - def __init__(self, ionFlux: float, etchantFlux: float, meanIonEnergy: float = ..., sigmaIonEnergy: float = ..., ionExponent: float = ..., etchStopDepth: float = ...) -> None: ... + def __init__( + self, + ionFlux: float, + etchantFlux: float, + meanIonEnergy: float = ..., + sigmaIonEnergy: float = ..., + ionExponent: float = ..., + etchStopDepth: float = ..., + ) -> None: ... @overload def __init__(self, parameters: SF6O2Parameters) -> None: ... def getParameters(self) -> SF6O2Parameters: ... @@ -461,7 +649,17 @@ class SF6O2Etching(ProcessModel): @overload def __init__(self) -> None: ... @overload - def __init__(self, ionFlux: float, etchantFlux: float, oxygenFlux: float, meanIonEnergy: float = ..., sigmaIonEnergy: float = ..., ionExponent: float = ..., oxySputterYield: float = ..., etchStopDepth: float = ...) -> None: ... + def __init__( + self, + ionFlux: float, + etchantFlux: float, + oxygenFlux: float, + meanIonEnergy: float = ..., + sigmaIonEnergy: float = ..., + ionExponent: float = ..., + oxySputterYield: float = ..., + etchStopDepth: float = ..., + ) -> None: ... @overload def __init__(self, parameters: SF6O2Parameters) -> None: ... def getParameters(self) -> SF6O2Parameters: ... @@ -506,33 +704,94 @@ class SF6O2ParametersPassivation: class SF6O2ParametersSi: A_ie: float A_sp: float + B_ie: float B_sp: float Eth_ie: float Eth_sp: float beta_sigma: float k_sigma: float rho: float + theta_g_ie: float + theta_g_sp: float def __init__(self) -> None: ... class SingleParticleALD(ProcessModel): - def __init__(self, stickingProbability: float, numCycles: float, growthPerCycle: float, totalCycles: float, coverageTimeStep: float, evFlux: float, inFlux: float, s0: float, gasMFP: float) -> None: ... + def __init__( + self, + stickingProbability: float, + numCycles: float, + growthPerCycle: float, + totalCycles: float, + coverageTimeStep: float, + evFlux: float, + inFlux: float, + s0: float, + gasMFP: float, + ) -> None: ... class SingleParticleProcess(ProcessModel): @overload - def __init__(self, rate: float = ..., stickingProbability: float = ..., sourceExponent: float = ..., maskMaterial: Material = ...) -> None: ... - @overload - def __init__(self, rate: float, stickingProbability: float, sourceExponent: float, maskMaterials: list[Material]) -> None: ... - @overload - def __init__(self, materialRates: dict[Material, float], stickingProbability: float, sourceExponent: float) -> None: ... + def __init__( + self, + rate: float = ..., + stickingProbability: float = ..., + sourceExponent: float = ..., + maskMaterial: Material = ..., + ) -> None: ... + @overload + def __init__( + self, + rate: float, + stickingProbability: float, + sourceExponent: float, + maskMaterials: list[Material], + ) -> None: ... + @overload + def __init__( + self, + materialRates: dict[Material, float], + stickingProbability: float, + sourceExponent: float, + ) -> None: ... class SphereDistribution(ProcessModel): def __init__(self, radius: float, gridDelta: float) -> None: ... class TEOSDeposition(ProcessModel): - def __init__(self, stickingProbabilityP1: float, rateP1: float, orderP1: float, stickingProbabilityP2: float = ..., rateP2: float = ..., orderP2: float = ...) -> None: ... + def __init__( + self, + stickingProbabilityP1: float, + rateP1: float, + orderP1: float, + stickingProbabilityP2: float = ..., + rateP2: float = ..., + orderP2: float = ..., + ) -> None: ... class TEOSPECVD(ProcessModel): - def __init__(self, stickingProbabilityRadical: float, depositionRateRadical: float, depositionRateIon: float, exponentIon: float, stickingProbabilityIon: float = ..., reactionOrderRadical: float = ..., reactionOrderIon: float = ..., minAngleIon: float = ...) -> None: ... + def __init__( + self, + stickingProbabilityRadical: float, + depositionRateRadical: float, + depositionRateIon: float, + exponentIon: float, + stickingProbabilityIon: float = ..., + reactionOrderRadical: float = ..., + reactionOrderIon: float = ..., + minAngleIon: float = ..., + ) -> None: ... + +class Time: + def __init__(self, *args, **kwargs) -> None: ... + def convertMillisecond(self) -> float: ... + def convertMinute(self) -> float: ... + def convertSecond(self) -> float: ... + @staticmethod + def getInstance() -> Time: ... + @staticmethod + def setUnit(arg0: str) -> None: ... + def toShortString(self) -> str: ... + def toString(self) -> str: ... class ToDiskMesh: @overload From 5ed3f29d931c1575b206052e895f044079685515 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Tue, 4 Feb 2025 11:20:48 +0100 Subject: [PATCH 17/25] Do not export enum values in Python --- python/pyWrap.cpp | 12 +++------ python/stubs/viennaps2d/viennaps2d.pyi | 35 -------------------------- python/stubs/viennaps3d/viennaps3d.pyi | 35 -------------------------- 3 files changed, 4 insertions(+), 78 deletions(-) diff --git a/python/pyWrap.cpp b/python/pyWrap.cpp index 59aebb8d..de6e3be7 100644 --- a/python/pyWrap.cpp +++ b/python/pyWrap.cpp @@ -328,8 +328,7 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { .value("INFO", LogLevel::INFO) .value("TIMING", LogLevel::TIMING) .value("INTERMEDIATE", LogLevel::INTERMEDIATE) - .value("DEBUG", LogLevel::DEBUG) - .export_values(); + .value("DEBUG", LogLevel::DEBUG); pybind11::class_>(module, "Logger", pybind11::module_local()) @@ -604,8 +603,7 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { .value("Dielectric", Material::Dielectric) .value("Metal", Material::Metal) .value("Air", Material::Air) - .value("GAS", Material::GAS) // 20 - .export_values(); + .value("GAS", Material::GAS); // 20 // Single Particle Process pybind11::class_, @@ -1111,8 +1109,7 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { pybind11::enum_(module, "HoleShape") .value("Full", HoleShape::Full) .value("Half", HoleShape::Half) - .value("Quarter", HoleShape::Quarter) - .export_values(); + .value("Quarter", HoleShape::Quarter); pybind11::class_>(module, "MakeHole") .def(pybind11::init>(module, "AtomicLayerProcess") diff --git a/python/stubs/viennaps2d/viennaps2d.pyi b/python/stubs/viennaps2d/viennaps2d.pyi index 8beb18c1..ce2dee1f 100644 --- a/python/stubs/viennaps2d/viennaps2d.pyi +++ b/python/stubs/viennaps2d/viennaps2d.pyi @@ -1,42 +1,7 @@ from _typeshed import Incomplete from typing import Callable, ClassVar, overload -Air: Material -Al2O3: Material -Cu: Material D: int -DEBUG: LogLevel -Dielectric: Material -ERROR: LogLevel -Full: HoleShape -GAS: Material -GaN: Material -Half: HoleShape -INFO: LogLevel -INTERMEDIATE: LogLevel -Mask: Material -Metal: Material -NEG_X: rayTraceDirection -NEG_Y: rayTraceDirection -NEG_Z: rayTraceDirection -POS_X: rayTraceDirection -POS_Y: rayTraceDirection -POS_Z: rayTraceDirection -PolySi: Material -Polymer: Material -Quarter: HoleShape -Si: Material -Si3N4: Material -SiC: Material -SiGe: Material -SiN: Material -SiO2: Material -SiON: Material -TIMING: LogLevel -TiN: Material -Undefined: Material -W: Material -WARNING: LogLevel __version__: str class AdvectionCallback: diff --git a/python/stubs/viennaps3d/viennaps3d.pyi b/python/stubs/viennaps3d/viennaps3d.pyi index 7c491a2a..f1c51ed5 100644 --- a/python/stubs/viennaps3d/viennaps3d.pyi +++ b/python/stubs/viennaps3d/viennaps3d.pyi @@ -1,42 +1,7 @@ from _typeshed import Incomplete from typing import Callable, ClassVar, overload -Air: Material -Al2O3: Material -Cu: Material D: int -DEBUG: LogLevel -Dielectric: Material -ERROR: LogLevel -Full: HoleShape -GAS: Material -GaN: Material -Half: HoleShape -INFO: LogLevel -INTERMEDIATE: LogLevel -Mask: Material -Metal: Material -NEG_X: rayTraceDirection -NEG_Y: rayTraceDirection -NEG_Z: rayTraceDirection -POS_X: rayTraceDirection -POS_Y: rayTraceDirection -POS_Z: rayTraceDirection -PolySi: Material -Polymer: Material -Quarter: HoleShape -Si: Material -Si3N4: Material -SiC: Material -SiGe: Material -SiN: Material -SiO2: Material -SiON: Material -TIMING: LogLevel -TiN: Material -Undefined: Material -W: Material -WARNING: LogLevel __version__: str class AdvectionCallback: From c169d3b08ee664fbbe3933d11fb8a9812ca364ba Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Tue, 4 Feb 2025 14:21:45 +0100 Subject: [PATCH 18/25] Remove printTimeInterval in psProcess --- include/viennaps/models/ion_only.hpp | 8 +- include/viennaps/models/psSF6O2Etching.hpp | 69 +---------------- include/viennaps/models/psSF6O2Parameters.hpp | 76 +++++++++++++++++++ include/viennaps/psProcess.hpp | 48 +++++------- python/pyWrap.cpp | 4 - python/stubs/viennaps2d/viennaps2d.pyi | 1 - python/stubs/viennaps3d/viennaps3d.pyi | 1 - 7 files changed, 102 insertions(+), 105 deletions(-) create mode 100644 include/viennaps/models/psSF6O2Parameters.hpp diff --git a/include/viennaps/models/ion_only.hpp b/include/viennaps/models/ion_only.hpp index 7e64bec2..d39ab72d 100644 --- a/include/viennaps/models/ion_only.hpp +++ b/include/viennaps/models/ion_only.hpp @@ -122,9 +122,11 @@ class IonOnlyIon // Set the flag to stop tracing if the energy is below the threshold if (NewEnergy > params.Si.Eth_ie) { E = NewEnergy; - auto direction = viennaray::ReflectionConedCosine( - rayDir, geomNormal, Rng, - M_PI_2 - std::min(incAngle, params.Ions.minAngle)); + // auto direction = viennaray::ReflectionConedCosine( + // rayDir, geomNormal, Rng, + // M_PI_2 - std::min(incAngle, params.Ions.minAngle)); + auto direction = + viennaray::ReflectionSpecular(rayDir, geomNormal); return std::pair>{sticking, direction}; } else { return std::pair>{ diff --git a/include/viennaps/models/psSF6O2Etching.hpp b/include/viennaps/models/psSF6O2Etching.hpp index 5242acee..62283ad4 100644 --- a/include/viennaps/models/psSF6O2Etching.hpp +++ b/include/viennaps/models/psSF6O2Etching.hpp @@ -10,77 +10,12 @@ #include "../psUnits.hpp" #include "../psVelocityField.hpp" +#include "psSF6O2Parameters.hpp" + namespace viennaps { using namespace viennacore; -template struct SF6O2Parameters { - // fluxes in (1e15 /cm² /s) - NumericType ionFlux = 12.; - NumericType etchantFlux = 1.8e3; - NumericType oxygenFlux = 1.0e2; - - // sticking probabilities - NumericType beta_F = 0.7; - NumericType beta_O = 1.; - - NumericType etchStopDepth = std::numeric_limits::lowest(); - - // Mask - struct MaskType { - NumericType rho = 500.; // 1e22 atoms/cm³ - NumericType beta_F = 0.01; - NumericType beta_O = 0.1; - - NumericType Eth_sp = 20.; // eV - NumericType A_sp = 0.0139; - NumericType B_sp = 9.3; - } Mask; - - // Si - struct SiType { - // density - NumericType rho = 5.02; // 1e22 atoms/cm³ - - // sputtering coefficients - NumericType Eth_sp = 20.; // eV - NumericType Eth_ie = 15.; // eV - - NumericType A_sp = 0.0337; - NumericType B_sp = 9.3; - NumericType theta_g_sp = M_PI_2; // angle where yield is zero [rad] - - NumericType A_ie = 7.; - NumericType B_ie = 0.8; - NumericType theta_g_ie = - constants::degToRad(78); // angle where yield is zero [rad] - - // chemical etching - NumericType k_sigma = 3.0e2; // in (1e15 cm⁻²s⁻¹) - NumericType beta_sigma = 4.0e-2; // in (1e15 cm⁻²s⁻¹) - } Si; - - // Passivation - struct PassivationType { - // sputtering coefficients - NumericType Eth_ie = 10.; // eV - NumericType A_ie = 3; - } Passivation; - - struct IonType { - NumericType meanEnergy = 100.; // eV - NumericType sigmaEnergy = 10.; // eV - NumericType exponent = 500.; - - NumericType inflectAngle = 1.55334303; - NumericType n_l = 10.; - NumericType minAngle = 1.3962634; - - NumericType thetaRMin = constants::degToRad(70.); - NumericType thetaRMax = constants::degToRad(90.); - } Ions; -}; - namespace impl { template diff --git a/include/viennaps/models/psSF6O2Parameters.hpp b/include/viennaps/models/psSF6O2Parameters.hpp new file mode 100644 index 00000000..51491ea3 --- /dev/null +++ b/include/viennaps/models/psSF6O2Parameters.hpp @@ -0,0 +1,76 @@ +#pragma once + +#include "../psConstants.hpp" + +namespace viennaps { + +using namespace viennacore; + +template struct SF6O2Parameters { + // fluxes in (1e15 /cm² /s) + NumericType ionFlux = 12.; + NumericType etchantFlux = 1.8e3; + NumericType oxygenFlux = 1.0e2; + + // sticking probabilities + NumericType beta_F = 0.7; + NumericType beta_O = 1.; + + NumericType etchStopDepth = std::numeric_limits::lowest(); + + // Mask + struct MaskType { + NumericType rho = 500.; // 1e22 atoms/cm³ + NumericType beta_F = 0.01; + NumericType beta_O = 0.1; + + NumericType Eth_sp = 20.; // eV + NumericType A_sp = 0.0139; + NumericType B_sp = 9.3; + } Mask; + + // Si + struct SiType { + // density + NumericType rho = 5.02; // 1e22 atoms/cm³ + + // sputtering coefficients + NumericType Eth_sp = 20.; // eV + NumericType Eth_ie = 15.; // eV + + NumericType A_sp = 0.0337; + NumericType B_sp = 9.3; + NumericType theta_g_sp = M_PI_2; // angle where yield is zero [rad] + + NumericType A_ie = 7.; + NumericType B_ie = 0.8; + NumericType theta_g_ie = + constants::degToRad(78); // angle where yield is zero [rad] + + // chemical etching + NumericType k_sigma = 3.0e2; // in (1e15 cm⁻²s⁻¹) + NumericType beta_sigma = 4.0e-2; // in (1e15 cm⁻²s⁻¹) + } Si; + + // Passivation + struct PassivationType { + // sputtering coefficients + NumericType Eth_ie = 10.; // eV + NumericType A_ie = 3; + } Passivation; + + struct IonType { + NumericType meanEnergy = 100.; // eV + NumericType sigmaEnergy = 10.; // eV + NumericType exponent = 500.; + + NumericType inflectAngle = 1.55334303; + NumericType n_l = 10.; + NumericType minAngle = 1.3962634; + + NumericType thetaRMin = constants::degToRad(70.); + NumericType thetaRMax = constants::degToRad(90.); + } Ions; +}; + +} // namespace viennaps diff --git a/include/viennaps/psProcess.hpp b/include/viennaps/psProcess.hpp index d2f559a1..6369dd1b 100644 --- a/include/viennaps/psProcess.hpp +++ b/include/viennaps/psProcess.hpp @@ -119,11 +119,6 @@ template class Process { // 0.5 to guarantee numerical stability. Defaults to 0.4999. void setTimeStepRatio(NumericType cfl) { timeStepRatio = cfl; } - // Sets the minimum time between printing intermediate results during the - // process. If this is set to a non-positive value, no intermediate results - // are printed. - void setPrintTimeInterval(NumericType passedTime) { printTime = passedTime; } - // A single flux calculation is performed on the domain surface. The result is // stored as point data on the nodes of the mesh. SmartPointer> calculateFlux() const { @@ -569,32 +564,28 @@ template class Process { // print debug output if (Logger::getLogLevel() >= 4) { - if (printTime >= 0. && - ((processDuration - remainingTime) - printTime * counter) > 0.) { - if (velocities) - diskMesh->getCellData().insertNextScalarData(*velocities, - "velocities"); - if (useCoverages) { - auto coverages = model->getSurfaceModel()->getCoverages(); - for (size_t idx = 0; idx < coverages->getScalarDataSize(); idx++) { - auto label = coverages->getScalarDataLabel(idx); - diskMesh->getCellData().insertNextScalarData( - *coverages->getScalarData(idx), label); - } - } - for (size_t idx = 0; idx < rates->getScalarDataSize(); idx++) { - auto label = rates->getScalarDataLabel(idx); + if (velocities) + diskMesh->getCellData().insertNextScalarData(*velocities, + "velocities"); + if (useCoverages) { + auto coverages = model->getSurfaceModel()->getCoverages(); + for (size_t idx = 0; idx < coverages->getScalarDataSize(); idx++) { + auto label = coverages->getScalarDataLabel(idx); diskMesh->getCellData().insertNextScalarData( - *rates->getScalarData(idx), label); + *coverages->getScalarData(idx), label); } - printDiskMesh(diskMesh, - name + "_" + std::to_string(counter) + ".vtp"); - if (domain->getCellSet()) { - domain->getCellSet()->writeVTU(name + "_cellSet_" + - std::to_string(counter) + ".vtu"); - } - counter++; } + for (size_t idx = 0; idx < rates->getScalarDataSize(); idx++) { + auto label = rates->getScalarDataLabel(idx); + diskMesh->getCellData().insertNextScalarData( + *rates->getScalarData(idx), label); + } + printDiskMesh(diskMesh, name + "_" + std::to_string(counter) + ".vtp"); + if (domain->getCellSet()) { + domain->getCellSet()->writeVTU(name + "_cellSet_" + + std::to_string(counter) + ".vtu"); + } + counter++; } // apply advection callback @@ -809,7 +800,6 @@ template class Process { bool lsVelocityOutput = false; unsigned maxIterations = 20; bool coveragesInitialized_ = false; - NumericType printTime = 0.; NumericType processTime = 0.; NumericType timeStepRatio = 0.4999; }; diff --git a/python/pyWrap.cpp b/python/pyWrap.cpp index de6e3be7..49e363d2 100644 --- a/python/pyWrap.cpp +++ b/python/pyWrap.cpp @@ -1253,10 +1253,6 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { .def("setMaxCoverageInitIterations", &Process::setMaxCoverageInitIterations, "Set the number of iterations to initialize the coverages.") - .def("setPrintTimeInterval", &Process::setPrintTimeInterval, - "Sets the minimum time between printing intermediate results during " - "the process. If this is set to a non-positive value, no " - "intermediate results are printed.") .def("setIntegrationScheme", &Process::setIntegrationScheme, "Set the integration scheme for solving the level-set equation. " "Possible integration schemes are specified in " diff --git a/python/stubs/viennaps2d/viennaps2d.pyi b/python/stubs/viennaps2d/viennaps2d.pyi index ce2dee1f..dcb5cd9d 100644 --- a/python/stubs/viennaps2d/viennaps2d.pyi +++ b/python/stubs/viennaps2d/viennaps2d.pyi @@ -560,7 +560,6 @@ class Process: def setIntegrationScheme(self, arg0) -> None: ... def setMaxCoverageInitIterations(self, arg0: int) -> None: ... def setNumberOfRaysPerPoint(self, arg0: int) -> None: ... - def setPrintTimeInterval(self, arg0: float) -> None: ... def setProcessDuration(self, arg0: float) -> None: ... def setProcessModel(self, arg0: ProcessModel) -> None: ... def setRayTracingDiskRadius(self, arg0: float) -> None: ... diff --git a/python/stubs/viennaps3d/viennaps3d.pyi b/python/stubs/viennaps3d/viennaps3d.pyi index f1c51ed5..783861dc 100644 --- a/python/stubs/viennaps3d/viennaps3d.pyi +++ b/python/stubs/viennaps3d/viennaps3d.pyi @@ -536,7 +536,6 @@ class Process: def setIntegrationScheme(self, arg0) -> None: ... def setMaxCoverageInitIterations(self, arg0: int) -> None: ... def setNumberOfRaysPerPoint(self, arg0: int) -> None: ... - def setPrintTimeInterval(self, arg0: float) -> None: ... def setProcessDuration(self, arg0: float) -> None: ... def setProcessModel(self, arg0: ProcessModel) -> None: ... def setRayTracingDiskRadius(self, arg0: float) -> None: ... From 14ab55b1406794318bb032edfaf793670d37b7cc Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Tue, 4 Feb 2025 16:43:15 +0100 Subject: [PATCH 19/25] Add coverage delta metric and custom surface data --- examples/holeEtching/holeEtching.cpp | 23 ++-- examples/holeEtching/ionOnly.cpp | 10 +- examples/holeEtching/testFluxes.py | 41 ++++--- include/viennaps/models/psSF6O2Etching.hpp | 101 ++++++++++++++---- include/viennaps/models/psSF6O2Parameters.hpp | 4 +- include/viennaps/psProcess.hpp | 82 ++++++++++++-- include/viennaps/psSurfaceModel.hpp | 5 + 7 files changed, 208 insertions(+), 58 deletions(-) diff --git a/examples/holeEtching/holeEtching.cpp b/examples/holeEtching/holeEtching.cpp index a1d8b648..7e6cf8d6 100644 --- a/examples/holeEtching/holeEtching.cpp +++ b/examples/holeEtching/holeEtching.cpp @@ -10,7 +10,7 @@ int main(int argc, char *argv[]) { using NumericType = double; constexpr int D = 2; - ps::Logger::setLogLevel(ps::LogLevel::INFO); + ps::Logger::setLogLevel(ps::LogLevel::DEBUG); omp_set_num_threads(16); // Parse the parameters @@ -25,15 +25,16 @@ int main(int argc, char *argv[]) { // geometry setup auto geometry = ps::SmartPointer>::New(); ps::MakeHole( - geometry, params.get("gridDelta") /* grid delta */, - params.get("xExtent") /*x extent*/, params.get("yExtent") /*y extent*/, - params.get("holeRadius") /*hole radius*/, - params.get("maskHeight") /* mask height*/, - params.get("taperAngle") /* tapering angle in degrees */, - 0 /* base height */, false /* periodic boundary */, true /*create mask*/, - ps::Material::Si) + geometry, params.get("gridDelta"), params.get("xExtent"), + params.get("yExtent"), params.get("holeRadius"), params.get("maskHeight"), + params.get("taperAngle"), 0 /* base height */, + false /* periodic boundary */, true /*create mask*/, ps::Material::Si) .apply(); + // set parameter units + ps::units::Length::setUnit(params.get("lengthUnit")); + ps::units::Time::setUnit(params.get("timeUnit")); + // use pre-defined model SF6O2 etching model ps::SF6O2Parameters modelParams; modelParams.ionFlux = params.get("ionFlux"); @@ -48,15 +49,11 @@ int main(int argc, char *argv[]) { auto model = ps::SmartPointer>::New(modelParams); - // set parameter units - ps::units::Length::setUnit(params.get("lengthUnit")); - ps::units::Time::setUnit(params.get("timeUnit")); - // process setup ps::Process process; process.setDomain(geometry); process.setProcessModel(model); - process.setMaxCoverageInitIterations(10); + process.setMaxCoverageInitIterations(50); process.setNumberOfRaysPerPoint(params.get("raysPerPoint")); process.setProcessDuration(params.get("processTime")); process.setIntegrationScheme( diff --git a/examples/holeEtching/ionOnly.cpp b/examples/holeEtching/ionOnly.cpp index 1b7574b0..65c1a812 100644 --- a/examples/holeEtching/ionOnly.cpp +++ b/examples/holeEtching/ionOnly.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include @@ -15,7 +16,7 @@ int main(int argc, char *argv[]) { // geometry setup auto geometry = ps::SmartPointer>::New(); - ps::MakeHole(geometry, 0.02, 1.0, 1.0, 0.175, 1.2, 1.193, 0, + ps::MakeHole(geometry, 0.02, 2.0, 2.0, 0.175, 1.2, 1.193, 0, false /* periodic boundary */, true /*create mask*/, ps::Material::Si) .apply(); @@ -25,6 +26,9 @@ int main(int argc, char *argv[]) { modelParams.Ions.meanEnergy = 100.0; modelParams.Ions.sigmaEnergy = 10.0; modelParams.Ions.exponent = 500; + modelParams.Ions.inflectAngle = ps::constants::degToRad(89.); + modelParams.Ions.minAngle = ps::constants::degToRad(85.); + modelParams.Ions.n_l = 10.; modelParams.Si.A_ie = 7.0; modelParams.Si.rho = 0.1; @@ -37,10 +41,10 @@ int main(int argc, char *argv[]) { ps::Process process; process.setDomain(geometry); process.setProcessModel(model); - process.setMaxCoverageInitIterations(10); process.setProcessDuration(10.0 / 60.0); + process.setTimeStepRatio(0.25); process.setIntegrationScheme( - viennals::IntegrationSchemeEnum::ENGQUIST_OSHER_1ST_ORDER); + viennals::IntegrationSchemeEnum::LAX_FRIEDRICHS_1ST_ORDER); // print initial surface geometry->saveSurfaceMesh("initial.vtp"); diff --git a/examples/holeEtching/testFluxes.py b/examples/holeEtching/testFluxes.py index d1827d1e..8ac1715c 100644 --- a/examples/holeEtching/testFluxes.py +++ b/examples/holeEtching/testFluxes.py @@ -14,11 +14,13 @@ print("Running 3D simulation.") import viennaps3d as vps - +vps.setNumThreads(16) vps.Logger.setLogLevel(vps.LogLevel.INFO) +vps.Length.setUnit("um") +vps.Time.setUnit("min") # hole geometry parameters -gridDelta = 0.04 # um +gridDelta = 0.03 # um xExtent = 1.0 yExtent = 1.0 holeRadius = 0.175 @@ -26,38 +28,51 @@ taperAngle = 1.193 # fluxes -ionFlux = [10., 10., 10., 10., 10.] -etchantFlux = [5.5e3, 5e3, 4e3, 3e3, 2e3] -oxygenFlux = [2e2, 3e2, 1e3, 1.5e3, 0.] +ionFlux = [10.0, 10.0, 10.0, 10.0, 10.0] +etchantFlux = [4.8e3, 4.5e3, 4e3, 3.5e3, 2e3] +oxygenFlux = [3e2, 8e2, 2e3, 2.5e3, 0.0] +A_O = [4, 3, 2, 1, 1] yo2 = [0.44, 0.5, 0.56, 0.62, 0] # etching model parameters params = vps.SF6O2Parameters() -params.Si.A_ie = 7.0 +params.Si.A_ie = 5.0 params.Si.Eth_ie = 15.0 params.Si.A_sp = 0.0337 params.Si.Eth_sp = 20.0 -params.Passivation.A_ie = 3.0 - params.Ions.exponent = 500 params.Ions.meanEnergy = 100.0 params.Ions.sigmaEnergy = 10.0 -params.Ions.minAngle = np.deg2rad(10.0) +params.Ions.minAngle = np.deg2rad(85.0) +params.Ions.inflectAngle = np.deg2rad(89.0) # simulation parameters -processDuration = 0.1 # s +processDuration = 3 # min integrationScheme = vps.ls.IntegrationSchemeEnum.ENGQUIST_OSHER_2ND_ORDER numberOfRaysPerPoint = int(1000) for i in range(len(yo2)): geometry = vps.Domain() - vps.MakeHole(geometry, gridDelta, xExtent, yExtent, holeRadius, maskHeight, taperAngle, 0.0, False, True, vps.Material.Si).apply() + vps.MakeHole( + geometry, + gridDelta, + xExtent, + yExtent, + holeRadius, + maskHeight, + taperAngle, + 0.0, + False, + True, + vps.Material.Si, + ).apply() process = vps.Process() process.setDomain(geometry) + process.setMaxCoverageInitIterations(40) process.setProcessDuration(processDuration) process.setIntegrationScheme(integrationScheme) process.setNumberOfRaysPerPoint(numberOfRaysPerPoint) @@ -65,9 +80,11 @@ params.ionFlux = ionFlux[i] params.etchantFlux = etchantFlux[i] params.oxygenFlux = oxygenFlux[i] + params.Passivation.A_ie = A_O[i] + model = vps.SF6O2Etching(params) process.setProcessModel(model) process.apply() - geometry.saveSurfaceMesh("hole_y{:.2f}_EO2.vtp".format(yo2[i])) \ No newline at end of file + geometry.saveSurfaceMesh("hole_y{:.2f}_EO2.vtp".format(yo2[i])) diff --git a/include/viennaps/models/psSF6O2Etching.hpp b/include/viennaps/models/psSF6O2Etching.hpp index 62283ad4..d95fdee2 100644 --- a/include/viennaps/models/psSF6O2Etching.hpp +++ b/include/viennaps/models/psSF6O2Etching.hpp @@ -22,6 +22,7 @@ template class SF6O2SurfaceModel : public SurfaceModel { public: using SurfaceModel::coverages; + using SurfaceModel::surfaceData; const SF6O2Parameters ¶ms; SF6O2SurfaceModel(const SF6O2Parameters &pParams) @@ -36,10 +37,21 @@ class SF6O2SurfaceModel : public SurfaceModel { std::vector cov(numGeometryPoints, 0.); coverages->insertNextScalarData(cov, "eCoverage"); coverages->insertNextScalarData(cov, "oCoverage"); + } + + void initializeSurfaceData(unsigned numGeometryPoints) override { if (Logger::getLogLevel() > 3) { - coverages->insertNextScalarData(cov, "ionEnhancedRate"); - coverages->insertNextScalarData(cov, "sputterRate"); - coverages->insertNextScalarData(cov, "chemicalRate"); + if (surfaceData == nullptr) { + surfaceData = SmartPointer>::New(); + } else { + surfaceData->clear(); + } + + std::vector data(numGeometryPoints, 0.); + surfaceData->insertNextScalarData(data, "ionEnhancedRate"); + surfaceData->insertNextScalarData(data, "sputterRate"); + surfaceData->insertNextScalarData(data, "chemicalRate"); + surfaceData->insertNextScalarData(data, "covSum"); } } @@ -61,9 +73,12 @@ class SF6O2SurfaceModel : public SurfaceModel { std::vector *ieRate = nullptr, *spRate = nullptr, *chRate = nullptr; if (Logger::getLogLevel() > 3) { - ieRate = coverages->getScalarData("ionEnhancedRate"); - spRate = coverages->getScalarData("sputterRate"); - chRate = coverages->getScalarData("chemicalRate"); + ieRate = surfaceData->getScalarData("ionEnhancedRate"); + spRate = surfaceData->getScalarData("sputterRate"); + chRate = surfaceData->getScalarData("chemicalRate"); + ieRate->resize(numPoints); + spRate->resize(numPoints); + chRate->resize(numPoints); } bool stop = false; @@ -129,9 +144,17 @@ class SF6O2SurfaceModel : public SurfaceModel { auto oCoverage = coverages->getScalarData("oCoverage"); oCoverage->resize(numPoints); + std::vector *covSum = nullptr; + const auto logLevel = Logger::getLogLevel(); + if (logLevel > 3) { + covSum = surfaceData->getScalarData("covSum"); + covSum->resize(numPoints); + } + + NumericType meanCoverage = 0.; for (size_t i = 0; i < numPoints; ++i) { - auto Gb_F = etchantFlux->at(i) * params.etchantFlux * params.beta_F; - auto Gb_O = oxygenFlux->at(i) * params.oxygenFlux * params.beta_O; + auto Gb_F = etchantFlux->at(i) * params.etchantFlux; + auto Gb_O = oxygenFlux->at(i) * params.oxygenFlux; auto GY_ie = ionEnhancedFlux->at(i) * params.ionFlux; auto GY_o = ionEnhancedPassivationFlux->at(i) * params.ionFlux; @@ -140,6 +163,16 @@ class SF6O2SurfaceModel : public SurfaceModel { eCoverage->at(i) = Gb_F < 1e-6 ? 0. : 1 / (1 + (a * (1 + 1 / b))); oCoverage->at(i) = Gb_O < 1e-6 ? 0. : 1 / (1 + (b * (1 + 1 / a))); + if (logLevel > 3) { + covSum->at(i) = eCoverage->at(i) + oCoverage->at(i); + meanCoverage += covSum->at(i); + } + } + if (logLevel > 3) { + meanCoverage /= numPoints; + Logger::getInstance() + .addInfo("Mean coverage: " + std::to_string(meanCoverage)) + .print(); } } }; @@ -169,10 +202,9 @@ class SF6SurfaceModel : public SF6O2SurfaceModel { if (etchantFlux->at(i) < 1e-6) { eCoverage->at(i) = 0; } else { - double tmp = - 1 + - (params.Si.k_sigma + 2 * ionEnhancedYield->at(i) * params.ionFlux) / - (etchantFlux->at(i) * params.etchantFlux * params.beta_F); + double tmp = 1 + (params.Si.k_sigma + + 2 * ionEnhancedYield->at(i) * params.ionFlux) / + (etchantFlux->at(i) * params.etchantFlux); eCoverage->at(i) = 1 / tmp; } } @@ -336,11 +368,21 @@ class SF6O2Etchant void surfaceCollision(NumericType rayWeight, const Vec3D &, const Vec3D &, const unsigned int primID, - const int, + const int materialId, viennaray::TracingData &localData, - const viennaray::TracingData *, + const viennaray::TracingData *globalData, RNG &) override final { - localData.getVectorData(0)[primID] += rayWeight; + // F surface coverage + const auto &phi_F = globalData->getVectorData(0)[primID]; + // O surface coverage + const auto &phi_O = globalData->getVectorData(1)[primID]; + // Obtain the sticking probability + NumericType beta = params.beta_F; + if (MaterialMap::isMaterial(materialId, Material::Mask)) + beta = params.Mask.beta_F; + NumericType S_eff = beta * std::max(1. - phi_F - phi_O, 0.); + + localData.getVectorData(0)[primID] += rayWeight * S_eff; } std::pair> surfaceReflection(NumericType rayWeight, const Vec3D &rayDir, @@ -379,11 +421,20 @@ class SF6Etchant void surfaceCollision(NumericType rayWeight, const Vec3D &, const Vec3D &, const unsigned int primID, - const int, + const int materialId, viennaray::TracingData &localData, - const viennaray::TracingData *, + const viennaray::TracingData *globalData, RNG &) override final { - localData.getVectorData(0)[primID] += rayWeight; + + // F surface coverage + const auto &phi_F = globalData->getVectorData(0)[primID]; + // Obtain the sticking probability + NumericType beta = params.beta_F; + if (MaterialMap::isMaterial(materialId, Material::Mask)) + beta = params.Mask.beta_F; + NumericType S_eff = beta * std::max(1. - phi_F, 0.); + + localData.getVectorData(0)[primID] += rayWeight * S_eff; } std::pair> surfaceReflection(NumericType rayWeight, const Vec3D &rayDir, @@ -420,12 +471,18 @@ class SF6O2Oxygen void surfaceCollision(NumericType rayWeight, const Vec3D &, const Vec3D &, const unsigned int primID, - const int, + const int materialId, viennaray::TracingData &localData, - const viennaray::TracingData *, + const viennaray::TracingData *globalData, RNG &) override final { - // Rate is normalized by dividing with the local sticking coefficient - localData.getVectorData(0)[primID] += rayWeight; + NumericType S_eff; + const auto &phi_F = globalData->getVectorData(0)[primID]; + const auto &phi_O = globalData->getVectorData(1)[primID]; + NumericType beta = params.beta_O; + if (MaterialMap::isMaterial(materialId, Material::Mask)) + beta = params.Mask.beta_O; + S_eff = beta * std::max(1. - phi_O - phi_F, 0.); + localData.getVectorData(0)[primID] += rayWeight * S_eff; } std::pair> surfaceReflection(NumericType rayWeight, const Vec3D &rayDir, diff --git a/include/viennaps/models/psSF6O2Parameters.hpp b/include/viennaps/models/psSF6O2Parameters.hpp index 51491ea3..334c08fd 100644 --- a/include/viennaps/models/psSF6O2Parameters.hpp +++ b/include/viennaps/models/psSF6O2Parameters.hpp @@ -21,8 +21,8 @@ template struct SF6O2Parameters { // Mask struct MaskType { NumericType rho = 500.; // 1e22 atoms/cm³ - NumericType beta_F = 0.01; - NumericType beta_O = 0.1; + NumericType beta_F = 0.7; + NumericType beta_O = 1.0; NumericType Eth_sp = 20.; // eV NumericType A_sp = 0.0139; diff --git a/include/viennaps/psProcess.hpp b/include/viennaps/psProcess.hpp index 6369dd1b..597c49d1 100644 --- a/include/viennaps/psProcess.hpp +++ b/include/viennaps/psProcess.hpp @@ -206,7 +206,7 @@ template class Process { // Run the process. void apply() { - /* ---------- Process Setup --------- */ + /* ---------- Check input --------- */ if (!model) { Logger::getInstance() .addWarning("No process model passed to psProcess.") @@ -221,6 +221,11 @@ template class Process { return; } + if (domain->getLevelSets().empty()) { + Logger::getInstance().addWarning("No level sets in domain.").print(); + return; + } + model->initialize(domain, processDuration); const auto name = model->getProcessName().value_or("default"); @@ -258,11 +263,12 @@ template class Process { return; } + /* ------ Process Setup ------ */ + const unsigned int logLevel = Logger::getLogLevel(); Timer processTimer; processTimer.start(); double remainingTime = processDuration; - assert(domain->getLevelSets().size() != 0 && "No level sets in domain."); const NumericType gridDelta = domain->getGrid().getGridDelta(); auto diskMesh = SmartPointer>::New(); @@ -357,12 +363,19 @@ template class Process { // Initialize coverages meshConverter.apply(); auto numPoints = diskMesh->getNodes().size(); + model->getSurfaceModel()->initializeSurfaceData(numPoints); if (!coveragesInitialized_) model->getSurfaceModel()->initializeCoverages(numPoints); - if (model->getSurfaceModel()->getCoverages() != nullptr) { + auto coverages = model->getSurfaceModel()->getCoverages(); + std::ofstream covMetricFile; + if (coverages != nullptr) { Timer timer; useCoverages = true; Logger::getInstance().addInfo("Using coverages.").print(); + // debug output + if (logLevel >= 5) + covMetricFile.open(name + "_covMetric.txt"); + if (!coveragesInitialized_) { timer.start(); Logger::getInstance().addInfo("Initializing coverages ... ").print(); @@ -381,9 +394,12 @@ template class Process { if (PyErr_CheckSignals() != 0) throw pybind11::error_already_set(); #endif + // save current coverages to compare with the new ones + auto prevStepCoverages = + SmartPointer>::New(*coverages); // move coverages to the ray tracer viennaray::TracingData rayTraceCoverages = - movePointDataToRayData(model->getSurfaceModel()->getCoverages()); + movePointDataToRayData(coverages); if (useProcessParams) { // store scalars in addition to coverages auto processParams = @@ -435,7 +451,18 @@ template class Process { rayTraceCoverages); model->getSurfaceModel()->updateCoverages(rates, materialIds); - if (Logger::getLogLevel() >= 3) { + // check for convergence + if (logLevel >= 5) { + coverages = model->getSurfaceModel()->getCoverages(); + auto metric = + calculateCoverageDeltaMetric(coverages, prevStepCoverages); + for (auto val : metric) { + covMetricFile << val << ";"; + } + covMetricFile << "\n"; + } + + if (logLevel >= 3) { auto coverages = model->getSurfaceModel()->getCoverages(); for (size_t idx = 0; idx < coverages->getScalarDataSize(); idx++) { auto label = coverages->getScalarDataLabel(idx); @@ -552,10 +579,28 @@ template class Process { .print(); } + // save coverages for comparison + SmartPointer> prevStepCoverages; + if (useCoverages && logLevel >= 5) { + coverages = model->getSurfaceModel()->getCoverages(); + prevStepCoverages = + SmartPointer>::New(*coverages); + } + // get velocities from rates auto velocities = model->getSurfaceModel()->calculateVelocities( rates, points, materialIds); + // calculate coverage metric + if (useCoverages && logLevel >= 5) { + auto metric = + calculateCoverageDeltaMetric(coverages, prevStepCoverages); + for (auto val : metric) { + covMetricFile << val << ";"; + } + covMetricFile << "\n"; + } + // prepare velocity field model->getVelocityField()->prepare(domain, velocities, processDuration - remainingTime); @@ -563,7 +608,7 @@ template class Process { transField->buildKdTree(points); // print debug output - if (Logger::getLogLevel() >= 4) { + if (logLevel >= 4) { if (velocities) diskMesh->getCellData().insertNextScalarData(*velocities, "velocities"); @@ -699,6 +744,8 @@ template class Process { .print(); } model->reset(); + if (logLevel >= 5) + covMetricFile.close(); } void writeParticleDataLogs(std::string fileName) { @@ -783,6 +830,29 @@ template class Process { } } + std::vector calculateCoverageDeltaMetric( + SmartPointer> updated, + SmartPointer> previous) const { + + assert(updated->getScalarDataSize() == previous->getScalarDataSize()); + std::vector delta(updated->getScalarDataSize(), 0.); + +#pragma omp parallel for + for (size_t i = 0; i < updated->getScalarDataSize(); i++) { + auto label = updated->getScalarDataLabel(i); + auto updatedData = updated->getScalarData(label); + auto previousData = previous->getScalarData(label); + for (size_t j = 0; j < updatedData->size(); j++) { + auto diff = updatedData->at(j) - previousData->at(j); + delta[i] += diff * diff; + } + + delta[i] /= updatedData->size(); + } + + return delta; + } + psDomainType domain; SmartPointer> model; NumericType processDuration; diff --git a/include/viennaps/psSurfaceModel.hpp b/include/viennaps/psSurfaceModel.hpp index 81e62357..3549c3d0 100644 --- a/include/viennaps/psSurfaceModel.hpp +++ b/include/viennaps/psSurfaceModel.hpp @@ -14,6 +14,7 @@ using namespace viennacore; template class SurfaceModel { protected: SmartPointer> coverages = nullptr; + SmartPointer> surfaceData = nullptr; SmartPointer> processParams = nullptr; public: @@ -27,6 +28,10 @@ template class SurfaceModel { // if no process parameters get initialized here, they wont be used at all } + virtual void initializeSurfaceData(unsigned numGeometryPoints) { + // if no surface data get initialized here, they wont be used at all + } + virtual SmartPointer> calculateVelocities(SmartPointer> rates, const std::vector> &coordinates, From ade75b0b72ec51ba35a9caf3874e198e2927b14d Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Tue, 4 Feb 2025 16:46:58 +0100 Subject: [PATCH 20/25] remove ion only tests --- examples/holeEtching/ionOnly.cpp | 57 ------- include/viennaps/models/ion_only.hpp | 214 --------------------------- 2 files changed, 271 deletions(-) delete mode 100644 examples/holeEtching/ionOnly.cpp delete mode 100644 include/viennaps/models/ion_only.hpp diff --git a/examples/holeEtching/ionOnly.cpp b/examples/holeEtching/ionOnly.cpp deleted file mode 100644 index 65c1a812..00000000 --- a/examples/holeEtching/ionOnly.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include -#include - -#include -#include -#include - -namespace ps = viennaps; - -int main(int argc, char *argv[]) { - using NumericType = double; - constexpr int D = 2; - - ps::Logger::setLogLevel(ps::LogLevel::INTERMEDIATE); - omp_set_num_threads(16); - - // geometry setup - auto geometry = ps::SmartPointer>::New(); - ps::MakeHole(geometry, 0.02, 2.0, 2.0, 0.175, 1.2, 1.193, 0, - false /* periodic boundary */, - true /*create mask*/, ps::Material::Si) - .apply(); - - // use pre-defined model SF6O2 etching model - ps::SF6O2Parameters modelParams; - modelParams.Ions.meanEnergy = 100.0; - modelParams.Ions.sigmaEnergy = 10.0; - modelParams.Ions.exponent = 500; - modelParams.Ions.inflectAngle = ps::constants::degToRad(89.); - modelParams.Ions.minAngle = ps::constants::degToRad(85.); - modelParams.Ions.n_l = 10.; - modelParams.Si.A_ie = 7.0; - - modelParams.Si.rho = 0.1; - modelParams.Mask.rho = 0.05; - - auto model = - ps::SmartPointer>::New(modelParams); - - // process setup - ps::Process process; - process.setDomain(geometry); - process.setProcessModel(model); - process.setProcessDuration(10.0 / 60.0); - process.setTimeStepRatio(0.25); - process.setIntegrationScheme( - viennals::IntegrationSchemeEnum::LAX_FRIEDRICHS_1ST_ORDER); - - // print initial surface - geometry->saveSurfaceMesh("initial.vtp"); - - // run the process - process.apply(); - - // print final surface - geometry->saveSurfaceMesh("ionOnly"); -} diff --git a/include/viennaps/models/ion_only.hpp b/include/viennaps/models/ion_only.hpp deleted file mode 100644 index d39ab72d..00000000 --- a/include/viennaps/models/ion_only.hpp +++ /dev/null @@ -1,214 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "../psConstants.hpp" -#include "../psProcessModel.hpp" -#include "../psSurfaceModel.hpp" -#include "../psUnits.hpp" -#include "../psVelocityField.hpp" - -#include "psSF6O2Etching.hpp" - -namespace viennaps { - -using namespace viennacore; - -namespace impl { - -template -class IonOnlySurfModel : public SurfaceModel { -public: - const SF6O2Parameters ¶ms; - - IonOnlySurfModel(const SF6O2Parameters &pParams) - : params(pParams) {} - - SmartPointer> - calculateVelocities(SmartPointer> rates, - const std::vector> &coordinates, - const std::vector &materialIds) override { - const auto numPoints = rates->getScalarData(0)->size(); - std::vector etchRate(numPoints, 0.); - - const auto flux = rates->getScalarData("ionFlux"); - - for (size_t i = 0; i < numPoints; ++i) { - - if (MaterialMap::isMaterial(materialIds[i], Material::Mask)) { - etchRate[i] = -params.Mask.rho * flux->at(i); - } else { - etchRate[i] = -params.Si.rho * flux->at(i); - } - } - - return SmartPointer>::New(std::move(etchRate)); - } -}; - -template -class IonOnlyIon - : public viennaray::Particle, NumericType> { -public: - IonOnlyIon(const SF6O2Parameters &pParams) : params(pParams) {} - - void surfaceCollision(NumericType rayWeight, const Vec3D &rayDir, - const Vec3D &geomNormal, - const unsigned int primID, const int materialId, - viennaray::TracingData &localData, - const viennaray::TracingData *globalData, - RNG &) override final { - // collect data for this hit - const double cosTheta = -DotProduct(rayDir, geomNormal); - NumericType angle = std::acos(std::max(std::min(cosTheta, 1.), 0.)); - - NumericType f_ie_theta = 1.; - if (cosTheta < 0.5) { - f_ie_theta = std::max(3 - 6 * angle / M_PI, 0.); - } - - NumericType Y_Si = - params.Si.A_ie * - std::max(std::sqrt(E) - std::sqrt(params.Si.Eth_ie), 0.) * f_ie_theta; - - localData.getVectorData(0)[primID] += Y_Si * rayWeight; - } - - std::pair> - surfaceReflection(NumericType rayWeight, const Vec3D &rayDir, - const Vec3D &geomNormal, - const unsigned int primId, const int materialId, - const viennaray::TracingData *globalData, - RNG &Rng) override final { - auto cosTheta = -DotProduct(rayDir, geomNormal); - - assert(cosTheta >= 0 && "Hit backside of disc"); - assert(cosTheta <= 1 + 1e-6 && "Error in calculating cos theta"); - - NumericType incAngle = - std::acos(std::max(std::min(cosTheta, static_cast(1.)), - static_cast(0.))); - - // Small incident angles are reflected with the energy fraction centered at - // 0 - NumericType Eref_peak; - NumericType A_energy = - 1. / (1. + params.Ions.n_l * (M_PI_2 / params.Ions.inflectAngle - 1.)); - if (incAngle >= params.Ions.inflectAngle) { - Eref_peak = (1 - (1 - A_energy) * (M_PI_2 - incAngle) / - (M_PI_2 - params.Ions.inflectAngle)); - } else { - Eref_peak = A_energy * std::pow(incAngle / params.Ions.inflectAngle, - params.Ions.n_l); - } - // Gaussian distribution around the Eref_peak scaled by the particle energy - NumericType NewEnergy = Eref_peak * E; - // std::normal_distribution normalDist(E * Eref_peak, 0.1 * E); - // do { - // NewEnergy = normalDist(Rng); - // } while (NewEnergy > E || NewEnergy < 0.); - - NumericType sticking = 0.; - // NumericType sticking = 1.; - // if (incAngle > params.Ions.thetaRMin) { - // sticking = - // 1. - std::min((incAngle - params.Ions.thetaRMin) / - // (params.Ions.thetaRMax - params.Ions.thetaRMin), - // NumericType(1.)); - // } - - // Set the flag to stop tracing if the energy is below the threshold - if (NewEnergy > params.Si.Eth_ie) { - E = NewEnergy; - // auto direction = viennaray::ReflectionConedCosine( - // rayDir, geomNormal, Rng, - // M_PI_2 - std::min(incAngle, params.Ions.minAngle)); - auto direction = - viennaray::ReflectionSpecular(rayDir, geomNormal); - return std::pair>{sticking, direction}; - } else { - return std::pair>{ - 1., Vec3D{0., 0., 0.}}; - } - } - void initNew(RNG &rngState) override final { - std::normal_distribution normalDist{params.Ions.meanEnergy, - params.Ions.sigmaEnergy}; - do { - E = normalDist(rngState); - } while (E <= 0.); - } - NumericType getSourceDistributionPower() const override final { - return params.Ions.exponent; - } - std::vector getLocalDataLabels() const override final { - return {"ionFlux"}; - } - -private: - const SF6O2Parameters ¶ms; - - NumericType E; -}; -} // namespace impl - -template -class IonOnlyEtching : public ProcessModel { -public: - IonOnlyEtching() { initializeModel(); } - - // All flux values are in units 1e15 / cm² - IonOnlyEtching(const double ionFlux, const double etchantFlux, - const double oxygenFlux, const NumericType meanEnergy /* eV */, - const NumericType sigmaEnergy /* eV */, // 5 parameters - const NumericType ionExponent = 300., - const NumericType oxySputterYield = 2., - const NumericType etchStopDepth = - std::numeric_limits::lowest()) { - params.ionFlux = ionFlux; - params.etchantFlux = etchantFlux; - params.oxygenFlux = oxygenFlux; - params.Ions.meanEnergy = meanEnergy; - params.Ions.sigmaEnergy = sigmaEnergy; - params.Ions.exponent = ionExponent; - params.Passivation.A_ie = oxySputterYield; - params.etchStopDepth = etchStopDepth; - initializeModel(); - } - - IonOnlyEtching(const SF6O2Parameters &pParams) - : params(pParams) { - initializeModel(); - } - - void setParameters(const SF6O2Parameters &pParams) { - params = pParams; - } - - SF6O2Parameters &getParameters() { return params; } - -private: - void initializeModel() { - // particles - auto ion = std::make_unique>(params); - - // surface model - auto surfModel = - SmartPointer>::New(params); - - // velocity field - auto velField = SmartPointer>::New(2); - - this->setSurfaceModel(surfModel); - this->setVelocityField(velField); - this->setProcessName("ionOnly"); - this->particles.clear(); - this->insertNextParticleType(ion); - } - - SF6O2Parameters params; -}; - -} // namespace viennaps From c5c9785a7bf67f30497d1af61e34fb8a9e95b392 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Tue, 4 Feb 2025 17:13:13 +0100 Subject: [PATCH 21/25] Fix cmake script --- examples/holeEtching/CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/holeEtching/CMakeLists.txt b/examples/holeEtching/CMakeLists.txt index 775a5d87..d2399a0a 100644 --- a/examples/holeEtching/CMakeLists.txt +++ b/examples/holeEtching/CMakeLists.txt @@ -8,6 +8,3 @@ configure_file(config.txt ${CMAKE_CURRENT_BINARY_DIR}/config.txt COPYONLY) add_dependencies(ViennaPS_Examples ${PROJECT_NAME}) viennacore_setup_bat(${PROJECT_NAME} ${VIENNAPS_ARTIFACTS_DIRECTORY}) - -add_executable("ionOnly" "ionOnly.cpp") -target_link_libraries("ionOnly" PRIVATE ViennaPS) From 5e3648c329a05444a5e7352db77e460dbe332264 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Tue, 4 Feb 2025 17:32:11 +0100 Subject: [PATCH 22/25] Bump ViennaRay --- CMakeLists.txt | 3 +-- include/viennaps/models/psSF6O2Etching.hpp | 19 ------------------- include/viennaps/psProcess.hpp | 2 +- 3 files changed, 2 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e1152cd9..9e291286 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,8 +108,7 @@ CPMAddPackage( CPMFindPackage( NAME ViennaRay - VERSION 3.1.0 - GIT_TAG test-reflection + VERSION 3.1.1 GIT_REPOSITORY "https://github.com/ViennaTools/ViennaRay" EXCLUDE_FROM_ALL ${VIENNAPS_BUILD_PYTHON}) diff --git a/include/viennaps/models/psSF6O2Etching.hpp b/include/viennaps/models/psSF6O2Etching.hpp index d95fdee2..e0474aec 100644 --- a/include/viennaps/models/psSF6O2Etching.hpp +++ b/include/viennaps/models/psSF6O2Etching.hpp @@ -51,7 +51,6 @@ class SF6O2SurfaceModel : public SurfaceModel { surfaceData->insertNextScalarData(data, "ionEnhancedRate"); surfaceData->insertNextScalarData(data, "sputterRate"); surfaceData->insertNextScalarData(data, "chemicalRate"); - surfaceData->insertNextScalarData(data, "covSum"); } } @@ -144,14 +143,6 @@ class SF6O2SurfaceModel : public SurfaceModel { auto oCoverage = coverages->getScalarData("oCoverage"); oCoverage->resize(numPoints); - std::vector *covSum = nullptr; - const auto logLevel = Logger::getLogLevel(); - if (logLevel > 3) { - covSum = surfaceData->getScalarData("covSum"); - covSum->resize(numPoints); - } - - NumericType meanCoverage = 0.; for (size_t i = 0; i < numPoints; ++i) { auto Gb_F = etchantFlux->at(i) * params.etchantFlux; auto Gb_O = oxygenFlux->at(i) * params.oxygenFlux; @@ -163,16 +154,6 @@ class SF6O2SurfaceModel : public SurfaceModel { eCoverage->at(i) = Gb_F < 1e-6 ? 0. : 1 / (1 + (a * (1 + 1 / b))); oCoverage->at(i) = Gb_O < 1e-6 ? 0. : 1 / (1 + (b * (1 + 1 / a))); - if (logLevel > 3) { - covSum->at(i) = eCoverage->at(i) + oCoverage->at(i); - meanCoverage += covSum->at(i); - } - } - if (logLevel > 3) { - meanCoverage /= numPoints; - Logger::getInstance() - .addInfo("Mean coverage: " + std::to_string(meanCoverage)) - .print(); } } }; diff --git a/include/viennaps/psProcess.hpp b/include/viennaps/psProcess.hpp index 597c49d1..1b42b27c 100644 --- a/include/viennaps/psProcess.hpp +++ b/include/viennaps/psProcess.hpp @@ -838,7 +838,7 @@ template class Process { std::vector delta(updated->getScalarDataSize(), 0.); #pragma omp parallel for - for (size_t i = 0; i < updated->getScalarDataSize(); i++) { + for (int i = 0; i < updated->getScalarDataSize(); i++) { auto label = updated->getScalarDataLabel(i); auto updatedData = updated->getScalarData(label); auto previousData = previous->getScalarData(label); From 7e73ce98fa6fe94d6eeb50db3cad15dd578c82ab Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Tue, 4 Feb 2025 17:34:06 +0100 Subject: [PATCH 23/25] Run tests only on PR --- .github/workflows/build.yml | 2 ++ .github/workflows/format.yml | 2 ++ .github/workflows/python.yml | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b8219213..5cb74265 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,6 +2,8 @@ name: 🧪 Run Tests on: push: + branches: + - master pull_request: branches: - master diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 8fc33954..9a5c931d 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -2,6 +2,8 @@ name: 🔍 Check Formatting on: push: + branches: + - master pull_request: branches: - master diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 32404b48..bd387d05 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -5,7 +5,11 @@ on: types: - published push: + branches: + - master pull_request: + branches: + - master workflow_dispatch: inputs: publish: From cb0f2240c96e1cfd47febd854bcd0cf6721d93eb Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Wed, 5 Feb 2025 10:15:54 +0100 Subject: [PATCH 24/25] Set units in process model test --- tests/processModel/processModel.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/processModel/processModel.cpp b/tests/processModel/processModel.cpp index 1a7dc0c7..0dd96c53 100644 --- a/tests/processModel/processModel.cpp +++ b/tests/processModel/processModel.cpp @@ -57,6 +57,8 @@ template void RunTest() { // SF6O2 etching { + units::Time::getInstance().setUnit("s"); + units::Length::getInstance().setUnit("nm"); auto model = SmartPointer>::New(1., 1., 1., 1., 1.); VC_TEST_ASSERT(model->getSurfaceModel()); From 403eb2846f37f89ac70efc0e1946a385c69db307 Mon Sep 17 00:00:00 2001 From: Tobias Reiter Date: Wed, 5 Feb 2025 15:24:17 +0100 Subject: [PATCH 25/25] Material dependent sticking --- examples/holeEtching/testFluxes.py | 11 ++- include/viennaps/models/psSF6O2Etching.hpp | 87 ++++++++++++------- include/viennaps/models/psSF6O2Parameters.hpp | 10 ++- python/pyWrap.cpp | 4 +- python/stubs/viennaps2d/viennaps2d.pyi | 5 +- python/stubs/viennaps3d/viennaps3d.pyi | 5 +- 6 files changed, 75 insertions(+), 47 deletions(-) diff --git a/examples/holeEtching/testFluxes.py b/examples/holeEtching/testFluxes.py index 8ac1715c..78bec270 100644 --- a/examples/holeEtching/testFluxes.py +++ b/examples/holeEtching/testFluxes.py @@ -2,7 +2,9 @@ import numpy as np # parse config file name and simulation dimension -parser = ArgumentParser(prog="holeEtching", description="Run a hole etching process.") +parser = ArgumentParser( + prog="testFluxes", description="Test different flux configurations." +) parser.add_argument("-D", "-DIM", dest="dim", type=int, default=2) args = parser.parse_args() @@ -16,6 +18,7 @@ vps.setNumThreads(16) vps.Logger.setLogLevel(vps.LogLevel.INFO) + vps.Length.setUnit("um") vps.Time.setUnit("min") @@ -29,9 +32,9 @@ # fluxes ionFlux = [10.0, 10.0, 10.0, 10.0, 10.0] -etchantFlux = [4.8e3, 4.5e3, 4e3, 3.5e3, 2e3] +etchantFlux = [4.8e3, 4.5e3, 4e3, 3.5e3, 4e3] oxygenFlux = [3e2, 8e2, 2e3, 2.5e3, 0.0] -A_O = [4, 3, 2, 1, 1] +A_O = [2, 2, 2, 1, 1] yo2 = [0.44, 0.5, 0.56, 0.62, 0] # etching model parameters @@ -87,4 +90,4 @@ process.setProcessModel(model) process.apply() - geometry.saveSurfaceMesh("hole_y{:.2f}_EO2.vtp".format(yo2[i])) + geometry.saveSurfaceMesh("hole_y{:.2f}_EO2.vtp".format(yo2[i]), True) diff --git a/include/viennaps/models/psSF6O2Etching.hpp b/include/viennaps/models/psSF6O2Etching.hpp index e0474aec..ea8c979e 100644 --- a/include/viennaps/models/psSF6O2Etching.hpp +++ b/include/viennaps/models/psSF6O2Etching.hpp @@ -353,15 +353,13 @@ class SF6O2Etchant viennaray::TracingData &localData, const viennaray::TracingData *globalData, RNG &) override final { - // F surface coverage - const auto &phi_F = globalData->getVectorData(0)[primID]; - // O surface coverage - const auto &phi_O = globalData->getVectorData(1)[primID]; - // Obtain the sticking probability - NumericType beta = params.beta_F; - if (MaterialMap::isMaterial(materialId, Material::Mask)) - beta = params.Mask.beta_F; - NumericType S_eff = beta * std::max(1. - phi_F - phi_O, 0.); + NumericType S_eff = 1.; + if (params.fluxIncludeSticking) { + const auto &phi_F = globalData->getVectorData(0)[primID]; + const auto &phi_O = globalData->getVectorData(1)[primID]; + NumericType beta = sticking(materialId); + S_eff = beta * std::max(1. - phi_F - phi_O, 0.); + } localData.getVectorData(0)[primID] += rayWeight * S_eff; } @@ -377,9 +375,7 @@ class SF6O2Etchant // O surface coverage const auto &phi_O = globalData->getVectorData(1)[primID]; // Obtain the sticking probability - NumericType beta = params.beta_F; - if (MaterialMap::isMaterial(materialId, Material::Mask)) - beta = params.Mask.beta_F; + NumericType beta = sticking(materialId); NumericType S_eff = beta * std::max(1. - phi_F - phi_O, 0.); auto direction = @@ -390,6 +386,16 @@ class SF6O2Etchant std::vector getLocalDataLabels() const override final { return {"etchantFlux"}; } + +private: + NumericType sticking(const int matieralId) const { + auto beta = params.beta_F.find(MaterialMap::mapToMaterial(matieralId)); + if (beta != params.beta_F.end()) + return beta->second; + + // default value + return 1.0; + } }; template @@ -406,14 +412,12 @@ class SF6Etchant viennaray::TracingData &localData, const viennaray::TracingData *globalData, RNG &) override final { - - // F surface coverage - const auto &phi_F = globalData->getVectorData(0)[primID]; - // Obtain the sticking probability - NumericType beta = params.beta_F; - if (MaterialMap::isMaterial(materialId, Material::Mask)) - beta = params.Mask.beta_F; - NumericType S_eff = beta * std::max(1. - phi_F, 0.); + NumericType S_eff = 1.; + if (params.fluxIncludeSticking) { + const auto &phi_F = globalData->getVectorData(0)[primID]; + NumericType beta = sticking(materialId); + S_eff = beta * std::max(1. - phi_F, 0.); + } localData.getVectorData(0)[primID] += rayWeight * S_eff; } @@ -427,9 +431,7 @@ class SF6Etchant // F surface coverage const auto &phi_F = globalData->getVectorData(0)[primID]; // Obtain the sticking probability - NumericType beta = params.beta_F; - if (MaterialMap::isMaterial(materialId, Material::Mask)) - beta = params.Mask.beta_F; + NumericType beta = sticking(materialId); NumericType S_eff = beta * std::max(1. - phi_F, 0.); auto direction = @@ -440,6 +442,16 @@ class SF6Etchant std::vector getLocalDataLabels() const override final { return {"etchantFlux"}; } + +private: + NumericType sticking(const int matieralId) const { + auto beta = params.beta_F.find(MaterialMap::mapToMaterial(matieralId)); + if (beta != params.beta_F.end()) + return beta->second; + + // default value + return 1.0; + } }; template @@ -456,13 +468,14 @@ class SF6O2Oxygen viennaray::TracingData &localData, const viennaray::TracingData *globalData, RNG &) override final { - NumericType S_eff; - const auto &phi_F = globalData->getVectorData(0)[primID]; - const auto &phi_O = globalData->getVectorData(1)[primID]; - NumericType beta = params.beta_O; - if (MaterialMap::isMaterial(materialId, Material::Mask)) - beta = params.Mask.beta_O; - S_eff = beta * std::max(1. - phi_O - phi_F, 0.); + NumericType S_eff = 1.; + if (params.fluxIncludeSticking) { + const auto &phi_F = globalData->getVectorData(0)[primID]; + const auto &phi_O = globalData->getVectorData(1)[primID]; + NumericType beta = sticking(materialId); + S_eff = beta * std::max(1. - phi_O - phi_F, 0.); + } + localData.getVectorData(0)[primID] += rayWeight * S_eff; } std::pair> @@ -475,9 +488,7 @@ class SF6O2Oxygen NumericType S_eff; const auto &phi_F = globalData->getVectorData(0)[primID]; const auto &phi_O = globalData->getVectorData(1)[primID]; - NumericType beta = params.beta_O; - if (MaterialMap::isMaterial(materialId, Material::Mask)) - beta = params.Mask.beta_O; + NumericType beta = sticking(materialId); S_eff = beta * std::max(1. - phi_O - phi_F, 0.); auto direction = @@ -488,6 +499,16 @@ class SF6O2Oxygen std::vector getLocalDataLabels() const override final { return {"oxygenFlux"}; } + +private: + NumericType sticking(const int matieralId) const { + auto beta = params.beta_O.find(MaterialMap::mapToMaterial(matieralId)); + if (beta != params.beta_O.end()) + return beta->second; + + // default value + return 1.0; + } }; } // namespace impl diff --git a/include/viennaps/models/psSF6O2Parameters.hpp b/include/viennaps/models/psSF6O2Parameters.hpp index 334c08fd..607c1001 100644 --- a/include/viennaps/models/psSF6O2Parameters.hpp +++ b/include/viennaps/models/psSF6O2Parameters.hpp @@ -13,16 +13,18 @@ template struct SF6O2Parameters { NumericType oxygenFlux = 1.0e2; // sticking probabilities - NumericType beta_F = 0.7; - NumericType beta_O = 1.; + std::unordered_map beta_F = {{Material::Si, 0.7}, + {Material::Mask, 0.7}}; + std::unordered_map beta_O = {{Material::Si, 1.}, + {Material::Mask, 1.}}; NumericType etchStopDepth = std::numeric_limits::lowest(); + bool fluxIncludeSticking = false; // Mask struct MaskType { + // density NumericType rho = 500.; // 1e22 atoms/cm³ - NumericType beta_F = 0.7; - NumericType beta_O = 1.0; NumericType Eth_sp = 20.; // eV NumericType A_sp = 0.0139; diff --git a/python/pyWrap.cpp b/python/pyWrap.cpp index 49e363d2..7841dbc8 100644 --- a/python/pyWrap.cpp +++ b/python/pyWrap.cpp @@ -688,8 +688,6 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { pybind11::class_::MaskType>(module, "SF6O2ParametersMask") .def(pybind11::init<>()) .def_readwrite("rho", &SF6O2Parameters::MaskType::rho) - .def_readwrite("beta_F", &SF6O2Parameters::MaskType::beta_F) - .def_readwrite("beta_O", &SF6O2Parameters::MaskType::beta_O) .def_readwrite("A_sp", &SF6O2Parameters::MaskType::A_sp) .def_readwrite("B_sp", &SF6O2Parameters::MaskType::B_sp) .def_readwrite("Eth_sp", &SF6O2Parameters::MaskType::Eth_sp); @@ -729,6 +727,8 @@ PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { .def_readwrite("etchantFlux", &SF6O2Parameters::etchantFlux) .def_readwrite("oxygenFlux", &SF6O2Parameters::oxygenFlux) .def_readwrite("etchStopDepth", &SF6O2Parameters::etchStopDepth) + .def_readwrite("fluxIncludeSticking", + &SF6O2Parameters::fluxIncludeSticking) .def_readwrite("beta_F", &SF6O2Parameters::beta_F) .def_readwrite("beta_O", &SF6O2Parameters::beta_O) .def_readwrite("Mask", &SF6O2Parameters::Mask) diff --git a/python/stubs/viennaps2d/viennaps2d.pyi b/python/stubs/viennaps2d/viennaps2d.pyi index dcb5cd9d..bc16ee46 100644 --- a/python/stubs/viennaps2d/viennaps2d.pyi +++ b/python/stubs/viennaps2d/viennaps2d.pyi @@ -658,9 +658,10 @@ class SF6O2Parameters: Mask: SF6O2ParametersMask Passivation: SF6O2ParametersPassivation Si: SF6O2ParametersSi - beta_F: float - beta_O: float + beta_F: dict[Material, float] + beta_O: dict[Material, float] etchStopDepth: float + fluxIncludeSticking: bool etchantFlux: float ionFlux: float oxygenFlux: float diff --git a/python/stubs/viennaps3d/viennaps3d.pyi b/python/stubs/viennaps3d/viennaps3d.pyi index 783861dc..def2c5c3 100644 --- a/python/stubs/viennaps3d/viennaps3d.pyi +++ b/python/stubs/viennaps3d/viennaps3d.pyi @@ -634,9 +634,10 @@ class SF6O2Parameters: Mask: SF6O2ParametersMask Passivation: SF6O2ParametersPassivation Si: SF6O2ParametersSi - beta_F: float - beta_O: float + beta_F: float[Material, float] + beta_O: float[Material, float] etchStopDepth: float + fluxIncludeSticking: bool etchantFlux: float ionFlux: float oxygenFlux: float