Skip to content

Commit 937d716

Browse files
tobre1kenyastyle
andauthored
feat: mask materials in geometric process models (#223)
* feat: get disk mesh from domain * fix: Plasma models fix (#221) * set a default rateFactor in plasma models * change default sticking in SF6C4F8 model * refactor: improve MaterialValueMap and update examples (#222) * 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 * feat: MaterialValueMap iterator includes custom values * feat(MaterialValueMap): get entries by index * refactor: SelectiveEpitaxy material handling and emulation examples * refactor: rename header file with MaterialMap * refactor: CF4O2 model and selective epitaxy * fix: clean up finFET example * refactor: MultiParticleProcess uses MaterialValueMap * fix: check all examples * format * refactor: move geometric model logic to strategy * feat: mask materials in geometric model * fix: missing header, python stubs * refactor: use mask materials in emulation examples --------- Co-authored-by: Roman Kostal <roman.kstl@gmail.com>
1 parent f777edb commit 937d716

11 files changed

Lines changed: 226 additions & 68 deletions

File tree

examples/emulation/FinFET.cpp

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ int main() {
5959
std::cout << "DP-Patterning ..." << std::flush;
6060
const NumericType etchDepth = 6; // nm
6161
auto dist = SmartPointer<BoxDistribution<double, D>>::New(
62-
std::array<NumericType, D>{-gridDelta, -gridDelta, -etchDepth},
63-
domain->getLevelSets().front());
62+
std::array<NumericType, D>{-gridDelta, -gridDelta, -etchDepth});
63+
dist->addMaskMaterial(Material::Si);
6464
Process<NumericType, D>(domain, dist).apply();
6565
std::cout << " done" << std::endl;
6666
}
@@ -108,8 +108,8 @@ int main() {
108108
// pattern STI material
109109
{
110110
std::cout << "STI Patterning ..." << std::flush;
111-
auto dist =
112-
IsotropicProcessGeometric::New(-35, domain->getLevelSets().front());
111+
auto dist = IsotropicProcessGeometric::New(-35);
112+
dist->addMaskMaterial(Material::Si); // protect silicon during etch
113113
Process<NumericType, D>(domain, dist).apply();
114114
std::cout << " done" << std::endl;
115115
}
@@ -155,12 +155,15 @@ int main() {
155155
// gate patterning
156156
{
157157
std::cout << "Dummy Gate Patterning ..." << std::flush;
158-
Vec3D<NumericType> direction = {0, 0, 1};
159158
std::vector<Material> masks = {Material::Mask, Material::Si,
160159
Material::SiO2};
161-
auto model = SmartPointer<DirectionalProcess<NumericType, D>>::New(
162-
direction, 1.0, 0., masks, false);
163-
Process<NumericType, D>(domain, model, 110.).apply();
160+
const NumericType etchDepth = 110; // nm
161+
auto dist = SmartPointer<BoxDistribution<double, D>>::New(
162+
std::array<NumericType, D>{-gridDelta, -gridDelta, -etchDepth});
163+
for (const auto &maskMaterial : masks) {
164+
dist->addMaskMaterial(maskMaterial);
165+
}
166+
Process<NumericType, D>(domain, dist).apply();
164167
std::cout << " done" << std::endl;
165168
}
166169
writeSurface(domain);
@@ -182,9 +185,15 @@ int main() {
182185

183186
{ // spacer etch
184187
std::cout << "Spacer Etch ..." << std::flush;
185-
auto mask = domain->getLevelSets()[domain->getLevelSets().size() - 2];
188+
auto materialsInDomain = domain->getMaterialsInDomain();
189+
materialsInDomain.erase(
190+
Material::Si3N4); // protect spacer material during etch
191+
const NumericType etchDepth = 50; // nm
186192
auto dist = SmartPointer<BoxDistribution<double, D>>::New(
187-
std::array<NumericType, D>{-gridDelta, -gridDelta, -50}, mask);
193+
std::array<NumericType, D>{-gridDelta, -gridDelta, -etchDepth});
194+
for (const auto &maskMaterial : materialsInDomain) {
195+
dist->addMaskMaterial(maskMaterial);
196+
}
188197
Process<NumericType, D>(domain, dist).apply();
189198
std::cout << " done" << std::endl;
190199
}

examples/emulation/FinFET.py

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,9 @@ def writeSurface(domain):
4646
# DP-Patterning
4747
print("DP-Patterning ...", end="", flush=True)
4848
etchDepth = 6.0 # nm
49-
dist = ps.BoxDistribution(
50-
halfAxes=[-gridDelta, -gridDelta, -etchDepth], mask=domain.getLevelSets()[0]
51-
)
52-
ps.Process(domain, dist, 0).apply()
49+
dist = ps.BoxDistribution(halfAxes=[-gridDelta, -gridDelta, -etchDepth])
50+
dist.addMaskMaterial(ps.Material.Si)
51+
ps.Process(domain, dist).apply()
5352
print(" done")
5453
writeSurface(domain)
5554

@@ -87,7 +86,8 @@ def writeSurface(domain):
8786

8887
# pattern STI material
8988
print("STI Patterning ...", end="", flush=True)
90-
dist = ps.SphereDistribution(radius=-35, mask=domain.getLevelSets()[0])
89+
dist = ps.SphereDistribution(radius=-35.0)
90+
dist.addMaskMaterial(ps.Material.Si)
9191
ps.Process(domain, dist, 0).apply()
9292
print(" done")
9393
writeSurface(domain)
@@ -125,13 +125,9 @@ def writeSurface(domain):
125125
print("Dummy Gate Patterning ...", end="", flush=True)
126126
direction = [0.0, 0.0, 1.0]
127127
masks = [ps.Material.Mask, ps.Material.Si, ps.Material.SiO2]
128-
model = ps.DirectionalProcess(
129-
direction=direction,
130-
directionalVelocity=1.0,
131-
isotropicVelocity=0.0,
132-
maskMaterial=masks,
133-
calculateVisibility=False,
134-
)
128+
model = ps.BoxDistribution(halfAxes=[-gridDelta, -gridDelta, -110.0])
129+
for mask in masks:
130+
model.addMaskMaterial(mask)
135131
ps.Process(domain, model, 110.0).apply()
136132
print(" done")
137133
writeSurface(domain)
@@ -151,8 +147,11 @@ def writeSurface(domain):
151147

152148
# Spacer Etch
153149
print("Spacer Etch ...", end="", flush=True)
154-
ls = domain.getLevelSets()[-2]
155-
dist = ps.BoxDistribution(halfAxes=[-gridDelta, -gridDelta, -50], mask=ls)
150+
masks = domain.getMaterialsInDomain()
151+
masks.remove(ps.Material.Si3N4)
152+
dist = ps.BoxDistribution(halfAxes=[-gridDelta, -gridDelta, -50])
153+
for material in masks:
154+
dist.addMaskMaterial(material)
156155
ps.Process(domain, dist, 0).apply()
157156
print(" done")
158157
writeSurface(domain)

examples/emulation/stackedNanowire.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -74,31 +74,31 @@
7474
# Deposit dummy gate material
7575
print("Depositing dummy gate material ...")
7676
domain.duplicateTopLevelSet(ps.Material.PolySi)
77-
ps.Process(domain, growth, 55.0).apply()
77+
ps.Process(domain, growth, 85.0).apply()
7878

79-
# CMP at 80nm height
80-
ps.Planarize(domain, 80.0).apply()
79+
# CMP at 100nm height
80+
ps.Planarize(domain, 100.0).apply()
8181

8282
domain.saveSurfaceMesh("StackedNanowire_" + str(n))
8383
n += 1
8484

8585
# Dummy gate mask addition
8686
print("Adding dummy gate mask ...")
8787
mask = ps.ls.Domain(bounds, boundaryConds, gridDelta)
88-
geo = ps.ls.MakeGeometry(mask, ps.ls.Box([-10, 30, 75], [80, 70, 80]))
88+
geo = ps.ls.MakeGeometry(mask, ps.ls.Box([-10, 30, 99], [80, 70, 105]))
8989
geo.setIgnoreBoundaryConditions(True)
9090
geo.apply()
91+
domain.insertNextLevelSetAsMaterial(mask, ps.Material.Mask)
92+
domain.saveSurfaceMesh("StackedNanowire_" + str(n))
93+
n += 1
9194

92-
ps.ls.BooleanOperation(
93-
mask, domain.getLevelSets()[-2], ps.ls.BooleanOperationEnum.UNION
94-
).apply()
95-
96-
tmpDomain = ps.Domain()
97-
tmpDomain.insertNextLevelSetAsMaterial(mask, ps.Material.Mask)
98-
tmpDomain.insertNextLevelSetAsMaterial(domain.getLevelSets()[-1], ps.Material.PolySi)
95+
masks = domain.getMaterialsInDomain()
96+
masks.remove(ps.Material.PolySi)
97+
geometricEtch = ps.BoxDistribution([-gridDelta, -gridDelta, -100])
98+
for material in masks:
99+
geometricEtch.addMaskMaterial(material)
99100

100-
geometricEtch = ps.BoxDistribution([-gridDelta, -gridDelta, -80], mask)
101-
ps.Process(tmpDomain, geometricEtch, 1.0).apply()
101+
ps.Process(domain, geometricEtch).apply()
102102

103103
domain.removeMaterial(ps.Material.Mask)
104104
domain.saveSurfaceMesh("StackedNanowire_" + str(n))
@@ -107,7 +107,7 @@
107107
# Spacer Deposition
108108
print("Depositing spacer material ...")
109109
domain.duplicateTopLevelSet(ps.Material.Si3N4)
110-
ps.Process(domain, growth, 12.0).apply()
110+
ps.Process(domain, growth, 10.0).apply()
111111
domain.saveSurfaceMesh("StackedNanowire_" + str(n))
112112
n += 1
113113

include/viennaps/models/psGeometricDistributionModels.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ class SphereDistribution : public ProcessModelCPU<NumericType, D> {
2525
this->setProcessName("SphereDistribution");
2626
this->processMetaData["Radius"] = std::vector<double>{radius};
2727
}
28+
29+
void addMaskMaterial(const Material material) {
30+
auto geomModel = this->getGeometricModel();
31+
assert(geomModel != nullptr);
32+
geomModel->addMaskMaterial(material);
33+
}
2834
};
2935

3036
template <typename NumericType, int D>
@@ -46,6 +52,12 @@ class BoxDistribution : public ProcessModelCPU<NumericType, D> {
4652
static_cast<double>(halfAxes[0]), static_cast<double>(halfAxes[1]),
4753
static_cast<double>(halfAxes[2])};
4854
}
55+
56+
void addMaskMaterial(const Material material) {
57+
auto geomModel = this->getGeometricModel();
58+
assert(geomModel != nullptr);
59+
geomModel->addMaskMaterial(material);
60+
}
4961
};
5062

5163
template <typename NumericType, int D>
@@ -65,6 +77,12 @@ class CustomSphereDistribution : public ProcessModelCPU<NumericType, D> {
6577
this->setGeometricModel(geomModel);
6678
this->setProcessName("CustomSphereDistribution");
6779
}
80+
81+
void addMaskMaterial(const Material material) {
82+
auto geomModel = this->getGeometricModel();
83+
assert(geomModel != nullptr);
84+
geomModel->addMaskMaterial(material);
85+
}
6886
};
6987

7088
namespace impl {

include/viennaps/process/psGeometricModel.hpp

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22

33
#include <lsGeometricAdvect.hpp>
44

5-
#include "../psDomain.hpp"
5+
#include "../psPreCompileMacros.hpp"
6+
7+
#include "../materials/psMaterial.hpp"
68

79
namespace viennaps {
810

911
using namespace viennacore;
1012

1113
VIENNAPS_TEMPLATE_ND(NumericType, D) class GeometricModel {
12-
SmartPointer<Domain<NumericType, D>> domain = nullptr;
1314
SmartPointer<viennals::GeometricAdvectDistribution<NumericType, D>> dist =
1415
nullptr;
1516
SmartPointer<viennals::Domain<NumericType, D>> mask = nullptr;
17+
std::vector<Material> maskMaterials;
1618

1719
public:
1820
GeometricModel() = default;
@@ -23,10 +25,6 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class GeometricModel {
2325
SmartPointer<viennals::Domain<NumericType, D>> passedMask = nullptr)
2426
: dist(passedDist), mask(passedMask) {}
2527

26-
void setDomain(SmartPointer<Domain<NumericType, D>> passedDomain) {
27-
domain = passedDomain;
28-
}
29-
3028
void setDistribution(
3129
SmartPointer<viennals::GeometricAdvectDistribution<NumericType, D>>
3230
passedDist) {
@@ -37,24 +35,17 @@ VIENNAPS_TEMPLATE_ND(NumericType, D) class GeometricModel {
3735
mask = passedMask;
3836
}
3937

40-
void apply() {
41-
if (!dist) {
42-
VIENNACORE_LOG_ERROR(
43-
"No GeometricAdvectDistribution passed to GeometricModel.");
44-
return;
45-
}
46-
47-
viennals::GeometricAdvect<NumericType, D>(domain->getLevelSets().back(),
48-
dist, mask)
49-
.apply();
50-
51-
for (int i = domain->getNumberOfLevelSets() - 1; i >= 0; --i) {
52-
viennals::BooleanOperation<NumericType, D>(
53-
domain->getLevelSets()[i], domain->getLevelSets().back(),
54-
viennals::BooleanOperationEnum::INTERSECT)
55-
.apply();
56-
}
38+
void addMaskMaterial(const Material material) {
39+
maskMaterials.push_back(material);
5740
}
41+
42+
void setMaskMaterials(const std::vector<Material> &materials) {
43+
maskMaterials = materials;
44+
}
45+
46+
auto &getDistribution() const { return dist; }
47+
auto &getMask() const { return mask; }
48+
auto &getMaskMaterials() const { return maskMaterials; }
5849
};
5950

6051
} // namespace viennaps

include/viennaps/process/psGeometricProcessStrategy.hpp

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,94 @@ class GeometricProcessStrategy : public ProcessStrategy<NumericType, D> {
1818
}
1919

2020
auto geometricModel = context.model->getGeometricModel();
21-
geometricModel->setDomain(context.domain);
22-
geometricModel->apply();
21+
assert(geometricModel);
22+
23+
auto dist = geometricModel->getDistribution();
24+
if (!dist) {
25+
VIENNACORE_LOG_ERROR(
26+
"No GeometricAdvectDistribution passed to GeometricModel.");
27+
return ProcessResult::INVALID_INPUT;
28+
}
29+
30+
auto mask = geometricModel->getMask();
31+
if (!mask) {
32+
// check if mask materials are set, if so create a mask level set from the
33+
// domain
34+
auto maskMaterials = geometricModel->getMaskMaterials();
35+
if (!maskMaterials.empty()) {
36+
auto domain = context.domain;
37+
auto materialMap = domain->getMaterialMap();
38+
if (!materialMap) {
39+
VIENNACORE_LOG_ERROR("Domain does not have a material map, cannot "
40+
"create mask level set "
41+
"from mask materials.");
42+
return ProcessResult::INVALID_INPUT;
43+
}
44+
45+
auto maskLevelSet = SmartPointer<viennals::Domain<NumericType, D>>::New(
46+
domain->getGrid());
47+
bool foundMaterial = false;
48+
49+
auto const &levelSets = domain->getLevelSets();
50+
for (auto &material : maskMaterials) {
51+
for (int j = 0; j < levelSets.size(); ++j) {
52+
if (materialMap->getMaterialAtIdx(j) == material) {
53+
auto lsCopy = SmartPointer<viennals::Domain<NumericType, D>>::New(
54+
levelSets[j]);
55+
56+
// remove all lower level sets that are not mask materials
57+
for (int k = j - 1; k >= 0; --k) {
58+
if (MaterialMap::isMaterial(materialMap->getMaterialAtIdx(k),
59+
maskMaterials))
60+
continue;
61+
62+
viennals::BooleanOperation<NumericType, D>(
63+
lsCopy, levelSets[k],
64+
viennals::BooleanOperationEnum::RELATIVE_COMPLEMENT)
65+
.apply();
66+
}
67+
68+
if (foundMaterial) {
69+
// union with mask level set
70+
viennals::BooleanOperation<NumericType, D>(
71+
maskLevelSet, lsCopy, viennals::BooleanOperationEnum::UNION)
72+
.apply();
73+
} else {
74+
maskLevelSet = lsCopy;
75+
foundMaterial = true;
76+
}
77+
}
78+
}
79+
}
80+
81+
if (maskLevelSet->getNumberOfPoints() > 0) {
82+
mask = maskLevelSet;
83+
if (Logger::hasDebug()) {
84+
auto dbgMesh = viennals::Mesh<NumericType>::New();
85+
viennals::ToMesh<NumericType, D>(mask, dbgMesh).apply();
86+
viennals::VTKWriter<NumericType>(dbgMesh, "geometric_mask_debug")
87+
.apply();
88+
}
89+
} else {
90+
VIENNACORE_LOG_WARNING(
91+
"None of the specified mask materials were found in the domain, "
92+
"cannot create mask level set from mask materials.");
93+
}
94+
}
95+
}
96+
97+
viennals::GeometricAdvect<NumericType, D>(
98+
context.domain->getLevelSets().back(), dist, mask)
99+
.apply();
100+
101+
// Intersect all other level sets with the last one to keep them consistent
102+
for (int i = context.domain->getNumberOfLevelSets() - 1; i >= 0; --i) {
103+
viennals::BooleanOperation<NumericType, D>(
104+
context.domain->getLevelSets()[i],
105+
context.domain->getLevelSets().back(),
106+
viennals::BooleanOperationEnum::INTERSECT)
107+
.apply();
108+
}
23109

24110
return ProcessResult::SUCCESS;
25111
}

0 commit comments

Comments
 (0)