1+ import viennals as vls
2+ import argparse
3+
4+ vls .Logger .setLogLevel (vls .LogLevel .INFO )
5+
6+ # 1. Define the Velocity Field
7+ # We inherit from viennals.VelocityField to define custom logic in Python
8+ class EtchingField (vls .VelocityField ):
9+ def getScalarVelocity (self , coordinate , material , normalVector , pointId ):
10+ if material == 2 :
11+ return - 0.1
12+ if material == 1 :
13+ return - 1.
14+ return 0.0
15+
16+ def getVectorVelocity (self , coordinate , material , normalVector , pointId ):
17+ return [0.0 ] * len (coordinate )
18+
19+ # 1. Define the Velocity Field
20+ # We inherit from viennals.VelocityField to define custom logic in Python
21+ class DepositionField (vls .VelocityField ):
22+ def getScalarVelocity (self , coordinate , material , normalVector , pointId ):
23+ return 0.75
24+
25+ def getVectorVelocity (self , coordinate , material , normalVector , pointId ):
26+ return [0.0 ] * len (coordinate )
27+
28+ def main ():
29+ # 1. Parse Arguments
30+ parser = argparse .ArgumentParser (description = "Run Square Etch simulation in 2D or 3D." )
31+ parser .add_argument (
32+ "-D" , "--dim" ,
33+ type = int ,
34+ default = 2 ,
35+ choices = [2 , 3 ],
36+ help = "Dimension of the simulation (2 or 3). Default is 2."
37+ )
38+ args = parser .parse_args ()
39+
40+ DIMENSION = args .dim
41+ vls .setDimension (DIMENSION )
42+ vls .setNumThreads (8 )
43+
44+ extent = 30.0
45+ gridDelta = 0.47
46+
47+ # Define bounds and boundary conditions
48+ trenchBottom = - 2.0
49+ bounds = [- extent , extent , - extent , extent ]
50+ origin = [0.0 , 0.0 ]
51+ if DIMENSION == 3 :
52+ bounds .append (- extent )
53+ bounds .append (extent )
54+ origin .append (0.0 )
55+ boundaryCons = []
56+ for i in range (DIMENSION - 1 ):
57+ boundaryCons .append (vls .BoundaryConditionEnum .REFLECTIVE_BOUNDARY )
58+ boundaryCons .append (vls .BoundaryConditionEnum .INFINITE_BOUNDARY )
59+
60+ planeNormal = list (origin )
61+ downNormal = list (origin )
62+ planeNormal [DIMENSION - 1 ] = 1.0
63+ downNormal [DIMENSION - 1 ] = - 1.0
64+
65+ # Create the Substrate Domain
66+ substrate = vls .Domain (bounds , boundaryCons , gridDelta )
67+
68+ # Create initial flat geometry (Plane at y/z=0)
69+ plane = vls .Plane (origin , planeNormal )
70+ vls .MakeGeometry (substrate , plane ).apply ()
71+
72+ # --------------------------------------
73+ # 3. Trench Geometry
74+ # --------------------------------------
75+ trench = vls .Domain (bounds , boundaryCons , gridDelta )
76+
77+ # Define Box Corners based on dimension
78+ minCorner = list (origin )
79+ maxCorner = list (origin )
80+ originMask = list (origin )
81+ for i in range (DIMENSION - 1 ):
82+ minCorner [i ] = - extent / 1.5
83+ maxCorner [i ] = extent / 1.5
84+ minCorner [DIMENSION - 1 ] = trenchBottom
85+ maxCorner [DIMENSION - 1 ] = 1.0
86+ originMask [DIMENSION - 1 ] = trenchBottom + 1e-9
87+
88+ box = vls .Box (minCorner , maxCorner )
89+ vls .MakeGeometry (trench , box ).apply ()
90+
91+ # Subtract trench from substrate (Relative Complement)
92+ vls .BooleanOperation (substrate , trench , vls .BooleanOperationEnum .RELATIVE_COMPLEMENT ).apply ()
93+
94+ # 4. Create the Mask Layer
95+ # The mask prevents etching at the bottom of the trench in specific configurations,
96+ # or acts as a hard stop/masking material.
97+ mask = vls .Domain (bounds , boundaryCons , gridDelta )
98+
99+ vls .MakeGeometry (mask , vls .Plane (originMask , downNormal )).apply ()
100+
101+ # Intersect with substrate geometry
102+ vls .BooleanOperation (mask , substrate , vls .BooleanOperationEnum .INTERSECT ).apply ()
103+
104+ # 5. Export Initial State
105+ print (f"Running in { DIMENSION } D." )
106+ print ("Extracting initial meshes..." )
107+ mesh = vls .Mesh ()
108+
109+ # Save substrate
110+ vls .ToSurfaceMesh (substrate , mesh ).apply ()
111+ vls .VTKWriter (mesh , f"substrate-{ DIMENSION } D.vtp" ).apply ()
112+
113+ # Save mask
114+ vls .ToSurfaceMesh (mask , mesh ).apply ()
115+ vls .VTKWriter (mesh , f"mask-{ DIMENSION } D.vtp" ).apply ()
116+
117+ print ("Creating new layer..." )
118+ polymer = vls .Domain (substrate )
119+
120+
121+ # 6. Setup Advection
122+ deposition = DepositionField ()
123+ etching = EtchingField ()
124+
125+ print ("Advecting" )
126+ advectionKernel = vls .Advect ()
127+
128+ # the level set to be advected has to be inserted last
129+ # the other could be taken as a mask layer for advection
130+ advectionKernel .insertNextLevelSet (mask )
131+ advectionKernel .insertNextLevelSet (substrate )
132+ advectionKernel .insertNextLevelSet (polymer )
133+
134+ advectionKernel .setSaveAdvectionVelocities (True )
135+ # advectionKernel.setVelocityField(etching)
136+
137+ advectionKernel .setVelocityField (deposition )
138+ advectionKernel .apply ()
139+
140+ vls .ToSurfaceMesh (polymer , mesh ).apply ()
141+ vls .VTKWriter (mesh , f"newLayer-{ DIMENSION } D.vtp" ).apply ()
142+
143+ advectionKernel .setSaveAdvectionVelocities (True )
144+ advectionKernel .setVelocityField (etching )
145+
146+ # Use default integration scheme (Lax Friedrichs 1st order) as in the C++ else branch
147+ # advectionKernel.setIntegrationScheme(viennals.IntegrationSchemeEnum.LAX_FRIEDRICHS_1ST_ORDER)
148+
149+ # 7. Time Loop
150+ finalTime = 10.0
151+ counter = 1
152+ currentTime = 0.0
153+
154+ # The advect kernel calculates stable time steps automatically.
155+ # We call apply() repeatedly until we reach finalTime.
156+
157+ # Note: In the C++ example, the loop structure is:
158+ # for (double time = 0.; time < finalTime; time += advectionKernel.getAdvectedTime())
159+ # This implies one step is taken, then time is updated.
160+
161+ # Save initial state
162+ vls .ToSurfaceMesh (polymer , mesh ).apply ()
163+ vls .VTKWriter (mesh , f"numerical-{ DIMENSION } D-0.vtp" ).apply ()
164+
165+ while currentTime < finalTime :
166+ advectionKernel .apply ()
167+
168+ stepTime = advectionKernel .getAdvectedTime ()
169+ currentTime += stepTime
170+
171+ print (f"Advection step: { counter } , time: { stepTime :.4f} (Total: { currentTime :.4f} )" )
172+
173+ # Export result
174+ vls .ToSurfaceMesh (polymer , mesh ).apply ()
175+ vls .VTKWriter (mesh , f"numerical-{ DIMENSION } D-{ counter } .vtp" ).apply ()
176+
177+ counter += 1
178+
179+ print (f"\n Number of Advection steps taken: { counter } " )
180+
181+ # Final export
182+ vls .ToSurfaceMesh (polymer , mesh ).apply ()
183+ vls .VTKWriter (mesh , f"final-{ DIMENSION } D.vtp" ).apply ()
184+
185+ if __name__ == "__main__" :
186+ main ()
0 commit comments