Skip to content

Commit f60262b

Browse files
authored
feat: add custom materials and map to values (#220)
* feat: add function to insert mask at front of material stack * feat: directional process rate based * feat: material value mapping in single particle process rates * feat: single particle process python bindings updated * feat: add mapping in IsotropicProcess * refactor: material mapping, allow custom material registration * feat: materials on CUDA device code * fix: plasma etching models * feat: add material factors in plasma etching model * test adding custom material to domain * refactor: use singleton MaterialRegistry * material registry tests * refactor: update directional process * refactor: udpate ion beam etching model * fix: faraday cage model, update python bindings * small map refactor * fix: bug in mask blur GDS * feat: parallel blur loop * fix: correct material check function in CSV file process * refactor: material info * feat: fix build * chore: format * fix: material info requires string for custom values
1 parent 88ee779 commit f60262b

58 files changed

Lines changed: 2305 additions & 768 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CMakeLists.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ include("cmake/cpm.cmake")
107107
CPMAddPackage(
108108
NAME ViennaCore
109109
VERSION 2.1.1
110+
GIT_TAG main
110111
GIT_REPOSITORY "https://github.com/ViennaTools/ViennaCore"
111112
EXCLUDE_FROM_ALL ${VIENNAPS_BUILD_PYTHON}
112113
OPTIONS "VIENNACORE_USE_GPU ${VIENNAPS_USE_GPU}")
@@ -180,8 +181,9 @@ if(VIENNAPS_PRECOMPILE_HEADERS)
180181
target_compile_definitions(${LIB_NAME} PRIVATE VIENNAPS_USE_PRECOMPILED=1
181182
VIENNACORE_COMPILE_SHARED_LIB=1)
182183
target_sources(
183-
${LIB_NAME} PRIVATE "lib/specMain.cpp" "lib/specGeometries.cpp" "lib/specModelsEmulation.cpp"
184-
"lib/specModelsSimulation.cpp" "lib/specProcess.cpp")
184+
${LIB_NAME}
185+
PRIVATE "lib/specMain.cpp" "lib/specGeometries.cpp" "lib/specModelsEmulation.cpp"
186+
"lib/specModelsSimulation.cpp" "lib/specProcess.cpp" "lib/MaterialRegistry.cpp")
185187

186188
set_target_properties(
187189
${LIB_NAME}
Lines changed: 121 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,127 @@
11
import viennaps as ps
22
import viennals as ls
3+
import matplotlib.pyplot as plt
4+
import numpy as np
35

46
ps.setDimension(2)
57
ls.setDimension(2)
68

7-
params = ps.readConfigFile("config.txt")
8-
geometry = ps.Domain()
9-
10-
# Create the geometry
11-
boundaryCons = [
12-
ps.BoundaryType.REFLECTIVE_BOUNDARY,
13-
ps.BoundaryType.INFINITE_BOUNDARY,
14-
]
15-
gridDelta = params["gridDelta"]
16-
bounds = [
17-
0.0,
18-
params["openingWidth"] / 2.0 + params["xPad"] + params["gapLength"],
19-
-gridDelta,
20-
params["openingDepth"] + params["gapHeight"] + gridDelta,
21-
]
22-
23-
substrate = ls.Domain(bounds, boundaryCons, gridDelta)
24-
normal = [0.0, 1.0]
25-
origin = [0.0, params["openingDepth"] + params["gapHeight"]]
26-
ls.MakeGeometry(substrate, ls.Plane(origin, normal)).apply()
27-
28-
geometry.insertNextLevelSetAsMaterial(substrate, ps.Material.Si)
29-
30-
vertBox = ls.Domain(bounds, boundaryCons, gridDelta)
31-
minPoint = [-gridDelta, 0.0]
32-
maxPoint = [
33-
params["openingWidth"] / 2.0,
34-
params["gapHeight"] + params["openingDepth"] + gridDelta,
35-
]
36-
ls.MakeGeometry(vertBox, ls.Box(minPoint, maxPoint)).apply()
37-
38-
geometry.applyBooleanOperation(vertBox, ls.BooleanOperationEnum.RELATIVE_COMPLEMENT)
39-
40-
horiBox = ls.Domain(bounds, boundaryCons, gridDelta)
41-
minPoint = [params["openingWidth"] / 2.0 - gridDelta, 0.0]
42-
maxPoint = [params["openingWidth"] / 2.0 + params["gapLength"], params["gapHeight"]]
43-
ls.MakeGeometry(horiBox, ls.Box(minPoint, maxPoint)).apply()
44-
geometry.applyBooleanOperation(horiBox, ls.BooleanOperationEnum.RELATIVE_COMPLEMENT)
45-
46-
geometry.saveVolumeMesh("SingleParticleALD_initial")
47-
48-
geometry.duplicateTopLevelSet(ps.Material.Al2O3)
49-
50-
gasMFP = ps.constants.gasMeanFreePath(
51-
params["pressure"], params["temperature"], params["diameter"]
52-
)
53-
print("Mean free path: ", gasMFP, " um")
54-
55-
model = ps.SingleParticleALD(
56-
stickingProbability=params["stickingProbability"],
57-
numCycles=int(params["numCycles"]),
58-
growthPerCycle=params["growthPerCycle"],
59-
totalCycles=int(params["totalCycles"]),
60-
coverageTimeStep=params["coverageTimeStep"],
61-
evFlux=params["evFlux"],
62-
inFlux=params["inFlux"],
63-
s0=params["s0"],
64-
gasMFP=gasMFP,
65-
)
66-
67-
alpParams = ps.AtomicLayerProcessParameters()
68-
alpParams.pulseTime = params["pulseTime"]
69-
alpParams.coverageTimeStep = params["coverageTimeStep"]
70-
alpParams.numCycles = int(params["numCycles"])
71-
72-
rayParams = ps.RayTracingParameters()
73-
rayParams.raysPerPoint = int(params["numRaysPerPoint"])
74-
75-
ALP = ps.Process(geometry, model)
76-
ALP.setParameters(rayParams)
77-
ALP.setParameters(alpParams)
78-
ALP.apply()
79-
80-
## TODO: Implement MeasureProfile in Python
81-
# MeasureProfile<NumericType, D>(domain, params.get("gapHeight") / 2.)
82-
# .save(params.get<std::string>("outputFile"));
83-
84-
geometry.saveVolumeMesh("SingleParticleALD_final")
9+
ps.Logger.setLogLevel(ps.LogLevel.DEBUG)
10+
11+
12+
def run_simulation(gd, filename):
13+
params = ps.readConfigFile("config.txt")
14+
geometry = ps.Domain()
15+
16+
# Create the geometry
17+
boundaryCons = [
18+
ps.BoundaryType.REFLECTIVE_BOUNDARY,
19+
ps.BoundaryType.INFINITE_BOUNDARY,
20+
]
21+
gridDelta = gd
22+
bounds = [
23+
0.0,
24+
params["openingWidth"] / 2.0 + params["xPad"] + params["gapLength"],
25+
-gridDelta,
26+
params["openingDepth"] + params["gapHeight"] + gridDelta,
27+
]
28+
29+
substrate = ls.Domain(bounds, boundaryCons, gridDelta)
30+
normal = [0.0, 1.0]
31+
origin = [0.0, params["openingDepth"] + params["gapHeight"]]
32+
ls.MakeGeometry(substrate, ls.Plane(origin, normal)).apply()
33+
34+
geometry.insertNextLevelSetAsMaterial(substrate, ps.Material.Si)
35+
36+
vertBox = ls.Domain(bounds, boundaryCons, gridDelta)
37+
minPoint = [-gridDelta, 0.0]
38+
maxPoint = [
39+
params["openingWidth"] / 2.0,
40+
params["gapHeight"] + params["openingDepth"] + gridDelta,
41+
]
42+
ls.MakeGeometry(vertBox, ls.Box(minPoint, maxPoint)).apply()
43+
44+
geometry.applyBooleanOperation(vertBox, ls.BooleanOperationEnum.RELATIVE_COMPLEMENT)
45+
46+
horiBox = ls.Domain(bounds, boundaryCons, gridDelta)
47+
minPoint = [params["openingWidth"] / 2.0 - gridDelta, 0.0]
48+
maxPoint = [params["openingWidth"] / 2.0 + params["gapLength"], params["gapHeight"]]
49+
ls.MakeGeometry(horiBox, ls.Box(minPoint, maxPoint)).apply()
50+
geometry.applyBooleanOperation(horiBox, ls.BooleanOperationEnum.RELATIVE_COMPLEMENT)
51+
52+
geometry.saveSurfaceMesh("SingleParticleALD_initial")
53+
54+
model = ps.SingleParticleProcess(rate=1.0, stickingProbability=1e-4)
55+
56+
geometry.duplicateTopLevelSet(ps.Material.Al2O3)
57+
58+
gasMFP = ps.constants.gasMeanFreePath(
59+
params["pressure"], params["temperature"], params["diameter"]
60+
)
61+
print("Mean free path: ", gasMFP, " um")
62+
63+
model = ps.SingleParticleALD(
64+
stickingProbability=params["stickingProbability"],
65+
numCycles=int(params["numCycles"]),
66+
growthPerCycle=params["growthPerCycle"],
67+
totalCycles=int(params["totalCycles"]),
68+
coverageTimeStep=params["coverageTimeStep"],
69+
evFlux=params["evFlux"],
70+
inFlux=params["inFlux"],
71+
s0=params["s0"],
72+
gasMFP=gasMFP,
73+
)
74+
model.setProcessName(filename)
75+
76+
alpParams = ps.AtomicLayerProcessParameters()
77+
alpParams.pulseTime = params["pulseTime"]
78+
alpParams.coverageTimeStep = params["coverageTimeStep"]
79+
alpParams.numCycles = 1
80+
81+
p = ps.Process(geometry, model, 1.0)
82+
p.setParameters(alpParams)
83+
p.setFluxEngineType(ps.FluxEngineType.GPU_TRIANGLE)
84+
p.apply()
85+
86+
# flux = p.calculateFlux()
87+
88+
# points = np.array(flux.getNodes())
89+
# fluxValues = np.array(flux.getCellData().getScalarData("particleFlux", True))
90+
91+
# cuts = points[:, 1] < 0.25
92+
# points = points[cuts]
93+
# fluxValues = fluxValues[cuts]
94+
95+
# ps.ls.VTKWriter(flux, filename).apply()
96+
97+
mesh = ps.ls.Mesh()
98+
ps.ToDiskMesh(geometry, mesh).apply()
99+
points = np.array(mesh.getNodes())
100+
cuts = points[:, 1] < 0.25
101+
points = points[cuts]
102+
return points
103+
# return points, fluxValues
104+
105+
106+
p = run_simulation(0.1, "flux_0p1")
107+
plt.plot(p[:, 0], p[:, 1], "--")
108+
p = run_simulation(0.01, "flux_0p01")
109+
plt.plot(p[:, 0], p[:, 1], ".-")
110+
p = run_simulation(0.05, "flux_0p05")
111+
plt.plot(p[:, 0], p[:, 1], "-")
112+
plt.show()
113+
114+
115+
# rayParams = ps.RayTracingParameters()
116+
# rayParams.raysPerPoint = int(params["numRaysPerPoint"])
117+
118+
# ALP = ps.Process(geometry, model)
119+
# ALP.setParameters(rayParams)
120+
# ALP.setParameters(alpParams)
121+
# ALP.apply()
122+
123+
# ## TODO: Implement MeasureProfile in Python
124+
# # MeasureProfile<NumericType, D>(domain, params.get("gapHeight") / 2.)
125+
# # .save(params.get<std::string>("outputFile"));
126+
127+
# geometry.saveVolumeMesh("SingleParticleALD_final")

examples/atomicLayerDeposition/config.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# all length units in micrometers
22

33
# Geometry
4-
gridDelta = 0.2
4+
gridDelta = 0.1
55
openingDepth = 0.5
66
openingWidth = 90
77
gapLength = 800

examples/exampleProcess/velocityField.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22

3+
#include <materials/psMaterials.hpp>
34
#include <process/psVelocityField.hpp>
4-
#include <psMaterials.hpp>
55
#include <vector>
66

77
template <class T, int D>

examples/holeEtching/config.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ A_Si=7 # Si yield constant
3030
etchStopDepth=-10 # maximum etching depth
3131
spatialScheme=EO_1
3232
temporalScheme=RK3
33-
fluxEngine=GT
33+
fluxEngine=CT
3434

3535
raysPerPoint=1000
36-
outputFile=final.vtp
36+
outputFile=final_CPU.vtp
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
from argparse import ArgumentParser
2+
import viennaps as ps
3+
4+
# parse config file name and simulation dimension
5+
parser = ArgumentParser(prog="holeEtching", description="Run a hole etching process.")
6+
parser.add_argument("-D", "-DIM", dest="dim", type=int, default=2)
7+
parser.add_argument("filename")
8+
args = parser.parse_args()
9+
10+
# switch between 2D and 3D mode
11+
if args.dim == 2:
12+
print("Running 2D simulation.")
13+
else:
14+
print("Running 3D simulation.")
15+
ps.setDimension(args.dim)
16+
ps.setNumThreads(16)
17+
18+
params = ps.readConfigFile(args.filename)
19+
20+
ps.Length.setUnit(params["lengthUnit"])
21+
ps.Time.setUnit(params["timeUnit"])
22+
23+
24+
def run_simulation(fluxEngine, suffix):
25+
# geometry setup, all units in um
26+
geometry = ps.Domain(
27+
gridDelta=params["gridDelta"],
28+
xExtent=params["xExtent"],
29+
yExtent=params["yExtent"],
30+
)
31+
32+
ps.MakeHole(
33+
domain=geometry,
34+
holeRadius=params["holeRadius"],
35+
holeDepth=0.0,
36+
maskHeight=params["maskHeight"],
37+
maskTaperAngle=params["taperAngle"],
38+
holeShape=ps.HoleShape.QUARTER,
39+
).apply()
40+
41+
# use pre-defined model SF6O2 etching model
42+
modelParams = ps.SF6O2Etching.defaultParameters()
43+
modelParams.ionFlux = params["ionFlux"]
44+
modelParams.etchantFlux = params["etchantFlux"]
45+
modelParams.passivationFlux = params["oxygenFlux"]
46+
modelParams.Ions.meanEnergy = params["meanEnergy"]
47+
modelParams.Ions.sigmaEnergy = params["sigmaEnergy"]
48+
modelParams.Ions.exponent = params["ionExponent"]
49+
modelParams.Passivation.A_ie = params["A_O"]
50+
modelParams.Substrate.A_ie = params["A_Si"]
51+
modelParams.etchStopDepth = params["etchStopDepth"]
52+
model = ps.SF6O2Etching(modelParams)
53+
54+
covParams = ps.CoverageParameters()
55+
covParams.tolerance = 1e-4
56+
57+
rayParams = ps.RayTracingParameters()
58+
rayParams.raysPerPoint = int(params["raysPerPoint"])
59+
60+
advParams = ps.AdvectionParameters()
61+
advParams.spatialScheme = ps.util.convertSpatialScheme(params["spatialScheme"])
62+
advParams.temporalScheme = ps.util.convertTemporalScheme(params["temporalScheme"])
63+
64+
# process setup
65+
process = ps.Process(geometry, model)
66+
process.setProcessDuration(params["processTime"]) # seconds
67+
process.setParameters(covParams)
68+
process.setParameters(rayParams)
69+
process.setParameters(advParams)
70+
71+
process.setFluxEngineType(fluxEngine)
72+
73+
# run the process
74+
process.apply()
75+
76+
# print final surface
77+
output_file = "hole_"
78+
output_file += suffix
79+
geometry.saveSurfaceMesh(filename=output_file, addInterfaces=True)
80+
81+
82+
run_simulation(ps.FluxEngineType.CPU_DISK, "CPU_disk")
83+
run_simulation(ps.FluxEngineType.GPU_DISK, "GPU_disk")
84+
run_simulation(ps.FluxEngineType.CPU_TRIANGLE, "CPU_triangle")
85+
run_simulation(ps.FluxEngineType.GPU_TRIANGLE, "GPU_triangle")

examples/ionBeamEtching/ionBeamEtching.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ int main(int argc, char *argv[]) {
3535
// copy top layer to capture deposition
3636
geometry->duplicateTopLevelSet(Material::Polymer);
3737

38-
IBEParameters<NumericType> ibeParams;
38+
auto ibeParams = IonBeamEtching<NumericType, D>::defaultParameters();
3939
ibeParams.tiltAngle = params.get("angle");
4040
ibeParams.exponent = params.get("exponent");
4141
ibeParams.thetaRMin = 0.;

examples/ionBeamEtching/ionBeamEtching.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
# copy top layer to capture deposition
3636
geometry.duplicateTopLevelSet(ps.Material.Polymer)
3737

38-
ibeParams = ps.IBEParameters()
38+
ibeParams = ps.IonBeamEtching.defaultParamters()
3939
ibeParams.tiltAngle = params["angle"]
4040
ibeParams.exponent = params["exponent"]
4141
ibeParams.thetaRMin = 0.0

0 commit comments

Comments
 (0)