Ghost layer generation using paving functions#

This example showcase how to use the paving functions to generate ghost layers.

The complex thing is that we want to intersect the current data with the ghost layer, but we do not want to modify the original buffer. As a result we perform the intersection in a space transformed by the paving function, and then map the result back to the original space.

Formally speaking for a paving function f:

\[\text{Ghost layer} = f(\text{patch}) \vee \text{box} \ = f( f^{-1}(f(\text{patch}) \vee \text{box}) ) \ = f( \text{patch} \vee f^{-1}(\text{box}) ),\]

where \(\vee\) denotes a ghost layer intersection.

24 import matplotlib.pyplot as plt
25 import numpy as np
26
27 import shamrock

Set box size

33 box_size_x = 2.0
34 box_size_y = 2.0
35 box_size_z = 2.0

Particle set

40 color_set = [
41     "blue",
42     "red",
43     "green",
44     "black",
45     "orange",
46     "purple",
47     "brown",
48     "pink",
49     "gray",
50     "olive",
51     "cyan",
52     "magenta",
53     "yellow",
54     "teal",
55     "navy",
56     "gold",
57     "lime",
58     "indigo",
59     "maroon",
60     "turquoise",
61 ]
62
63 x_set = np.linspace(0.2, box_size_x - 0.2, 4)
64 y_set = np.linspace(0.2, box_size_y - 0.2, 4)
65
66 parts = []
67 i = 0
68 for x in x_set:
69     for y in y_set:
70         parts.append({"x": x, "y": y, "color": color_set[i % len(color_set)]})
71         i += 1

Utility to plot the paving function

 77 margin = 0.0
 78
 79
 80 def add_rect(x, y, w, h, facecolor="grey", alpha=0.5):
 81     plt.gca().add_patch(
 82         plt.Rectangle(
 83             (x + margin, y + margin),
 84             w - 2 * margin,
 85             h - 2 * margin,
 86             alpha=alpha,
 87             fill=True,
 88             facecolor=facecolor,
 89             edgecolor="black",
 90             linewidth=4,
 91         )
 92     )
 93
 94
 95 def add_rect_aabb(aabb, facecolor="grey", alpha=0.5):
 96     add_rect(
 97         aabb.lower[0],
 98         aabb.lower[1],
 99         aabb.upper[0] - aabb.lower[0],
100         aabb.upper[1] - aabb.lower[1],
101         facecolor=facecolor,
102         alpha=alpha,
103     )
104
105
106 def ghost_intersect(part, box_to_intersect):
107     """
108     dummy ghost layer intersection to showcase how it works
109     """
110     x, y, z = part["x"], part["y"], 0.0
111
112     part_aabb = shamrock.math.AABB_f64_3((x - 0.3, y - 0.3, 0.0), (x + 0.3, y + 0.3, 0.0))
113
114     intersect = box_to_intersect.get_intersect(part_aabb)
115
116     _x, _y, _z = intersect.upper
117     _z = 1.0  # we want to be sure that the volume is not null but the z is not used
118     intersect.upper = (_x, _y, _z)
119
120     return intersect.is_volume_not_null()
121
122
123 def plot_paving_function(pav_func, pav_func_name):
124     box_to_intersect = shamrock.math.AABB_f64_3((0.0, 0.0, 0.0), (box_size_x, box_size_y, 0.0))
125
126     def get_indices():
127         for i in range(-3, 4):
128             for j in range(-2, 3):
129                 if i == 0 and j == 0:
130                     continue
131                 yield i, j
132
133     plt.figure()
134
135     for i, j in get_indices():
136         box_to_intersect_inv_mapped = pav_func.f_aabb_inv(box_to_intersect, i, j, 0)
137
138         add_rect_aabb(box_to_intersect_inv_mapped)
139
140     for part in parts:
141         x, y, z = (part["x"], part["y"], 0.0)
142         plt.scatter(x, y, color=part["color"])
143
144     plt.title(f"Paving function: {pav_func_name}\n1. Inverse map current sim box")
145     plt.xlabel("x")
146     plt.ylabel("y")
147     plt.axis("equal")
148
149     plt.figure()
150
151     for i, j in get_indices():
152         box_to_intersect_inv_mapped = pav_func.f_aabb_inv(box_to_intersect, i, j, 0)
153
154         add_rect_aabb(box_to_intersect_inv_mapped)
155
156         for part in parts:
157             x, y, z = (part["x"], part["y"], 0.0)
158             if ghost_intersect(part, box_to_intersect_inv_mapped):
159                 plt.scatter(x, y, color=part["color"])
160
161     plt.title(f"Paving function: {pav_func_name}\n2. Inverse ghost layer with inverse map")
162     plt.xlabel("x")
163     plt.ylabel("y")
164     plt.axis("equal")
165
166     plt.figure()
167
168     for i, j in get_indices():
169         box_to_intersect_inv_mapped = pav_func.f_aabb_inv(box_to_intersect, i, j, 0)
170         box_to_intersect_mapped = pav_func.f_aabb(box_to_intersect, i, j, 0)
171
172         add_rect_aabb(box_to_intersect_mapped)
173
174         for part in parts:
175             x, y, z = (part["x"], part["y"], 0.0)
176
177             if ghost_intersect(part, box_to_intersect_inv_mapped):
178                 x, y, z = pav_func.f((x, y, 0.0), i, j, 0)
179                 plt.scatter(x, y, color=part["color"])
180
181     for part in parts:
182         x, y, z = (part["x"], part["y"], 0.0)
183         plt.scatter(x, y, color=part["color"])
184
185     plt.title(f"Paving function: {pav_func_name}\n3. Map back the resulting ghost layer")
186     plt.xlabel("x")
187     plt.ylabel("y")
188     plt.axis("equal")

Testing the paving functions#

Periodic paving function

198 plot_paving_function(
199     shamrock.math.paving_function_general_3d(
200         (box_size_x, box_size_y, box_size_z),
201         (box_size_x / 2.0, box_size_y / 2.0, box_size_z / 2.0),
202         True,
203         True,
204         True,
205     ),
206     "Periodic box",
207 )
  • Paving function: Periodic box 1. Inverse map current sim box
  • Paving function: Periodic box 2. Inverse ghost layer with inverse map
  • Paving function: Periodic box 3. Map back the resulting ghost layer

Periodic & reflective paving function

213 plot_paving_function(
214     shamrock.math.paving_function_general_3d(
215         (box_size_x, box_size_y, box_size_z),
216         (box_size_x / 2.0, box_size_y / 2.0, box_size_z / 2.0),
217         False,
218         True,
219         True,
220     ),
221     "reflective in x periodic in y",
222 )
  • Paving function: reflective in x periodic in y 1. Inverse map current sim box
  • Paving function: reflective in x periodic in y 2. Inverse ghost layer with inverse map
  • Paving function: reflective in x periodic in y 3. Map back the resulting ghost layer

Fully reflective paving function

228 plot_paving_function(
229     shamrock.math.paving_function_general_3d(
230         (box_size_x, box_size_y, box_size_z),
231         (box_size_x / 2.0, box_size_y / 2.0, box_size_z / 2.0),
232         False,
233         False,
234         True,
235     ),
236     "Fully reflective",
237 )
  • Paving function: Fully reflective 1. Inverse map current sim box
  • Paving function: Fully reflective 2. Inverse ghost layer with inverse map
  • Paving function: Fully reflective 3. Map back the resulting ghost layer

Periodic & reflective paving function with shear

243 plot_paving_function(
244     shamrock.math.paving_function_general_3d_shear_x(
245         (box_size_x, box_size_y, box_size_z),
246         (box_size_x / 2.0, box_size_y / 2.0, box_size_z / 2.0),
247         False,
248         True,
249         True,
250         0.3,
251     ),
252     "reflective in x periodic in y with shear",
253 )
  • Paving function: reflective in x periodic in y with shear 1. Inverse map current sim box
  • Paving function: reflective in x periodic in y with shear 2. Inverse ghost layer with inverse map
  • Paving function: reflective in x periodic in y with shear 3. Map back the resulting ghost layer

Testing the list of indices with the paving function#

261 def test_paving_index_intersecting(pav_func, pav_func_name):
262     radius_int = 3.5
263     box_to_intersect = shamrock.math.AABB_f64_3(
264         (box_size_x / 2 - radius_int, box_size_y / 2 - radius_int, box_size_z / 2 - radius_int),
265         (box_size_x / 2 + radius_int, box_size_y / 2 + radius_int, box_size_z / 2 + radius_int),
266     )
267
268     indices = pav_func.get_paving_index_intersecting(box_to_intersect)
269
270     def get_indices():
271         x_r = 6
272         y_r = 4
273         for i in range(-x_r, x_r + 1):
274             for j in range(-y_r, y_r + 1):
275                 yield i, j
276
277     domain = shamrock.math.AABB_f64_3((0.0, 0.0, 0.0), (box_size_x, box_size_y, box_size_z))
278
279     plt.figure()
280
281     for i, j in get_indices():
282         domain_mapped = pav_func.f_aabb(domain, i, j, 0)
283
284         if [i, j, 0] in indices:
285             add_rect_aabb(domain_mapped, facecolor="green")
286         else:
287             add_rect_aabb(domain_mapped, facecolor="grey")
288
289     add_rect_aabb(box_to_intersect, facecolor="lightblue", alpha=0.5)
290
291     plt.title(f"Paving function: {pav_func_name}\n")
292     plt.xlabel("x")
293     plt.ylabel("y")
294     plt.axis("equal")

Periodic paving function

300 test_paving_index_intersecting(
301     shamrock.math.paving_function_general_3d(
302         (box_size_x, box_size_y, box_size_z),
303         (box_size_x / 2.0, box_size_y / 2.0, box_size_z / 2.0),
304         True,
305         True,
306         True,
307     ),
308     "Periodic box",
309 )
Paving function: Periodic box

Periodic & reflective paving function

315 test_paving_index_intersecting(
316     shamrock.math.paving_function_general_3d(
317         (box_size_x, box_size_y, box_size_z),
318         (box_size_x / 2.0, box_size_y / 2.0, box_size_z / 2.0),
319         False,
320         True,
321         True,
322     ),
323     "reflective in x periodic in y",
324 )
Paving function: reflective in x periodic in y

Fully reflective paving function

330 test_paving_index_intersecting(
331     shamrock.math.paving_function_general_3d(
332         (box_size_x, box_size_y, box_size_z),
333         (box_size_x / 2.0, box_size_y / 2.0, box_size_z / 2.0),
334         False,
335         False,
336         True,
337     ),
338     "Fully reflective",
339 )
Paving function: Fully reflective

Periodic & reflective paving function with shear

345 test_paving_index_intersecting(
346     shamrock.math.paving_function_general_3d_shear_x(
347         (box_size_x, box_size_y, box_size_z),
348         (box_size_x / 2.0, box_size_y / 2.0, box_size_z / 2.0),
349         False,
350         True,
351         True,
352         0.3,
353     ),
354     "reflective in x periodic in y with shear",
355 )
Paving function: reflective in x periodic in y with shear

Periodic & reflective paving function with strong shear

360 test_paving_index_intersecting(
361     shamrock.math.paving_function_general_3d_shear_x(
362         (box_size_x, box_size_y, box_size_z),
363         (box_size_x / 2.0, box_size_y / 2.0, box_size_z / 2.0),
364         False,
365         True,
366         True,
367         1.8,
368     ),
369     "reflective in x periodic in y with shear",
370 )
371
372 plt.show()
Paving function: reflective in x periodic in y with shear

Total running time of the script: (0 minutes 4.737 seconds)

Estimated memory usage: 159 MB

Gallery generated by Sphinx-Gallery