diff --git a/.gitignore b/.gitignore index 1f16c653..fef10701 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,9 @@ install* CMakeSettings.json docs/doxygen/*.txt dependencies/ -!docs/doxygen/doxygen_template.txt \ No newline at end of file +!docs/doxygen/doxygen_template.txt +*.so +*.py[cod] +*.egg-info +*env* +.eggs/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 80ca01dc..cc2d0369 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,20 @@ -cmake_minimum_required(VERSION 3.12 FATAL_ERROR) +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) -project("ViennaPS" VERSION 1.0.0) +project(ViennaPS VERSION 1.3.0) -include(GNUInstallDirs) +# Store version in cache +set(VIENNAPS_VERSION + ${PROJECT_VERSION} + CACHE STRING "The version of ViennaPS" FORCE) -# c++17 for inline constexpr variables +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_CXX_STANDARD "17") -# Set default build type +add_definitions(-DVIENNAPS_VERSION=${PROJECT_VERSION}) + +include(GNUInstallDirs) + +# Setup of default build type set(DEFAULT_BUILD_TYPE "Release") if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.") @@ -19,41 +26,116 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) "RelWithDebInfo") endif() -# tell VS to export all symbols to its dll files +# Tell VS to export all symbols to its dll files if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE - CACHE BOOL "Export all symbols") + CACHE BOOL "Export all symbols" FORCE) add_compile_definitions(_USE_MATH_DEFINES) endif() +# ################################################################################################## +# CONFIGURATION OPTIONS +# ################################################################################################## + +# Build the included examples +option(VIENNAPS_BUILD_EXAMPLES "Build examples." OFF) + +# Build the application binary +option(VIENNAPS_BUILD_APPLICATION "Build ViennaPS application." OFF) + +# ################################################################################################## +# AUTOMATIC DEPENDENCY PREPARATION +# ################################################################################################## + +# With the stampfile mechanism, cmake automatically retriggers the configure step after the +# buildDependencies targed has been executed. Thus all dependencies that were built with +# buildDependencies should then be made available by the find_package calls. +set(STAMPFILE ${CMAKE_BINARY_DIR}/target.stamp) +# When the buildDependencies target is executed, the stamp file is touched +add_custom_target(buildDependencies COMMAND ${CMAKE_COMMAND} -E touch ${STAMPFILE}) + +# Include the external dependencies +include(ExternalProject) +if(NOT DEFINED VIENNAPS_DEPENDENCIES_DIR) + set(DEPENDENCIES_DIR ${CMAKE_SOURCE_DIR}/dependencies) +else() + set(DEPENDENCIES_DIR + ${VIENNAPS_DEPENDENCIES_DIR} + CACHE PATH "Directory for downloading, building and installing external dependencies") +endif() +add_subdirectory(external/upstream) + +# Create the initial stamp file +file(TOUCH ${STAMPFILE}) +# Include the stamp file, so that cmake is forced to re-run once the file has been touched +include(${STAMPFILE}) + +# Binary store the binary directory for use in subprojects set(ViennaPS_BINARY_DIR ${PROJECT_BINARY_DIR} CACHE PATH "Path to local ViennaPS installation" FORCE) -# build dependencies -set(DEPENDENCIES_DIR ${CMAKE_SOURCE_DIR}/dependencies) +# ################################################################################################## +# DEPENDENCY CHECKS +# ################################################################################################## +set(DEPENDENCIES_FOUND TRUE) -# Include all external dependencies -include(ExternalProject) -add_custom_target(buildDependencies) -add_subdirectory(external/upstream) +list(APPEND VIENNAPS_DEPENDENCIES "viennals_external") +find_package(ViennaLS QUIET CONFIG PATHS ${ViennaLS_DIR} NO_DEFAULT_PATH) +if(NOT ViennaLS_FOUND) + set(DEPENDENCIES_FOUND FALSE) +endif() + +list(APPEND VIENNAPS_DEPENDENCIES "viennaray_external") +find_package(ViennaRay QUIET CONFIG PATHS ${ViennaRay_DIR} NO_DEFAULT_PATH) +if(NOT ViennaRay_FOUND) + set(DEPENDENCIES_FOUND FALSE) +endif() + +if(VIENNAPS_BUILD_PYTHON) + list(APPEND VIENNAPS_DEPENDENCIES "pybind11_external") + find_package(pybind11 QUIET PATHS ${pybind11_DIR} NO_DEFAULT_PATH) + if(NOT pybind11_FOUND) + set(DEPENDENCIES_FOUND FALSE) + endif() +endif() + +if(DEPENDENCIES_FOUND) + # Remove the buildDependencies target from the ALL target to prevent unnecessary re-builds + set_target_properties(buildDependencies PROPERTIES EXCLUDE_FROM_ALL true) +else() + message(WARNING "Not all dependencies were found. Execute buildDependencies target first.") + # Add the buildDependencies target to the ALL target + set_target_properties(buildDependencies PROPERTIES EXCLUDE_FROM_ALL false) +endif() + +# ################################################################################################## +# DIRECTORY CONFIGURATIONS +# ################################################################################################## # install config files locations are provided by GNUInstallDirs add_library(${PROJECT_NAME} INTERFACE) -# set the correct paths for installation +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/") +endif() set(LOCAL_CONFIG_DIR "lib/cmake/${PROJECT_NAME}") +# This variable is used by the example, test, python and precompiled library target, since those are +# compiled before the project is installed in its proper install location. +set(VIENNAPS_BUILD_INCLUDE_DIRS "${${PROJECT_NAME}_SOURCE_DIR}/include") + # Adding the install interface generator expression makes sure that the include files are installed # to the proper location (provided by GNUInstallDirs) -set(VIENNAPS_BUILD_INCLUDE_DIRS "${${PROJECT_NAME}_SOURCE_DIR}/include") target_include_directories( ${PROJECT_NAME} INTERFACE $ $) +target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_11) -target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_17) - +# ################################################################################################## +# CMAKE CONFIG FILE SETUP +# ################################################################################################## include(CMakePackageConfigHelpers) write_basic_package_version_file( "${PROJECT_NAME}ConfigVersion.cmake" @@ -67,23 +149,29 @@ configure_package_config_file( # ################################################################################################## # BUILD EXAMPLES # ################################################################################################## -option(VIENNAPS_BUILD_EXAMPLES "Build examples." OFF) -if(VIENNAPS_BUILD_EXAMPLES) +if(VIENNAPS_BUILD_EXAMPLES AND DEPENDENCIES_FOUND) add_subdirectory(Examples) -endif(VIENNAPS_BUILD_EXAMPLES) +endif() # ################################################################################################## # BUILD VIENNAPS APPLICATION # ################################################################################################## -option(VIENNAPS_BUILD_APPLICATION "Build ViennaPS application." OFF) -if(VIENNAPS_BUILD_APPLICATION) +if(VIENNAPS_BUILD_APPLICATION AND DEPENDENCIES_FOUND) add_subdirectory(app) -endif(VIENNAPS_BUILD_APPLICATION) +endif() # ################################################################################################## -# INSTALL +# BUILD VIENNAPS PYTHON BINDINGS # ################################################################################################## +# Build Python bindings +option(VIENNAPS_BUILD_PYTHON "Build for python3.x." OFF) +if(VIENNAPS_BUILD_PYTHON AND DEPENDENCIES_FOUND) + add_subdirectory(Python) +endif() +# ################################################################################################## +# INSTALL +# ################################################################################################## install( TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}_Targets @@ -100,10 +188,9 @@ install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" DESTINATION ${LOCAL_CONFIG_DIR}) -# install include files +# Install include files file(GLOB_RECURSE HEADER_FILES "${PROJECT_SOURCE_DIR}/include/*.hpp") - -install(FILES ${HEADER_FILES} DESTINATION "include") +install(FILES ${HEADER_FILES} DESTINATION include) # uninstall target if(NOT TARGET uninstall) diff --git a/Examples/CantileverWetEtching/CMakeLists.txt b/Examples/CantileverWetEtching/CMakeLists.txt index 8d1e2037..676d83d7 100644 --- a/Examples/CantileverWetEtching/CMakeLists.txt +++ b/Examples/CantileverWetEtching/CMakeLists.txt @@ -5,6 +5,7 @@ project("CantileverWetEtching") add_executable(${PROJECT_NAME} ${PROJECT_NAME}.cpp) target_include_directories(${PROJECT_NAME} PUBLIC ${VIENNAPS_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} PRIVATE ${VIENNAPS_LIBRARIES}) +configure_file(CantileverWetEtching.py ${CMAKE_CURRENT_BINARY_DIR}/CantileverWetEtching.py COPYONLY) configure_file(cantilever_mask.gds ${CMAKE_CURRENT_BINARY_DIR}/cantilever_mask.gds COPYONLY) add_dependencies(buildExamples ${PROJECT_NAME}) diff --git a/Examples/CantileverWetEtching/CantileverWetEtching.py b/Examples/CantileverWetEtching/CantileverWetEtching.py new file mode 100644 index 00000000..6d9332a3 --- /dev/null +++ b/Examples/CantileverWetEtching/CantileverWetEtching.py @@ -0,0 +1,55 @@ +# This example only works in 3D mode +import viennaps3d as vps +import viennals3d as vls + +maskFileName = "cantilever_mask.gds" + +minutes = int(120 / 5) # total etch time (2 hours) +x_add = 50.0 # add space to domain boundary +y_add = 50.0 +gridDelta = 5.0 # um + +# read GDS mask file +boundaryConditions = [ + vls.lsBoundaryConditionEnum.REFLECTIVE_BOUNDARY, + vls.lsBoundaryConditionEnum.REFLECTIVE_BOUNDARY, + vls.lsBoundaryConditionEnum.INFINITE_BOUNDARY, +] + +gds_mask = vps.GDSGeometry(gridDelta) +gds_mask.setBoundaryConditions(boundaryConditions) +gds_mask.setBoundaryPadding(x_add, y_add) +vps.GDSReader(gds_mask, maskFileName).apply() + +# convert GDS geometry to level set +mask = gds_mask.layerToLevelSet(1, 0.0, 4 * gridDelta, True) + +# create plane geometry as substrate +bounds = gds_mask.getBounds() +plane = vls.lsDomain(bounds, boundaryConditions, gridDelta) +vls.lsMakeGeometry(plane, vls.lsPlane([0.0, 0.0, 0.0], [0.0, 0.0, 1.0])).apply() + +# combine geometries +geometry = vps.Domain() +geometry.insertNextLevelSet(mask) +geometry.insertNextLevelSet(plane) +geometry.printSurface("InitialGeometry.vtp", True) + +# wet etch process +model = vps.WetEtching(0) + +process = vps.Process() +process.setDomain(geometry) +process.setProcessModel(model) +process.setProcessDuration(5.0 * 60.0) # 5 minutes of etching +process.setPrintTimeInterval(-1.0) +process.setIntegrationScheme( + vls.lsIntegrationSchemeEnum.STENCIL_LOCAL_LAX_FRIEDRICHS_1ST_ORDER +) + +for n in range(minutes): + process.apply() + # run process + geometry.printSurface("WetEtchingSurface_" + str(n) + ".vtp", True) + +geometry.printSurface("FinalGeometry.vtp", True) diff --git a/Examples/ExampleProcess/Particles.hpp b/Examples/ExampleProcess/Particles.hpp index 5f9aefdf..7a5f82d3 100644 --- a/Examples/ExampleProcess/Particles.hpp +++ b/Examples/ExampleProcess/Particles.hpp @@ -30,8 +30,6 @@ class Particle : public rayParticle, NumericType> { direction}; } void initNew(rayRNG &RNG) override final {} - - int getRequiredLocalDataSize() const override final { return 1; } NumericType getSourceDistributionPower() const override final { return sourcePower; } diff --git a/Examples/ExampleProcess/SurfaceModel.hpp b/Examples/ExampleProcess/SurfaceModel.hpp index c70b1326..5b3f7fa0 100644 --- a/Examples/ExampleProcess/SurfaceModel.hpp +++ b/Examples/ExampleProcess/SurfaceModel.hpp @@ -29,8 +29,8 @@ class SurfaceModel : public psSurfaceModel { *Rates->getScalarData("particleRate")); } - void - updateCoverages(psSmartPointer> Rates) override { + void updateCoverages(psSmartPointer> Rates, + const std::vector &materialIds) override { // update coverages } }; \ No newline at end of file diff --git a/Examples/ExampleProcess/VelocityField.hpp b/Examples/ExampleProcess/VelocityField.hpp index 797d2f67..973762fb 100644 --- a/Examples/ExampleProcess/VelocityField.hpp +++ b/Examples/ExampleProcess/VelocityField.hpp @@ -22,7 +22,7 @@ template class VelocityField : public psVelocityField { } void setVelocities(psSmartPointer> passedVelocities) override { - // additional alerations can be made to the velocities here + // additional alterations can be made to the velocities here velocities = passedVelocities; } diff --git a/Examples/GDSReader/CMakeLists.txt b/Examples/GDSReader/CMakeLists.txt index f35efd37..c68807cd 100644 --- a/Examples/GDSReader/CMakeLists.txt +++ b/Examples/GDSReader/CMakeLists.txt @@ -5,6 +5,7 @@ project("GDSReader") add_executable(${PROJECT_NAME} ${PROJECT_NAME}.cpp) target_include_directories(${PROJECT_NAME} PUBLIC ${VIENNAPS_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} PRIVATE ${VIENNAPS_LIBRARIES}) +configure_file(GDSReader.py ${CMAKE_CURRENT_BINARY_DIR}/GDSReader.py COPYONLY) configure_file(mask.gds ${CMAKE_CURRENT_BINARY_DIR}/mask.gds COPYONLY) add_dependencies(buildExamples ${PROJECT_NAME}) diff --git a/Examples/GDSReader/GDSReader.cpp b/Examples/GDSReader/GDSReader.cpp index bec283e8..f4f18ee7 100644 --- a/Examples/GDSReader/GDSReader.cpp +++ b/Examples/GDSReader/GDSReader.cpp @@ -10,14 +10,12 @@ int main(int argc, char **argv) { // read GDS mask file const NumericType gridDelta = 0.01; - typename lsDomain::BoundaryType boundaryCons[D]; - for (int i = 0; i < D - 1; i++) - boundaryCons[i] = - lsDomain::BoundaryType::REFLECTIVE_BOUNDARY; - boundaryCons[D - 1] = - lsDomain::BoundaryType::INFINITE_BOUNDARY; + lsBoundaryConditionEnum boundaryConds[D] = { + lsBoundaryConditionEnum::REFLECTIVE_BOUNDARY, + lsBoundaryConditionEnum::REFLECTIVE_BOUNDARY, + lsBoundaryConditionEnum::INFINITE_BOUNDARY}; auto mask = psSmartPointer>::New(gridDelta); - mask->setBoundaryConditions(boundaryCons); + mask->setBoundaryConditions(boundaryConds); psGDSReader(mask, "mask.gds").apply(); // geometry setup @@ -28,7 +26,7 @@ int main(int argc, char **argv) { NumericType origin[D] = {0., 0., 0.}; NumericType normal[D] = {0., 0., 1.}; auto plane = psSmartPointer>::New( - bounds, boundaryCons, gridDelta); + bounds, boundaryConds, gridDelta); lsMakeGeometry( plane, psSmartPointer>::New(origin, normal)) .apply(); diff --git a/Examples/GDSReader/GDSReader.py b/Examples/GDSReader/GDSReader.py new file mode 100644 index 00000000..85f5af74 --- /dev/null +++ b/Examples/GDSReader/GDSReader.py @@ -0,0 +1,38 @@ +import viennaps3d as vps + +try: + # ViennaLS Python bindings are needed for the extrusion tool + import viennals3d as vls +except ModuleNotFoundError: + print("ViennaLS Python module not found. Can not parse GDS file.") + exit(1) + +gridDelta = 0.01 +boundaryConds = [ + vls.lsBoundaryConditionEnum.REFLECTIVE_BOUNDARY, + vls.lsBoundaryConditionEnum.REFLECTIVE_BOUNDARY, + vls.lsBoundaryConditionEnum.INFINITE_BOUNDARY, +] + +mask = vps.GDSGeometry(gridDelta) +mask.setBoundaryConditions(boundaryConds) +vps.GDSReader(mask, "mask.gds").apply() + +bounds = mask.getBounds() +geometry = vps.Domain() + +# substrate plane +origin = [0.0, 0.0, 0.0] +normal = [0.0, 0.0, 1.0] +plane = vls.lsDomain(bounds, boundaryConds, gridDelta) +vls.lsMakeGeometry(plane, vls.lsPlane(origin, normal)).apply() + +geometry.insertNextLevelSet(plane) + +layer0 = mask.layerToLevelSet(0, 0.0, 0.1, False) +geometry.insertNextLevelSet(layer0) + +layer1 = mask.layerToLevelSet(1, -0.15, 0.45, False) +geometry.insertNextLevelSet(layer1) + +geometry.printSurface("Geometry.vtp", True) diff --git a/Examples/HoleEtching/CMakeLists.txt b/Examples/HoleEtching/CMakeLists.txt index ee4d705a..5db799aa 100644 --- a/Examples/HoleEtching/CMakeLists.txt +++ b/Examples/HoleEtching/CMakeLists.txt @@ -5,6 +5,7 @@ project("HoleEtching") add_executable(${PROJECT_NAME} ${PROJECT_NAME}.cpp) target_include_directories(${PROJECT_NAME} PUBLIC ${VIENNAPS_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} PRIVATE ${VIENNAPS_LIBRARIES}) +configure_file(HoleEtching.py ${CMAKE_CURRENT_BINARY_DIR}/HoleEtching.py COPYONLY) configure_file(config.txt ${CMAKE_CURRENT_BINARY_DIR}/config.txt COPYONLY) add_dependencies(buildExamples ${PROJECT_NAME}) diff --git a/Examples/HoleEtching/HoleEtching.cpp b/Examples/HoleEtching/HoleEtching.cpp index 5f2b9faa..48e68eb4 100644 --- a/Examples/HoleEtching/HoleEtching.cpp +++ b/Examples/HoleEtching/HoleEtching.cpp @@ -1,8 +1,10 @@ +#include #include #include #include #include #include +#include #include "Parameters.hpp" @@ -10,6 +12,8 @@ int main(int argc, char *argv[]) { using NumericType = double; constexpr int D = 3; + psLogger::setLogLevel(psLogLevel::INTERMEDIATE); + // Parse the parameters Parameters params; if (argc > 1) { @@ -34,8 +38,8 @@ int main(int argc, char *argv[]) { // use pre-defined model SF6O2 etching model auto model = psSmartPointer>::New( params.ionFlux /*ion flux*/, params.etchantFlux /*etchant flux*/, - params.oxygenFlux /*oxygen flux*/, params.rfBias /*rf bias*/, - params.A_O /*oxy sputter yield*/, + params.oxygenFlux /*oxygen flux*/, params.meanEnergy /*mean energy*/, + params.sigmaEnergy /*energy sigma*/, params.A_O /*oxy sputter yield*/, params.etchStopDepth /*max etch depth*/); // process setup @@ -52,9 +56,6 @@ int main(int argc, char *argv[]) { // run the process process.apply(); - // write collected particle meta data (ion energy distribution) to a file - process.writeParticleDataLogs("ionEnergyDistribution.txt"); - // print final surface geometry->printSurface("final.vtp"); } diff --git a/Examples/HoleEtching/HoleEtching.py b/Examples/HoleEtching/HoleEtching.py new file mode 100644 index 00000000..026ca5f2 --- /dev/null +++ b/Examples/HoleEtching/HoleEtching.py @@ -0,0 +1,55 @@ +# switch between 2D and 3D mode +DIM = 3 + +if DIM == 2: + import viennaps2d as vps +else: + import viennaps3d as vps + +# print intermediate output surfaces during the process +vps.Logger.setLogLevel(vps.LogLevel.INTERMEDIATE) + +# parse parameters +params = vps.psReadConfigFile("config.txt") + +# geometry setup +geometry = vps.Domain() +vps.MakeHole( + domain=geometry, + gridDelta=params["gridDelta"], + xExtent=params["xExtent"], + yExtent=params["yExtent"], + holeRadius=params["holeRadius"], + holeDepth=params["maskHeight"], + taperingAngle=params["taperAngle"], + makeMask=True, + material=vps.Material.Si, +).apply() + +# use pre-defined model SF6O2 etching model +model = vps.SF6O2Etching( + ionFlux=params["ionFlux"], + etchantFlux=params["etchantFlux"], + oxygenFlux=params["oxygenFlux"], + meanIonEnergy=params["meanEnergy"], + sigmaIonEnergy=params["sigmaEnergy"], + oxySputterYield=params["A_O"], + etchStopDepth=params["etchStopDepth"], +) + +# process setup +process = vps.Process() +process.setDomain(geometry) +process.setProcessModel(model) +process.setMaxCoverageInitIterations(10) +process.setNumberOfRaysPerPoint(int(params["raysPerPoint"])) +process.setProcessDuration(params["processTime"]) + +# print initial surface +geometry.printSurface(filename="initial.vtp", addMaterialIds=True) + +# run the process +process.apply() + +# print final surface +geometry.printSurface(filename="final.vtp", addMaterialIds=True) diff --git a/Examples/HoleEtching/Parameters.hpp b/Examples/HoleEtching/Parameters.hpp index 76cabd67..7c6bc05b 100644 --- a/Examples/HoleEtching/Parameters.hpp +++ b/Examples/HoleEtching/Parameters.hpp @@ -21,7 +21,8 @@ template struct Parameters { T ionFlux = 12.; T etchantFlux = 1.8e3; T oxygenFlux = 1.0e2; - T rfBias = 50; // W + T meanEnergy = 100; // eV + T sigmaEnergy = 10; // eV T A_O = 2.; T etchStopDepth = -100; @@ -43,7 +44,8 @@ template struct Parameters { psUtils::Item{"etchantFlux", etchantFlux}, // psUtils::Item{"oxygenFlux", oxygenFlux}, // psUtils::Item{"ionFlux", ionFlux}, // - psUtils::Item{"rfBias", rfBias}, // + psUtils::Item{"meanEnergy", meanEnergy}, // + psUtils::Item{"sigmaEnergy", sigmaEnergy}, // psUtils::Item{"A_O", A_O}, // psUtils::Item{"etchStopDepth", etchStopDepth}, // psUtils::Item{"raysPerPoint", raysPerPoint} // diff --git a/Examples/HoleEtching/config.txt b/Examples/HoleEtching/config.txt index 6439efde..20510197 100644 --- a/Examples/HoleEtching/config.txt +++ b/Examples/HoleEtching/config.txt @@ -12,14 +12,15 @@ maskHeight=0.4 taperAngle=0 # Process paramters -processTime=100 # seconds +processTime=0.1 # seconds # all flux values are units 1e15 / cm² ionFlux=10. etchantFlux=1800. -oxygenFlux=100. +oxygenFlux=200. -rfBias=105 # rf plasma bias (W) controls the ion energy distribution +meanEnergy=100 # eV +sigmaEnergy=10 A_O=3 # passivation layer sputtering coefficient etchStopDepth=-10 # maximum etching depth diff --git a/Examples/OxideRegrowth/CMakeLists.txt b/Examples/OxideRegrowth/CMakeLists.txt index e43cb235..f1650366 100644 --- a/Examples/OxideRegrowth/CMakeLists.txt +++ b/Examples/OxideRegrowth/CMakeLists.txt @@ -5,6 +5,7 @@ project("OxideRegrowth") add_executable(${PROJECT_NAME} ${PROJECT_NAME}.cpp) target_include_directories(${PROJECT_NAME} PUBLIC ${VIENNAPS_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} PRIVATE ${VIENNAPS_LIBRARIES}) +configure_file(OxideRegrowth.py ${CMAKE_CURRENT_BINARY_DIR}/OxideRegrowth.py COPYONLY) configure_file(config.txt ${CMAKE_CURRENT_BINARY_DIR}/config.txt COPYONLY) add_dependencies(buildExamples ${PROJECT_NAME}) diff --git a/Examples/OxideRegrowth/OxideRegrowth.cpp b/Examples/OxideRegrowth/OxideRegrowth.cpp index 4965531c..b4ccff5f 100644 --- a/Examples/OxideRegrowth/OxideRegrowth.cpp +++ b/Examples/OxideRegrowth/OxideRegrowth.cpp @@ -79,6 +79,4 @@ int main(int argc, char **argv) { process.apply(); psWriteVisualizationMesh(domain, "FinalStack").apply(); - - return 0; } \ No newline at end of file diff --git a/Examples/OxideRegrowth/OxideRegrowth.py b/Examples/OxideRegrowth/OxideRegrowth.py new file mode 100644 index 00000000..a9fccaf2 --- /dev/null +++ b/Examples/OxideRegrowth/OxideRegrowth.py @@ -0,0 +1,68 @@ +# This model/example currently only works in 2D mode +import viennaps2d as vps + +vps.Logger.setLogLevel(vps.LogLevel.INTERMEDIATE) + +# parse the parameters +params = vps.ReadConfigFile("config.txt") + +stability = ( + 2 + * params["diffusionCoefficient"] + / max(params["scallopVelocity"], params["centerVelocity"]) +) +print(f"Stability: {stability}") +if 0.5 * stability <= params["gridDelta"]: + print("Unstable parameters. Reduce grid spacing!") + +domain = vps.Domain() +vps.MakeStack( + domain=domain, + gridDelta=params["gridDelta"], + xExtent=params["xExtent"], + yExtent=0.0, + numLayers=int(params["numLayers"]), + layerHeight=params["layerHeight"], + substrateHeight=params["substrateHeight"], + holeRadius=params["trenchWidth"] / 2.0, + maskHeight=0.0, +).apply() +# copy top layer for deposition +domain.duplicateTopLevelSet(vps.Material.Polymer) + +domain.generateCellSet( + params["substrateHeight"] + params["numLayers"] * params["layerHeight"] + 10.0, True +) +cellSet = domain.getCellSet() +cellSet.addScalarData("byproductSum", 0.0) +cellSet.writeVTU("initial.vtu") +# we need neighborhood information for solving the +# convection-diffusion equation on the cell set +cellSet.buildNeighborhood() + +# The redeposition model captures byproducts from the selective etching +# process in the cell set. The byproducts are then distributed by solving a +# convection-diffusion equation on the cell set. +model = vps.OxideRegrowthModel( + params["nitrideEtchRate"] / 60.0, + params["oxideEtchRate"] / 60.0, + params["redepositionRate"], + params["redepositionThreshold"], + params["redepositionTimeInt"], + params["diffusionCoefficient"], + params["sink"], + params["scallopVelocity"], + params["centerVelocity"], + params["substrateHeight"] + params["numLayers"] * params["layerHeight"], + params["trenchWidth"], +) + +process = vps.Process() +process.setDomain(domain) +process.setProcessModel(model) +process.setProcessDuration(params["targetEtchDepth"] / params["nitrideEtchRate"] * 60.0) +process.setPrintTimeInterval(30.0) + +process.apply() + +vps.WriteVisualizationMesh(domain, "FinalStack").apply() diff --git a/Examples/OxideRegrowth/config.txt b/Examples/OxideRegrowth/config.txt index c4043a52..aaf64458 100644 --- a/Examples/OxideRegrowth/config.txt +++ b/Examples/OxideRegrowth/config.txt @@ -1,5 +1,5 @@ # Domain -gridDelta=2. +gridDelta=2.5 xExtent=300.0 # Geometry diff --git a/Examples/StackEtching/CMakeLists.txt b/Examples/StackEtching/CMakeLists.txt index aaaadeec..29ad783f 100644 --- a/Examples/StackEtching/CMakeLists.txt +++ b/Examples/StackEtching/CMakeLists.txt @@ -5,6 +5,7 @@ project("StackEtching") add_executable(${PROJECT_NAME} ${PROJECT_NAME}.cpp) target_include_directories(${PROJECT_NAME} PUBLIC ${VIENNAPS_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} PRIVATE ${VIENNAPS_LIBRARIES}) +configure_file(StackEtching.py ${CMAKE_CURRENT_BINARY_DIR}/StackEtching.py COPYONLY) configure_file(config.txt ${CMAKE_CURRENT_BINARY_DIR}/config.txt COPYONLY) add_dependencies(buildExamples ${PROJECT_NAME}) diff --git a/Examples/StackEtching/Parameters.hpp b/Examples/StackEtching/Parameters.hpp index c2b81f1a..0d7753e5 100644 --- a/Examples/StackEtching/Parameters.hpp +++ b/Examples/StackEtching/Parameters.hpp @@ -23,7 +23,9 @@ template struct Parameters { T etchantFlux = 150; T polymerFlux = 10; T ionFlux = 56; - T rfBiasPower = 105; // W; + + T energyMean = 100.; // eV + T energySigma = 10.; // eV Parameters() {} @@ -41,7 +43,8 @@ template struct Parameters { psUtils::Item{"etchantFlux", etchantFlux}, // psUtils::Item{"polymerFlux", polymerFlux}, // psUtils::Item{"ionFlux", ionFlux}, // - psUtils::Item{"rfBiasPower", rfBiasPower}, // + psUtils::Item{"energyMean", energyMean}, // + psUtils::Item{"energySigma", energySigma}, // psUtils::Item{"processTime", processTime} // ); } diff --git a/Examples/StackEtching/StackEtching.cpp b/Examples/StackEtching/StackEtching.cpp index fbe66cb4..4f4d1bf4 100644 --- a/Examples/StackEtching/StackEtching.cpp +++ b/Examples/StackEtching/StackEtching.cpp @@ -37,25 +37,22 @@ int main(int argc, char *argv[]) { // use pre-defined model Fluorocarbon etching model auto model = psSmartPointer>::New( - params.ionFlux, params.etchantFlux, params.polymerFlux, - params.rfBiasPower); + params.ionFlux, params.etchantFlux, params.polymerFlux, params.energyMean, + params.energySigma); // process setup psProcess process; process.setDomain(geometry); process.setProcessModel(model); process.setProcessDuration(params.processTime); - process.setMaxCoverageInitIterations(1); - process.setSmoothFlux(true); + process.setMaxCoverageInitIterations(10); + process.setTimeStepRatio(0.25); // print initial surface psWriteVisualizationMesh(geometry, "initial").apply(); process.apply(); - // write collected particle meta data (ion energy distribution) to a file - process.writeParticleDataLogs("ionEnergyDistribution.txt"); - // print final surface psWriteVisualizationMesh(geometry, "final").apply(); diff --git a/Examples/StackEtching/StackEtching.py b/Examples/StackEtching/StackEtching.py new file mode 100644 index 00000000..86198996 --- /dev/null +++ b/Examples/StackEtching/StackEtching.py @@ -0,0 +1,71 @@ +import viennaps2d as vps + +extrude = True +try: + # ViennaLS Python bindings are needed for the extrusion tool + import viennals3d as vls +except ModuleNotFoundError: + print("ViennaLS Python module not found. Can not extrude.") + extrude = False + + +# Set process verbosity +vps.Logger.setLogLevel(vps.LogLevel.INTERMEDIATE) + +# Parse process parameters +params = vps.ReadConfigFile("config.txt") + +# Geometry setup +geometry = vps.Domain() +vps.MakeStack( + geometry, + gridDelta=params["gridDelta"], + xExtent=params["xExtent"], + yExtent=params["yExtent"], + numLayers=int(params["numLayers"]), + layerHeight=params["layerHeight"], + substrateHeight=params["substrateHeight"], + holeRadius=params["holeRadius"], + maskHeight=params["maskHeight"], + periodicBoundary=False, +).apply() + +geometry.duplicateTopLevelSet(vps.Material.Polymer) + +model = vps.FluorocarbonEtching( + ionFlux=params["ionFlux"], + etchantFlux=params["etchantFlux"], + polyFlux=params["polyFlux"], + meanIonEnergy=params["meanIonEnergy"], + sigmaIonEnergy=params["sigmaIonEnergy"], + ionExponent=params["ionExponent"], +) + +process = vps.Process() +process.setDomain(geometry) +process.setProcessModel(model) +process.setProcessDuration(params["processTime"]) +process.setMaxCoverageInitIterations(10) +process.setTimeStepRatio(0.25) + +# print initial surface +vps.WriteVisualizationMesh(geometry, "initial").apply() + +process.apply() + +# print final surface +vps.WriteVisualizationMesh(geometry, "final").apply() + +if extrude: + print("Extruding to 3D ...") + extruded = vps.Domain3D() + extrudeExtent = [-20.0, 20.0] + boundaryConds = [ + vls.lsBoundaryConditionEnum.REFLECTIVE_BOUNDARY, + vls.lsBoundaryConditionEnum.REFLECTIVE_BOUNDARY, + vls.lsBoundaryConditionEnum.INFINITE_BOUNDARY, + ] + + vps.Extrude(geometry, extruded, extrudeExtent, 0, boundaryConds).apply() + + extruded.printSurface("extruded_surface.vtp", True) diff --git a/Examples/StackEtching/config.txt b/Examples/StackEtching/config.txt index 955386e1..cda9e49c 100644 --- a/Examples/StackEtching/config.txt +++ b/Examples/StackEtching/config.txt @@ -1,5 +1,5 @@ # Domain -gridDelta = 2. +gridDelta = 2.5 xExtent = 120.0 yExtent = 120.0 @@ -11,9 +11,11 @@ holeRadius = 75 maskHeight = 50 # Process -processTime = 600 +processTime = 20 # fluxes in 1e15 atoms/cm3 etchantFlux = 150 -polymerFlux = 10 +polyFlux = 10 ionFlux = 56 -rfBiasPower = 215 \ No newline at end of file +meanIonEnergy = 100. +sigmaIonEnergy = 10. +ionExponent = 100. \ No newline at end of file diff --git a/Examples/TEOSTrenchDeposition/CMakeLists.txt b/Examples/TEOSTrenchDeposition/CMakeLists.txt index 774fe895..04056d18 100644 --- a/Examples/TEOSTrenchDeposition/CMakeLists.txt +++ b/Examples/TEOSTrenchDeposition/CMakeLists.txt @@ -5,6 +5,7 @@ project("TEOSTrenchDeposition") add_executable(SingleTEOS SingleTEOS.cpp) target_include_directories(SingleTEOS PUBLIC ${VIENNAPS_INCLUDE_DIRS}) target_link_libraries(SingleTEOS PRIVATE ${VIENNAPS_LIBRARIES}) +configure_file(SingleTEOS.py ${CMAKE_CURRENT_BINARY_DIR}/SingleTEOS.py COPYONLY) configure_file(SingleTEOS_config.txt ${CMAKE_CURRENT_BINARY_DIR}/SingleTEOS_config.txt COPYONLY) add_dependencies(buildExamples SingleTEOS) @@ -12,6 +13,7 @@ add_dependencies(buildExamples SingleTEOS) add_executable(MultiTEOS MultiTEOS.cpp) target_include_directories(MultiTEOS PUBLIC ${VIENNAPS_INCLUDE_DIRS}) target_link_libraries(MultiTEOS PRIVATE ${VIENNAPS_LIBRARIES}) +configure_file(MultiTEOS.py ${CMAKE_CURRENT_BINARY_DIR}/MultiTEOS.py COPYONLY) configure_file(MultiTEOS_config.txt ${CMAKE_CURRENT_BINARY_DIR}/MultiTEOS_config.txt COPYONLY) add_dependencies(buildExamples MultiTEOS) diff --git a/Examples/TEOSTrenchDeposition/MultiTEOS.py b/Examples/TEOSTrenchDeposition/MultiTEOS.py new file mode 100644 index 00000000..debc2d81 --- /dev/null +++ b/Examples/TEOSTrenchDeposition/MultiTEOS.py @@ -0,0 +1,52 @@ +# switch between 2D and 3D mode +DIM = 2 + +if DIM == 2: + import viennaps2d as vps +else: + import viennaps3d as vps + +params = vps.ReadConfigFile("MultiTEOS_config.txt") + +geometry = vps.Domain() +vps.MakeTrench( + domain=geometry, + gridDelta=params["gridDelta"], + xExtent=params["xExtent"], + yExtent=params["yExtent"], + trenchWidth=params["trenchWidth"], + trenchDepth=params["trenchHeight"], + taperingAngle=params["taperAngle"], + baseHeight=0.0, + periodicBoundary=False, + makeMask=False, + material=vps.Material.Si, +).apply() + +# copy top layer to capture deposition +geometry.duplicateTopLevelSet(vps.Material.SiO2) + +# process model encompasses surface model and particle types +model = vps.TEOSDeposition( + stickingProbabilityP1=params["stickingProbabilityP1"], + rateP1=params["depositionRateP1"], + orderP1=params["reactionOrderP1"], + stickingProbabilityP2=params["stickingProbabilityP2"], + rateP2=params["depositionRateP2"], + orderP2=params["reactionOrderP2"], +) + +process = vps.Process() +process.setDomain(geometry) +process.setProcessModel(model) +process.setNumberOfRaysPerPoint(int(params["numRaysPerPoint"])) +process.setProcessDuration(params["processTime"]) + +geometry.printSurface("MultiTEOS_initial.vtp") + +process.apply() + +geometry.printSurface("MultiTEOS_final.vtp") + +if DIM == 2: + vps.WriteVisualizationMesh(geometry, "MultiTEOS_final").apply() diff --git a/Examples/TEOSTrenchDeposition/SingleTEOS.py b/Examples/TEOSTrenchDeposition/SingleTEOS.py new file mode 100644 index 00000000..f84bed8a --- /dev/null +++ b/Examples/TEOSTrenchDeposition/SingleTEOS.py @@ -0,0 +1,49 @@ +# switch between 2D and 3D mode +DIM = 2 + +if DIM == 2: + import viennaps2d as vps +else: + import viennaps3d as vps + +params = vps.ReadConfigFile("SingleTEOS_config.txt") + +geometry = vps.Domain() +vps.MakeTrench( + domain=geometry, + gridDelta=params["gridDelta"], + xExtent=params["xExtent"], + yExtent=params["yExtent"], + trenchWidth=params["trenchWidth"], + trenchDepth=params["trenchHeight"], + taperingAngle=params["taperAngle"], + baseHeight=0.0, + periodicBoundary=False, + makeMask=False, + material=vps.Material.Si, +).apply() + +# copy top layer to capture deposition +geometry.duplicateTopLevelSet(vps.Material.SiO2) + +# process model encompasses surface model and particle types +model = vps.TEOSDeposition( + stickingProbabilityP1=params["stickingProbabilityP1"], + rateP1=params["depositionRateP1"], + orderP1=params["reactionOrderP1"], +) + +process = vps.Process() +process.setDomain(geometry) +process.setProcessModel(model) +process.setNumberOfRaysPerPoint(int(params["numRaysPerPoint"])) +process.setProcessDuration(params["processTime"]) + +geometry.printSurface("SingleTEOS_initial.vtp") + +process.apply() + +geometry.printSurface("SingleTEOS_final.vtp") + +if DIM == 2: + vps.WriteVisualizationMesh(geometry, "SingleTEOS_final").apply() diff --git a/Examples/TrenchDeposition/CMakeLists.txt b/Examples/TrenchDeposition/CMakeLists.txt index ae185d67..038e7bf7 100644 --- a/Examples/TrenchDeposition/CMakeLists.txt +++ b/Examples/TrenchDeposition/CMakeLists.txt @@ -5,6 +5,7 @@ project("TrenchDeposition") add_executable(${PROJECT_NAME} ${PROJECT_NAME}.cpp) target_include_directories(${PROJECT_NAME} PUBLIC ${VIENNAPS_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} PRIVATE ${VIENNAPS_LIBRARIES}) +configure_file(TrenchDeposition.py ${CMAKE_CURRENT_BINARY_DIR}/TrenchDeposition.py COPYONLY) configure_file(config.txt ${CMAKE_CURRENT_BINARY_DIR}/config.txt COPYONLY) add_dependencies(buildExamples ${PROJECT_NAME}) diff --git a/Examples/TrenchDeposition/TrenchDeposition.py b/Examples/TrenchDeposition/TrenchDeposition.py new file mode 100644 index 00000000..4f811b44 --- /dev/null +++ b/Examples/TrenchDeposition/TrenchDeposition.py @@ -0,0 +1,45 @@ +# switch between 2D and 3D mode +DIM = 2 + +if DIM == 2: + import viennaps2d as vps +else: + import viennaps3d as vps + +params = vps.ReadConfigFile("config.txt") + +geometry = vps.Domain() +vps.MakeTrench( + domain=geometry, + gridDelta=params["gridDelta"], + xExtent=params["xExtent"], + yExtent=params["yExtent"], + trenchWidth=params["trenchWidth"], + trenchDepth=params["trenchHeight"], + taperingAngle=params["taperAngle"], + baseHeight=0.0, + periodicBoundary=False, + makeMask=False, + material=vps.Material.Si, +).apply() + +geometry.duplicateTopLevelSet(vps.Material.SiO2) + +model = vps.SimpleDeposition( + stickingProbability=params["stickingProbability"], + sourceExponent=params["sourcePower"], +) + +process = vps.Process() +process.setDomain(geometry) +process.setProcessModel(model) +process.setProcessDuration(params["processTime"]) + +geometry.printSurface("initial.vtp") + +process.apply() + +geometry.printSurface("final.vtp") + +if DIM == 2: + vps.WriteVisualizationMesh(geometry, "final").apply() diff --git a/Examples/TrenchDepositionGeometric/CMakeLists.txt b/Examples/TrenchDepositionGeometric/CMakeLists.txt index c6206981..81d00df6 100644 --- a/Examples/TrenchDepositionGeometric/CMakeLists.txt +++ b/Examples/TrenchDepositionGeometric/CMakeLists.txt @@ -5,6 +5,8 @@ project("TrenchDepositionGeometric") add_executable(${PROJECT_NAME} ${PROJECT_NAME}.cpp) target_include_directories(${PROJECT_NAME} PUBLIC ${VIENNAPS_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} PRIVATE ${VIENNAPS_LIBRARIES}) +configure_file(TrenchDepositionGeometric.py + ${CMAKE_CURRENT_BINARY_DIR}/TrenchDepositionGeometric.py COPYONLY) configure_file(config.txt ${CMAKE_CURRENT_BINARY_DIR}/config.txt COPYONLY) add_dependencies(buildExamples ${PROJECT_NAME}) diff --git a/Examples/TrenchDepositionGeometric/TrenchDepositionGeometric.cpp b/Examples/TrenchDepositionGeometric/TrenchDepositionGeometric.cpp index 742f81bd..11990fca 100644 --- a/Examples/TrenchDepositionGeometric/TrenchDepositionGeometric.cpp +++ b/Examples/TrenchDepositionGeometric/TrenchDepositionGeometric.cpp @@ -39,17 +39,12 @@ int main(int argc, char *argv[]) { process.setDomain(geometry); process.setProcessModel(model); - auto mesh = psSmartPointer>::New(); - psToSurfaceMesh(geometry, mesh).apply(); - psVTKWriter(mesh, "initial.vtp").apply(); + geometry->printSurface("initial.vtp"); process.apply(); - psToSurfaceMesh(geometry, mesh).apply(); - psVTKWriter(mesh, "final.vtp").apply(); + geometry->printSurface("final.vtp"); if constexpr (D == 2) psWriteVisualizationMesh(geometry, "final").apply(); - - return EXIT_SUCCESS; } diff --git a/Examples/TrenchDepositionGeometric/TrenchDepositionGeometric.py b/Examples/TrenchDepositionGeometric/TrenchDepositionGeometric.py new file mode 100644 index 00000000..4817e2fb --- /dev/null +++ b/Examples/TrenchDepositionGeometric/TrenchDepositionGeometric.py @@ -0,0 +1,44 @@ +# switch between 2D and 3D mode +DIM = 2 + +if DIM == 2: + import viennaps2d as vps +else: + import viennaps3d as vps + +params = vps.ReadConfigFile("config.txt") + +geometry = vps.Domain() +vps.MakeTrench( + domain=geometry, + gridDelta=params["gridDelta"], + xExtent=params["xExtent"], + yExtent=params["yExtent"], + trenchWidth=params["trenchWidth"], + trenchDepth=params["trenchHeight"], + taperingAngle=params["taperAngle"], + baseHeight=0.0, + periodicBoundary=False, + makeMask=False, + material=vps.Material.Si, +).apply() + +# copy top layer to capture deposition +geometry.duplicateTopLevelSet(vps.Material.SiO2) + +model = vps.SphereDistribution( + radius=params["layerThickness"], gridDelta=params["gridDelta"] +) + +process = vps.Process() +process.setDomain(geometry) +process.setProcessModel(model) + +geometry.printSurface("initial.vtp") + +process.apply() + +geometry.printSurface("final.vtp") + +if DIM == 2: + vps.WriteVisualizationMesh(geometry, "final").apply() diff --git a/Examples/VolumeModel/CMakeLists.txt b/Examples/VolumeModel/CMakeLists.txt index 409b1d4b..b58629a9 100644 --- a/Examples/VolumeModel/CMakeLists.txt +++ b/Examples/VolumeModel/CMakeLists.txt @@ -5,6 +5,7 @@ project("VolumeModel") add_executable(${PROJECT_NAME} ${PROJECT_NAME}.cpp) target_include_directories(${PROJECT_NAME} PUBLIC ${VIENNAPS_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} PRIVATE ${VIENNAPS_LIBRARIES}) +configure_file(VolumeModel.py ${CMAKE_CURRENT_BINARY_DIR}/VolumeModel.py COPYONLY) configure_file(config.txt ${CMAKE_CURRENT_BINARY_DIR}/config.txt COPYONLY) add_dependencies(buildExamples ${PROJECT_NAME}) diff --git a/Examples/VolumeModel/VolumeModel.cpp b/Examples/VolumeModel/VolumeModel.cpp index fd0eb140..43440b47 100644 --- a/Examples/VolumeModel/VolumeModel.cpp +++ b/Examples/VolumeModel/VolumeModel.cpp @@ -44,6 +44,4 @@ int main(int argc, char *argv[]) { process.apply(); geometry->getCellSet()->writeVTU("DamageModel.vtu"); - - return EXIT_SUCCESS; } diff --git a/Examples/VolumeModel/VolumeModel.py b/Examples/VolumeModel/VolumeModel.py new file mode 100644 index 00000000..a19b979c --- /dev/null +++ b/Examples/VolumeModel/VolumeModel.py @@ -0,0 +1,31 @@ +import viennaps3d as vps + +# parse the parameters +params = vps.ReadConfigFile("config.txt") + +geometry = vps.Domain() +vps.MakeFin( + domain=geometry, + gridDelta=params["gridDelta"], + xExtent=params["xExtent"], + yExtent=params["yExtent"], + finWidth=params["finWidth"], + finHeight=params["finHeight"], +).apply() + + +# generate cell set with depth 5 +geometry.generateCellSet(-5.0, False) + +model = vps.PlasmaDamage( + ionEnergy=params["ionEnergy"], meanFreePath=params["meanFreePath"], maskMaterial=-1 +) + +process = vps.Process() +process.setDomain(geometry) +process.setProcessModel(model) +process.setProcessDuration(0) # apply only damage model + +process.apply() + +geometry.getCellSet().writeVTU("DamageModel.vtu") diff --git a/Examples/VolumeModel/config.txt b/Examples/VolumeModel/config.txt index 91249ae1..b5079fbc 100644 --- a/Examples/VolumeModel/config.txt +++ b/Examples/VolumeModel/config.txt @@ -9,4 +9,5 @@ finWidth=5 finHeight=15 # Process +ionEnergy=100. meanFreePath=0.75 \ No newline at end of file diff --git a/LICENSE b/LICENSE index 6154650e..c5b63630 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ Parts of the code (located in the 'external/' subfolder) have different licenses. The individual licenses apply for this specific part. Please consult -the respective LICECNSE files. +the respective LICENSE files. Copyright (c) 2015 Institute for Microelectronics, TU Wien. diff --git a/Python/CMakeLists.txt b/Python/CMakeLists.txt new file mode 100644 index 00000000..8d3dec3e --- /dev/null +++ b/Python/CMakeLists.txt @@ -0,0 +1,60 @@ +cmake_minimum_required(VERSION 3.14) + +project(ViennaPSPython) + +include("${CMAKE_SOURCE_DIR}/cmake/prepare.cmake") + +# Point find_package to the binary directory instead of the install location +find_package(ViennaPS REQUIRED PATHS ${ViennaPS_BINARY_DIR} NO_DEFAULT_PATH) + +list(PREPEND VIENNAPS_INCLUDE_DIRS ${VIENNAPS_BUILD_INCLUDE_DIRS}) + +# Add subfolders to the include directories, since during installation the directory structure is +# flattened, but in the source tree - which we are using here - it is not. +list(PREPEND VIENNAPS_INCLUDE_DIRS ${VIENNAPS_BUILD_INCLUDE_DIRS}/CellSet + ${VIENNAPS_BUILD_INCLUDE_DIRS}/Geometries ${VIENNAPS_BUILD_INCLUDE_DIRS}/Models + ${VIENNAPS_BUILD_INCLUDE_DIRS}/Compact) + +message(STATUS "ViennaPS version: ${VIENNAPS_VERSION}") + +set(PYBIND11_PYTHON_VERSION + 3 + CACHE STRING "Python version") + +find_package(pybind11 REQUIRED PATHS ${pybind11_DIR} NO_DEFAULT_PATH) + +set(VIENNAPS_PYTHON_SOURCE pyWrap.cpp) +set(VIENNAPS_PYTHON_MODULE_NAME "viennaps") +set(VIENNAPS_LIBRARY_OUTPUT_DIR ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) + +# ################################################################################################## +# BUILD 2D PYTHON LIBRARY +# ################################################################################################## +set(VIENNAPS_PYTHON_MODULE_NAME_2D "_${VIENNAPS_PYTHON_MODULE_NAME}2d") + +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${VIENNAPS_LIBRARY_OUTPUT_DIR}/viennaps2d) +pybind11_add_module(${VIENNAPS_PYTHON_MODULE_NAME_2D} ${VIENNAPS_PYTHON_SOURCE}) +target_include_directories(${VIENNAPS_PYTHON_MODULE_NAME_2D} PUBLIC ${VIENNAPS_INCLUDE_DIRS}) +target_link_libraries(${VIENNAPS_PYTHON_MODULE_NAME_2D} PRIVATE ${VIENNAPS_LIBRARIES}) +target_compile_definitions( + ${VIENNAPS_PYTHON_MODULE_NAME_2D} + PRIVATE -DVIENNAPS_PYTHON_DIMENSION=2 -DVIENNAPS_MODULE_NAME=${VIENNAPS_PYTHON_MODULE_NAME_2D}) + +set(MODULE_NAME ${VIENNAPS_PYTHON_MODULE_NAME_2D}) +configure_file(__init__.py.in ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/__init__.py) + +# ################################################################################################## +# BUILD 3D PYTHON LIBRARY +# ################################################################################################## +set(VIENNAPS_PYTHON_MODULE_NAME_3D "_${VIENNAPS_PYTHON_MODULE_NAME}3d") + +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${VIENNAPS_LIBRARY_OUTPUT_DIR}/viennaps3d) +pybind11_add_module(${VIENNAPS_PYTHON_MODULE_NAME_3D} ${VIENNAPS_PYTHON_SOURCE}) +target_include_directories(${VIENNAPS_PYTHON_MODULE_NAME_3D} PUBLIC ${VIENNAPS_INCLUDE_DIRS}) +target_link_libraries(${VIENNAPS_PYTHON_MODULE_NAME_3D} PRIVATE ${VIENNAPS_LIBRARIES}) +target_compile_definitions( + ${VIENNAPS_PYTHON_MODULE_NAME_3D} + PRIVATE -DVIENNAPS_PYTHON_DIMENSION=3 -DVIENNAPS_MODULE_NAME=${VIENNAPS_PYTHON_MODULE_NAME_3D}) + +set(MODULE_NAME ${VIENNAPS_PYTHON_MODULE_NAME_3D}) +configure_file(__init__.py.in ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/__init__.py) diff --git a/Python/__init__.py.in b/Python/__init__.py.in new file mode 100644 index 00000000..573a563b --- /dev/null +++ b/Python/__init__.py.in @@ -0,0 +1,45 @@ +""" +ViennaPS +======== + +ViennaPS is a header-only C++ process simulation library, +which includes surface and volume representations, +a ray tracer, and physical models for the simulation of +microelectronic fabrication processes. +""" + +from .@MODULE_NAME@ import * + +#Config file reader helper function +def ReadConfigFile(fileName : str): + """Read a config file in the ViennaPS standard config file format. + + Parameters + ---------- + fileName : str + Name of the config file. + + Returns + ------- + dict + A dictionary containing the parameters from the config file. + """ + par_dict = {} + + with open(fileName, "r") as file: + lines = file.readlines() + for line in lines: + + line = line[:line.find('#')] # remove comments + + if len(line) > 0: + par_name = line[:line.find('=')].strip(' ') + par_value = line[line.find('=')+1:] + + try: + val = float(par_value) + except: val = par_value + + par_dict[par_name] = val + + return par_dict \ No newline at end of file diff --git a/Python/pyWrap.cpp b/Python/pyWrap.cpp new file mode 100644 index 00000000..9830c447 --- /dev/null +++ b/Python/pyWrap.cpp @@ -0,0 +1,859 @@ +/* + This file is used to generate the python module of ViennaPS. + It uses pybind11 to create the modules. + + All necessary headers are included here and the interface + of the classes which should be exposed defined +*/ + +#include "pyWrap.hpp" + +PYBIND11_MODULE(VIENNAPS_MODULE_NAME, module) { + module.doc() = + "ViennaPS is a header-only C++ process simulation library which " + "includes surface and volume representations, a ray tracer, and physical " + "models for the simulation of microelectronic fabrication processes. The " + "main design goals are simplicity and efficiency, tailored towards " + "scientific simulations."; + + // set version string of python module + module.attr("__version__") = VIENNAPS_MODULE_VERSION; + + // wrap omp_set_num_threads to control number of threads + module.def("setNumThreads", &omp_set_num_threads); + + // it was giving an error that it could'nt convert this type to python + // pybind11::bind_vector>>( + // module, "VectorDouble"); + + // psProcessParams + pybind11::class_, psSmartPointer>>( + module, "ProcessParams") + .def(pybind11::init<>()) + .def("insertNextScalar", &psProcessParams::insertNextScalar) + .def("getScalarData", (T & (psProcessParams::*)(int)) & + psProcessParams::getScalarData) + .def("getScalarData", (const T &(psProcessParams::*)(int) const) & + psProcessParams::getScalarData) + .def("getScalarData", (T & (psProcessParams::*)(std::string)) & + psProcessParams::getScalarData) + .def("getScalarDataIndex", &psProcessParams::getScalarDataIndex) + .def("getScalarData", (std::vector & (psProcessParams::*)()) & + psProcessParams::getScalarData) + .def("getScalarData", + (const std::vector &(psProcessParams::*)() const) & + psProcessParams::getScalarData) + .def("getScalarDataLabel", &psProcessParams::getScalarDataLabel); + + // psSurfaceModel + pybind11::class_, psSmartPointer>, + PypsSurfaceModel>(module, "SurfaceModel") + .def(pybind11::init<>()) + .def("initializeCoverages", &psSurfaceModel::initializeCoverages) + .def("initializeProcessParameters", + &psSurfaceModel::initializeProcessParameters) + .def("getCoverages", &psSurfaceModel::getCoverages) + .def("getProcessParameters", &psSurfaceModel::getProcessParameters) + .def("calculateVelocities", &psSurfaceModel::calculateVelocities) + .def("updateCoverages", &psSurfaceModel::updateCoverages); + + pybind11::enum_(module, "LogLevel") + .value("ERROR", psLogLevel::ERROR) + .value("WARNING", psLogLevel::WARNING) + .value("INFO", psLogLevel::INFO) + .value("TIMING", psLogLevel::TIMING) + .value("INTERMEDIATE", psLogLevel::INTERMEDIATE) + .value("DEBUG", psLogLevel::DEBUG) + .export_values(); + + // some unexpected behaviour can happen as it is working with multithreading + pybind11::class_>(module, "Logger") + .def_static("setLogLevel", &psLogger::setLogLevel) + .def_static("getLogLevel", &psLogger::getLogLevel) + .def_static("getInstance", &psLogger::getInstance, + pybind11::return_value_policy::reference) + .def("addDebug", &psLogger::addDebug) + .def("addTiming", (psLogger & (psLogger::*)(std::string, double)) & + psLogger::addTiming) + .def("addTiming", + (psLogger & (psLogger::*)(std::string, double, double)) & + psLogger::addTiming) + .def("addInfo", &psLogger::addInfo) + .def("addWarning", &psLogger::addWarning) + .def("addError", &psLogger::addError, pybind11::arg("s"), + pybind11::arg("shouldAbort") = true) + .def("print", [](psLogger &instance) { instance.print(std::cout); }); + + // psVelocityField + pybind11::class_, psSmartPointer>, + PyVelocityField> + velocityField(module, "VelocityField"); + // constructors + velocityField + .def(pybind11::init<>()) + // methods + .def("getScalarVelocity", &psVelocityField::getScalarVelocity) + .def("getVectorVelocity", &psVelocityField::getVectorVelocity) + .def("getDissipationAlpha", &psVelocityField::getDissipationAlpha) + .def("getTranslationFieldOptions", + &psVelocityField::getTranslationFieldOptions) + .def("setVelocities", &psVelocityField::setVelocities); + + pybind11::class_, + psSmartPointer>>( + module, "DefaultVelocityField", velocityField) + // constructors + .def(pybind11::init<>()) + // methods + .def("getScalarVelocity", &psDefaultVelocityField::getScalarVelocity) + .def("getVectorVelocity", &psDefaultVelocityField::getVectorVelocity) + .def("getDissipationAlpha", + &psDefaultVelocityField::getDissipationAlpha) + .def("getTranslationFieldOptions", + &psDefaultVelocityField::getTranslationFieldOptions) + .def("setVelocities", &psDefaultVelocityField::setVelocities); + + // psDomain + pybind11::class_, DomainType>(module, "Domain") + // constructors + .def(pybind11::init()) + .def(pybind11::init(&DomainType::New<>)) + // methods + .def("insertNextLevelSet", &psDomain::insertNextLevelSet, + pybind11::arg("levelset"), pybind11::arg("wrapLowerLevelSet") = true, + "Insert a level set to domain.") + .def("insertNextLevelSetAsMaterial", + &psDomain::insertNextLevelSetAsMaterial) + .def("duplicateTopLevelSet", &psDomain::duplicateTopLevelSet) + .def("setMaterialMap", &psDomain::setMaterialMap) + .def("getMaterialMap", &psDomain::getMaterialMap) + .def("generateCellSet", &psDomain::generateCellSet, + "Generate the cell set.") + .def("getLevelSets", + [](psDomain &d) + -> std::optional>>> { + auto levelsets = d.getLevelSets(); + if (levelsets) + return *levelsets; + return std::nullopt; + }) + .def("getCellSet", &psDomain::getCellSet, "Get the cell set.") + .def("getGrid", &psDomain::getGrid, "Get the grid") + .def("setUseCellSet", &psDomain::setUseCellSet) + .def("getUseCellSet", &psDomain::getUseCellSet) + .def("print", &psDomain::print) + .def("printSurface", &psDomain::printSurface, + pybind11::arg("filename"), pybind11::arg("addMaterialIds") = false, + "Print the surface of the domain.") + .def("writeLevelSets", &psDomain::writeLevelSets) + .def("clear", &psDomain::clear); + + // Enum psMaterial + pybind11::enum_(module, "Material") + .value("Undefined", psMaterial::Undefined) + .value("Mask", psMaterial::Mask) + .value("Si", psMaterial::Si) + .value("SiO2", psMaterial::SiO2) + .value("Si3N4", psMaterial::Si3N4) + .value("SiN", psMaterial::SiN) + .value("SiON", psMaterial::SiON) + .value("SiC", psMaterial::SiC) + .value("PolySi", psMaterial::PolySi) + .value("GaN", psMaterial::GaN) + .value("W", psMaterial::W) + .value("Al2O3", psMaterial::Al2O3) + .value("TiN", psMaterial::TiN) + .value("Cu", psMaterial::Cu) + .value("Polymer", psMaterial::Polymer) + .value("Dielectric", psMaterial::Dielectric) + .value("Metal", psMaterial::Metal) + .value("Air", psMaterial::Air) + .value("GAS", psMaterial::GAS) + .export_values(); + + // psMaterialMap + pybind11::class_>(module, + "MaterialMap") + .def(pybind11::init<>()) + .def("insertNextMaterial", &psMaterialMap::insertNextMaterial, + pybind11::arg("material") = psMaterial::Undefined) + .def("getMaterialAtIdx", &psMaterialMap::getMaterialAtIdx) + .def("getMaterialMap", &psMaterialMap::getMaterialMap) + .def("size", &psMaterialMap::size) + .def_static("mapToMaterial", &psMaterialMap::mapToMaterial, + "Map a float to a material.") + .def_static("isMaterial", &psMaterialMap::isMaterial); + + // csDenseCellSet + pybind11::class_, psSmartPointer>>( + module, "DenseCellSet") + .def(pybind11::init()) + .def("getBoundingBox", &csDenseCellSet::getBoundingBox) + .def( + "addScalarData", + [](csDenseCellSet &cellSet, std::string name, T initValue) { + cellSet.addScalarData(name, initValue); + // discard return value + }, + "Add a scalar value to be stored and modified in each cell.") + .def("getCellGrid", &csDenseCellSet::getCellGrid, + "Get the underlying mesh of the cell set.") + .def("getDepth", &csDenseCellSet::getDepth, + "Get the depth of the cell set.") + .def("getGridDelta", &csDenseCellSet::getGridDelta, + "Get the cell size.") + .def("getNodes", &csDenseCellSet::getNodes, + "Get the nodes of the cell set which correspond to the corner " + "points of the cells.") + .def("getElements", &csDenseCellSet::getElements, + "Get elements (cells). The indicies in the elements correspond to " + "the corner nodes.") + .def("getSurface", &csDenseCellSet::getSurface, + "Get the surface level-set.") + .def("getNumberOfCells", &csDenseCellSet::getNumberOfCells) + .def("getFillingFraction", &csDenseCellSet::getFillingFraction, + "Get the filling fraction of the cell containing the point.") + .def("getScalarData", &csDenseCellSet::getScalarData, + "Get the data stored at each cell.") + .def("setCellSetPosition", &csDenseCellSet::setCellSetPosition, + "Set whether the cell set should be created below (false) or above " + "(true) the surface.") + .def("getCellSetPosition", &csDenseCellSet::getCellSetPosition) + .def("setFillingFraction", + pybind11::overload_cast( + &csDenseCellSet::setFillingFraction), + "Sets the filling fraction at given cell index.") + .def("setFillingFraction", + pybind11::overload_cast &, const T>( + &csDenseCellSet::setFillingFraction), + "Sets the filling fraction for cell which contains given point.") + .def("addFillingFraction", + pybind11::overload_cast( + &csDenseCellSet::addFillingFraction), + "Add to the filling fraction at given cell index.") + .def("addFillingFraction", + pybind11::overload_cast &, const T>( + &csDenseCellSet::addFillingFraction), + "Add to the filling fraction for cell which contains given point.") + .def("addFillingFractionInMaterial", + &csDenseCellSet::addFillingFractionInMaterial, + "Add to the filling fraction for cell which contains given point " + "only if the cell has the specified material ID.") + .def("writeVTU", &csDenseCellSet::writeVTU, + "Write the cell set as .vtu file") + .def("writeCellSetData", &csDenseCellSet::writeCellSetData, + "Save cell set data in simple text format.") + .def("readCellSetData", &csDenseCellSet::readCellSetData, + "Read cell set data from text.") + .def("clear", &csDenseCellSet::clear, + "Clear the filling fractions.") + .def("updateMaterials", &csDenseCellSet::updateMaterials, + "Update the material IDs of the cell set. This function should be " + "called if the level sets, the cell set is made out of, have " + "changed. This does not work if the surface of the volume has " + "changed. In this case, call the function 'updateSurface' first.") + .def("updateSurface", &csDenseCellSet::updateSurface, + "Updates the surface of the cell set. The new surface should be " + "below the old surface as this function can only remove cells from " + "the cell set.") + .def("buildNeighborhood", &csDenseCellSet::buildNeighborhood, + "Generate fast neighbor access for each cell.") + .def("getNeighbors", &csDenseCellSet::getNeighbors, + "Get the neighbor indices for a cell."); + + // Shim to instantiate the particle class + pybind11::class_, psSmartPointer>> particle( + module, "Particle"); + particle.def("surfaceCollision", &psParticle::surfaceCollision) + .def("surfaceReflection", &psParticle::surfaceReflection) + .def("initNew", &psParticle::initNew) + .def("getLocalDataLabels", &psParticle::getLocalDataLabels) + .def("getSourceDistributionPower", + &psParticle::getSourceDistributionPower); + + pybind11::class_, psSmartPointer>>( + module, "DiffuseParticle", particle) + .def(pybind11::init( + &psSmartPointer>::New), + pybind11::arg("stickingProbability") = 1.0, + pybind11::arg("cosineExponent") = 1., + pybind11::arg("dataLabel") = "flux") + .def("surfaceCollision", &psDiffuseParticle::surfaceCollision) + .def("surfaceReflection", &psDiffuseParticle::surfaceReflection) + .def("initNew", &psDiffuseParticle::initNew) + .def("getLocalDataLabels", &psDiffuseParticle::getLocalDataLabels) + .def("getSourceDistributionPower", + &psDiffuseParticle::getSourceDistributionPower); + + pybind11::class_>( + module, "SpecularParticle", particle) + .def(pybind11::init( + &psSmartPointer::New), + pybind11::arg("stickingProbability") = 1.0, + pybind11::arg("cosineExponent") = 1., + pybind11::arg("dataLabel") = "flux") + .def("surfaceCollision", &psSpecularParticle::surfaceCollision) + .def("surfaceReflection", &psSpecularParticle::surfaceReflection) + .def("initNew", &psSpecularParticle::initNew) + .def("getLocalDataLabels", &psSpecularParticle::getLocalDataLabels) + .def("getSourceDistributionPower", + &psSpecularParticle::getSourceDistributionPower); + + /**************************************************************************** + * VISUALIZATION * + ****************************************************************************/ + + // visualization classes are not bound with smart pointer holder types + // since they should not be passed to other classes + pybind11::class_>(module, "ToDiskMesh") + .def(pybind11::init>>(), + pybind11::arg("domain"), pybind11::arg("mesh")) + .def(pybind11::init()) + .def("setDomain", &psToDiskMesh::setDomain, + "Set the domain in the mesh converter.") + .def("setMesh", &psToDiskMesh::setMesh, + "Set the mesh in the mesh converter"); + // static assertion failed: Holder classes are only supported for custom types + // .def("setTranslator", &psToDiskMesh::setTranslator, + // "Set the translator in the mesh converter. It used to convert " + // "level-set point IDs to mesh point IDs.") + // .def("getTranslator", &psToDiskMesh::getTranslator, + // "Retrieve the translator from the mesh converter."); + + pybind11::class_>(module, + "WriteVisualizationMesh") + .def(pybind11::init()) + .def(pybind11::init(), pybind11::arg("domain"), + pybind11::arg("fileName")) + .def("apply", &psWriteVisualizationMesh::apply) + .def("setFileName", &psWriteVisualizationMesh::setFileName, + "Set the output file name. The file name will be appended by " + "'_volume.vtu'.") + .def("setDomain", &psWriteVisualizationMesh::setDomain, + "Set the domain in the mesh converter."); + + /**************************************************************************** + * PROCESS * + ****************************************************************************/ + + // psProcessModel + pybind11::class_, psSmartPointer>> + processModel(module, "ProcessModel"); + + // constructors + processModel + .def(pybind11::init<>()) + // methods + .def("setProcessName", &psProcessModel::setProcessName) + .def("getProcessName", &psProcessModel::getProcessName) + .def("getSurfaceModel", &psProcessModel::getSurfaceModel) + .def("getAdvectionCallback", &psProcessModel::getAdvectionCallback) + .def("getGeometricModel", &psProcessModel::getGeometricModel) + .def("getVelocityField", &psProcessModel::getVelocityField) + .def("getParticleLogSize", &psProcessModel::getParticleLogSize) + .def("getParticleTypes", + [](psProcessModel &pm) { + // Get smart pointer to vector of unique_ptr from the process + // model + auto unique_ptrs_sp = pm.getParticleTypes(); + + // Dereference the smart pointer to access the vector + auto &unique_ptrs = *unique_ptrs_sp; + + // Create vector to hold shared_ptr + std::vector>> shared_ptrs; + + // Loop over unique_ptrs and create shared_ptrs from them + for (auto &uptr : unique_ptrs) { + shared_ptrs.push_back( + std::shared_ptr>(uptr.release())); + } + + // Return the new vector of shared_ptr + return shared_ptrs; + }) + .def("setSurfaceModel", + [](psProcessModel &pm, psSmartPointer> &sm) { + pm.setSurfaceModel(sm); + }) + .def("setAdvectionCallback", + [](psProcessModel &pm, + psSmartPointer> &ac) { + pm.setAdvectionCallback(ac); + }) + .def("insertNextParticleType", + [](psProcessModel &pm, + psSmartPointer> &passedParticle) { + if (passedParticle) { + auto particle = + std::make_unique>(*passedParticle.get()); + pm.insertNextParticleType(particle); + } + }) + // IMPORTANT: here it may be needed to write this function for any + // type of passed Particle + .def("setGeometricModel", + [](psProcessModel &pm, + psSmartPointer> &gm) { + pm.setGeometricModel(gm); + }) + .def("setVelocityField", [](psProcessModel &pm, + psSmartPointer> &vf) { + pm.setVelocityField>(vf); + }); + + // psProcess + pybind11::class_, psSmartPointer>>(module, + "Process") + // constructors + .def(pybind11::init(&psSmartPointer>::New<>)) + + // methods + .def("setDomain", &psProcess::setDomain, "Set the process domain.") + .def("setProcessDuration", &psProcess::setProcessDuration, + "Set the process duration.") + .def("setSourceDirection", &psProcess::setSourceDirection, + "Set source direction of the process.") + .def("setNumberOfRaysPerPoint", &psProcess::setNumberOfRaysPerPoint, + "Set the number of rays to traced for each particle in the process. " + "The number is per point in the process geometry") + .def("setMaxCoverageInitIterations", + &psProcess::setMaxCoverageInitIterations, + "Set the number of iterations to initialize the coverages.") + .def("setPrintTimeInterval", &psProcess::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("setProcessModel", + &psProcess::setProcessModel>, + "Set the process model.") + .def("apply", &psProcess::apply, "Run the process.") + .def("setIntegrationScheme", &psProcess::setIntegrationScheme, + "Set the integration scheme for solving the level-set equation. " + "Should be used out of the ones specified in " + "lsIntegrationSchemeEnum.") + .def("setTimeStepRatio", &psProcess::setTimeStepRatio, + "Set the CFL condition to use during advection. The CFL condition " + "sets the maximum distance a surface can be moved during one " + "advection step. It MUST be below 0.5 to guarantee numerical " + "stability. Defaults to 0.4999."); + + // psAdvectionCallback + pybind11::class_, + psSmartPointer>, + PyAdvectionCallback>(module, "AdvectionCallback") + // constructors + .def(pybind11::init<>()) + // methods + .def("applyPreAdvect", &psAdvectionCallback::applyPreAdvect) + .def("applyPostAdvect", &psAdvectionCallback::applyPostAdvect) + .def_readwrite("domain", &PyAdvectionCallback::domain); + + // enums + pybind11::enum_(module, "rayTraceDirection") + .value("POS_X", rayTraceDirection::POS_X) + .value("POS_Y", rayTraceDirection::POS_Y) + .value("POS_Z", rayTraceDirection::POS_Z) + .value("NEG_X", rayTraceDirection::NEG_X) + .value("NEG_Y", rayTraceDirection::NEG_Y) + .value("NEG_Z", rayTraceDirection::NEG_Z); + + /**************************************************************************** + * GEOMETRIES * + ****************************************************************************/ + + // constructors with custom enum need lambda to work: seems to be an issue + // with implicit move constructor + + // psMakePlane + pybind11::class_, psSmartPointer>>( + module, "MakePlane") + .def(pybind11::init([](DomainType Domain, const T GridDelta, + const T XExtent, const T YExtent, const T Height, + const bool Periodic, const psMaterial Material) { + return psSmartPointer>::New( + Domain, GridDelta, XExtent, YExtent, Height, Periodic, + Material); + }), + pybind11::arg("domain"), pybind11::arg("gridDelta"), + pybind11::arg("xExtent"), pybind11::arg("yExtent"), + pybind11::arg("height") = 0., + pybind11::arg("periodicBoundary") = false, + pybind11::arg("material") = psMaterial::Undefined) + .def(pybind11::init( + [](DomainType Domain, T Height, const psMaterial Material) { + return psSmartPointer>::New(Domain, Height, + Material); + }), + pybind11::arg("domain"), pybind11::arg("height") = 0., + pybind11::arg("material") = psMaterial::Undefined) + .def("apply", &psMakePlane::apply, + "Create a plane geometry or add plane to existing geometry."); + + // psMakeTrench + pybind11::class_, psSmartPointer>>( + module, "MakeTrench") + .def(pybind11::init([](DomainType Domain, const T GridDelta, + const T XExtent, const T YExtent, + const T TrenchWidth, const T TrenchDepth, + const T TaperingAngle, const T BaseHeight, + const bool PeriodicBoundary, const bool MakeMask, + const psMaterial Material) { + return psSmartPointer>::New( + Domain, GridDelta, XExtent, YExtent, TrenchWidth, TrenchDepth, + TaperingAngle, BaseHeight, PeriodicBoundary, MakeMask, + Material); + }), + pybind11::arg("domain"), pybind11::arg("gridDelta"), + pybind11::arg("xExtent"), pybind11::arg("yExtent"), + pybind11::arg("trenchWidth"), pybind11::arg("trenchDepth"), + pybind11::arg("taperingAngle") = 0., + pybind11::arg("baseHeight") = 0., + pybind11::arg("periodicBoundary") = false, + pybind11::arg("makeMask") = false, + pybind11::arg("material") = psMaterial::Undefined) + .def("apply", &psMakeTrench::apply, "Create a trench geometry."); + + // psMakeHole + pybind11::class_, psSmartPointer>>( + module, "MakeHole") + .def(pybind11::init([](DomainType domain, const T GridDelta, + const T xExtent, const T yExtent, + const T HoleRadius, const T HoleDepth, + const T TaperingAngle, const T BaseHeight, + const bool PeriodicBoundary, const bool MakeMask, + const psMaterial material) { + return psSmartPointer>::New( + domain, GridDelta, xExtent, yExtent, HoleRadius, HoleDepth, + TaperingAngle, BaseHeight, PeriodicBoundary, MakeMask, + material); + }), + pybind11::arg("domain"), pybind11::arg("gridDelta"), + pybind11::arg("xExtent"), pybind11::arg("yExtent"), + pybind11::arg("holeRadius"), pybind11::arg("holeDepth"), + pybind11::arg("taperingAngle") = 0., + pybind11::arg("baseHeight") = 0., + pybind11::arg("periodicBoundary") = false, + pybind11::arg("makeMask") = false, + pybind11::arg("material") = psMaterial::Undefined) + .def("apply", &psMakeHole::apply, "Create a hole geometry."); + + // psMakeFin + pybind11::class_, psSmartPointer>>(module, + "MakeFin") + .def(pybind11::init([](DomainType Domain, const T gridDelta, + const T xExtent, const T yExtent, const T FinWidth, + const T FinHeight, const T BaseHeight, + const bool PeriodicBoundary, const bool MakeMask, + const psMaterial material) { + return psSmartPointer>::New( + Domain, gridDelta, xExtent, yExtent, FinWidth, FinHeight, + BaseHeight, PeriodicBoundary, MakeMask, material); + }), + pybind11::arg("domain"), pybind11::arg("gridDelta"), + pybind11::arg("xExtent"), pybind11::arg("yExtent"), + pybind11::arg("finWidth"), pybind11::arg("finHeight"), + pybind11::arg("baseHeight") = 0., + pybind11::arg("periodicBoundary") = false, + pybind11::arg("makeMask") = false, + pybind11::arg("material") = psMaterial::Undefined) + .def("apply", &psMakeFin::apply, "Create a fin geometry."); + + // psMakeStack + pybind11::class_, psSmartPointer>>( + module, "MakeStack") + .def(pybind11::init( + &psSmartPointer>::New< + DomainType &, const T /*gridDelta*/, const T /*xExtent*/, + const T /*yExtent*/, const int /*numLayers*/, + const T /*layerHeight*/, const T /*substrateHeight*/, + const T /*holeRadius*/, const T /*maskHeight*/, + const bool /*PeriodicBoundary*/>), + pybind11::arg("domain"), pybind11::arg("gridDelta"), + pybind11::arg("xExtent"), pybind11::arg("yExtent"), + pybind11::arg("numLayers"), pybind11::arg("layerHeight"), + pybind11::arg("substrateHeight"), pybind11::arg("holeRadius"), + pybind11::arg("maskHeight"), + pybind11::arg("periodicBoundary") = false) + .def("apply", &psMakeStack::apply, + "Create a stack of alternating SiO2 and Si3N4 layers.") + .def("getTopLayer", &psMakeStack::getTopLayer, + "Returns the number of layers included in the stack") + .def("getHeight", &psMakeStack::getHeight, + "Returns the total height of the stack."); + + /**************************************************************************** + * MODELS * + ****************************************************************************/ + // Simple Deposition + pybind11::class_, + psSmartPointer>>( + module, "SimpleDeposition", processModel) + .def(pybind11::init( + &psSmartPointer>::New), + pybind11::arg("stickingProbability") = 0.1, + pybind11::arg("sourceExponent") = 1.); + + // TEOS Deposition + pybind11::class_, psSmartPointer>>( + module, "TEOSDeposition", processModel) + .def(pybind11::init( + &psSmartPointer>::New< + const T /*st1*/, const T /*rate1*/, const T /*order1*/, + const T /*st2*/, const T /*rate2*/, const T /*order2*/>), + pybind11::arg("stickingProbabilityP1"), pybind11::arg("rateP1"), + pybind11::arg("orderP1"), + pybind11::arg("stickingProbabilityP2") = 0., + pybind11::arg("rateP2") = 0., pybind11::arg("orderP2") = 0.); + + // SF6O2 Etching + pybind11::class_, psSmartPointer>>( + module, "SF6O2Etching", processModel) + .def(pybind11::init( + &psSmartPointer>::New< + const double /*ionFlux*/, const double /*etchantFlux*/, + const double /*oxygenFlux*/, const T /*meanIonEnergy*/, + const T /*sigmaIonEnergy*/, const T /*ionExponent*/, + const T /*oxySputterYield*/, const T /*etchStopDepth*/>), + pybind11::arg("ionFlux"), pybind11::arg("etchantFlux"), + pybind11::arg("oxygenFlux"), pybind11::arg("meanIonEnergy") = 100., + pybind11::arg("sigmaIonEnergy") = 10., + pybind11::arg("ionExponent") = 100., + pybind11::arg("oxySputterYield") = 3., + pybind11::arg("etchStopDepth") = std::numeric_limits::lowest()); + + // Fluorocarbon Etching + pybind11::class_, + psSmartPointer>>( + module, "FluorocarbonEtching", processModel) + .def( + pybind11::init(&psSmartPointer>::New< + const double /*ionFlux*/, const double /*etchantFlux*/, + const double /*polyFlux*/, T /*meanEnergy*/, + const T /*sigmaEnergy*/, const T /*ionExponent*/, + const T /*deltaP*/, const T /*etchStopDepth*/>), + pybind11::arg("ionFlux"), pybind11::arg("etchantFlux"), + pybind11::arg("polyFlux"), pybind11::arg("meanIonEnergy") = 100., + pybind11::arg("sigmaIonEnergy") = 10., + pybind11::arg("ionExponent") = 100., pybind11::arg("deltaP") = 0., + pybind11::arg("etchStopDepth") = std::numeric_limits::lowest()); + + // Isotropic Process + pybind11::class_, + psSmartPointer>>( + module, "IsotropicProcess", processModel) + .def(pybind11::init([](const T rate, const psMaterial mask) { + return psSmartPointer>::New(rate, mask); + }), + pybind11::arg("isotropic rate"), + pybind11::arg("mask material") = psMaterial::Mask); + + // Directional Etching + pybind11::class_, + psSmartPointer>>( + module, "DirectionalEtching", processModel) + .def(pybind11::init([](const std::array &direction, const T dirVel, + const T isoVel, const psMaterial mask) { + return psSmartPointer>::New( + direction, dirVel, isoVel, mask); + }), + pybind11::arg("direction"), + pybind11::arg("directionalVelocity") = 1., + pybind11::arg("isotropicVelocity") = 0., + pybind11::arg("mask material") = psMaterial::Mask); + + // Sphere Distribution + pybind11::class_, + psSmartPointer>>( + module, "SphereDistribution", processModel) + .def(pybind11::init([](const T radius, const T gridDelta, + psSmartPointer> mask) { + return psSmartPointer>::New( + radius, gridDelta, mask); + }), + pybind11::arg("radius"), pybind11::arg("gridDelta"), + pybind11::arg("mask")) + .def(pybind11::init([](const T radius, const T gridDelta) { + return psSmartPointer>::New( + radius, gridDelta, nullptr); + }), + pybind11::arg("radius"), pybind11::arg("gridDelta")); + + // Box Distribution + pybind11::class_, + psSmartPointer>>( + module, "BoxDistribution", processModel) + .def( + pybind11::init([](const std::array &halfAxes, const T gridDelta, + psSmartPointer> mask) { + return psSmartPointer>::New(halfAxes, + gridDelta, mask); + }), + pybind11::arg("halfAxes"), pybind11::arg("gridDelta"), + pybind11::arg("mask")) + .def(pybind11::init( + [](const std::array &halfAxes, const T gridDelta) { + return psSmartPointer>::New( + halfAxes, gridDelta, nullptr); + }), + pybind11::arg("halfAxes"), pybind11::arg("gridDelta")); + + // Plasma Damage + pybind11::class_, psSmartPointer>>( + module, "PlasmaDamage", processModel) + .def(pybind11::init( + &psSmartPointer>::New), + pybind11::arg("ionEnergy") = 100., + pybind11::arg("meanFreePath") = 1., + pybind11::arg("maskMaterial") = 0); + + // Oxide Regrowth + pybind11::class_, + psSmartPointer>>( + module, "OxideRegrowthModel", processModel) + .def(pybind11::init(&psSmartPointer>::New< + const T, const T, const T, const T, const T, const T, + const T, const T, const T, const T, const T>), + pybind11::arg("nitrideEtchRate"), pybind11::arg("oxideEtchRate"), + pybind11::arg("redepositionRate"), + pybind11::arg("redepositionThreshold"), + pybind11::arg("redepositionTimeInt"), + pybind11::arg("diffusionCoefficient"), pybind11::arg("sinkStrength"), + pybind11::arg("scallopVelocity"), pybind11::arg("centerVelocity"), + pybind11::arg("topHeight"), pybind11::arg("centerWidth")); + + pybind11::class_, psSmartPointer>>( + module, "Planarize") + .def(pybind11::init( + &psSmartPointer>::New), + pybind11::arg("geometry"), pybind11::arg("cutoffHeight") = 0.) + .def("apply", &psPlanarize::apply, "Apply the planarization."); + +#if VIENNAPS_PYTHON_DIMENSION > 2 + pybind11::class_, psSmartPointer>>( + module, "WetEtching", processModel) + .def(pybind11::init(&psSmartPointer>::New), + pybind11::arg("maskId") = 0); + + // GDS file parsing + pybind11::class_, psSmartPointer>>( + module, "GDSGeometry") + // constructors + .def(pybind11::init(&psSmartPointer>::New<>)) + .def(pybind11::init(&psSmartPointer>::New), + pybind11::arg("gridDelta")) + // methods + .def("setGridDelta", &psGDSGeometry::setGridDelta, + "Set the grid spacing.") + .def( + "setBoundaryConditions", + [](psGDSGeometry &gds, + std::vector::BoundaryType> &bcs) { + if (bcs.size() == D) + gds.setBoundaryConditions(bcs.data()); + }, + "Set the boundary conditions") + .def("setBoundaryPadding", &psGDSGeometry::setBoundaryPadding, + "Set padding between the largest point of the geometry and the " + "boundary of the domain.") + .def("print", &psGDSGeometry::print, "Print the geometry contents.") + .def("layerToLevelSet", &psGDSGeometry::layerToLevelSet, + "Convert a layer of the GDS geometry to a level set domain.") + .def( + "getBounds", + [](psGDSGeometry &gds) -> std::array { + auto b = gds.getBounds(); + std::array bounds; + for (unsigned i = 0; i < 6; ++i) + bounds[i] = b[i]; + return bounds; + }, + "Get the bounds of the geometry."); + + pybind11::class_, psSmartPointer>>( + module, "GDSReader") + // constructors + .def(pybind11::init(&psSmartPointer>::New<>)) + .def(pybind11::init(&psSmartPointer>::New< + psSmartPointer> &, std::string>)) + // methods + .def("setGeometry", &psGDSReader::setGeometry, + "Set the domain to be parsed in.") + .def("setFileName", &psGDSReader::setFileName, + "Set name of the GDS file.") + .def("apply", &psGDSReader::apply, "Parse the GDS file."); +#else + // wrap a 3D domain in 2D mode to be used with psExtrude + // psDomain + pybind11::class_, psSmartPointer>>(module, + "Domain3D") + // constructors + .def(pybind11::init()) + .def(pybind11::init(&psSmartPointer>::New<>)) + // methods + .def("insertNextLevelSet", &psDomain::insertNextLevelSet, + pybind11::arg("levelset"), pybind11::arg("wrapLowerLevelSet") = true, + "Insert a level set to domain.") + .def("insertNextLevelSetAsMaterial", + &psDomain::insertNextLevelSetAsMaterial) + .def("duplicateTopLevelSet", &psDomain::duplicateTopLevelSet) + .def("setMaterialMap", &psDomain::setMaterialMap) + .def("getMaterialMap", &psDomain::getMaterialMap) + .def("generateCellSet", &psDomain::generateCellSet, + "Generate the cell set.") + .def("getLevelSets", + [](psDomain &d) + -> std::optional>>> { + auto levelsets = d.getLevelSets(); + if (levelsets) + return *levelsets; + return std::nullopt; + }) + .def("getCellSet", &psDomain::getCellSet, "Get the cell set.") + .def("getGrid", &psDomain::getGrid, "Get the grid") + .def("setUseCellSet", &psDomain::setUseCellSet) + .def("getUseCellSet", &psDomain::getUseCellSet) + .def("print", &psDomain::print) + .def("printSurface", &psDomain::printSurface, + pybind11::arg("filename"), pybind11::arg("addMaterialIds") = true, + "Print the surface of the domain.") + .def("writeLevelSets", &psDomain::writeLevelSets) + .def("clear", &psDomain::clear); + + pybind11::class_>(module, "Extrude") + .def(pybind11::init()) + .def(pybind11::init> &, + psSmartPointer> &, std::array, + const int, + std::array, 3>>(), + pybind11::arg("inputDomain"), pybind11::arg("outputDomain"), + pybind11::arg("extent"), pybind11::arg("extrudeDimension"), + pybind11::arg("boundaryConditions")) + .def("setInputDomain", &psExtrude::setInputDomain, + "Set the input domain to be extruded.") + .def("setOutputDomain", &psExtrude::setOutputDomain, + "Set the output domain. The 3D output domain will be overwritten by " + "the extruded domain.") + .def("setExtent", &psExtrude::setExtent, + "Set the min and max extent in the extruded dimension.") + .def("setExtrudeDimension", &psExtrude::setExtrudeDimension, + "Set which index of the added dimension (x: 0, y: 1, z: 2).") + .def("setBoundaryConditions", + pybind11::overload_cast, 3>>( + &psExtrude::setBoundaryConditions), + "Set the boundary conditions in the extruded domain.") + .def("apply", &psExtrude::apply, "Run the extrusion."); + +#endif + + // rayReflection.hpp + module.def("rayReflectionSpecular", &rayReflectionSpecular, + "Specular reflection,"); + module.def("rayReflectionDiffuse", &rayReflectionDiffuse, + "Diffuse reflection."); + module.def("rayReflectionConedCosine", &rayReflectionConedCosine, + "Coned cosine reflection."); +} \ No newline at end of file diff --git a/Python/pyWrap.hpp b/Python/pyWrap.hpp new file mode 100644 index 00000000..8a15680a --- /dev/null +++ b/Python/pyWrap.hpp @@ -0,0 +1,312 @@ +#pragma once + +#define PYBIND11_DETAILED_ERROR_MESSAGES +#define VIENNAPS_PYTHON_BUILD + +// correct module name macro +#define TOKENPASTE_INTERNAL(x, y, z) x##y##z +#define TOKENPASTE(x, y, z) TOKENPASTE_INTERNAL(x, y, z) +#define STRINGIZE2(s) #s +#define STRINGIZE(s) STRINGIZE2(s) +#define VIENNAPS_MODULE_VERSION STRINGIZE(VIENNAPS_VERSION) + +#include +#include + +#include +#include +#include +#include +#include + +// all header files which define API functions +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// geometries +#include +#include +#include +#include +#include + +// visualization +#include +#include +#include + +// models +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// CellSet +#include + +// Compact +#include + +// other +#include +#include +#include +#include +#include + +// always use double for python export +typedef double T; +typedef std::vector VectorHRLEcoord; +// get dimension from cmake define +constexpr int D = VIENNAPS_PYTHON_DIMENSION; +typedef psSmartPointer> DomainType; + +PYBIND11_DECLARE_HOLDER_TYPE(Types, psSmartPointer) +PYBIND11_MAKE_OPAQUE(std::vector>) + +PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr) + +// define trampoline classes for interface functions +// ALSO NEED TO ADD TRAMPOLINE CLASSES FOR CLASSES +// WHICH HOLD REFERENCES TO INTERFACE(ABSTRACT) CLASSES + +class PypsSurfaceModel : public psSurfaceModel { + using psSurfaceModel::Coverages; + using psSurfaceModel::processParams; + using psSurfaceModel::getCoverages; + using psSurfaceModel::getProcessParameters; + typedef std::vector vect_type; + +public: + void initializeCoverages(unsigned numGeometryPoints) override { + PYBIND11_OVERRIDE(void, psSurfaceModel, initializeCoverages, + numGeometryPoints); + } + + void initializeProcessParameters() override { + PYBIND11_OVERRIDE(void, psSurfaceModel, initializeProcessParameters, ); + } + + psSmartPointer> + calculateVelocities(psSmartPointer> Rates, + const std::vector> &coordinates, + const std::vector &materialIds) override { + PYBIND11_OVERRIDE(psSmartPointer>, psSurfaceModel, + calculateVelocities, Rates, coordinates, materialIds); + } + + void updateCoverages(psSmartPointer> Rates, + const std::vector &materialIds) override { + PYBIND11_OVERRIDE(void, psSurfaceModel, updateCoverages, Rates, + materialIds); + } +}; + +// psAdvectionCallback +class PyAdvectionCallback : public psAdvectionCallback { +protected: + using ClassName = psAdvectionCallback; + +public: + using ClassName::domain; + + bool applyPreAdvect(const T processTime) override { + PYBIND11_OVERRIDE(bool, ClassName, applyPreAdvect, processTime); + } + + bool applyPostAdvect(const T advectionTime) override { + PYBIND11_OVERRIDE(bool, ClassName, applyPostAdvect, advectionTime); + } +}; + +// Particle Class +template class psParticle : public rayParticle, T> { + using ClassName = rayParticle, T>; + +public: + void surfaceCollision(T rayWeight, const rayTriple &rayDir, + const rayTriple &geomNormal, + const unsigned int primID, const int materialID, + rayTracingData &localData, + const rayTracingData *globalData, + rayRNG &Rng) override final { + PYBIND11_OVERRIDE(void, ClassName, surfaceCollision, rayWeight, rayDir, + geomNormal, primID, materialID, localData, globalData, + Rng); + } + + std::pair> + surfaceReflection(T rayWeight, const rayTriple &rayDir, + const rayTriple &geomNormal, const unsigned int primID, + const int materialID, const rayTracingData *globalData, + rayRNG &Rng) override final { + using Pair = std::pair>; + PYBIND11_OVERRIDE(Pair, ClassName, surfaceReflection, rayWeight, rayDir, + geomNormal, primID, materialID, globalData, Rng); + } + + void initNew(rayRNG &RNG) override final { + PYBIND11_OVERRIDE(void, ClassName, initNew, RNG); + } + + T getSourceDistributionPower() const override final { + PYBIND11_OVERRIDE(T, ClassName, getSourceDistributionPower); + } + + std::vector getLocalDataLabels() const override final { + PYBIND11_OVERRIDE(std::vector, ClassName, getLocalDataLabels); + } +}; + +// Default particle classes +template +class psDiffuseParticle : public rayParticle, T> { + using ClassName = rayParticle, T>; + +public: + psDiffuseParticle(const T pStickingProbability, const T pCosineExponent, + const std::string &pDataLabel) + : stickingProbability(pStickingProbability), + cosineExponent(pCosineExponent), dataLabel(pDataLabel) {} + + void surfaceCollision(T rayWeight, const rayTriple &rayDir, + const rayTriple &geomNormal, + const unsigned int primID, const int materialID, + rayTracingData &localData, + const rayTracingData *globalData, + rayRNG &Rng) override final { + localData.getVectorData(0)[primID] += rayWeight; + } + + std::pair> + surfaceReflection(T rayWeight, const rayTriple &rayDir, + const rayTriple &geomNormal, const unsigned int primID, + const int materialID, const rayTracingData *globalData, + rayRNG &Rng) override final { + auto direction = rayReflectionDiffuse(geomNormal, Rng); + return {stickingProbability, direction}; + } + + void initNew(rayRNG &RNG) override final {} + + T getSourceDistributionPower() const override final { return cosineExponent; } + + std::vector getLocalDataLabels() const override final { + return {dataLabel}; + } + +private: + const T stickingProbability = 1.; + const T cosineExponent = 1.; + const std::string dataLabel = "flux"; +}; + +class psSpecularParticle : public rayParticle { + using ClassName = rayParticle; + +public: + psSpecularParticle(const T pStickingProbability, const T pCosineExponent, + const std::string &pDataLabel) + : stickingProbability(pStickingProbability), + cosineExponent(pCosineExponent), dataLabel(pDataLabel) {} + + void surfaceCollision(T rayWeight, const rayTriple &rayDir, + const rayTriple &geomNormal, + const unsigned int primID, const int materialID, + rayTracingData &localData, + const rayTracingData *globalData, + rayRNG &Rng) override final { + localData.getVectorData(0)[primID] += rayWeight; + } + + std::pair> + surfaceReflection(T rayWeight, const rayTriple &rayDir, + const rayTriple &geomNormal, const unsigned int primID, + const int materialID, const rayTracingData *globalData, + rayRNG &Rng) override final { + auto direction = rayReflectionSpecular(rayDir, geomNormal); + return {stickingProbability, direction}; + } + + void initNew(rayRNG &RNG) override final {} + + T getSourceDistributionPower() const override final { return cosineExponent; } + + std::vector getLocalDataLabels() const override final { + return {dataLabel}; + } + +private: + const T stickingProbability = 1.; + const T cosineExponent = 1.; + const std::string dataLabel = "flux"; +}; + +// psVelocityField +class PyVelocityField : public psVelocityField { + using psVelocityField::psVelocityField; + +public: + T getScalarVelocity(const std::array &coordinate, int material, + const std::array &normalVector, + unsigned long pointId) override { + PYBIND11_OVERRIDE(T, psVelocityField, getScalarVelocity, coordinate, + material, normalVector, pointId); + } + // if we declare a typedef for std::array, we will no longer get this + // error: the compiler doesn't understand why std::array gets 2 template + // arguments + // add template argument as the preprocessor becomes confused with the comma + // in std::array + typedef std::array arrayType; + std::array getVectorVelocity(const std::array &coordinate, + int material, + const std::array &normalVector, + unsigned long pointId) override { + PYBIND11_OVERRIDE( + arrayType, // add template argument here, as the preprocessor becomes + // confused with the comma in std::array + psVelocityField, getVectorVelocity, coordinate, material, + normalVector, pointId); + } + + T getDissipationAlpha(int direction, int material, + const std::array ¢ralDifferences) override { + PYBIND11_OVERRIDE(T, psVelocityField, getDissipationAlpha, direction, + material, centralDifferences); + } + void setVelocities(psSmartPointer> passedVelocities) override { + PYBIND11_OVERRIDE(void, psVelocityField, setVelocities, + passedVelocities); + } + int getTranslationFieldOptions() const override { + PYBIND11_OVERRIDE(int, psVelocityField, getTranslationFieldOptions, ); + } +}; + +// a function to declare GeometricDistributionModel of type DistType +template +void declare_GeometricDistributionModel(pybind11::module &m, + const std::string &typestr) { + using Class = GeometricDistributionModel; + + pybind11::class_>(m, typestr.c_str()) + .def(pybind11::init>(), pybind11::arg("dist")) + .def(pybind11::init, + psSmartPointer>>(), + pybind11::arg("dist"), pybind11::arg("mask")) + .def("apply", &Class::apply); +} \ No newline at end of file diff --git a/README.md b/README.md index 36bf7858..c324ef4a 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,8 @@ Releases are tagged on the master branch and available in the [releases section] * [ViennaRay](https://github.com/ViennaTools/viennaray) +* [pybind11](https://github.com/pybind/pybind11) (only for building Python libs) + ## Installing The CMake configuration automatically checks if the dependencies are installed. If CMake is unable to find them, the dependencies will be built from source with the _buildDependencies_ target. @@ -47,6 +49,31 @@ This will install the necessary headers and CMake files to the specified path. I If one wants to use a specific installation of one or more of the dependencies, just pass the corresponding _*_DIR_ variable as a configuration option (e.g. -DViennaLS_DIR=/path/to/viennals/install -DViennaRay_DIR=/path/to/viennaray/install) +## Building the Python package + +The Python package can be built and installed using the `pip` command: + +```bash +git clone https://github.com/ViennaTools/ViennaPS.git +cd ViennaPS +pip install --user . +``` + +> Some functionalities of the ViennaPS Python module only work in combination with the ViennaLS Python module. It is therefore recommended to additionally install the ViennaLS Python module on your system. Instructions to do so can be found in the [ViennaLS Git Repository](https://github.com/ViennaTools/viennals). + +## Using the Python package + +The 2D version of the library can be imported as follows: +```python +import viennaps2d as vps +``` + +In order to switch to three dimensions, only the import needs to be changed: + +```python +import viennaps3d as vps +``` + ## Integration in CMake projects In order to use this library in your CMake project, add the following lines to the CMakeLists.txt of your project: @@ -78,7 +105,7 @@ cd Examples/ExampleName ./ExampleName config.txt ``` -Individual examples can also be build by calling `make` in their respective build folder. +Individual examples can also be build by calling `make` in their respective build folder. An equivalent Python script, using the ViennaPS Python bindings, is also given for each example. ### Trench Deposition @@ -90,7 +117,7 @@ The picture show an example of the trench deposition process for various value o ### SF6O2 Hole Etching -This example demonstrates a hole etching process with a SF6O2 plasma etching chemistry with ion bombardement. The process and geometry parameters can be varied in the __config.txt__ file. +This example demonstrates a hole etching process with a SF6O2 plasma etching chemistry with ion bombardment. The process and geometry parameters can be varied in the __config.txt__ file. Below the results after 1, 2, and 3 seconds of etching are shown.
@@ -110,6 +137,8 @@ This example demonstrates capturing etching byproducts and the subsequent redepo ## Application +> __Deprecation Warning__: The ViennaPS application is no longer updated with new functionalities added to ViennaPS after release 1.2.0. Please use the Python bindings instead. + It is also possible to build an application which can parse a custom configuration file and execute pre-defined processes. The application can be built using CMake: > __Important__: Make sure all dependencies are installed and have been built previously ```bash @@ -119,7 +148,7 @@ make buildApplication ``` This creates 2 executables `ViennaPS2D` and `ViennaPS3D` which run processes in 2 or 3 dimensions respectively. Every configuration file can be run in 2D or 3D mode. -The configuration file must obey a certain structure in order to be parsed correctly. An example for a configuration file can be seen in _SampleConfig.txt_. The configuration file is parsed line by line and each succesfully parsed line is executed immediately. A detailed documentation for the configuration file can be found in **app/README.md**. +The configuration file must obey a certain structure in order to be parsed correctly. An example for a configuration file can be seen in _SampleConfig.txt_. The configuration file is parsed line by line and each successfully parsed line is executed immediately. A detailed documentation for the configuration file can be found in **app/README.md**. ## Contributing diff --git a/app/Application2D.cpp b/app/Application.cpp similarity index 64% rename from app/Application2D.cpp rename to app/Application.cpp index e4eb56f5..608b433e 100644 --- a/app/Application2D.cpp +++ b/app/Application.cpp @@ -2,7 +2,7 @@ int main(int argc, char **argv) { - Application<2> app(argc, argv); + Application app(argc, argv); app.run(); return 0; diff --git a/app/Application.hpp b/app/Application.hpp index e0c66dcf..901f0178 100644 --- a/app/Application.hpp +++ b/app/Application.hpp @@ -4,25 +4,31 @@ #include #include -#include + +// #include #include #include #include #include +#include #include #include #include #include +#include #include #include +#include #include +#include #include #include #include #include +#include #include template class Application { @@ -37,7 +43,7 @@ template class Application { void run() { if (clArgC < 2) { - std::cout << "No input file specified. " << std::endl; + psLogger::getInstance().addError("No input file specified.").print(); return; } @@ -45,7 +51,7 @@ template class Application { inputFile.open(clArgV[1], std::fstream::in); if (!inputFile.is_open()) { - std::cout << "Could not open input file." << std::endl; + psLogger::getInstance().addError("Could not open input file.").print(); return; } @@ -77,7 +83,7 @@ template class Application { break; case CommandType::OUTPUT: - writeVTP(); + writeOutput(); break; case CommandType::NONE: @@ -92,25 +98,50 @@ template class Application { } } + void printGeometry(std::string fileName) { + params->fileName = fileName; + writeOutput(); + } + protected: virtual void runSimpleDeposition(psSmartPointer> processGeometry, psSmartPointer processParams) { + // copy top layer for deposition - auto depoLayer = psSmartPointer>::New( - processGeometry->getLevelSets()->back()); - processGeometry->insertNextLevelSet(depoLayer); + processGeometry->duplicateTopLevelSet(processParams->material); auto model = psSmartPointer>::New( - processParams->sticking, processParams->cosinePower); + processParams->rate, processParams->sticking, + processParams->cosinePower); psProcess process; process.setDomain(processGeometry); process.setProcessModel(model); + process.setSmoothFlux(processParams->smoothFlux); process.setNumberOfRaysPerPoint(processParams->raysPerPoint); - process.setProcessDuration(processParams->processTime * - processParams->rate / processParams->sticking); - process.setPrintTimeInterval(params->printTimeInterval); + process.setProcessDuration(processParams->processTime); + process.setIntegrationScheme(params->integrationScheme); + process.apply(); + } + + virtual void + runTEOSDeposition(psSmartPointer> processGeometry, + psSmartPointer processParams) { + // copy top layer for deposition + processGeometry->duplicateTopLevelSet(processParams->material); + + auto model = psSmartPointer>::New( + processParams->stickingP1, processParams->rateP1, + processParams->orderP1, processParams->stickingP2, + processParams->rateP2, processParams->orderP2); + + psProcess process; + process.setDomain(processGeometry); + process.setProcessModel(model); + process.setSmoothFlux(processParams->smoothFlux); + process.setNumberOfRaysPerPoint(processParams->raysPerPoint); + process.setProcessDuration(processParams->processTime); process.setIntegrationScheme(params->integrationScheme); process.apply(); } @@ -119,17 +150,38 @@ template class Application { runSF6O2Etching(psSmartPointer> processGeometry, psSmartPointer processParams) { auto model = psSmartPointer>::New( - processParams->totalIonFlux, processParams->totalEtchantFlux, - processParams->totalOxygenFlux, processParams->ionEnergy, + processParams->ionFlux, processParams->etchantFlux, + processParams->oxygenFlux, processParams->ionEnergy, + processParams->sigmaIonEnergy, processParams->ionExponent, processParams->A_O); psProcess process; process.setDomain(processGeometry); process.setProcessModel(model); process.setMaxCoverageInitIterations(10); + process.setSmoothFlux(processParams->smoothFlux); + process.setNumberOfRaysPerPoint(processParams->raysPerPoint); + process.setProcessDuration(processParams->processTime); + process.setIntegrationScheme(params->integrationScheme); + process.apply(); + } + + virtual void runFluorocarbonEtching( + psSmartPointer> processGeometry, + psSmartPointer processParams) { + auto model = psSmartPointer>::New( + processParams->ionFlux, processParams->etchantFlux, + processParams->oxygenFlux, processParams->ionEnergy, + processParams->sigmaIonEnergy, processParams->ionExponent, + processParams->deltaP); + + psProcess process; + process.setDomain(processGeometry); + process.setProcessModel(model); + process.setMaxCoverageInitIterations(10); + process.setSmoothFlux(processParams->smoothFlux); process.setNumberOfRaysPerPoint(processParams->raysPerPoint); process.setProcessDuration(processParams->processTime); - process.setPrintTimeInterval(params->printTimeInterval); process.setIntegrationScheme(params->integrationScheme); process.apply(); } @@ -143,7 +195,6 @@ template class Application { psProcess process; process.setDomain(processGeometry); process.setProcessModel(model); - process.setPrintTimeInterval(params->printTimeInterval); process.setIntegrationScheme(params->integrationScheme); process.apply(); } @@ -157,7 +208,6 @@ template class Application { psProcess process; process.setDomain(processGeometry); process.setProcessModel(model); - process.setPrintTimeInterval(params->printTimeInterval); process.setIntegrationScheme(params->integrationScheme); process.apply(); } @@ -168,13 +218,12 @@ template class Application { auto model = psSmartPointer>::New( getDirection(processParams->direction), processParams->directionalRate, - processParams->isotropicRate, processParams->maskId); + processParams->isotropicRate, processParams->maskMaterial); psProcess process; process.setDomain(processGeometry); process.setProcessModel(model); process.setProcessDuration(params->processTime); - process.setPrintTimeInterval(params->printTimeInterval); process.setIntegrationScheme(params->integrationScheme); process.apply(); } @@ -185,19 +234,16 @@ template class Application { if (params->rate > 0.) { // copy top layer for deposition - auto depoLayer = psSmartPointer>::New( - processGeometry->getLevelSets()->back()); - processGeometry->insertNextLevelSet(depoLayer); + processGeometry->duplicateTopLevelSet(processParams->material); } auto model = psSmartPointer>::New( - processParams->rate, processParams->maskId); + processParams->rate, processParams->maskMaterial); psProcess process; process.setDomain(processGeometry); process.setProcessModel(model); process.setProcessDuration(params->processTime); - process.setPrintTimeInterval(params->printTimeInterval); process.setIntegrationScheme(params->integrationScheme); process.apply(); } @@ -216,11 +262,11 @@ template class Application { process.setProcessDuration(params->processTime); process.setIntegrationScheme( lsIntegrationSchemeEnum::STENCIL_LOCAL_LAX_FRIEDRICHS_1ST_ORDER); - process.setPrintTimeInterval(params->printTimeInterval); process.apply(); } else { - std::cout << "Warning: Wet etch model only implemented in 3D." - << std::endl; + psLogger::getInstance() + .addError("Warning: Wet etch model only implemented in 3D.") + .print(); } } @@ -229,8 +275,7 @@ template class Application { std::cout << "\tx-Extent: " << params->xExtent << "\n\ty-Extent: " << params->yExtent << "\n\tResolution: " << params->gridDelta - << "\n\tPrint intermediate: " - << boolString(params->printTimeInterval) + << "\n\tLog level: " << params->logLevel << "\n\tPeriodic boundary: " << boolString(params->periodicBoundary) << "\n\tUsing integration scheme: " @@ -285,6 +330,21 @@ template class Application { .apply(); } break; + + case GeometryType::STACK: + std::cout << "Stack\n\tNumber of layers: " << params->numLayers + << "\n\tLayer height: " << params->layerHeight + << "\n\tSubstrate height: " << params->substrateHeight + << "\n\tHole radius: " << params->holeRadius + << "\n\tMask height: " << params->maskHeight << "\n\n"; + psMakeStack( + geometry, params->gridDelta, params->xExtent, params->yExtent, + params->numLayers, params->layerHeight, params->substrateHeight, + params->holeRadius, params->maskHeight, params->periodicBoundary) + .apply(); + + break; + case GeometryType::GDS: { std::cout << "GDS file import\n\tFile name: " << params->fileName << "\n\tLayer: " << params->layers @@ -319,8 +379,9 @@ template class Application { params->maskHeight, params->maskInvert); geometry->insertNextLevelSetAsMaterial(layer, params->material); } else { - std::cout << "Warning: Can only parse GDS geometries in 3D application." - << std::endl; + psLogger::getInstance() + .addError("Can only parse GDS geometries in 3D application.") + .print(); } break; } @@ -356,40 +417,78 @@ template class Application { void runProcess() { if (geometry->getLevelSets()->empty()) { - std::cout << "Cannot run process on empty geometry." << std::endl; + psLogger::getInstance() + .addError("Cannot run process on empty geometry.") + .print(); return; } std::cout << "\tModel: "; switch (params->processType) { - case ProcessType::DEPOSITION: { + case ProcessType::SIMPLEDEPOSITION: { std::cout << "Single particle deposition\n\tRate: " << params->rate << "\n\tTime: " << params->processTime + << "\n\tMaterial: " << materialString(params->material) << "\n\tSticking probability: " << params->sticking << "\n\tCosine exponent: " << params->cosinePower << "\n\tUsing " << params->raysPerPoint << " rays per source grid point\n\n"; - - // copy top layer to capture deposition - auto topLayerCopy = psSmartPointer>::New( - geometry->getLevelSets()->back()); - geometry->insertNextLevelSet(topLayerCopy); runSimpleDeposition(geometry, params); break; } + case ProcessType::TEOSDEPOSITION: { + if (params->rateP2 != 0.) { + std::cout << "Multi particle TEOS deposition" + << "\n\tP1 rate: " << params->rateP1 + << "\n\tP1 sticking probability: " << params->stickingP1 + << "\n\tP2 reaction order: " << params->orderP1 + << "\n\tP2 rate: " << params->rateP2 + << "\n\tP2 sticking probability: " << params->stickingP2 + << "\n\tP2 reaction order: " << params->orderP2 + << "\n\tTime: " << params->processTime + << "\n\tMaterial: " << materialString(params->material) + << "\n\tUsing " << params->raysPerPoint + << " rays per source grid point\n\n"; + } else { + std::cout << "Single particle TEOS deposition\n\tRate: " + << params->rateP1 + << "\n\tSticking probability: " << params->stickingP1 + << "\n\tReaction order: " << params->orderP1 + << "\n\tTime: " << params->processTime + << "\n\tMaterial: " << materialString(params->material) + << "\n\tUsing " << params->raysPerPoint + << " rays per source grid point\n\n"; + } + runTEOSDeposition(geometry, params); + break; + } + case ProcessType::SF6O2ETCHING: { std::cout << "SF6O2 etching\n\tTime: " << params->processTime - << "\n\tEtchant flux: " << params->totalEtchantFlux - << "\n\tOxygen flux: " << params->totalOxygenFlux - << "\n\tIon flux: " << params->totalIonFlux + << "\n\tEtchant flux: " << params->etchantFlux + << "\n\tOxygen flux: " << params->oxygenFlux + << "\n\tIon flux: " << params->ionFlux << "\n\tIon energy: " << params->ionEnergy + << "\n\tIon exponent: " << params->ionExponent << "\n\tA_O: " << params->A_O << "\n\tUsing " << params->raysPerPoint << " rays per source grid point\n\n"; runSF6O2Etching(geometry, params); break; } + case ProcessType::FLUOROCARBONETCHING: { + std::cout << "Fluorocarbon etching\n\tTime: " << params->processTime + << "\n\tEtchant flux: " << params->etchantFlux + << "\n\tOxygen flux: " << params->oxygenFlux + << "\n\tIon flux: " << params->ionFlux + << "\n\tIon energy: " << params->ionEnergy + << "\n\tDelta P: " << params->deltaP << "\n\tUsing " + << params->raysPerPoint << " rays per source grid point\n\n"; + runFluorocarbonEtching(geometry, params); + break; + } + case ProcessType::SPHEREDISTRIBUTION: { std::cout << "Sphere Distribution\n\tRadius: " << params->radius << "\n\n"; @@ -431,8 +530,9 @@ template class Application { } case ProcessType::NONE: - std::cout << "Process model could not be parsed. Skipping line." - << std::endl; + psLogger::getInstance() + .addWarning("Process model could not be parsed. Skipping line.") + .print(); break; default: @@ -445,16 +545,41 @@ template class Application { psPlanarize(geometry, params->maskZPos).apply(); } - void writeVTP() { + void writeOutput() { if (geometry->getLevelSets()->empty()) { std::cout << "Cannot write empty geometry." << std::endl; return; } - std::cout << "\tOut file name: " << params->fileName << ".vtp\n\n"; - geometry->printSurface(params->fileName + ".vtp"); + + std::string outFileName = params->fileName; + if (params->out == OutputType::SURFACE) { + std::cout << "\tWriting surface ...\n"; + const std::string suffix = ".vtp"; + // check if string ends with .vtp + if (!(outFileName.size() >= suffix.size() && + 0 == outFileName.compare(outFileName.size() - suffix.size(), + suffix.size(), suffix))) { + outFileName += ".vtp"; + } + geometry->printSurface(outFileName); + } else { + std::cout << "Writing volume ...\n"; + const std::string suffix = ".vtu"; + // check if string ends with .vtu + if (!outFileName.size() >= suffix.size() && + 0 == outFileName.compare(outFileName.size() - suffix.size(), + suffix.size(), suffix)) { + outFileName.erase(outFileName.length() - 4); + } + psWriteVisualizationMesh(geometry, params->fileName) + .apply(); + outFileName += "_volume.vtu"; + } + std::cout << "\tOut file name: " << outFileName << "\n\n"; } - std::array getDirection(const std::string &directionString) { + static std::array + getDirection(const std::string &directionString) { std::array direction = {0}; if (directionString == "negZ") { @@ -476,13 +601,62 @@ template class Application { } else if (directionString == "posX") { direction[0] = 1.; } else { - std::cout << "Invalid direction: " << directionString << std::endl; + psLogger::getInstance() + .addError("Invalid direction: " + directionString) + .print(); } return direction; } - std::string boolString(const int in) { return in == 0 ? "false" : "true"; } + static inline std::string boolString(const int in) { + return in == 0 ? "false" : "true"; + } + + static std::string materialString(const psMaterial material) { + switch (material) { + case psMaterial::Undefined: + return "Undefined"; + case psMaterial::Mask: + return "Mask"; + case psMaterial::Si: + return "Si"; + case psMaterial::Si3N4: + return "Si3N4"; + case psMaterial::SiO2: + return "SiO2"; + case psMaterial::SiON: + return "SiON"; + case psMaterial::PolySi: + return "PolySi"; + case psMaterial::Polymer: + return "Polymer"; + case psMaterial::SiC: + return "SiC"; + case psMaterial::SiN: + return "SiN"; + case psMaterial::Metal: + return "Metal"; + case psMaterial::W: + return "W"; + case psMaterial::TiN: + return "TiN"; + case psMaterial::GaN: + return "GaN"; + case psMaterial::GAS: + return "GAS"; + case psMaterial::Air: + return "Air"; + case psMaterial::Al2O3: + return "Al2O3"; + case psMaterial::Dielectric: + return "Dielectric"; + case psMaterial::Cu: + return "Cu"; + } + + return "Unknown material"; + } std::string intSchemeString(lsIntegrationSchemeEnum scheme) { switch (scheme) { diff --git a/app/Application3D.cpp b/app/Application3D.cpp deleted file mode 100644 index ac757a30..00000000 --- a/app/Application3D.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "Application.hpp" - -int main(int argc, char **argv) { - - Application<3> app(argc, argv); - app.run(); - - return 0; -} \ No newline at end of file diff --git a/app/ApplicationParameters.hpp b/app/ApplicationParameters.hpp index 9b2178b8..09f7c211 100644 --- a/app/ApplicationParameters.hpp +++ b/app/ApplicationParameters.hpp @@ -3,17 +3,20 @@ #include #include +#include #include #include enum class CommandType { NONE, INIT, GEOMETRY, PROCESS, OUTPUT, PLANARIZE }; -enum class GeometryType { NONE, TRENCH, HOLE, PLANE, GDS, IMPORT }; +enum class GeometryType { NONE, TRENCH, HOLE, PLANE, STACK, GDS, IMPORT }; enum class ProcessType { NONE, SF6O2ETCHING, - DEPOSITION, + FLUOROCARBONETCHING, + SIMPLEDEPOSITION, + TEOSDEPOSITION, SPHEREDISTRIBUTION, BOXDISTRIBUTION, DIRECTIONALETCHING, @@ -21,6 +24,8 @@ enum class ProcessType { ISOTROPIC }; +enum class OutputType { SURFACE, VOLUME }; + #ifdef VIENNAPS_USE_DOUBLE using NumericType = double; #else @@ -28,7 +33,7 @@ using NumericType = float; #endif struct ApplicationParameters { - NumericType printTimeInterval = 0; + int logLevel = 2; GeometryType geometryType = GeometryType::NONE; ProcessType processType = ProcessType::NONE; @@ -51,6 +56,10 @@ struct ApplicationParameters { // MAKE hole NumericType holeDepth = 0.2; NumericType holeRadius = 0.2; + // MAKE stack + int numLayers = 11; + NumericType layerHeight = 1.; + NumericType substrateHeight = 1.; // GDS / IMPORT int layers = 0; std::string fileName = ""; @@ -59,20 +68,35 @@ struct ApplicationParameters { NumericType xPadding = 0.; NumericType yPadding = 0.; psMaterial material = psMaterial::Si; + psMaterial maskMaterial = psMaterial::Mask; // Process NumericType processTime = 1; int raysPerPoint = 3000; - // SF6O2Etching - NumericType totalEtchantFlux = 4.5e16; - NumericType totalOxygenFlux = 1e18; - NumericType totalIonFlux = 2e16; - NumericType ionEnergy = 100; + NumericType etchStopDepth = std::numeric_limits::lowest(); + NumericType smoothFlux = 1.; + // Plasma etching + // fluxes in in (1e15 atoms/cm³) + NumericType etchantFlux = 1.8e3; + NumericType oxygenFlux = 1.0e2; + NumericType ionFlux = 12.; + NumericType ionEnergy = 100; // eV + NumericType sigmaIonEnergy = 10; // eV + NumericType ionExponent = 100.; NumericType A_O = 3.; + // Fluorocarbon etching + NumericType deltaP = 0; // Deposition NumericType rate = 1.; NumericType sticking = 1.; NumericType cosinePower = 1.; + // TEOS Deposition + NumericType rateP1 = 1.; + NumericType stickingP1 = 1.; + NumericType orderP1 = 1.; + NumericType rateP2 = 0.; + NumericType stickingP2 = 1.; + NumericType orderP2 = 1.; // Directional etching NumericType directionalRate = 1.; NumericType isotropicRate = 0.; @@ -83,6 +107,9 @@ struct ApplicationParameters { // box std::array halfAxes = {1., 1., 1.}; + // output + OutputType out = OutputType::SURFACE; + ApplicationParameters() {} void defaultParameters(bool all = false) { @@ -99,11 +126,14 @@ struct ApplicationParameters { maskHeight = 0.1; processTime = 1; raysPerPoint = 3000; - totalEtchantFlux = 4.5e16; - totalOxygenFlux = 1e18; - totalIonFlux = 2e16; + etchantFlux = 100; + oxygenFlux = 100; + ionFlux = 100; ionEnergy = 100; + ionExponent = 100.; + sigmaIonEnergy = 10; A_O = 3.; + deltaP = 0.; rate = 1.; sticking = 1.; cosinePower = 1.; @@ -114,9 +144,20 @@ struct ApplicationParameters { halfAxes[0] = 1.; halfAxes[1] = 1.; halfAxes[2] = 1.; + numLayers = 11; + layerHeight = 1.; + substrateHeight = 1.; + etchStopDepth = std::numeric_limits::lowest(); + smoothFlux = 1.; + rateP1 = 1.; + stickingP1 = 1.; + orderP1 = 1.; + rateP2 = 0.; + stickingP2 = 1.; + orderP2 = 1.; if (all) { - printTimeInterval = 0; + logLevel = 2; geometryType = GeometryType::NONE; processType = ProcessType::NONE; gridDelta = 0.02; @@ -126,4 +167,4 @@ struct ApplicationParameters { integrationScheme = lsIntegrationSchemeEnum::ENGQUIST_OSHER_1ST_ORDER; } } -}; +}; \ No newline at end of file diff --git a/app/ApplicationParser.hpp b/app/ApplicationParser.hpp index 6f300dc2..d1d08e4d 100644 --- a/app/ApplicationParser.hpp +++ b/app/ApplicationParser.hpp @@ -53,6 +53,49 @@ class ApplicationParser { } private: + void parseMaterial(const std::string materialString, psMaterial &material) { + if (materialString == "Undefined") { + material = psMaterial::Undefined; + } else if (materialString == "Si") { + material = psMaterial::Si; + } else if (materialString == "SiO2") { + material = psMaterial::SiO2; + } else if (materialString == "Si3N4") { + material = psMaterial::Si3N4; + } else if (materialString == "PolySi") { + material = psMaterial::PolySi; + } else if (materialString == "Polymer") { + material = psMaterial::Polymer; + } else if (materialString == "Al2O3") { + material = psMaterial::Al2O3; + } else if (materialString == "SiC") { + material = psMaterial::SiC; + } else if (materialString == "Metal") { + material = psMaterial::Metal; + } else if (materialString == "W") { + material = psMaterial::W; + } else if (materialString == "Dielectric") { + material = psMaterial::Dielectric; + } else if (materialString == "SiON") { + material = psMaterial::SiON; + } else if (materialString == "SiN") { + material = psMaterial::SiN; + } else if (materialString == "TiN") { + material = psMaterial::TiN; + } else if (materialString == "Cu") { + material = psMaterial::Cu; + } else if (materialString == "Air") { + material = psMaterial::Air; + } else if (materialString == "GAS") { + material = psMaterial::GAS; + } else if (materialString == "GaN") { + material = psMaterial::GaN; + } else { + std::cout << "Unknown material: " << materialString << std::endl; + material = psMaterial::Undefined; + } + } + void parseInit(std::istringstream &stream) { unsigned integrationSchemeNum = 0; auto config = parseLineStream(stream); @@ -60,7 +103,7 @@ class ApplicationParser { config, psUtils::Item{"xExtent", params->xExtent}, psUtils::Item{"yExtent", params->yExtent}, psUtils::Item{"resolution", params->gridDelta}, - psUtils::Item{"printTimeInterval", params->printTimeInterval}, + psUtils::Item{"logLevel", params->logLevel}, psUtils::Item{"periodic", params->periodicBoundary}, psUtils::Item{"integrationScheme", integrationSchemeNum}); if (integrationSchemeNum > 9) { @@ -70,29 +113,44 @@ class ApplicationParser { } params->integrationScheme = static_cast(integrationSchemeNum); + psLogger::setLogLevel(static_cast(params->logLevel)); } void parseGeometry(std::istringstream &stream) { std::string type; stream >> type; auto config = parseLineStream(stream); + std::string material; if (type == "Trench") { params->geometryType = GeometryType::TRENCH; psUtils::AssignItems(config, psUtils::Item{"width", params->trenchWidth}, psUtils::Item{"depth", params->trenchHeight}, psUtils::Item{"zPos", params->maskZPos}, psUtils::Item{"tapering", params->taperAngle}, - psUtils::Item{"mask", params->mask}); + psUtils::Item{"mask", params->mask}, + psUtils::Item{"material", material}); + parseMaterial(material, params->material); } else if (type == "Hole") { params->geometryType = GeometryType::HOLE; psUtils::AssignItems(config, psUtils::Item{"radius", params->holeRadius}, psUtils::Item{"depth", params->holeDepth}, psUtils::Item{"zPos", params->maskZPos}, psUtils::Item{"tapering", params->taperAngle}, - psUtils::Item{"mask", params->mask}); + psUtils::Item{"mask", params->mask}, + psUtils::Item{"material", material}); + parseMaterial(material, params->material); } else if (type == "Plane") { params->geometryType = GeometryType::PLANE; psUtils::AssignItems(config, psUtils::Item{"zPos", params->maskZPos}); + parseMaterial(material, params->material); + } else if (type == "Stack") { + params->geometryType = GeometryType::STACK; + psUtils::AssignItems( + config, psUtils::Item{"numLayers", params->numLayers}, + psUtils::Item{"layerHeight", params->layerHeight}, + psUtils::Item{"maskHeight", params->maskHeight}, + psUtils::Item{"substrateHeight", params->substrateHeight}, + psUtils::Item{"holeRadius", params->holeRadius}); } else if (type == "GDS") { params->geometryType = GeometryType::GDS; psUtils::AssignItems(config, psUtils::Item{"file", params->fileName}, @@ -101,14 +159,16 @@ class ApplicationParser { psUtils::Item{"maskHeight", params->maskHeight}, psUtils::Item{"invert", params->maskInvert}, psUtils::Item{"xPadding", params->xPadding}, - psUtils::Item{"yPadding", params->yPadding}); + psUtils::Item{"yPadding", params->yPadding}, + psUtils::Item{"material", material}); + parseMaterial(material, params->material); } else if (type == "Import") { params->geometryType = GeometryType::IMPORT; psUtils::AssignItems(config, psUtils::Item{"file", params->fileName}, psUtils::Item{"layers", params->layers}); } else { params->geometryType = GeometryType::NONE; - std::cout << "Invalid mask type." << std::endl; + std::cout << "Invalid geometry type." << std::endl; exit(1); } } @@ -117,24 +177,42 @@ class ApplicationParser { std::string model; stream >> model; auto config = parseLineStream(stream); - if (model == "Deposition") { - params->processType = ProcessType::DEPOSITION; + if (model == "SimpleDeposition") { + std::string material; + params->processType = ProcessType::SIMPLEDEPOSITION; psUtils::AssignItems(config, psUtils::Item{"rate", params->rate}, psUtils::Item{"time", params->processTime}, psUtils::Item{"sticking", params->sticking}, psUtils::Item{"cosineExponent", params->cosinePower}, - psUtils::Item{"raysPerPoint", params->raysPerPoint}); + psUtils::Item{"smoothFlux", params->smoothFlux}, + psUtils::Item{"raysPerPoint", params->raysPerPoint}, + psUtils::Item{"material", material}); + parseMaterial(material, params->material); } else if (model == "SF6O2Etching") { params->processType = ProcessType::SF6O2ETCHING; psUtils::AssignItems( config, psUtils::Item{"time", params->processTime}, - psUtils::Item{"ionFlux", params->totalIonFlux}, - psUtils::Item{"ionEnergy", params->ionEnergy}, - psUtils::Item{"etchantFlux", params->totalEtchantFlux}, - psUtils::Item{"oxygenFlux", params->totalOxygenFlux}, + psUtils::Item{"ionFlux", params->ionFlux}, + psUtils::Item{"meanIonEnergy", params->ionEnergy}, + psUtils::Item{"sigmaIonEnergy", params->sigmaIonEnergy}, + psUtils::Item{"etchantFlux", params->etchantFlux}, + psUtils::Item{"oxygenFlux", params->oxygenFlux}, psUtils::Item{"A_O", params->A_O}, - psUtils::Item{"raysPerPoint", params->raysPerPoint}, - psUtils::Item{"maskId", params->maskId}); + psUtils::Item{"smoothFlux", params->smoothFlux}, + psUtils::Item{"ionExponent", params->ionExponent}, + psUtils::Item{"raysPerPoint", params->raysPerPoint}); + } else if (model == "FluorocarbonEtching") { + params->processType = ProcessType::FLUOROCARBONETCHING; + psUtils::AssignItems( + config, psUtils::Item{"time", params->processTime}, + psUtils::Item{"ionFlux", params->ionFlux}, + psUtils::Item{"meanIonEnergy", params->ionEnergy}, + psUtils::Item{"sigmaIonEnergy", params->sigmaIonEnergy}, + psUtils::Item{"etchantFlux", params->etchantFlux}, + psUtils::Item{"polyFlux", params->oxygenFlux}, + psUtils::Item{"deltaP", params->deltaP}, + psUtils::Item{"smoothFlux", params->smoothFlux}, + psUtils::Item{"raysPerPoint", params->raysPerPoint}); } else if (model == "SphereDistribution") { params->processType = ProcessType::SPHEREDISTRIBUTION; psUtils::AssignItems(config, psUtils::Item{"radius", params->radius}); @@ -146,17 +224,25 @@ class ApplicationParser { psUtils::Item{"halfAxisZ", params->halfAxes[2]}); } else if (model == "DirectionalEtching") { params->processType = ProcessType::DIRECTIONALETCHING; + std::string material = "Undefined"; psUtils::AssignItems( config, psUtils::Item{"direction", params->direction}, psUtils::Item{"directionalRate", params->directionalRate}, psUtils::Item{"isotropicRate", params->isotropicRate}, psUtils::Item{"time", params->processTime}, - psUtils::Item{"maskId", params->maskId}); + psUtils::Item{"maskMaterial", material}); + parseMaterial(material, params->maskMaterial); } else if (model == "Isotropic") { params->processType = ProcessType::ISOTROPIC; + std::string material = "Undefined"; + std::string maskMaterial = "Mask"; psUtils::AssignItems(config, psUtils::Item{"rate", params->rate}, psUtils::Item{"time", params->processTime}, - psUtils::Item{"maskId", params->maskId}); + psUtils::Item{"time", params->processTime}, + psUtils::Item{"material", material}, + psUtils::Item{"maskMaterial", maskMaterial}); + parseMaterial(material, params->material); + parseMaterial(maskMaterial, params->maskMaterial); } else if (model == "WetEtching") { params->processType = ProcessType::WETETCHING; psUtils::AssignItems(config, psUtils::Item{"time", params->processTime}, @@ -172,7 +258,21 @@ class ApplicationParser { psUtils::AssignItems(config, psUtils::Item{"height", params->maskZPos}); } - void parseOutput(std::istringstream &stream) { stream >> params->fileName; } + void parseOutput(std::istringstream &stream) { + std::string outType; + stream >> outType; + std::transform(outType.begin(), outType.end(), outType.begin(), + [](unsigned char c) { return std::tolower(c); }); + if (outType == "surface") { + params->out = OutputType::SURFACE; + } else if (outType == "volume") { + params->out = OutputType::VOLUME; + } else { + std::cout << "Unknown output type. Using default."; + } + + stream >> params->fileName; + } std::unordered_map parseLineStream(std::istringstream &input) { diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 43f1adf7..9d015c33 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -35,12 +35,14 @@ set_property(DIRECTORY PROPERTY EXCLUDE_FROM_ALL TRUE) add_custom_target(buildApplication) -add_executable(ViennaPS2D ${PROJECT_SOURCE_DIR}/Application2D.cpp) +add_executable(ViennaPS2D ${PROJECT_SOURCE_DIR}/Application.cpp) +target_compile_definitions(ViennaPS2D PRIVATE VIENNAPS_APP_DIM=2) target_include_directories(ViennaPS2D PUBLIC ${VIENNAPS_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}) target_link_libraries(ViennaPS2D ${VIENNAPS_LIBRARIES}) add_dependencies(buildApplication ViennaPS2D) -add_executable(ViennaPS3D ${PROJECT_SOURCE_DIR}/Application3D.cpp) +add_executable(ViennaPS3D ${PROJECT_SOURCE_DIR}/Application.cpp) +target_compile_definitions(ViennaPS3D PRIVATE VIENNAPS_APP_DIM=3) target_include_directories(ViennaPS3D PUBLIC ${VIENNAPS_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}) target_link_libraries(ViennaPS3D ${VIENNAPS_LIBRARIES}) add_dependencies(buildApplication ViennaPS3D) diff --git a/app/Interrupt.hpp b/app/Interrupt.hpp new file mode 100644 index 00000000..300329b7 --- /dev/null +++ b/app/Interrupt.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include +#include + +class InterruptException : public std::exception { +public: + InterruptException(int s) : S(s) {} + int S; +}; + +void sig_to_exception(int s) { throw InterruptException(s); } \ No newline at end of file diff --git a/app/README.md b/app/README.md index bf0545de..84644b83 100644 --- a/app/README.md +++ b/app/README.md @@ -1,12 +1,12 @@ # Configuration File Documentation -The configuration file must obey a certain structure in order to be parsed correctly. An example for a configuration file can be seen in **SampleConfig.txt**. The configuration file is parsed line by line and each succesfully parsed line is executed immediately. +The configuration file must obey a certain structure in order to be parsed correctly. An example for a configuration file can be seen in **SampleConfig.txt**. The configuration file is parsed line by line and each successfully parsed line is executed immediately. ## Commands Every line has to start with a command statement, followed by a list of parameters for the command. Possible commands are: - **INIT**: Initialize the simulation domain. -- **GEOMETRY** _\_: Create or import a geometry. Possible options for creating geometries are: _Trench_, _Hole_ and _Plane_. It is also possible to import geometries from _.lvst_ files by specifying _Import_ or to import layers from GDSII file format by secifying _GDS_ (only possible in 3D mode). Parameters for the geometries are described below. +- **GEOMETRY** _\_: Create or import a geometry. Possible options for creating geometries are: _Trench_, _Hole_ and _Plane_. It is also possible to import geometries from _.lvst_ files by specifying _Import_ or to import layers from GDSII file format by specifying _GDS_ (only possible in 3D mode). Parameters for the geometries are described below. - **PROCESS** _\_: Run a process. Possible process types are: _Deposition_, _GeometricUniformDeposition_, _SF6O2Etching_ and _DirectionalEtching_. Parameters for the processes are described below. - **PLANARIZE**: Planarize the geometry at a given height. - **OUTPUT** _\_: Print the surface of the geometry in _fileName.vtp_ file format. @@ -26,7 +26,7 @@ All parameters which are parsed additional to a command are described below. For
resolution
distance between grid points in the domain (numeric value, default: 0.02)
printTimeInterval
-
minimum time between outputing intermediate disk meshes for each process step. If this value is negative, no intermediate meshes will be printed (numeric value, default: 0)
+
minimum time between outputting intermediate disk meshes for each process step. If this value is negative, no intermediate meshes will be printed (numeric value, default: 0)
periodic
use periodic boundary conditions (boolean, default: 0)
@@ -137,7 +137,9 @@ All parameters which are parsed additional to a command are described below. For
ionFlux
total flux of ions in plasma (numeric value, default: 2e16)
ionEnergy
-
mean ion energy (numeric value, default: 100)
+
mean ion energy in eV (numeric value, default: 100)
+
rfBias
+
rf bias in W (numeric value, default: 105)
etchantFlux
total flux of etchant species in plasma (numeric value, default: 4.5e16)
oxygenFlux
@@ -150,6 +152,29 @@ All parameters which are parsed additional to a command are described below. For
ID of mask material (integer value, default: 0)
+--- +**PROCESS FluorocarbonEtching** +
+
time
+
process time (numeric value, default: 1)
+
ionFlux
+
total flux of ions in plasma (numeric value, default: 2e16)
+
ionEnergy
+
mean ion energy in eV (numeric value, default: 100)
+
plasmaFrequency
+
RF plasma frequency in MHz (numeric value, default: 0.1)
+
etchantFlux
+
total flux of etchant species in plasma (numeric value, default: 4.5e16)
+
oxygenFlux
+
total flux of oxygen in plasma (numeric value, default: 1e18)
+
temperature
+
reactor temperature in K (numeric value, default: 300)
+
raysPerPoint
+
number of rays traced per grid point in the surface geometry (integer value, default: 3000)
+
maskId
+
ID of mask material (integer value, default: 0)
+
+ --- **PROCESS DirectionalEtching**
@@ -161,8 +186,8 @@ All parameters which are parsed additional to a command are described below. For
etching rate in primal direction (numeric value, default: 1)
isotropicRate
isotropic etching rate (numeric value, default: 0)
-
maskId
-
ID of mask material (integer value, default: 0)
+
maskMaterial
+
mask material (string, default: Mask)
--- @@ -172,8 +197,10 @@ All parameters which are parsed additional to a command are described below. For
process time (numeric value, default: 1)
rate
process rate, can be negative for etching (numeric value, default: 0) -
maskId
-
ID of mask material (integer value, default: 0)
+
material
+
deposited material if rate is positive (string, default: Undefined)
+
maskMaterial
+
mask material (string, default: Mask)
--- diff --git a/app/SampleConfig.txt b/app/SampleConfig.txt index 374bd7dd..99097fab 100644 --- a/app/SampleConfig.txt +++ b/app/SampleConfig.txt @@ -1,5 +1,5 @@ INIT xExtent=1 yExtent=1 resolution=0.01 printTimeInterval=-1 periodic=0 GEOMETRY Trench width=0.4 depth=0.5 zPos=0.5 tapering=10 -OUTPUT initial +OUTPUT SURFACE initial PROCESS SphereDistribution radius=0.1 -OUTPUT result \ No newline at end of file +OUTPUT SURFACE result \ No newline at end of file diff --git a/external/upstream/CMakeLists.txt b/external/upstream/CMakeLists.txt index e3c835be..5dc073da 100644 --- a/external/upstream/CMakeLists.txt +++ b/external/upstream/CMakeLists.txt @@ -4,3 +4,7 @@ set_property(DIRECTORY PROPERTY EP_BASE ${DEPENDENCIES_DIR}) add_subdirectory(viennals) add_subdirectory(viennaray) + +if(VIENNAPS_BUILD_PYTHON) + add_subdirectory(pybind11) +endif() diff --git a/external/upstream/pybind11/CMakeLists.txt b/external/upstream/pybind11/CMakeLists.txt new file mode 100644 index 00000000..198229aa --- /dev/null +++ b/external/upstream/pybind11/CMakeLists.txt @@ -0,0 +1,62 @@ +# ################################################################################################## +# Check pybind11 Dependency +# ################################################################################################## +# If the path to an installation was provided by the user +if(DEFINED pybind11_DIR AND NOT DEFINED CACHE{pybind11_DIR}) + # Required for windows to convert backslashes to forward slashes + file(TO_CMAKE_PATH "${pybind11_DIR}" pybind11_DIR) + set(pybind11_DIR + "${pybind11_DIR}" + CACHE PATH "Path to pybind11 installation" FORCE) + find_package(pybind11 REQUIRED PATHS ${pybind11_FOUND} NO_DEFAULT_PATH) + message(STATUS "pybind11_DIR: ${pybind11_DIR}") + add_library(pybind11_external INTERFACE) # dummy target +else() + find_package(pybind11 QUIET) + if(pybind11_FOUND) + message(STATUS "Found pybind11 at ${pybind11_DIR}.") + add_library(pybind11_external INTERFACE) # dummy target + else() + message( + STATUS + "Couldn't find pybind11. Dependency will be built with the buildDependencies target (e.g. `make buildDependencies`)." + ) + + # Get the number of cores + include(ProcessorCount) + ProcessorCount(NPROC) + + ExternalProject_Add( + pybind11_external + GIT_REPOSITORY https://github.com/pybind/pybind11.git + # Hash of tags/v2.6.2 + GIT_TAG # v2.6.2 + 8de7772cc72daca8e947b79b83fea46214931604 + # GIT_SHALLOW 1 + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= + # -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} + -DCMAKE_CXX_EXTENSIONS=${CMAKE_CXX_EXTENSIONS} + -DCMAKE_CXX_STANDARD_REQUIRED=${CMAKE_CXX_STANDARD_REQUIRED} + # -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF + BUILD_COMMAND ${CMAKE_COMMAND} --build . --parallel ${NPROC} + # # No install needed INSTALL_COMMAND + INSTALL_COMMAND ${CMAKE_COMMAND} --install . + # "cmake --install build" + UPDATE_COMMAND "" + USES_TERMINAL_DOWNLOAD 1 + USES_TERMINAL_UPDATE 1 + GIT_PROGRESS 1) + + # Get install and build directory + ExternalProject_Get_Property(pybind11_external INSTALL_DIR BINARY_DIR) + set(pybind11_DIR "${INSTALL_DIR}") + # add to buildDependencies target + add_dependencies(buildDependencies pybind11_external) + + endif() + set(pybind11_DIR + "${pybind11_DIR}" + CACHE PATH "Path to pybind11 installation" FORCE) +endif() diff --git a/include/CellSet/csBVH.hpp b/include/CellSet/csBVH.hpp index d6ed0a62..e06bce98 100644 --- a/include/CellSet/csBVH.hpp +++ b/include/CellSet/csBVH.hpp @@ -4,6 +4,8 @@ #include +/// Helper class to quickly determine the cell in which a given point resides +/// in. To do so, an octree is built around the cell set structure. template class csBVH { private: using BVPtrType = lsSmartPointer>; diff --git a/include/CellSet/csDenseCellSet.hpp b/include/CellSet/csDenseCellSet.hpp index 50abdc6d..85bbba84 100644 --- a/include/CellSet/csDenseCellSet.hpp +++ b/include/CellSet/csDenseCellSet.hpp @@ -177,7 +177,10 @@ template class csDenseCellSet { std::vector *getFillingFractions() const { return fillingFractions; } T getFillingFraction(const std::array &point) { - auto idx = findIndex(point); + csTriple point3 = {0., 0., 0.}; + for (int i = 0; i < D; i++) + point3[i] = point[i]; + auto idx = findIndex(point3); if (idx < 0) return -1.; @@ -214,7 +217,7 @@ template class csDenseCellSet { } // Add to the filling fraction at given cell index. - bool addFillingFraction(int idx, T fill) { + bool addFillingFraction(const int idx, const T fill) { if (idx < 0) return false; @@ -330,7 +333,7 @@ template class csDenseCellSet { // Update the material IDs of the cell set. This function should be called if // the level sets, the cell set is made out of, have changed. This does not // work if the surface of the volume has changed. In this case, call the - // funciton update surface first. + // function "updateSurface" first. void updateMaterials() { auto materialIds = getScalarData("Material"); @@ -572,7 +575,7 @@ template class csDenseCellSet { } int findSurfaceHitPoint(csTriple &hitPoint, const csTriple &direction) { - // find surface hitpoint + // find surface hit point auto idx = findIndex(hitPoint); if (idx > 0) diff --git a/include/CellSet/csTracingKernel.hpp b/include/CellSet/csTracingKernel.hpp index bc301c0d..6d4a521b 100644 --- a/include/CellSet/csTracingKernel.hpp +++ b/include/CellSet/csTracingKernel.hpp @@ -59,29 +59,11 @@ template class csTracingKernel { RTCRayHit{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; const int threadID = omp_get_thread_num(); - constexpr int numRngStates = 7; - unsigned int seeds[numRngStates]; + unsigned int seed = mRunNumber; if (mUseRandomSeeds) { std::random_device rd; - for (size_t i = 0; i < numRngStates; ++i) { - seeds[i] = static_cast(rd()); - } - } else { - for (size_t i = 0; i < numRngStates; ++i) { - seeds[i] = static_cast((omp_get_thread_num() + 1) * 31 + - i + mRunNumber); - } + seed = static_cast(rd()); } - // It seems really important to use two separate seeds / states for - // sampling the source and sampling reflections. When we use only one - // state for both, then the variance is very high. - rayRNG RngState1(seeds[0]); - rayRNG RngState2(seeds[1]); - rayRNG RngState3(seeds[2]); - rayRNG RngState4(seeds[3]); - rayRNG RngState5(seeds[4]); - rayRNG RngState6(seeds[5]); - rayRNG RngState7(seeds[6]); // thread-local particle object auto particle = mParticle->clone(); @@ -96,10 +78,13 @@ template class csTracingKernel { #pragma omp for schedule(dynamic) for (long long idx = 0; idx < mNumRays; ++idx) { - particle->initNew(RngState6); + // particle specific RNG seed + auto particleSeed = rayInternal::tea<3>(idx, seed); + rayRNG RngState(particleSeed); - mSource.fillRay(rayHit.ray, idx, RngState1, RngState2, RngState3, - RngState4); // fills also tnear + particle->initNew(RngState); + + mSource.fillRay(rayHit.ray, idx, RngState); // fills also tnear #ifdef VIENNARAY_USE_RAY_MASKING rayHit.ray.mask = -1; @@ -165,7 +150,7 @@ template class csTracingKernel { // get fill and reflection const auto fillnDirection = - particle->surfaceHit(rayDir, geomNormal, reflect, RngState5); + particle->surfaceHit(rayDir, geomNormal, reflect, RngState); if (mGeometry.getMaterialId(rayHit.hit.primID) != excludeMaterial) { // trace in cell set @@ -185,7 +170,7 @@ template class csTracingKernel { while (volumeParticle.energy >= 0) { volumeParticle.distance = -1; while (volumeParticle.distance < 0) - volumeParticle.distance = normalDist(RngState7); + volumeParticle.distance = normalDist(RngState); auto travelDist = csUtil::multNew(volumeParticle.direction, volumeParticle.distance); csUtil::add(volumeParticle.position, travelDist); @@ -199,7 +184,7 @@ template class csTracingKernel { if (newIdx != volumeParticle.cellId) { volumeParticle.cellId = newIdx; - auto fill = particle->collision(volumeParticle, RngState7, + auto fill = particle->collision(volumeParticle, RngState, particleStack); path.addGridData(newIdx, fill); } diff --git a/include/Compact/psCSVWriter.hpp b/include/Compact/psCSVWriter.hpp index 791688b4..914b8164 100644 --- a/include/Compact/psCSVWriter.hpp +++ b/include/Compact/psCSVWriter.hpp @@ -78,7 +78,7 @@ template class psCSVWriter { if (!initialize()) return false; - // The first row determins the number of colums + // The first row determins the number of columns if (numCols == 0) numCols = data.size(); @@ -105,7 +105,7 @@ template class psCSVWriter { if (!initialize()) return false; - // The first row determins the number of colums + // The first row determins the number of columns if (numCols == 0) numCols = data.size(); diff --git a/include/Compact/psQueues.hpp b/include/Compact/psQueues.hpp index 5e8aa3db..718d7804 100644 --- a/include/Compact/psQueues.hpp +++ b/include/Compact/psQueues.hpp @@ -13,7 +13,7 @@ #include // A bounded priority queue implementation. -// If a certain predifined number of elements are already stored in the queue, +// If a certain predefined number of elements are already stored in the queue, // then a new item with a worse value than the worst already in the queue won't // be added when enqueue is called with the new item. template > diff --git a/include/Compact/psRectilinearGridInterpolation.hpp b/include/Compact/psRectilinearGridInterpolation.hpp index 8bb3168d..a1977097 100644 --- a/include/Compact/psRectilinearGridInterpolation.hpp +++ b/include/Compact/psRectilinearGridInterpolation.hpp @@ -30,7 +30,7 @@ class psRectilinearGridInterpolation // For rectilinear grid interpolation to work, we first have to ensure that // our input coordinates are arranged in a certain way - // Future improvement: parallelize the recursive sorting using OpenMP taks + // Future improvement: parallelize the recursive sorting using OpenMP task bool rearrange(typename VectorType::iterator start, typename VectorType::iterator end, int axis, bool capture) { bool equalSize = true; @@ -170,7 +170,7 @@ class psRectilinearGridInterpolation gridIndices[i] = uniqueValues[i].size() - 1; normalizedCoordinates[i] = 1.; } else { - // The corrdinate is somewhere in between (excluding) the lowest and + // The coordinate is somewhere in between (excluding) the lowest and // greatest grid point. // The following function returns an iterator pointing to the first diff --git a/include/Geometries/psMakeFin.hpp b/include/Geometries/psMakeFin.hpp index eb8263cc..015c8006 100644 --- a/include/Geometries/psMakeFin.hpp +++ b/include/Geometries/psMakeFin.hpp @@ -6,7 +6,7 @@ #include /** - * Creates a fin gemeotry in in z(3D)/y(2D) direction. + * Creates a fin geometry in in z(3D)/y(2D) direction. */ template class psMakeFin { using LSPtrType = psSmartPointer>; diff --git a/include/Geometries/psMakeHole.hpp b/include/Geometries/psMakeHole.hpp index 88650193..c83712af 100644 --- a/include/Geometries/psMakeHole.hpp +++ b/include/Geometries/psMakeHole.hpp @@ -9,8 +9,8 @@ #include /** - * Creates a hole geometry in z direction. For 2D geometries a regular trench is - * created. + * Creates a hole geometry in z direction. In 2D mode, this is equivalent to a + * trench geometry */ template class psMakeHole { using LSPtrType = psSmartPointer>; diff --git a/include/Geometries/psMakeStack.hpp b/include/Geometries/psMakeStack.hpp index d825b922..9b1b3123 100644 --- a/include/Geometries/psMakeStack.hpp +++ b/include/Geometries/psMakeStack.hpp @@ -19,7 +19,7 @@ template class psMakeStack { const NumericType gridDelta; const NumericType xExtent; const NumericType yExtent; - NumericType bounds[2 * D]; + double bounds[2 * D]; NumericType normal[D]; NumericType origin[D] = {0.}; @@ -185,7 +185,7 @@ template class psMakeStack { lsMakeGeometry( substrate, lsSmartPointer>::New(origin, normal)) .apply(); - domain->insertNextLevelSet(substrate, psMaterial::Si); + domain->insertNextLevelSetAsMaterial(substrate, psMaterial::Si); // Si3N4/SiO2 layers for (int i = 0; i < numLayers; ++i) { diff --git a/include/Models/DirectionalEtching.hpp b/include/Models/DirectionalEtching.hpp index bbb51085..0e9f1f8c 100644 --- a/include/Models/DirectionalEtching.hpp +++ b/include/Models/DirectionalEtching.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -8,21 +9,21 @@ template class DirectionalEtchVelocityField : public psVelocityField { const std::array direction; - const NumericType dirVel = 1.; - const NumericType isoVel = 0.; - const int maskId; + const NumericType dirVel; + const NumericType isoVel; + const psMaterial maskMaterial; public: DirectionalEtchVelocityField(std::array dir, const NumericType dVel, const NumericType iVel, - const int mask = 0) - : direction(dir), dirVel(dVel), isoVel(iVel), maskId(mask) {} + const psMaterial mask) + : direction(dir), dirVel(dVel), isoVel(iVel), maskMaterial(mask) {} std::array getVectorVelocity(const std::array &coordinate, int material, const std::array &normalVector, unsigned long) override { - if (material != maskId) { + if (!psMaterialMap::isMaterial(material, maskMaterial)) { std::array dir(direction); for (unsigned i = 0; i < D; ++i) { if (dir[i] == 0.) { @@ -33,7 +34,7 @@ class DirectionalEtchVelocityField : public psVelocityField { } return dir; } else { - return {0}; + return {0.}; } } @@ -42,32 +43,20 @@ class DirectionalEtchVelocityField : public psVelocityField { int getTranslationFieldOptions() const override { return 0; } }; -template -class DirectionalEtchingSurfaceModel : public psSurfaceModel { -public: - psSmartPointer> calculateVelocities( - psSmartPointer> Rates, - const std::vector> &coordinates, - const std::vector &materialIds) override { - return nullptr; - } -}; - template class DirectionalEtching : public psProcessModel { public: DirectionalEtching(const std::array direction, const NumericType directionalVelocity = 1., const NumericType isotropicVelocity = 0., - const int maskId = 0) { - // surface model - auto surfModel = - psSmartPointer>::New(); + const psMaterial mask = psMaterial::Mask) { + // default surface model + auto surfModel = psSmartPointer>::New(); // velocity field auto velField = psSmartPointer>::New( - direction, directionalVelocity, isotropicVelocity, maskId); + direction, directionalVelocity, isotropicVelocity, mask); this->setSurfaceModel(surfModel); this->setVelocityField(velField); diff --git a/include/Models/FluorocarbonEtching.hpp b/include/Models/FluorocarbonEtching.hpp index ffa0dada..173f7ea1 100644 --- a/include/Models/FluorocarbonEtching.hpp +++ b/include/Models/FluorocarbonEtching.hpp @@ -2,6 +2,8 @@ #include +#include + #include #include #include @@ -10,19 +12,26 @@ #include #include +namespace FluorocarbonImplementation { + +// sticking probabilities +static constexpr double beta_e = 0.9; +static constexpr double beta_e_mask = 0.1; +static constexpr double beta_p = 0.26; +static constexpr double beta_p_mask = 0.01; +static constexpr double beta_pe = 0.6; + template -class FluorocarbonSurfaceModel : public psSurfaceModel { +class SurfaceModel : public psSurfaceModel { using psSurfaceModel::Coverages; static constexpr double eps = 1e-6; public: - FluorocarbonSurfaceModel(const NumericType ionFlux, - const NumericType etchantFlux, - const NumericType polyFlux, - const NumericType passedEtchStopDepth) + SurfaceModel(const NumericType ionFlux, const NumericType etchantFlux, + const NumericType polyFlux, const NumericType passedDeltaP, + const NumericType passedEtchStopDepth) : totalIonFlux(ionFlux), totalEtchantFlux(etchantFlux), - totalPolyFlux(polyFlux), - F_ev(2.7 * etchantFlux * std::exp(-0.168 / (kB * temperature))), + totalPolyFlux(polyFlux), delta_p(passedDeltaP), etchStopDepth(passedEtchStopDepth) {} void initializeCoverages(unsigned numGeometryPoints) override { @@ -41,7 +50,7 @@ class FluorocarbonSurfaceModel : public psSurfaceModel { psSmartPointer> Rates, const std::vector> &coordinates, const std::vector &materialIds) override { - updateCoverages(Rates); + updateCoverages(Rates, materialIds); const auto numPoints = materialIds.size(); std::vector etchRate(numPoints, 0.); @@ -66,33 +75,44 @@ class FluorocarbonSurfaceModel : public psSurfaceModel { auto matId = psMaterialMap::mapToMaterial(materialIds[i]); assert(matId == psMaterial::Mask || matId == psMaterial::Polymer || matId == psMaterial::Si || matId == psMaterial::SiO2 || - matId == psMaterial::Si3N4 && "Unexptected material"); - if (matId == psMaterial::Mask) - continue; - if (pCoverage->at(i) >= 1.) { - assert(pCoverage->at(i) == 1. && "Correctness assumption"); + matId == psMaterial::Si3N4 && "Unexpected material"); + if (matId == psMaterial::Mask) { + etchRate[i] = (-1. / psParameters::Mask::rho) * + ionSputteringRate->at(i) * totalIonFlux; + } else if (pCoverage->at(i) >= 1.) { // Deposition - etchRate[i] = - (1 / rho_p) * (polyRate->at(i) * totalPolyFlux - - ionpeRate->at(i) * totalIonFlux * peCoverage->at(i)); + etchRate[i] = (1 / psParameters::Polymer::rho) * + (polyRate->at(i) * totalPolyFlux - + ionpeRate->at(i) * totalIonFlux * peCoverage->at(i)); assert(etchRate[i] >= 0 && "Negative deposition"); } else if (matId == psMaterial::Polymer) { // Etching depo layer - etchRate[i] = std::min( - (1 / rho_p) * (polyRate->at(i) * totalPolyFlux - - ionpeRate->at(i) * totalIonFlux * peCoverage->at(i)), - 0.); + etchRate[i] = + std::min((1 / psParameters::Polymer::rho) * + (polyRate->at(i) * totalPolyFlux - + ionpeRate->at(i) * totalIonFlux * peCoverage->at(i)), + 0.); } else { - NumericType mat_density = 0; - if (matId == psMaterial::Si) // crystalline Si at the bottom + NumericType mat_density = 1.; + NumericType F_ev = 0.; + if (matId == psMaterial::Si) // Etching Si { - mat_density = -rho_Si; + mat_density = -psParameters::Si::rho; + F_ev = psParameters::Si::K * totalEtchantFlux * + std::exp(-psParameters::Si::E_a / + (psParameters::kB * psParameters::roomTemperature)); } else if (matId == psMaterial::SiO2) // Etching SiO2 { - mat_density = -rho_SiO2; - } else if (matId == psMaterial::Si3N4) // Etching SiNx + F_ev = psParameters::SiO2::K * totalEtchantFlux * + std::exp(-psParameters::SiO2::E_a / + (psParameters::kB * psParameters::roomTemperature)); + mat_density = -psParameters::SiO2::rho; + } else if (matId == psMaterial::Si3N4) // Etching Si3N4 { - mat_density = -rho_SiNx; + F_ev = psParameters::SiO2::K * totalEtchantFlux * + std::exp(-psParameters::SiO2::E_a / + (psParameters::kB * psParameters::roomTemperature)); + mat_density = -psParameters::Si3N4::rho; } etchRate[i] = (1 / mat_density) * @@ -101,22 +121,7 @@ class FluorocarbonSurfaceModel : public psSurfaceModel { ionSputteringRate->at(i) * totalIonFlux * (1 - eCoverage->at(i))); } - if (std::isnan(etchRate[i])) { - std::cout << "Error in calculating etch rate at point x = " - << coordinates[i][0] << ", y = " << coordinates[i][1] - << ", z = " << coordinates[i][2] << std::endl; - std::cout << "Material: " << static_cast(matId) << std::endl; - std::cout << "Rates and coverages at this point:\neCoverage: " - << eCoverage->at(i) << "\npCoverage: " << pCoverage->at(i) - << "\npeCoverage: " << peCoverage->at(i) - << "\nionEnhancedRate: " << ionEnhancedRate->at(i) - << "\nionSputteringRate: " << ionSputteringRate->at(i) - << "\nionpeRate: " << ionpeRate->at(i) - << "\npolyRate: " << polyRate->at(i) << std::endl; - } - - // etch rate is in cm / s - etchRate[i] *= 1e7; // to convert to nm / s + // etch rate is in nm / s assert(!std::isnan(etchRate[i]) && "etchRate NaN"); } @@ -129,14 +134,13 @@ class FluorocarbonSurfaceModel : public psSurfaceModel { return psSmartPointer>::New(etchRate); } - void - updateCoverages(psSmartPointer> Rates) override { + void updateCoverages(psSmartPointer> Rates, + const std::vector &materialIds) override { const auto ionEnhancedRate = Rates->getScalarData("ionEnhancedRate"); const auto ionpeRate = Rates->getScalarData("ionpeRate"); const auto polyRate = Rates->getScalarData("polyRate"); const auto etchantRate = Rates->getScalarData("etchantRate"); - const auto etchantOnPolyRate = Rates->getScalarData("etchantOnPolyRate"); const auto eCoverage = Coverages->getScalarData("eCoverage"); const auto pCoverage = Coverages->getScalarData("pCoverage"); @@ -149,42 +153,53 @@ class FluorocarbonSurfaceModel : public psSurfaceModel { peCoverage->resize(numPoints); // pe coverage - for (size_t i = 0; i < numPoints; ++i) { - if (etchantOnPolyRate->at(i) == 0.) { + for (std::size_t i = 0; i < numPoints; ++i) { + if (etchantRate->at(i) == 0.) { peCoverage->at(i) = 0.; } else { - peCoverage->at(i) = (etchantOnPolyRate->at(i) * totalEtchantFlux) / - (etchantOnPolyRate->at(i) * totalEtchantFlux + + peCoverage->at(i) = (etchantRate->at(i) * totalEtchantFlux * beta_pe) / + (etchantRate->at(i) * totalEtchantFlux * beta_pe + ionpeRate->at(i) * totalIonFlux); } assert(!std::isnan(peCoverage->at(i)) && "peCoverage NaN"); } // polymer coverage - for (size_t i = 0; i < numPoints; ++i) { - if (polyRate->at(i) == 0.) { + for (std::size_t i = 0; i < numPoints; ++i) { + if (polyRate->at(i) < eps) { pCoverage->at(i) = 0.; } else if (peCoverage->at(i) < eps || ionpeRate->at(i) < eps) { pCoverage->at(i) = 1.; } else { pCoverage->at(i) = - std::min((polyRate->at(i) * totalPolyFlux - delta_p) / + std::max((polyRate->at(i) * totalPolyFlux * beta_p - delta_p) / (ionpeRate->at(i) * totalIonFlux * peCoverage->at(i)), - 1.); + 0.); } assert(!std::isnan(pCoverage->at(i)) && "pCoverage NaN"); } // etchant coverage - for (size_t i = 0; i < numPoints; ++i) { + for (std::size_t i = 0; i < numPoints; ++i) { if (pCoverage->at(i) < 1.) { if (etchantRate->at(i) == 0.) { eCoverage->at(i) = 0; } else { + NumericType F_ev; + if (psMaterialMap::isMaterial(materialIds[i], psMaterial::Si)) { + F_ev = psParameters::Si::K * totalEtchantFlux * + std::exp(-psParameters::Si::E_a / + (psParameters::kB * psParameters::roomTemperature)); + } else { + F_ev = psParameters::SiO2::K * totalEtchantFlux * + std::exp(-psParameters::SiO2::E_a / + (psParameters::kB * psParameters::roomTemperature)); + } eCoverage->at(i) = - (etchantRate->at(i) * totalEtchantFlux * (1 - pCoverage->at(i))) / + (etchantRate->at(i) * totalEtchantFlux * beta_e * + (1 - pCoverage->at(i))) / (k_ie * ionEnhancedRate->at(i) * totalIonFlux + k_ev * F_ev + - etchantRate->at(i) * totalEtchantFlux); + etchantRate->at(i) * totalEtchantFlux * beta_e); } } else { eCoverage->at(i) = 0.; @@ -194,24 +209,14 @@ class FluorocarbonSurfaceModel : public psSurfaceModel { } private: - static constexpr double rho_SiO2 = 2.3 * 1e7; // in (1e15 atoms/cm³) - static constexpr double rho_SiNx = 2.3 * 1e7; // in (1e15 atoms/cm³) - static constexpr double rho_Si = 5.02 * 1e7; // in (1e15 atoms/cm³) - static constexpr double rho_p = 2 * 1e7; // in (1e15 atoms/cm³) - - static constexpr double k_ie = 1.; - static constexpr double k_ev = 1.; - - static constexpr double delta_p = 0.; - - static constexpr double kB = 0.000086173324; // m² kg s⁻² K⁻¹ - static constexpr double temperature = 300.; // K + static constexpr double k_ie = 2.; + static constexpr double k_ev = 2.; // fluxes in (1e15 /cm²) const NumericType totalIonFlux; const NumericType totalEtchantFlux; const NumericType totalPolyFlux; - const NumericType F_ev; + const NumericType delta_p; const NumericType etchStopDepth = 0.; }; @@ -222,10 +227,12 @@ class FluorocarbonSurfaceModel : public psSurfaceModel { // Electrochemical Society 150(10) 2003 pp. 1896-1902 template -class FluorocarbonIon - : public rayParticle, NumericType> { +class Ion : public rayParticle, NumericType> { public: - FluorocarbonIon(const NumericType passedPower) : power(passedPower) {} + Ion(const NumericType passedMeanEnergy, const NumericType passedSigmaEnergy, + const NumericType passedPower) + : meanEnergy(passedMeanEnergy), sigmaEnergy(passedSigmaEnergy), + power(passedPower) {} void surfaceCollision(NumericType rayWeight, const rayTriple &rayDir, const rayTriple &geomNormal, @@ -238,15 +245,26 @@ class FluorocarbonIon assert(E >= 0 && "Negative energy ion"); const auto cosTheta = -rayInternal::DotProduct(rayDir, geomNormal); + const double angle = + std::acos(std::max(std::min(cosTheta, NumericType(1)), NumericType(0))); assert(cosTheta >= 0 && "Hit backside of disc"); assert(cosTheta <= 1 + 4 && "Error in calculating cos theta"); + NumericType f_theta; + if (cosTheta > 0.5) { + f_theta = 1.; + } else { + f_theta = 3. - 6. * angle / M_PI; + } + const auto sqrtE = std::sqrt(E); - const auto f_e_sp = (1 + B_sp * (1 - cosTheta * cosTheta)) * cosTheta; - const auto Y_s = Ae_sp * std::max(sqrtE - sqrtE_th_sp, 0.) * f_e_sp; - const auto Y_ie = Ae_ie * std::max(sqrtE - sqrtE_th_ie, 0.) * cosTheta; - const auto Y_p = Ap_ie * std::max(sqrtE - sqrtE_th_p, 0.) * cosTheta; + auto Y_s = psParameters::Si::A_sp * + std::max(sqrtE - psParameters::Si::Eth_sp_Ar_sqrt, 0.) * + (1 + psParameters::Si::B_sp * (1 - cosTheta * cosTheta)) * + cosTheta; + auto Y_ie = Ae_ie * std::max(sqrtE - sqrtE_th_ie, 0.) * f_theta; + auto Y_p = Ap_ie * std::max(sqrtE - sqrtE_th_p, 0.) * f_theta; // sputtering yield Y_s localData.getVectorData(0)[primID] += rayWeight * Y_s; @@ -263,30 +281,30 @@ class FluorocarbonIon const unsigned int primId, const int materialId, const rayTracingData *globalData, rayRNG &Rng) override final { - const auto cosTheta = -rayInternal::DotProduct(rayDir, geomNormal); - const double Phi = std::acos(cosTheta); + // Small incident angles are reflected with the energy fraction centered at + // 0 + double incAngle = std::acos(-rayInternal::DotProduct(rayDir, geomNormal)); double Eref_peak; - if (Phi >= Phi_inflect) { - Eref_peak = - 1 - (1 - A) * std::pow((halfPI - Phi) / (halfPI - Phi_inflect), n_r); + if (incAngle >= psParameters::Ion::inflectAngle) { + Eref_peak = (1 - (1 - psParameters::Ion::A) * (M_PI_2 - incAngle) / + (M_PI_2 - psParameters::Ion::inflectAngle)); } else { - Eref_peak = A * std::pow(Phi / Phi_inflect, n_l); + Eref_peak = psParameters::Ion::A * + std::pow(incAngle / psParameters::Ion::inflectAngle, + psParameters::Ion::n_l); } - - const double TempEnergy = Eref_peak * E; - double NewEnergy; + // Gaussian distribution around the Eref_peak scaled by the particle energy + NumericType NewEnergy; + std::normal_distribution normalDist(Eref_peak * E, 0.1 * E); do { - NewEnergy = - TempEnergy + (std::min((E - TempEnergy), TempEnergy) + 0.05 * E) * - (1 - 2. * uniDist(Rng)) * - std::sqrt(std::fabs(std::log(uniDist(Rng)))); + NewEnergy = normalDist(Rng); } while (NewEnergy > E || NewEnergy < 0.); - if (NewEnergy > 4.) { + if (NewEnergy > minEnergy) { E = NewEnergy; auto direction = rayReflectionSpecular(rayDir, geomNormal); - return std::pair>{1 - Eref_peak, + return std::pair>{1. - Eref_peak, direction}; } else { return std::pair>{ @@ -294,55 +312,35 @@ class FluorocarbonIon } } void initNew(rayRNG &RNG) override final { + std::normal_distribution normalDist{meanEnergy, sigmaEnergy}; do { - auto rand1 = uniDist(RNG) * (twoPI - 2 * peak) + peak; - E = (1 + std::cos(rand1)) * (power / 2 * 0.75 + 10); - } while (E < 4.); + E = normalDist(RNG); + } while (E < minEnergy); } - int getRequiredLocalDataSize() const override final { return 3; } - NumericType getSourceDistributionPower() const override final { return 100.; } - std::vector getLocalDataLabels() const override final { - return std::vector{"ionSputteringRate", "ionEnhancedRate", - "ionpeRate"}; + NumericType getSourceDistributionPower() const override final { + return power; } - - void logData(rayDataLog &dataLog) override final { - NumericType max = 0.75 * power + 20 + 1e-6; - int idx = static_cast(50 * E / max); - assert(idx < 50 && idx >= 0); - dataLog.data[0][idx] += 1.; + std::vector getLocalDataLabels() const override final { + return {"ionSputteringRate", "ionEnhancedRate", "ionpeRate"}; } private: - static constexpr double sqrtE_th_sp = 4.2426406871; static constexpr double sqrtE_th_ie = 2.; static constexpr double sqrtE_th_p = 2.; - static constexpr double twoPI = 6.283185307179586; - static constexpr double halfPI = 6.283185307179586 / 4; - - static constexpr double Ae_sp = 0.00339; static constexpr double Ae_ie = 0.0361; - static constexpr double Ap_ie = 8 * 0.0361; - - static constexpr double B_sp = 9.3; - - static constexpr double Phi_inflect = 1.55334303; - static constexpr double n_r = 1.; - static constexpr double n_l = 10.; - - const double A = 1. / (1. + (n_l / n_r) * (halfPI / Phi_inflect - 1.)); - std::uniform_real_distribution uniDist; + static constexpr double Ap_ie = 4 * 0.0361; + static constexpr NumericType minEnergy = 4.; + const NumericType meanEnergy; + const NumericType sigmaEnergy; const NumericType power; - static constexpr double peak = 0.2; NumericType E; }; template -class FluorocarbonPolymer - : public rayParticle, NumericType> { +class Polymer : public rayParticle, NumericType> { public: void surfaceCollision(NumericType rayWeight, const rayTriple &rayDir, @@ -352,7 +350,7 @@ class FluorocarbonPolymer const rayTracingData *globalData, rayRNG &Rng) override final { // collect data for this hit - localData.getVectorData(0)[primID] += rayWeight * gamma_p; + localData.getVectorData(0)[primID] += rayWeight; } std::pair> surfaceReflection(NumericType rayWeight, const rayTriple &rayDir, @@ -361,21 +359,22 @@ class FluorocarbonPolymer const rayTracingData *globalData, rayRNG &Rng) override final { auto direction = rayReflectionDiffuse(geomNormal, Rng); - return std::pair>{gamma_p, direction}; + NumericType stick; + if (psMaterialMap::isMaterial(materialId, psMaterial::Mask)) { + stick = beta_p_mask; + } else { + stick = beta_p; + } + return std::pair>{stick, direction}; } - int getRequiredLocalDataSize() const override final { return 1; } NumericType getSourceDistributionPower() const override final { return 1.; } std::vector getLocalDataLabels() const override final { - return std::vector{"polyRate"}; + return {"polyRate"}; } - -private: - static constexpr NumericType gamma_p = 0.26; }; template -class FluorocarbonEtchant - : public rayParticle, NumericType> { +class Etchant : public rayParticle, NumericType> { public: void surfaceCollision(NumericType rayWeight, const rayTriple &rayDir, @@ -385,92 +384,69 @@ class FluorocarbonEtchant const rayTracingData *globalData, rayRNG &Rng) override final { // collect data for this hit - localData.getVectorData(0)[primID] += rayWeight * gamma_e; + localData.getVectorData(0)[primID] += rayWeight; } std::pair> surfaceReflection(NumericType rayWeight, const rayTriple &rayDir, const rayTriple &geomNormal, - const unsigned int primId, const int materialId, + const unsigned int primID, const int materialId, const rayTracingData *globalData, rayRNG &Rng) override final { auto direction = rayReflectionDiffuse(geomNormal, Rng); - // const auto &phi_e = globalData.getVectorData(1)[id]; - NumericType sticking = gamma_e; // * phi_e; - return std::pair>{sticking, direction}; - } - int getRequiredLocalDataSize() const override final { return 1; } - NumericType getSourceDistributionPower() const override final { return 1.; } - std::vector getLocalDataLabels() const override final { - return std::vector{"etchantRate"}; - } -private: - static constexpr NumericType gamma_e = 0.9; -}; + const auto &phi_e = globalData->getVectorData(0)[primID]; + const auto &phi_p = globalData->getVectorData(1)[primID]; + const auto &phi_pe = globalData->getVectorData(2)[primID]; -template -class FluorocarbonEtchantOnPoly - : public rayParticle, - NumericType> { -public: - void surfaceCollision(NumericType rayWeight, - const rayTriple &rayDir, - const rayTriple &geomNormal, - const unsigned int primID, const int materialId, - rayTracingData &localData, - const rayTracingData *globalData, - rayRNG &Rng) override final { - // collect data for this hit - localData.getVectorData(0)[primID] += rayWeight * gamma_pe; - } - std::pair> - surfaceReflection(NumericType rayWeight, const rayTriple &rayDir, - const rayTriple &geomNormal, - const unsigned int primId, const int materialId, - const rayTracingData *globalData, - rayRNG &Rng) override final { - auto direction = rayReflectionDiffuse(geomNormal, Rng); - // const auto &phi_pe = globalData.getVectorData(0)[id]; - NumericType sticking = gamma_pe; // * phi_pe; - return std::pair>{sticking, direction}; + NumericType Seff; + if (psMaterialMap::isMaterial(materialId, psMaterial::Mask)) { + Seff = beta_e_mask * std::max(1. - phi_e - phi_p, 0.); + } else if (psMaterialMap::isMaterial(materialId, psMaterial::Polymer)) { + Seff = beta_pe * std::max(1. - phi_pe, 0.); + } else { + Seff = beta_e * std::max(1. - phi_e - phi_p, 0.); + } + + return std::pair>{Seff, direction}; } - int getRequiredLocalDataSize() const override final { return 1; } NumericType getSourceDistributionPower() const override final { return 1.; } std::vector getLocalDataLabels() const override final { - return std::vector{"etchantOnPolyRate"}; + return {"etchantRate"}; } - -private: - static constexpr NumericType gamma_pe = 0.6; }; +} // namespace FluorocarbonImplementation template class FluorocarbonEtching : public psProcessModel { public: FluorocarbonEtching(const double ionFlux, const double etchantFlux, - const double polyFlux, const NumericType rfBiasPower, - const NumericType etchStopDepth = 0.) { + const double polyFlux, const NumericType meanEnergy, + const NumericType sigmaEnergy, + const NumericType ionExponent = 100., + const NumericType deltaP = 0., + const NumericType etchStopDepth = + std::numeric_limits::lowest()) { // particles - auto ion = std::make_unique>(rfBiasPower); - auto etchant = std::make_unique>(); - auto poly = std::make_unique>(); - auto etchantOnPoly = - std::make_unique>(); + auto ion = std::make_unique>( + meanEnergy, sigmaEnergy, ionExponent); + auto etchant = + std::make_unique>(); + auto poly = + std::make_unique>(); // surface model - auto surfModel = - psSmartPointer>::New( - ionFlux, etchantFlux, polyFlux, etchStopDepth); + auto surfModel = psSmartPointer>::New(ionFlux, etchantFlux, polyFlux, deltaP, + etchStopDepth); // velocity field - auto velField = psSmartPointer>::New(); + auto velField = psSmartPointer>::New(2); this->setSurfaceModel(surfModel); this->setVelocityField(velField); this->setProcessName("FluorocarbonEtching"); - this->insertNextParticleType(ion, 50); + this->insertNextParticleType(ion); this->insertNextParticleType(etchant); this->insertNextParticleType(poly); - this->insertNextParticleType(etchantOnPoly); } }; \ No newline at end of file diff --git a/include/Models/GeometricDistributionModels.hpp b/include/Models/GeometricDistributionModels.hpp index 412563a3..aa024ee5 100644 --- a/include/Models/GeometricDistributionModels.hpp +++ b/include/Models/GeometricDistributionModels.hpp @@ -30,7 +30,7 @@ class GeometricDistributionModel : public psGeometricModel { : dist(passedDist), mask(passedMask) {} void apply() { - if (dist) + if (dist) { if (mask) { lsGeometricAdvect(domain->getLevelSets()->back(), dist, mask) @@ -39,6 +39,7 @@ class GeometricDistributionModel : public psGeometricModel { lsGeometricAdvect(domain->getLevelSets()->back(), dist) .apply(); } + } } }; diff --git a/include/Models/IsotropicProcess.hpp b/include/Models/IsotropicProcess.hpp index 1525fff9..fde8681f 100644 --- a/include/Models/IsotropicProcess.hpp +++ b/include/Models/IsotropicProcess.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -8,21 +9,22 @@ template class IsotropicVelocityField : public psVelocityField { const NumericType vel = 1.; - const int maskId; + const psMaterial maskMaterial; public: - IsotropicVelocityField(const NumericType rate, const int mask = 0) - : vel(rate), maskId(mask) {} + IsotropicVelocityField(const NumericType rate, + const psMaterial mask = psMaterial::Mask) + : vel(rate), maskMaterial(mask) {} NumericType getScalarVelocity(const std::array & /*coordinate*/, int material, const std::array & /* normalVector */, unsigned long /*pointID*/) override { - if (material != maskId) { - return vel; + if (psMaterialMap::isMaterial(material, maskMaterial)) { + return 0.; } else { - return 0; + return vel; } } @@ -31,29 +33,17 @@ class IsotropicVelocityField : public psVelocityField { int getTranslationFieldOptions() const override { return 0; } }; -template -class IsotropicSurfaceModel : public psSurfaceModel { -public: - psSmartPointer> calculateVelocities( - psSmartPointer> Rates, - const std::vector> &coordinates, - const std::vector &materialIds) override { - return nullptr; - } -}; - template class IsotropicProcess : public psProcessModel { public: - IsotropicProcess(const NumericType isotropicRate = 0., - const int maskId = -1) { - // surface model - auto surfModel = - psSmartPointer>::New(); + IsotropicProcess(const NumericType isotropicRate, + const psMaterial maskMaterial = psMaterial::Mask) { + // default surface model + auto surfModel = psSmartPointer>::New(); // velocity field auto velField = psSmartPointer>::New( - isotropicRate, maskId); + isotropicRate, maskMaterial); this->setSurfaceModel(surfModel); this->setVelocityField(velField); diff --git a/include/Models/ModelParameters.hpp b/include/Models/ModelParameters.hpp new file mode 100644 index 00000000..1d5488e5 --- /dev/null +++ b/include/Models/ModelParameters.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include + +namespace psParameters { + +constexpr double kB = 8.617333262 * 1e-5; // eV / K +constexpr double roomTemperature = 300.; // K + +namespace Si { + +// sputtering coefficients in Ar +constexpr double Eth_sp_Ar = 20.; // eV +constexpr double Eth_sp_Ar_sqrt = 4.47213595499958; +constexpr double A_sp = 0.0337; +constexpr double B_sp = 9.3; + +// chemical etching +constexpr double K = 0.029997010728956663; +constexpr double E_a = 0.108; // eV + +// density +constexpr double rho = 5.02; // 1e22 atoms/cm³ + +} // namespace Si + +namespace SiO2 { + +// sputtering coefficients in Ar +constexpr double Eth_sp_Ar = 18.; // eV +constexpr double Eth_sp_Ar_sqrt = 4.242640687119285; +constexpr double A_sp = 0.0139; + +// chemical etching +constexpr double K = 0.002789491704544977; +constexpr double E_a = 0.168; // eV + +// density +constexpr double rho = 2.3; // 1e22 atoms/cm³ + +} // namespace SiO2 + +namespace Polymer { +// density +static constexpr double rho = 2; // 1e22 atoms/cm³ +} // namespace Polymer + +namespace Si3N4 { +// density +static constexpr double rho = 2.3; // 1e22 atoms/cm³ +} // namespace Si3N4 + +namespace Mask { +// density +static constexpr double rho = 500.; // 1e22 atoms/cm³ +} // namespace Mask + +namespace Ion { +constexpr double inflectAngle = 1.55334303; +constexpr double n_l = 10.; +constexpr double A = 1. / (1. + n_l * (M_PI_2 / inflectAngle - 1.)); +constexpr double minAngle = 1.3962634; +} // namespace Ion + +} // namespace psParameters \ No newline at end of file diff --git a/include/Models/SF6O2Etching.hpp b/include/Models/SF6O2Etching.hpp index 9affc25c..d6496e7c 100644 --- a/include/Models/SF6O2Etching.hpp +++ b/include/Models/SF6O2Etching.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -8,27 +8,31 @@ #include #include -#include #include #include +namespace SF6O2Implementation { + +// sticking probabilities +constexpr double beta_F = 0.7; +constexpr double beta_O = 1.; + template -class SF6O2SurfaceModel : public psSurfaceModel { +class SurfaceModel : public psSurfaceModel { using psSurfaceModel::Coverages; // fluxes in (1e15 /cm²) const NumericType totalIonFlux = 12.; const NumericType totalEtchantFlux = 1.8e3; const NumericType totalOxygenFlux = 1.0e2; - static constexpr double rho_Si = 5.02 * 1e7; // in (1e15 atoms/cm³) static constexpr NumericType k_sigma_Si = 3.0e2; // in (1e15 cm⁻²s⁻¹) static constexpr NumericType beta_sigma_Si = 5.0e-2; // in (1e15 cm⁻²s⁻¹) - const NumericType etchStop = 0.; + const NumericType etchStop; public: - SF6O2SurfaceModel(const double ionFlux, const double etchantFlux, - const double oxygenFlux, const NumericType etchStopDepth) + SurfaceModel(const double ionFlux, const double etchantFlux, + const double oxygenFlux, const NumericType etchStopDepth) : totalIonFlux(ionFlux), totalEtchantFlux(etchantFlux), totalOxygenFlux(oxygenFlux), etchStop(etchStopDepth) {} @@ -47,7 +51,7 @@ class SF6O2SurfaceModel : public psSurfaceModel { psSmartPointer> Rates, const std::vector> &coordinates, const std::vector &materialIds) override { - updateCoverages(Rates); + updateCoverages(Rates, materialIds); const auto numPoints = Rates->getScalarData(0)->size(); std::vector etchRate(numPoints, 0.); @@ -65,15 +69,13 @@ class SF6O2SurfaceModel : public psSurfaceModel { break; } - if (psMaterialMap::isMaterial(materialIds[i], psMaterial::Si)) { - - etchRate[i] = - -(1 / rho_Si) * - (k_sigma_Si * eCoverage->at(i) / 4. + - ionSputteringRate->at(i) * totalIonFlux + - eCoverage->at(i) * ionEnhancedRate->at(i) * totalIonFlux); + if (!psMaterialMap::isMaterial(materialIds[i], psMaterial::Mask)) { - etchRate[i] *= 1e4; // to convert to micrometers / s + etchRate[i] = -(1 / psParameters::Si::rho) * + (k_sigma_Si * eCoverage->at(i) / 4. + + ionSputteringRate->at(i) * totalIonFlux + + eCoverage->at(i) * ionEnhancedRate->at(i) * + totalIonFlux); // in nm / s } } @@ -82,11 +84,11 @@ class SF6O2SurfaceModel : public psSurfaceModel { psLogger::getInstance().addInfo("Etch stop depth reached.").print(); } - return psSmartPointer>::New(etchRate); + return psSmartPointer>::New(std::move(etchRate)); } - void - updateCoverages(psSmartPointer> Rates) override { + void updateCoverages(psSmartPointer> Rates, + const std::vector &materialIds) override { // update coverages based on fluxes const auto numPoints = Rates->getScalarData(0)->size(); @@ -96,7 +98,7 @@ class SF6O2SurfaceModel : public psSurfaceModel { const auto oxygenSputteringRate = Rates->getScalarData("oxygenSputteringRate"); - // etchant flourine coverage + // etchant fluorine coverage auto eCoverage = Coverages->getScalarData("eCoverage"); eCoverage->resize(numPoints); // oxygen coverage @@ -107,10 +109,10 @@ class SF6O2SurfaceModel : public psSurfaceModel { eCoverage->at(i) = 0; } else { eCoverage->at(i) = - etchantRate->at(i) * totalEtchantFlux / - (etchantRate->at(i) * totalEtchantFlux + + etchantRate->at(i) * totalEtchantFlux * beta_F / + (etchantRate->at(i) * totalEtchantFlux * beta_F + (k_sigma_Si + 2 * ionEnhancedRate->at(i) * totalIonFlux) * - (1 + (oxygenRate->at(i) * totalOxygenFlux) / + (1 + (oxygenRate->at(i) * totalOxygenFlux * beta_O) / (beta_sigma_Si + oxygenSputteringRate->at(i) * totalIonFlux))); } @@ -119,10 +121,10 @@ class SF6O2SurfaceModel : public psSurfaceModel { oCoverage->at(i) = 0; } else { oCoverage->at(i) = - oxygenRate->at(i) * totalOxygenFlux / - (oxygenRate->at(i) * totalOxygenFlux + + oxygenRate->at(i) * totalOxygenFlux * beta_O / + (oxygenRate->at(i) * totalOxygenFlux * beta_O + (beta_sigma_Si + oxygenSputteringRate->at(i) * totalIonFlux) * - (1 + (etchantRate->at(i) * totalEtchantFlux) / + (1 + (etchantRate->at(i) * totalEtchantFlux * beta_F) / (k_sigma_Si + 2 * ionEnhancedRate->at(i) * totalIonFlux))); } @@ -131,10 +133,12 @@ class SF6O2SurfaceModel : public psSurfaceModel { }; template -class SF6O2Ion : public rayParticle, NumericType> { +class Ion : public rayParticle, NumericType> { public: - SF6O2Ion(NumericType passedPower = 100., NumericType oxySputterYield = 3) - : power(passedPower), A_O(oxySputterYield) {} + Ion(const NumericType passedMeanEnergy, const NumericType passedSigmaEnergy, + const NumericType passedPower, const NumericType oxySputterYield) + : meanEnergy(passedMeanEnergy), sigmaEnergy(passedSigmaEnergy), + power(passedPower), A_O(oxySputterYield) {} void surfaceCollision(NumericType rayWeight, const rayTriple &rayDir, @@ -147,40 +151,39 @@ class SF6O2Ion : public rayParticle, NumericType> { // collect data for this hit assert(primID < localData.getVectorData(0).size() && "id out of bounds"); - auto cosTheta = -rayInternal::DotProduct(rayDir, geomNormal); + const double cosTheta = -rayInternal::DotProduct(rayDir, geomNormal); assert(cosTheta >= 0 && "Hit backside of disc"); assert(cosTheta <= 1 + 1e6 && "Error in calculating cos theta"); - const double angle = - std::acos(std::max(std::min(cosTheta, static_cast(1.)), - static_cast(0.))); + const double angle = std::acos(std::max(std::min(cosTheta, 1.), 0.)); + NumericType f_Si_theta; if (cosTheta > 0.5) { f_Si_theta = 1.; - f_O_theta = 1.; } else { - f_Si_theta = std::max(3. - 6. * angle / rayInternal::PI, 0.); - f_O_theta = std::max(3. - 6. * angle / rayInternal::PI, 0.); + f_Si_theta = 3. - 6. * angle / rayInternal::PI; } - - f_p_theta = (1 + B_sp * (1 - cosTheta * cosTheta)) * cosTheta; - - const double sqrtE = std::sqrt(E); - const double Y_sp = - A_sp * std::max(sqrtE - std::sqrt(Eth_sp), 0.) * f_p_theta; - const double Y_Si = + NumericType f_O_theta = f_Si_theta; + NumericType f_sp_theta = + (1 + psParameters::Si::B_sp * (1 - cosTheta * cosTheta)) * cosTheta; + + double sqrtE = std::sqrt(E); + NumericType Y_sp = psParameters::Si::A_sp * + std::max(sqrtE - psParameters::Si::Eth_sp_Ar_sqrt, 0.) * + f_sp_theta; + NumericType Y_Si = A_Si * std::max(sqrtE - std::sqrt(Eth_Si), 0.) * f_Si_theta; - const double Y_O = A_O * std::max(sqrtE - std::sqrt(Eth_O), 0.) * f_O_theta; + NumericType Y_O = A_O * std::max(sqrtE - std::sqrt(Eth_O), 0.) * f_O_theta; // sputtering yield Y_sp ionSputteringRate - localData.getVectorData(0)[primID] += Y_sp; + localData.getVectorData(0)[primID] += rayWeight * Y_sp; // ion enhanced etching yield Y_Si ionEnhancedRate - localData.getVectorData(1)[primID] += Y_Si; + localData.getVectorData(1)[primID] += rayWeight * Y_Si; // ion enhanced O sputtering yield Y_O oxygenSputteringRate - localData.getVectorData(2)[primID] += Y_O; + localData.getVectorData(2)[primID] += rayWeight * Y_O; } std::pair> @@ -189,119 +192,80 @@ class SF6O2Ion : public rayParticle, NumericType> { const unsigned int primId, const int materialId, const rayTracingData *globalData, rayRNG &Rng) override final { - auto cosTheta = -rayInternal::DotProduct(rayDir, geomNormal); assert(cosTheta >= 0 && "Hit backside of disc"); assert(cosTheta <= 1 + 1e-6 && "Error in calculating cos theta"); - const NumericType incAngle = + 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; - if (incAngle >= inflectAngle) { - Eref_peak = - Eref_max * - (1 - (1 - A) * std::pow((halfPI - incAngle) / (halfPI - inflectAngle), - n_r)); + if (incAngle >= psParameters::Ion::inflectAngle) { + Eref_peak = (1 - (1 - psParameters::Ion::A) * (M_PI_2 - incAngle) / + (M_PI_2 - psParameters::Ion::inflectAngle)); } else { - Eref_peak = Eref_max * A * std::pow(incAngle / inflectAngle, n_l); + Eref_peak = psParameters::Ion::A * + std::pow(incAngle / psParameters::Ion::inflectAngle, + psParameters::Ion::n_l); } // Gaussian distribution around the Eref_peak scaled by the particle energy - NumericType tempEnergy = Eref_peak * E; - NumericType NewEnergy; + std::normal_distribution normalDist(E * Eref_peak, 0.1 * E); do { - const auto rand1 = uniDist(Rng); - const auto rand2 = uniDist(Rng); - NewEnergy = tempEnergy + - (std::min((E - tempEnergy), tempEnergy) + E * 0.05) * - (1 - 2. * rand1) * std::sqrt(std::fabs(std::log(rand2))); - + NewEnergy = normalDist(Rng); } while (NewEnergy > E || NewEnergy < 0.); // Set the flag to stop tracing if the energy is below the threshold if (NewEnergy > minEnergy) { E = NewEnergy; + // auto direction = rayReflectionConedCosine( + // M_PI_2 - std::min(incAngle, minAngle), rayDir, geomNormal, Rng); + auto direction = rayReflectionSpecular(rayDir, geomNormal); - auto direction = rayReflectionConedCosine( - rayDir, geomNormal, Rng, std::min(incAngle, minAngle)); - - return std::pair>{0., direction}; + return std::pair> { + 1. - Eref_peak, direction + } } else { return std::pair>{ 1., rayTriple{0., 0., 0.}}; } } void initNew(rayRNG &RNG) override final { + std::normal_distribution normalDist{meanEnergy, sigmaEnergy}; do { - auto rand1 = uniDist(RNG) * (twoPI - 2 * peak) + peak; - E = (1 + std::cos(rand1)) * (power / 2 * 0.75 + 10); + E = normalDist(RNG); } while (E < minEnergy); } - - int getRequiredLocalDataSize() const override final { return 3; } - NumericType getSourceDistributionPower() const override final { - return 1000.; + return power; } - std::vector getLocalDataLabels() const override final { - return std::vector{"ionSputteringRate", "ionEnhancedRate", - "oxygenSputteringRate"}; - } - - void logData(rayDataLog &dataLog) override final { - NumericType max = 0.75 * power + 20 + 1e-6; - int idx = static_cast(50 * E / max); - assert(idx < 50 && idx >= 0); - dataLog.data[0][idx] += 1.; + return {"ionSputteringRate", "ionEnhancedRate", "oxygenSputteringRate"}; } private: - std::uniform_real_distribution uniDist; - - static constexpr NumericType A_sp = 0.00339; static constexpr NumericType A_Si = 7.; - const NumericType A_O = 2.; + static constexpr NumericType B_sp = 9.3; + const NumericType A_O; - static constexpr NumericType Eth_sp = 18.; static constexpr NumericType Eth_Si = 15.; static constexpr NumericType Eth_O = 10.; - static constexpr NumericType B_sp = 9.3; - - static constexpr NumericType Eref_max = 1.; - - static constexpr NumericType inflectAngle = 1.55334; - static constexpr NumericType minAngle = 1.3962634; - static constexpr NumericType n_l = 10.; - static constexpr NumericType n_r = 1.; - - static constexpr NumericType twoPI = 6.283185307179586; - static constexpr NumericType halfPI = 6.283185307179586 / 4; - - static constexpr NumericType A = - 1. / (1. + (n_l / n_r) * (halfPI / inflectAngle - 1.)); - - NumericType f_p_theta; - NumericType f_Si_theta; - NumericType f_O_theta; - NumericType f_SiO2_theta; // ion energy static constexpr NumericType minEnergy = 4.; // Discard particles with energy < 1eV + const NumericType meanEnergy; + const NumericType sigmaEnergy; const NumericType power; - static constexpr NumericType peak = 0.2; NumericType E; }; template -class SF6O2Etchant - : public rayParticle, NumericType> { +class Etchant : public rayParticle, NumericType> { public: void surfaceCollision(NumericType rayWeight, const rayTriple &rayDir, @@ -310,17 +274,7 @@ class SF6O2Etchant rayTracingData &localData, const rayTracingData *globalData, rayRNG &Rng) override final { - - // NumericType Seff; - // // F surface coverage - // const auto &phi_F = globalData->getVectorData(0)[primID]; - // // O surface coverage - // const auto &phi_O = globalData->getVectorData(1)[primID]; - - // Seff = gamma_F * std::max(1. - phi_F - phi_O, 0.); - - // Rate is normalized by dividing with the local sticking coefficient - localData.getVectorData(0)[primID] += rayWeight; // * Seff; + localData.getVectorData(0)[primID] += rayWeight; } std::pair> surfaceReflection(NumericType rayWeight, const rayTriple &rayDir, @@ -328,32 +282,25 @@ class SF6O2Etchant const unsigned int primID, const int materialId, const rayTracingData *globalData, rayRNG &Rng) override final { - auto direction = rayReflectionDiffuse(geomNormal, Rng); - NumericType Seff; // 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 - Seff = gamma_F * std::max(1. - phi_F - phi_O, 0.); + NumericType Seff = beta_F * std::max(1. - phi_F - phi_O, 0.); + auto direction = rayReflectionDiffuse(geomNormal, Rng); return std::pair>{Seff, direction}; } - void initNew(rayRNG &RNG) override final {} - int getRequiredLocalDataSize() const override final { return 1; } NumericType getSourceDistributionPower() const override final { return 1.; } std::vector getLocalDataLabels() const override final { - return std::vector{"etchantRate"}; + return {"etchantRate"}; } - -private: - static constexpr NumericType gamma_F = 0.7; }; template -class SF6O2Oxygen - : public rayParticle, NumericType> { +class Oxygen : public rayParticle, NumericType> { public: void surfaceCollision(NumericType rayWeight, const rayTriple &rayDir, @@ -362,15 +309,8 @@ class SF6O2Oxygen rayTracingData &localData, const rayTracingData *globalData, rayRNG &Rng) override final { - // NumericType Seff; - // // F surface coverage - // const auto &phi_F = globalData->getVectorData(0)[primID]; - // // O surface coverage - // const auto &phi_O = globalData->getVectorData(1)[primID]; - // Seff = gamma_O * std::max(1. - phi_O - phi_F, 0.); - // Rate is normalized by dividing with the local sticking coefficient - localData.getVectorData(0)[primID] += rayWeight; // * Seff; + localData.getVectorData(0)[primID] += rayWeight; } std::pair> surfaceReflection(NumericType rayWeight, const rayTriple &rayDir, @@ -378,51 +318,52 @@ class SF6O2Oxygen const unsigned int primID, const int materialId, const rayTracingData *globalData, rayRNG &Rng) override final { - auto direction = rayReflectionDiffuse(geomNormal, Rng); NumericType Seff; const auto &phi_F = globalData->getVectorData(0)[primID]; - // O surface coverage const auto &phi_O = globalData->getVectorData(1)[primID]; - Seff = gamma_O * std::max(1. - phi_O - phi_F, 0.); + Seff = beta_O * std::max(1. - phi_O - phi_F, 0.); + auto direction = rayReflectionDiffuse(geomNormal, Rng); return std::pair>{Seff, direction}; } - void initNew(rayRNG &RNG) override final {} - int getRequiredLocalDataSize() const override final { return 1; } NumericType getSourceDistributionPower() const override final { return 1.; } std::vector getLocalDataLabels() const override final { - return std::vector{"oxygenRate"}; + return {"oxygenRate"}; } - -private: - static constexpr NumericType gamma_O = 1.; }; +} // namespace SF6O2Implementation template class SF6O2Etching : public psProcessModel { public: SF6O2Etching(const double ionFlux, const double etchantFlux, - const double oxygenFlux, const NumericType rfBias, + const double oxygenFlux, const NumericType meanEnergy, + const NumericType sigmaEnergy, + const NumericType ionExponent = 100., const NumericType oxySputterYield = 2., - const NumericType etchStopDepth = 0.) { + const NumericType etchStopDepth = + std::numeric_limits::lowest()) { // particles - auto ion = - std::make_unique>(rfBias, oxySputterYield); - auto etchant = std::make_unique>(); - auto oxygen = std::make_unique>(); + auto ion = std::make_unique>( + meanEnergy, sigmaEnergy, ionExponent, oxySputterYield); + auto etchant = + std::make_unique>(); + auto oxygen = + std::make_unique>(); // surface model - auto surfModel = psSmartPointer>::New( - ionFlux, etchantFlux, oxygenFlux, etchStopDepth); + auto surfModel = + psSmartPointer>::New( + ionFlux, etchantFlux, oxygenFlux, etchStopDepth); // velocity field - auto velField = psSmartPointer>::New(); + auto velField = psSmartPointer>::New(2); this->setSurfaceModel(surfModel); this->setVelocityField(velField); this->setProcessName("SF6O2Etching"); - this->insertNextParticleType(ion, 50 /* log particle energies */); + this->insertNextParticleType(ion); this->insertNextParticleType(etchant); this->insertNextParticleType(oxygen); } diff --git a/include/Models/SimpleDeposition.hpp b/include/Models/SimpleDeposition.hpp index 6ed8715c..d5cd7d71 100644 --- a/include/Models/SimpleDeposition.hpp +++ b/include/Models/SimpleDeposition.hpp @@ -9,26 +9,32 @@ #include #include +namespace SimpleDepositionImplementation { template -class SimpleDepositionSurfaceModel : public psSurfaceModel { +class SurfaceModel : public psSurfaceModel { + const NumericType rate; + public: + SurfaceModel(const NumericType pRate) : rate(pRate) {} + psSmartPointer> calculateVelocities( psSmartPointer> Rates, const std::vector> &coordinates, const std::vector &materialIds) override { - const auto depoRate = Rates->getScalarData("depoRate"); - return psSmartPointer>::New(*depoRate); + auto depoRate = *Rates->getScalarData("depoRate"); + std::for_each(depoRate.begin(), depoRate.end(), + [this](NumericType &v) { v *= rate; }); + + return psSmartPointer>::New(std::move(depoRate)); } }; template -class SimpleDepositionParticle - : public rayParticle, - NumericType> { +class Particle : public rayParticle, NumericType> { public: - SimpleDepositionParticle(const NumericType passedSticking, - const NumericType passedSourcePower) + Particle(const NumericType passedSticking, + const NumericType passedSourcePower) : stickingProbability(passedSticking), sourcePower(passedSourcePower) {} void surfaceCollision(NumericType rayWeight, @@ -51,32 +57,34 @@ class SimpleDepositionParticle direction}; } void initNew(rayRNG &RNG) override final {} - int getRequiredLocalDataSize() const override final { return 1; } NumericType getSourceDistributionPower() const override final { return sourcePower; } std::vector getLocalDataLabels() const override final { - return std::vector{"depoRate"}; + return {"depoRate"}; } private: const NumericType stickingProbability = 0.1; const NumericType sourcePower = 1.; }; +} // namespace SimpleDepositionImplementation template class SimpleDeposition : public psProcessModel { public: - SimpleDeposition(const NumericType stickingProbability = 0.1, + SimpleDeposition(const NumericType rate = 1., + const NumericType stickingProbability = 0.1, const NumericType sourceDistributionPower = 1.) { // particles - auto depoParticle = - std::make_unique>( - stickingProbability, sourceDistributionPower); + auto depoParticle = std::make_unique< + SimpleDepositionImplementation::Particle>( + stickingProbability, sourceDistributionPower); // surface model auto surfModel = - psSmartPointer>::New(); + psSmartPointer>::New(rate); // velocity field auto velField = psSmartPointer>::New(); diff --git a/include/Models/StackRedeposition.hpp b/include/Models/StackRedeposition.hpp index 42552498..28874467 100644 --- a/include/Models/StackRedeposition.hpp +++ b/include/Models/StackRedeposition.hpp @@ -182,7 +182,7 @@ class ByproductDynamics : public psAdvectionCallback { cellSet->updateMaterials(); const auto gridDelta = cellSet->getGridDelta(); - // add byproducs + // add byproducts for (size_t j = 0; j < nodes.size(); j++) { cellSet->addFillingFraction(nodes[j], etchRate * advectedTime / gridDelta); @@ -214,7 +214,7 @@ class ByproductDynamics : public psAdvectionCallback { #pragma omp parallel for for (int e = 0; e < data->size(); e++) { - if (psMaterialMap::isMaterial(materialIds->at(e), psMaterial::GAS)) { + if (!psMaterialMap::isMaterial(materialIds->at(e), psMaterial::GAS)) { continue; } @@ -302,7 +302,7 @@ class OxideRegrowthModel : public psProcessModel { const NumericType redepositionThreshold, const NumericType redepositionTimeInt, const NumericType diffusionCoefficient, const NumericType sinkStrength, - const NumericType scallopVelocitiy, const NumericType centerVelocity, + const NumericType scallopVelocity, const NumericType centerVelocity, const NumericType topHeight, const NumericType centerWidth) { auto veloField = @@ -312,7 +312,7 @@ class OxideRegrowthModel : public psProcessModel { auto surfModel = psSmartPointer>::New(); auto dynamics = psSmartPointer>::New( - diffusionCoefficient, sinkStrength, scallopVelocitiy, centerVelocity, + diffusionCoefficient, sinkStrength, scallopVelocity, centerVelocity, topHeight, centerWidth / 2., nitrideEtchRate, redepositionRate, redepositionThreshold, redepositionTimeInt); diff --git a/include/Models/TEOSDeposition.hpp b/include/Models/TEOSDeposition.hpp index a5ba1d76..bf6faa8e 100644 --- a/include/Models/TEOSDeposition.hpp +++ b/include/Models/TEOSDeposition.hpp @@ -3,21 +3,23 @@ #include #include +namespace TEOSDepositionImplementation { template -class SingleTEOSSurfaceModel : public psSurfaceModel { +class SingleSurfaceModel : public psSurfaceModel { using psSurfaceModel::Coverages; const NumericType depositionRate; const NumericType reactionOrder; public: - SingleTEOSSurfaceModel(const NumericType passedRate, - const NumericType passedOrder) + SingleSurfaceModel(const NumericType passedRate, + const NumericType passedOrder) : depositionRate(passedRate), reactionOrder(passedOrder) {} psSmartPointer> calculateVelocities( psSmartPointer> Rates, const std::vector> &coordinates, const std::vector &materialIDs) override { + updateCoverages(Rates, materialIDs); // define the surface reaction here auto particleFlux = Rates->getScalarData("particleFlux"); std::vector velocity(particleFlux->size(), 0.); @@ -31,15 +33,15 @@ class SingleTEOSSurfaceModel : public psSurfaceModel { return psSmartPointer>::New(velocity); } - void - updateCoverages(psSmartPointer> Rates) override { + void updateCoverages(psSmartPointer> Rates, + const std::vector &materialIDs) override { // update coverages based on fluxes auto particleFlux = Rates->getScalarData("particleFlux"); auto Coverage = Coverages->getScalarData("Coverage"); assert(Coverage->size() == particleFlux->size()); for (std::size_t i = 0; i < Coverage->size(); i++) { - Coverage->at(i) = std::min(particleFlux->at(i), 1.); + Coverage->at(i) = std::min(particleFlux->at(i), NumericType(1)); } } @@ -55,17 +57,17 @@ class SingleTEOSSurfaceModel : public psSurfaceModel { }; template -class MultiTEOSSurfaceModel : public psSurfaceModel { +class MultiSurfaceModel : public psSurfaceModel { const NumericType depositionRateP1; const NumericType reactionOrderP1; const NumericType depositionRateP2; const NumericType reactionOrderP2; public: - MultiTEOSSurfaceModel(const NumericType passedRateP1, - const NumericType passedOrderP1, - const NumericType passedRateP2, - const NumericType passedOrderP2) + MultiSurfaceModel(const NumericType passedRateP1, + const NumericType passedOrderP1, + const NumericType passedRateP2, + const NumericType passedOrderP2) : depositionRateP1(passedRateP1), reactionOrderP1(passedOrderP1), depositionRateP2(passedRateP2), reactionOrderP2(passedOrderP2) {} @@ -92,12 +94,12 @@ class MultiTEOSSurfaceModel : public psSurfaceModel { // Particle type (modify at you own risk) template -class TEOSSingleParticle - : public rayParticle, NumericType> { +class SingleParticle + : public rayParticle, NumericType> { public: - TEOSSingleParticle(const NumericType pStickingProbability, - const NumericType pReactionOrder, - const std::string pDataLabel = "particleFlux") + SingleParticle(const NumericType pStickingProbability, + const NumericType pReactionOrder, + const std::string pDataLabel = "particleFlux") : stickingProbability(pStickingProbability), reactionOrder(pReactionOrder), dataLabel(pDataLabel) {} std::pair> @@ -131,7 +133,6 @@ class TEOSSingleParticle rayRNG &Rng) override final { localData.getVectorData(0)[primID] += rayWeight; } - int getRequiredLocalDataSize() const override final { return 1; } NumericType getSourceDistributionPower() const override final { return 1; } std::vector getLocalDataLabels() const override final { return {dataLabel}; @@ -144,10 +145,10 @@ class TEOSSingleParticle }; template -class TEOSMultiParticle - : public rayParticle, NumericType> { +class MultiParticle + : public rayParticle, NumericType> { public: - TEOSMultiParticle(const NumericType pStickingProbability, std::string pLabel) + MultiParticle(const NumericType pStickingProbability, std::string pLabel) : stickingProbability(pStickingProbability), dataLabel(pLabel) {} std::pair> surfaceReflection(NumericType rayWeight, const rayTriple &rayDir, @@ -168,7 +169,6 @@ class TEOSMultiParticle rayRNG &Rng) override final { localData.getVectorData(0)[primID] += rayWeight; } - int getRequiredLocalDataSize() const override final { return 1; } NumericType getSourceDistributionPower() const override final { return 1; } std::vector getLocalDataLabels() const override final { return {dataLabel}; @@ -178,6 +178,7 @@ class TEOSMultiParticle const NumericType stickingProbability; const std::string dataLabel; }; +} // namespace TEOSDepositionImplementation template class TEOSDeposition : public psProcessModel { @@ -194,12 +195,14 @@ class TEOSDeposition : public psProcessModel { // use single particle model // particle - auto particle = std::make_unique>( + auto particle = std::make_unique< + TEOSDepositionImplementation::SingleParticle>( pStickingP1, pOrderP1); // surface model - auto surfModel = psSmartPointer>::New( - pRateP1, pOrderP1); + auto surfModel = + psSmartPointer>::New(pRateP1, pOrderP1); this->setSurfaceModel(surfModel); this->insertNextParticleType(particle); @@ -208,14 +211,17 @@ class TEOSDeposition : public psProcessModel { // use multi (two) particle model // particles - auto particle1 = std::make_unique>( + auto particle1 = std::make_unique< + TEOSDepositionImplementation::MultiParticle>( pStickingP1, "particleFluxP1"); - auto particle2 = std::make_unique>( + auto particle2 = std::make_unique< + TEOSDepositionImplementation::MultiParticle>( pStickingP2, "particleFluxP2"); // surface model - auto surfModel = psSmartPointer>::New( - pRateP1, pOrderP1, pRateP2, pOrderP2); + auto surfModel = + psSmartPointer>::New(pRateP1, pOrderP1, pRateP2, pOrderP2); this->setSurfaceModel(surfModel); this->insertNextParticleType(particle1); diff --git a/include/psAdvectionCallback.hpp b/include/psAdvectionCallback.hpp index 3bdef96b..08c53bcf 100644 --- a/include/psAdvectionCallback.hpp +++ b/include/psAdvectionCallback.hpp @@ -1,5 +1,4 @@ -#ifndef PS_ADVECTION_CALLBACK -#define PS_ADVECTION_CALLBACK +#pragma once #include #include @@ -17,5 +16,3 @@ template class psAdvectionCallback { virtual bool applyPostAdvect(const NumericType advectionTime) { return true; } }; - -#endif \ No newline at end of file diff --git a/include/psDomain.hpp b/include/psDomain.hpp index 0dffc7f5..e3b5641f 100644 --- a/include/psDomain.hpp +++ b/include/psDomain.hpp @@ -1,5 +1,4 @@ -#ifndef PS_DOMAIN_HPP -#define PS_DOMAIN_HPP +#pragma once #include #include @@ -221,5 +220,3 @@ template class psDomain { } } }; - -#endif // PS_DOMAIN_HPP diff --git a/include/psExtrude.hpp b/include/psExtrude.hpp index 44fe872e..9a7c33be 100644 --- a/include/psExtrude.hpp +++ b/include/psExtrude.hpp @@ -14,8 +14,8 @@ template class psExtrude { public: psExtrude() {} - psExtrude(lsSmartPointer> passedInputDomain, - lsSmartPointer> passedOutputDomain, + psExtrude(psSmartPointer> &passedInputDomain, + psSmartPointer> &passedOutputDomain, std::array passedExtent, const int passedExtrudeDim, std::array, 3> passedBoundaryConds) : inputDomain(passedInputDomain), outputDomain(passedOutputDomain), diff --git a/include/psGDSReader.hpp b/include/psGDSReader.hpp index 9bd489fd..1292e214 100644 --- a/include/psGDSReader.hpp +++ b/include/psGDSReader.hpp @@ -63,7 +63,7 @@ template class psGDSReader { double units; // units in micron double userUnits; - // unsused + // unused float currentWidth; char *tempStr; diff --git a/include/psGeometricModel.hpp b/include/psGeometricModel.hpp index e065a280..e677424f 100644 --- a/include/psGeometricModel.hpp +++ b/include/psGeometricModel.hpp @@ -1,5 +1,4 @@ -#ifndef PS_GEOMETRIC_MODEL_HPP -#define PS_GEOMETRIC_MODEL_HPP +#pragma once #include #include @@ -15,5 +14,3 @@ template class psGeometricModel { virtual void apply(){}; }; - -#endif \ No newline at end of file diff --git a/include/psMaterials.hpp b/include/psMaterials.hpp index bdb91e9d..e4d06d62 100644 --- a/include/psMaterials.hpp +++ b/include/psMaterials.hpp @@ -11,16 +11,18 @@ enum class psMaterial : int { Si3N4 = 3, SiN = 4, SiON = 5, - PolySi = 6, - W = 7, - Al2O3 = 8, - TiN = 9, - Cu = 10, - Polymer = 11, - Dielectric = 12, - Metal = 13, - Air = 14, - GAS = 15 + SiC = 6, + PolySi = 7, + GaN = 8, + W = 9, + Al2O3 = 10, + TiN = 11, + Cu = 12, + Polymer = 13, + Dielectric = 14, + Metal = 15, + Air = 16, + GAS = 17 }; class psMaterialMap { @@ -45,7 +47,7 @@ class psMaterialMap { std::size_t size() const { return map->getNumberOfLayers(); } static inline psMaterial mapToMaterial(const int matId) { - if (matId > 15 || matId < -1) + if (matId > 17 || matId < -1) return psMaterial::Undefined; return static_cast(matId); } diff --git a/include/psPlanarize.hpp b/include/psPlanarize.hpp index c1a800d4..5732a404 100644 --- a/include/psPlanarize.hpp +++ b/include/psPlanarize.hpp @@ -31,4 +31,4 @@ template class psPlanarize { .apply(); } } -}; \ No newline at end of file +}; diff --git a/include/psPointData.hpp b/include/psPointData.hpp index 25dface4..4d121a91 100644 --- a/include/psPointData.hpp +++ b/include/psPointData.hpp @@ -1,5 +1,4 @@ -#ifndef PS_POINT_DATA_HPP -#define PS_POINT_DATA_HPP +#pragma once #include @@ -15,14 +14,14 @@ template using psPointData = lsPointData; // CellDataType data; // public: -// void insterNextCellData(std::vector &passedData, std::string +// void insertNextCellData(std::vector &passedData, std::string // dataLabel) // { // data.push_back(passedData); // labels.push_back(dataLabel); // } -// void insterNextCellData(std::vector &&passedData, +// void insertNextCellData(std::vector &&passedData, // std::string dataLabel) // { // data.push_back(std::move(passedData)); @@ -45,5 +44,3 @@ template using psPointData = lsPointData; // return nullptr; // } // }; - -#endif \ No newline at end of file diff --git a/include/psProcess.hpp b/include/psProcess.hpp index cca90c39..ef395fb1 100644 --- a/include/psProcess.hpp +++ b/include/psProcess.hpp @@ -1,5 +1,4 @@ -#ifndef PS_PROCESS -#define PS_PROCESS +#pragma once #include #include @@ -10,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -19,6 +17,9 @@ #include #include +/// This class server as the main process tool, applying a user- or pre-defined +/// process model to a domain. Depending on the user inputs surface advection, a +/// single callback function or a geometric advection is applied. template class psProcess { using translatorType = std::unordered_map; using psDomainType = psSmartPointer>; @@ -56,6 +57,12 @@ template class psProcess { integrationScheme = passedIntegrationScheme; } + /// Set the CFL condition to use during advection. + /// The CFL condition sets the maximum distance a surface can + /// be moved during one advection step. It MUST be below 0.5 + /// to guarantee numerical stability. Defaults to 0.4999. + void setTimeStepRatio(const double &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. @@ -71,7 +78,7 @@ template class psProcess { .print(); return; } - auto name = model->getProcessName(); + const auto name = model->getProcessName(); if (!domain) { psLogger::getInstance() @@ -131,6 +138,7 @@ template class psProcess { lsAdvect advectionKernel; advectionKernel.setVelocityField(transField); advectionKernel.setIntegrationScheme(integrationScheme); + advectionKernel.setTimeStepRatio(timeStepRatio); for (auto dom : *domain->getLevelSets()) { meshConverter.insertNextLevelSet(dom); @@ -204,6 +212,13 @@ template class psProcess { rayTrace.setMaterialIds(materialIds); for (size_t iterations = 0; iterations < maxIterations; iterations++) { + // We need additional signal handling when running the C++ code from + // the + // Python bindings to allow interrupts in the Python scripts +#ifdef VIENNAPS_PYTHON_BUILD + if (PyErr_CheckSignals() != 0) + throw pybind11::error_already_set(); +#endif // move coverages to the ray tracer rayTracingData rayTraceCoverages = movePointDataToRayData(model->getSurfaceModel()->getCoverages()); @@ -235,7 +250,8 @@ template class psProcess { // fill up rates vector with rates from this particle type auto &localData = rayTrace.getLocalData(); - for (int i = 0; i < particle->getRequiredLocalDataSize(); ++i) { + int numRates = particle->getLocalDataLabels().size(); + for (int i = 0; i < numRates; ++i) { auto rate = std::move(localData.getVectorData(i)); // normalize fluxes @@ -255,8 +271,7 @@ template class psProcess { // move coverages back in the model moveRayDataToPointData(model->getSurfaceModel()->getCoverages(), rayTraceCoverages); - model->getSurfaceModel()->updateCoverages(Rates); - coveragesInitialized = true; + model->getSurfaceModel()->updateCoverages(Rates, materialIds); if (psLogger::getLogLevel() >= 3) { auto coverages = model->getSurfaceModel()->getCoverages(); @@ -277,6 +292,8 @@ template class psProcess { .print(); } } + coveragesInitialized = true; + timer.finish(); psLogger::getInstance() .addTiming("Coverage initialization", timer) @@ -294,6 +311,13 @@ template class psProcess { .addInfo("Remaining time: " + std::to_string(remainingTime)) .print(); + // We need additional signal handling when running the C++ code from the + // Python bindings to allow interrupts in the Python scripts +#ifdef VIENNAPS_PYTHON_BUILD + if (PyErr_CheckSignals() != 0) + throw pybind11::error_already_set(); +#endif + auto Rates = psSmartPointer>::New(); meshConverter.apply(); auto materialIds = *diskMesh->getCellData().getScalarData("MaterialIds"); @@ -337,7 +361,7 @@ template class psProcess { rayTrace.apply(); // fill up rates vector with rates from this particle type - auto numRates = particle->getRequiredLocalDataSize(); + auto numRates = particle->getLocalDataLabels().size(); auto &localData = rayTrace.getLocalData(); for (int i = 0; i < numRates; ++i) { auto rate = std::move(localData.getVectorData(i)); @@ -367,16 +391,16 @@ template class psProcess { } // get velocities from rates - auto velocitites = model->getSurfaceModel()->calculateVelocities( + auto velocities = model->getSurfaceModel()->calculateVelocities( Rates, points, materialIds); - model->getVelocityField()->setVelocities(velocitites); + model->getVelocityField()->setVelocities(velocities); if (model->getVelocityField()->getTranslationFieldOptions() == 2) transField->buildKdTree(points); // print debug output if (psLogger::getLogLevel() >= 4) { - if (velocitites) - diskMesh->getCellData().insertNextScalarData(*velocitites, + if (velocities) + diskMesh->getCellData().insertNextScalarData(*velocities, "velocities"); if (useCoverages) { auto coverages = model->getSurfaceModel()->getCoverages(); @@ -626,6 +650,5 @@ template class psProcess { bool coveragesInitialized = false; NumericType printTime = 0.; NumericType processTime = 0.; + NumericType timeStepRatio = 0.4999; }; - -#endif diff --git a/include/psProcessModel.hpp b/include/psProcessModel.hpp index dcbdd87d..6273ec58 100644 --- a/include/psProcessModel.hpp +++ b/include/psProcessModel.hpp @@ -1,5 +1,4 @@ -#ifndef PS_PROCESS_MODEL -#define PS_PROCESS_MODEL +#pragma once #include #include @@ -9,6 +8,8 @@ #include +/// The process model combines all models (particle types, surface model, +/// geometric model, advection callback) template class psProcessModel { private: using ParticleTypeList = @@ -87,5 +88,3 @@ template class psProcessModel { passedVelocityField); } }; - -#endif \ No newline at end of file diff --git a/include/psProcessParams.hpp b/include/psProcessParams.hpp index ea2f944b..1d47f0a8 100644 --- a/include/psProcessParams.hpp +++ b/include/psProcessParams.hpp @@ -1,13 +1,9 @@ +#pragma once -#ifndef PS_PROCESS_PARAMS -#define PS_PROCESS_PARAMS - +#include #include -#include #include -// TODO: Implement ViennaPS messaging system - template class psProcessParams { private: std::vector scalarData; @@ -34,7 +30,7 @@ template class psProcessParams { return i; } } - rayMessage::getInstance() + psLogger::getInstance() .addError("Can not find scalar data label in psProcessParams.") .print(); return -1; @@ -45,12 +41,10 @@ template class psProcessParams { const std::vector &getScalarData() const { return scalarData; } std::string getScalarDataLabel(int i) const { if (i >= scalarDataLabels.size()) - rayMessage::getInstance() + psLogger::getInstance() .addError( "Getting scalar data label in psProcessParams out of range.") .print(); return scalarDataLabels[i]; } }; - -#endif \ No newline at end of file diff --git a/include/psSmartPointer.hpp b/include/psSmartPointer.hpp index 85f25863..9e4e0f01 100644 --- a/include/psSmartPointer.hpp +++ b/include/psSmartPointer.hpp @@ -1,8 +1,5 @@ -#ifndef PS_SMART_POINTER_HPP -#define PS_SMART_POINTER_HPP +#pragma once #include template using psSmartPointer = lsSmartPointer; - -#endif // PS_SMART_POINTER_HPP \ No newline at end of file diff --git a/include/psSurfaceModel.hpp b/include/psSurfaceModel.hpp index 3505055e..f90bea41 100644 --- a/include/psSurfaceModel.hpp +++ b/include/psSurfaceModel.hpp @@ -1,5 +1,4 @@ -#ifndef PS_SURFACE_MODEL -#define PS_SURFACE_MODEL +#pragma once #include #include @@ -33,8 +32,6 @@ template class psSurfaceModel { return nullptr; } - virtual void updateCoverages(psSmartPointer> Rates) { - } + virtual void updateCoverages(psSmartPointer> Rates, + const std::vector &materialIds) {} }; - -#endif \ No newline at end of file diff --git a/include/psToDiskMesh.hpp b/include/psToDiskMesh.hpp index a837e44e..e35b8f4a 100644 --- a/include/psToDiskMesh.hpp +++ b/include/psToDiskMesh.hpp @@ -37,7 +37,8 @@ template class psToDiskMesh { void apply() { lsToDiskMesh meshConverter; meshConverter.setMesh(mesh); - meshConverter.setMaterialMap(domain->getMaterialMap()->getMaterialMap()); + if (domain->getMaterialMap()) + meshConverter.setMaterialMap(domain->getMaterialMap()->getMaterialMap()); if (translator.get()) meshConverter.setTranslator(translator); for (const auto ls : *domain->getLevelSets()) { @@ -45,4 +46,4 @@ template class psToDiskMesh { } meshConverter.apply(); } -}; \ No newline at end of file +}; diff --git a/include/psToSurfaceMesh.hpp b/include/psToSurfaceMesh.hpp index 35b1fb66..5b67e763 100644 --- a/include/psToSurfaceMesh.hpp +++ b/include/psToSurfaceMesh.hpp @@ -21,4 +21,4 @@ template class psToSurfaceMesh { void setMesh(psSmartPointer> passedMesh) { meshConverter.setMesh(passedMesh); } -}; \ No newline at end of file +}; diff --git a/include/psTranslationField.hpp b/include/psTranslationField.hpp index de9c6351..8f28357e 100644 --- a/include/psTranslationField.hpp +++ b/include/psTranslationField.hpp @@ -1,5 +1,4 @@ -#ifndef PS_TRANSLATIONFIELD_HPP -#define PS_TRANSLATIONFIELD_HPP +#pragma once #include #include @@ -82,5 +81,3 @@ class psTranslationField : public lsVelocityField { const psSmartPointer> modelVelocityField; const psSmartPointer materialMap; }; - -#endif \ No newline at end of file diff --git a/include/psUtils.hpp b/include/psUtils.hpp index 30aacadb..49452edb 100644 --- a/include/psUtils.hpp +++ b/include/psUtils.hpp @@ -1,5 +1,4 @@ -#ifndef PS_UTIL_HPP -#define PS_UTIL_HPP +#pragma once #include #include @@ -185,7 +184,7 @@ class Item { } }; -// If the key is found inthe unordered_map, then the +// If the key is found in the unordered_map, then the template void AssignItems(std::unordered_map &map, Item &&item) { @@ -208,4 +207,3 @@ void AssignItems(std::unordered_map &map, } }; // namespace psUtils -#endif \ No newline at end of file diff --git a/include/psVTKWriter.hpp b/include/psVTKWriter.hpp index 7418f3ac..8fe107eb 100644 --- a/include/psVTKWriter.hpp +++ b/include/psVTKWriter.hpp @@ -1,8 +1,5 @@ -#ifndef PS_VTK_WRITER_HPP -#define PS_VTK_WRITER_HPP +#pragma once #include template using psVTKWriter = lsVTKWriter; - -#endif // PS_VTK_WRITER_HPP \ No newline at end of file diff --git a/include/psVelocityField.hpp b/include/psVelocityField.hpp index 96a2f8b6..911fd595 100644 --- a/include/psVelocityField.hpp +++ b/include/psVelocityField.hpp @@ -1,5 +1,4 @@ -#ifndef PS_VELOCITY_FIELD -#define PS_VELOCITY_FIELD +#pragma once #include #include @@ -64,5 +63,3 @@ class psDefaultVelocityField : public psVelocityField { psSmartPointer> velocities; const int translationFieldOptions = 1; // default: use map translator }; - -#endif \ No newline at end of file diff --git a/include/psWriteVisualizationMesh.hpp b/include/psWriteVisualizationMesh.hpp index 13af3ccd..72c17b74 100644 --- a/include/psWriteVisualizationMesh.hpp +++ b/include/psWriteVisualizationMesh.hpp @@ -35,4 +35,4 @@ template class psWriteVisualizationMesh { psSmartPointer> domain; psSmartPointer materialMap; std::string fileName; -}; \ No newline at end of file +}; diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..4e90befe --- /dev/null +++ b/setup.py @@ -0,0 +1,260 @@ +# Original Copyright Notice: +# +# Copyright (c) 2016 The Pybind Development Team, All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# You are under no obligation whatsoever to provide any bug fixes, patches, or +# upgrades to the features, functionality or performance of the source code +# ("Enhancements") to anyone; however, if you choose to make your Enhancements +# available either publicly, or directly to the author of this software, without +# imposing a separate written license agreement for such Enhancements, then you +# hereby grant the following license: a non-exclusive, royalty-free perpetual +# license to install, use, modify, prepare derivative works, incorporate into +# other computer software, distribute, and sublicense such enhancements or +# derivative works thereof, in binary and source code form. + +import os +import re +import shutil +import subprocess +import sys +from pathlib import Path + +from setuptools import Extension, setup +from setuptools.command.build_ext import build_ext + +# Convert distutils Windows platform specifiers to CMake -A arguments +PLAT_TO_CMAKE = { + "win32": "Win32", + "win-amd64": "x64", + "win-arm32": "ARM", + "win-arm64": "ARM64", +} + + +# A CMakeExtension needs a sourcedir instead of a file list. +# The name must be the _single_ output extension from the CMake build. +# If you need multiple extensions, see scikit-build. +class CMakeExtension(Extension): + def __init__(self, name: str) -> None: + super().__init__(name, sources=[]) + self.sourcedir = os.fspath(Path("").resolve()) + + +class CMakeBuild(build_ext): + def build_extension(self, ext: CMakeExtension) -> None: + # Must be in this form due to bug in .resolve() only fixed in Python 3.10+ + ext_fullpath = Path.cwd() / self.get_ext_fullpath(ext.name) + extdir = ext_fullpath.parent.resolve() + + # Using this requires trailing slash for auto-detection & inclusion of + # auxiliary "native" libs + + debug = int(os.environ.get("DEBUG", 0)) if self.debug is None else self.debug + cfg = "Debug" if debug else "Release" + + # CMake lets you override the generator - we need to check this. + # Can be set with Conda-Build, for example. + cmake_generator = os.environ.get("CMAKE_GENERATOR", "") + + # Set Python_EXECUTABLE instead if you use PYBIND11_FINDPYTHON + # EXAMPLE_VERSION_INFO shows you how to pass a value into the C++ code + # from Python. + cmake_args = [ + f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}{os.sep}", + f"-DPYTHON_EXECUTABLE={sys.executable}", + f"-DCMAKE_BUILD_TYPE={cfg}", # not used on MSVC, but no harm + "-DVIENNAPS_BUILD_PYTHON=ON", + ] + + build_args = [] + # Adding CMake arguments set as environment variable + # (needed e.g. to build for ARM OSx on conda-forge) + if "CMAKE_ARGS" in os.environ: + cmake_args += [item for item in os.environ["CMAKE_ARGS"].split(" ") if item] + + if self.compiler.compiler_type != "msvc": + # Using Ninja-build since it a) is available as a wheel and b) + # multithreads automatically. MSVC would require all variables be + # exported for Ninja to pick it up, which is a little tricky to do. + # Users can override the generator with CMAKE_GENERATOR in CMake + # 3.15+. + if not cmake_generator or cmake_generator == "Ninja": + try: + import ninja + + ninja_executable_path = Path(ninja.BIN_DIR) / "ninja" + cmake_args += [ + "-GNinja", + f"-DCMAKE_MAKE_PROGRAM:FILEPATH={ninja_executable_path}", + ] + except ImportError: + pass + + else: + # Single config generators are handled "normally" + single_config = any(x in cmake_generator for x in {"NMake", "Ninja"}) + + # CMake allows an arch-in-generator style for backward compatibility + contains_arch = any(x in cmake_generator for x in {"ARM", "Win64"}) + + # Specify the arch if using MSVC generator, but only if it doesn't + # contain a backward-compatibility arch spec already in the + # generator name. + if not single_config and not contains_arch: + cmake_args += ["-A", PLAT_TO_CMAKE[self.plat_name]] + + # Multi-config generators have a different way to specify configs + if not single_config: + cmake_args += [ + f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{cfg.upper()}={extdir}" + ] + build_args += ["--config", cfg] + + if sys.platform.startswith("darwin"): + # Cross-compile support for macOS - respect ARCHFLAGS if set + archs = re.findall(r"-arch (\S+)", os.environ.get("ARCHFLAGS", "")) + if archs: + cmake_args += ["-DCMAKE_OSX_ARCHITECTURES={}".format(";".join(archs))] + for var in os.environ: + if var.startswith("OpenMP_"): + cmake_args += [f"-D{var}='{os.environ[var]}'"] + + # Set CMAKE_BUILD_PARALLEL_LEVEL to control the parallel build level + # across all generators. + if "CMAKE_BUILD_PARALLEL_LEVEL" not in os.environ: + # self.parallel is a Python 3 only way to set parallel jobs by hand + # using -j in the build_ext call, not supported by pip or PyPA-build. + if hasattr(self, "parallel") and self.parallel: + # CMake 3.12+ only. + build_args += [f"-j{self.parallel}"] + + build_temp = Path(self.build_temp) / ext.name + if not build_temp.exists(): + build_temp.mkdir(parents=True) + + # Configure the project + subprocess.run( + ["cmake", ext.sourcedir, *cmake_args], cwd=build_temp, check=True + ) + + # Build dependencies if necessary + subprocess.run( + ["cmake", "--build", ".", *build_args], cwd=build_temp, check=True + ) + + # Build python bindings + subprocess.run( + ["cmake", "--build", ".", *build_args], cwd=build_temp, check=True + ) + + # On windows move the generated pyd (dll in disguise) files to the corresponding + # folders. Ideally this should be done in CMake, but we have not yet implemented + # this. + if sys.platform == "win32": + pyd_files = [f for f in os.listdir(extdir) if f.endswith(".pyd")] + for f in pyd_files: + if f.startswith("_viennaps2d"): + shutil.move( + src=os.path.join(extdir, f), + dst=os.path.join(extdir, "viennaps2d", f), + ) + elif f.startswith("_viennaps3d"): + shutil.move( + src=os.path.join(extdir, f), + dst=os.path.join(extdir, "viennaps3d", f), + ) + + # Generate stubs (*.pyi files) for autocompletion and type hints + try: + import mypy.stubgen as stubgen + + # Make sure that the extdir is in sys.path so that + # stubgen can be run on packages defined there. + sys.path.insert(0, str(extdir)) + + # Don't create __pycache__ directory + sys.dont_write_bytecode = True + + # Initialize the stubgen parser options + options = stubgen.parse_options( + [ + "-o", + str(os.path.abspath(extdir)), + "-p", + "viennaps2d", + "-p", + "viennaps3d", + ] + ) + + # Generate the stubs + stubgen.generate_stubs(options) + + # Remove mypy_cache, if it exists + if os.path.exists(os.path.join(extdir, ".mypy_cache")): + shutil.rmtree(os.path.join(extdir, ".mypy_cache")) + except ModuleNotFoundError: + pass + except ImportError: + pass + + # Check if the ViennaLS Python bindings are installed on the system + try: + import viennals3d + except ModuleNotFoundError: + print( + "ViennaLS Python bindings not found. " + "Please also build the ViennaLS Python module to " + " use all functionalities of ViennaPS." + ) + + +setup( + name="ViennaPS", + version="1.3.0", + author="Institute for Microelectronics", + author_email="viennatools@iue.tuwien.ac.at", + license="MIT", + url="https://github.com/ViennaTools/ViennaPS", + description="Semiconductor fabrication process simulation library.", + long_description="ViennaPS is a header-only C++ process simulation library," + "which includes surface and volume representations," + "a ray tracer, and physical models for the simulation of" + "microelectronic fabrication processes..", + ext_modules=[CMakeExtension("viennaps")], + cmdclass={"build_ext": CMakeBuild}, + zip_safe=False, + setup_requires=[ + "mypy", + ], + requires=[ + "ViennaLS", + "ViennaRay", + ], + python_requires=">=3.7", +)