2D Etching with MultiParticleProcess (Neutrals + Ions)
This tutorial shows how to use MultiParticleProcess in 2D to model etching driven by multiple particle species.
You will run two cases:
- Etching with a single neutral particle species
- Etching with neutral + ion particle species, including optional mask etching
The key concept is that MultiParticleProcess computes one flux per particle species, and you provide a rate function that converts these fluxes into a local surface velocity (etch or deposition), optionally depending on the material.
Download Jupyter Notebook: 08_multi_particle_etch.ipynb
1. Import ViennaPS
import viennaps as ps # ViennaPS 2D
2. Helper: Create a Masked Trench Domain
We define a small helper function so we can reset the geometry between process steps.
def createTrenchMask():
extent = 30
gridDelta = 0.3
domain = ps.Domain(xExtent=extent, gridDelta=gridDelta)
# trenchDepth=0 creates a flat bottom surface, maskHeight adds a mask on top
ps.MakeTrench(domain, trenchWidth=10.0, trenchDepth=0.0, maskHeight=2.0).apply()
return domain
What this creates
- A 2D domain of width
30with resolution0.3 - A trench opening of width
10 - No etched depth in the substrate (
trenchDepth=0.0), but a mask layer of height2.0
This is a simple setup to demonstrate selective etching and mask interaction.
Step 1: Etching with a Single Neutral Particle
3. Initialize Domain and Save Initial State
domain = createTrenchMask()
domain.saveVolumeMesh("multiParticleEtching_1")
This writes the starting geometry to a VTU file for later comparison.
4. Create the Multi-Particle Model and Add a Neutral Species
model = ps.MultiParticleProcess()
# one neutral particle species
model.addNeutralParticle(stickingProbability=0.2)
Meaning
addNeutralParticle(...)adds a neutral particle sourcestickingProbability=0.2controls how often neutrals stick/react at impact-
With only one species, the flux array will contain:
fluxes[0]→ neutral flux
5. Define the Etch Rate Function
MultiParticleProcess needs a function that maps local fluxes to a local surface rate.
neutralRate = 1.0
def rateFunction(fluxes, material):
if material == ps.Material.Mask:
return 0 # no etching of mask
return -neutralRate * fluxes[0]
model.setRateFunction(rateFunction)
Notes
- The function is called for each surface point during the process.
materiallets you implement selectivity (here: mask is protected).- The negative sign means etching (surface moves inward).
-
For this single-neutral case:
- Only
fluxes[0]exists (neutral flux).
- Only
6. Run the Process and Save Result
processDuration = 5.0
ps.Process(domain, model, processDuration).apply()
domain.saveVolumeMesh("multiParticleEtching_2")
You now have an etched profile driven only by neutral transport, with the mask unaffected.
Step 2: Etching with Neutral + Ion Particles
7. Reset Domain
domain = createTrenchMask()
This restores the original geometry so the second case starts from the same initial state.
8. Create a Model with Neutral and Ion Species
model = ps.MultiParticleProcess()
model.addNeutralParticle(stickingProbability=0.2)
# add an ion species with a directional angular distribution
model.addIonParticle(sourcePower=500.0, thetaRMin=60.0, thetaRMax=90.0)
Meaning
-
This time the flux array contains two entries:
fluxes[0]→ neutral fluxfluxes[1]→ ion flux
-
The ion parameters shape the incoming ion distribution:
sourcePoweraffects the ion intensity (model-dependent)thetaRMin,thetaRMaxrestrict the angular range (more directional)
9. Define a Combined Rate Function (Selectivity + Weights)
neutralWeight = 1.0
ionWeight = 1.0
maskEtchFactor = 0.1 # 0 = mask fully protected
def rateFunction(fluxes, material):
neutralFlux = fluxes[0]
ionFlux = fluxes[1]
if material == ps.Material.Mask:
return -maskEtchFactor * ionWeight * ionFlux
return -(neutralWeight * neutralFlux + ionWeight * ionFlux)
model.setRateFunction(rateFunction)
What this does
- Substrate etch rate depends on both neutrals and ions.
-
Mask etching is allowed, but reduced:
- only ions etch the mask
- scaled by
maskEtchFactor
This is a typical pattern:
- neutrals contribute more to isotropic components
- ions contribute more to directional components
- mask etch can be tuned separately
10. Run the Process, Save, and Visualize
processDuration = 5.0
ps.Process(domain, model, processDuration).apply()
domain.saveVolumeMesh("multiParticleEtching_3")
domain.show()
At this point you can compare:
multiParticleEtching_2(neutral-only etch)multiParticleEtching_3(neutral + ion, with optional mask erosion)
Download Jupyter Notebook: 08_multi_particle_etch.ipynb
