Skip to content

Commit e7507ab

Browse files
committed
3d
1 parent 675e73b commit e7507ab

File tree

93 files changed

+3753
-824
lines changed

Some content is hidden

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

93 files changed

+3753
-824
lines changed

.github/workflows/cmake_macos.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ jobs:
7878
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
7979
-DENABLE_SAMRAI_TESTS=OFF -DCMAKE_C_COMPILER_LAUNCHER=ccache \
8080
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DlowResourceTests=ON \
81-
-DCMAKE_CXX_FLAGS="-DPHARE_DIAG_DOUBLES=1 "
81+
-DCMAKE_CXX_FLAGS="-DPHARE_DIAG_DOUBLES=1 -DPHARE_SIMULATORS=2 "
8282
8383
- name: Build
8484
working-directory: ${{github.workspace}}/build

pyphare/pyphare/core/box.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ def __eq__(self, other):
6262
)
6363

6464
def __sub__(self, other):
65+
if isinstance(other, (list, tuple)):
66+
assert all([isinstance(item, Box) for item in other])
67+
return remove_all(self, other)
6568
assert isinstance(other, Box)
6669
return remove(self, other)
6770

@@ -179,5 +182,42 @@ def copy(arr, replace):
179182
return list(boxes.values())
180183

181184

185+
def remove_all(box, to_remove):
186+
if len(to_remove) > 0:
187+
remaining = box - to_remove[0]
188+
for to_rm in to_remove[1:]:
189+
tmp, remove = [], []
190+
for i, rem in enumerate(remaining):
191+
if rem * to_rm is not None:
192+
remove.append(i)
193+
tmp += rem - to_rm
194+
for rm in reversed(remove):
195+
del remaining[rm]
196+
remaining += tmp
197+
return remaining
198+
return box
199+
200+
201+
182202
def amr_to_local(box, ref_box):
183203
return Box(box.lower - ref_box.lower, box.upper - ref_box.lower)
204+
205+
206+
def select(data, box):
207+
return data[tuple([slice(l, u + 1) for l,u in zip(box.lower, box.upper)])]
208+
209+
class DataSelector:
210+
"""
211+
can't assign return to function unless []
212+
usage
213+
DataSelector(data)[box] = val
214+
"""
215+
def __init__(self, data):
216+
self.data = data
217+
def __getitem__(self, box_or_slice):
218+
if isinstance(box_or_slice, Box):
219+
return select(self.data, box_or_slice)
220+
return self.data[box_or_slice]
221+
222+
def __setitem__(self, box_or_slice, val):
223+
self.__getitem__(box_or_slice)[:] = val

pyphare/pyphare/core/gridlayout.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,24 @@ def yeeCoords(self, knode, iStart, centering, direction, ds, origin, derivOrder)
359359

360360
return x
361361

362+
def meshCoords(self, qty):
363+
ndim = self.ndim
364+
assert ndim > 0 and ndim < 4
365+
x = self.yeeCoordsFor(qty, "x")
366+
if ndim == 1:
367+
return x
368+
y = self.yeeCoordsFor(qty, "y")
369+
if ndim == 2:
370+
X, Y = np.meshgrid(x, y, indexing="ij")
371+
return np.array([X.flatten(), Y.flatten()]).T.reshape(
372+
(len(x), len(y), ndim)
373+
)
374+
z = self.yeeCoordsFor(qty, "z")
375+
X, Y, Z = np.meshgrid(x, y, z, indexing="ij")
376+
return np.array([X.flatten(), Y.flatten(), Z.flatten()]).T.reshape(
377+
(len(x), len(y), len(z), ndim)
378+
)
379+
362380
def yeeCoordsFor(self, qty, direction, withGhosts=True, **kwargs):
363381
"""
364382
from a qty and a direction, returns a 1d array containing

pyphare/pyphare/cpp/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#
44

55

6-
def cpp_lib(override=None):
6+
def cpp_lib():
77
import importlib
88

99
return importlib.import_module("pybindlibs.cpp")

pyphare/pyphare/pharein/maxwellian_fluid_model.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ def validate(self, sim, atol=1e-15):
193193
self.validate1d(sim, atol)
194194
elif sim.ndim == 2:
195195
self.validate2d(sim, atol)
196+
elif sim.ndim == 3:
197+
self.validate3d(sim, atol)
198+
else:
199+
raise ValueError("Unknown dimension")
196200

197201
def validate1d(self, sim, atol):
198202
domain_box = Box([0] * sim.ndim, sim.cells)
@@ -301,3 +305,79 @@ def getCoord(L, R, idir):
301305
)
302306
if sim.strict:
303307
raise RuntimeError("Simulation is not periodic")
308+
309+
def validate3d(self, sim, atol):
310+
domain_box = Box([0] * sim.ndim, sim.cells)
311+
layout = GridLayout(domain_box, domain_box.lower, sim.dl, sim.interp_order)
312+
nbrDualGhosts = layout.nbrGhostsPrimal(sim.interp_order)
313+
nbrPrimalGhosts = layout.nbrGhostsPrimal(sim.interp_order)
314+
directions = ["X", "Y", "Z"]
315+
domain = sim.simulation_domain()
316+
bx = self.model_dict["bx"]
317+
by = self.model_dict["by"]
318+
bz = self.model_dict["bz"]
319+
is_periodic = True
320+
not_periodic = []
321+
322+
def getCoord(L, R, idir):
323+
if idir == 0:
324+
return (L, np.zeros_like(L), np.zeros_like(L)), (
325+
R,
326+
np.zeros_like(R),
327+
np.zeros_like(R),
328+
)
329+
else:
330+
return (np.zeros_like(L), np.zeros_like(L), L), (
331+
np.zeros_like(R),
332+
np.zeros_like(R),
333+
R,
334+
)
335+
336+
phare_utilities.debug_print("3d periodic validation")
337+
for idir in np.arange(sim.ndim):
338+
phare_utilities.debug_print("validating direction ...", idir)
339+
if sim.boundary_types[idir] == "periodic":
340+
phare_utilities.debug_print(f"direction {idir} is periodic?")
341+
dual_left = (np.arange(-nbrDualGhosts, nbrDualGhosts) + 0.5) * sim.dl[0]
342+
dual_right = dual_left + domain[0]
343+
primal_left = np.arange(-nbrPrimalGhosts, nbrPrimalGhosts) * sim.dl[0]
344+
primal_right = primal_left + domain[0]
345+
346+
direction = directions[idir]
347+
348+
for b_i, b_name in zip((bx, by, bz), ("Bx", "By", "Bz")):
349+
if layout.qtyIsDual(b_name, direction):
350+
L, R = dual_left, dual_right
351+
else:
352+
L, R = primal_left, primal_right
353+
354+
coordsL, coordsR = getCoord(L, R, idir)
355+
check = np.allclose(b_i(*coordsL), b_i(*coordsR), atol=atol, rtol=0)
356+
if not check:
357+
not_periodic += [(b_name, idir)]
358+
is_periodic &= check
359+
360+
for pop in self.populations:
361+
functions = ("vx", "vy", "vz", "vthx", "vthy", "vthz")
362+
L, R = primal_left, primal_right
363+
coordsL, coordsR = getCoord(L, R, idir)
364+
365+
for fn in functions:
366+
f = self.model_dict[pop][fn]
367+
fL = f(*coordsL)
368+
fR = f(*coordsR)
369+
check = np.allclose(fL, fR, atol=atol, rtol=0)
370+
phare_utilities.debug_print(
371+
f"checked {fn} : fL = {fL} and fR = {fR} and check = {check}"
372+
)
373+
if not check:
374+
not_periodic += [(fn, idir)]
375+
is_periodic &= check
376+
377+
if not is_periodic:
378+
print(
379+
"Warning: Simulation is periodic but some functions are not : ",
380+
not_periodic,
381+
)
382+
if sim.strict:
383+
raise RuntimeError("Simulation is not periodic")

pyphare/pyphare/pharesee/geometry.py

Lines changed: 82 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,24 @@ def domain_border_ghost_boxes(domain_box, patches):
5454
elif domain_box.ndim == 2:
5555
upper_x, upper_y = domain_box.upper
5656
return {
57-
"bottom": Box(
58-
(
59-
0,
60-
0,
61-
),
62-
(upper_x, ghost_box_width),
63-
),
64-
"top": Box((0, upper_y - ghost_box_width), (upper_x, upper_y)),
6557
"left": Box((0, 0), (ghost_box_width, upper_y)),
6658
"right": Box((upper_x - ghost_box_width, 0), (upper_x, upper_y)),
59+
"bottom": Box((0, 0), (upper_x, ghost_box_width)),
60+
"top": Box((0, upper_y - ghost_box_width), (upper_x, upper_y)),
6761
}
6862

69-
raise ValueError("Unhandeled dimension")
63+
else:
64+
upper_x, upper_y, upper_z = domain_box.upper
65+
return {
66+
"left": Box((0, 0, 0), (ghost_box_width, upper_y, upper_z)),
67+
"right": Box(
68+
(upper_x - ghost_box_width, 0, 0), (upper_x, upper_y, upper_z)
69+
),
70+
"bottom": Box((0, 0, 0), (upper_x, ghost_box_width, upper_z)),
71+
"top": Box((0, upper_y - ghost_box_width, 0), (upper_x, upper_y, upper_z)),
72+
"front": Box((0, 0, 0), (upper_x, upper_y, ghost_box_width)),
73+
"back": Box((0, 0, upper_z - ghost_box_width), (upper_x, upper_y, upper_z)),
74+
}
7075

7176

7277
def touch_domain_border(box, domain_box, border):
@@ -79,21 +84,29 @@ def touch_domain_border(box, domain_box, border):
7984

8085

8186
def periodicity_shifts(domain_box):
87+
shifts = {}
88+
8289
if domain_box.ndim == 1:
8390
shape_x = domain_box.shape
84-
return {
91+
shifts = {
8592
"left": shape_x,
8693
"right": -shape_x,
8794
}
95+
shifts.update({"leftright": [shifts["left"], shifts["right"]]})
8896

8997
if domain_box.ndim == 2:
9098
shape_x, shape_y = domain_box.shape
99+
if domain_box.ndim == 3:
100+
shape_x, shape_y, shape_z = domain_box.shape
101+
102+
if domain_box.ndim > 1:
91103
shifts = {
92104
"left": [(shape_x, 0)],
93105
"right": [(-shape_x, 0)],
94106
"bottom": [(0, shape_y)],
95107
"top": [(0, -shape_y)],
96108
}
109+
97110
shifts.update(
98111
{
99112
"bottomleft": [*shifts["left"], *shifts["bottom"], (shape_x, shape_y)],
@@ -134,7 +147,7 @@ def periodicity_shifts(domain_box):
134147
shifts["topleft"][-1],
135148
shifts["topright"][-1],
136149
],
137-
"bottomtopleftright": [ # one patch covers domain
150+
"leftrightbottomtop": [ # one patch covers domain
138151
*shifts["bottomleft"],
139152
*shifts["topright"],
140153
shifts["bottomright"][-1],
@@ -144,7 +157,35 @@ def periodicity_shifts(domain_box):
144157
)
145158

146159
if domain_box.ndim == 3:
147-
raise ValueError("Unhandeled dimension")
160+
front = {
161+
f"{k}front": [(v[0], v[1], shape_z) for v in l] for k, l in shifts.items()
162+
}
163+
back = {
164+
f"{k}back": [([v[0], v[1], -shape_z]) for v in l] for k, l in shifts.items()
165+
}
166+
167+
shifts = {k: [([v[0], v[1], 0]) for v in l] for k, l in shifts.items()}
168+
169+
shifts.update(front)
170+
shifts.update(back)
171+
shifts.update(
172+
{
173+
"back": [(0, 0, -shape_z)],
174+
"front": [(0, 0, shape_z)],
175+
"leftrightbottomtopfrontback": [
176+
*shifts["bottomleftfront"],
177+
*shifts["bottomrightback"],
178+
*shifts["topleftfront"],
179+
*shifts["toprightback"],
180+
],
181+
}
182+
)
183+
184+
assert len(list(shifts.keys())) == len(
185+
set(["".join(sorted(k)) for k in list(shifts.keys())])
186+
)
187+
188+
shifts = {"".join(sorted(k)): l for k, l in shifts.items()}
148189

149190
return shifts
150191

@@ -246,7 +287,7 @@ def borders_per(patch):
246287
]
247288

248289
for patch_i, ref_patch in enumerate(border_patches):
249-
in_sides = borders_per_patch[ref_patch]
290+
in_sides = "".join(sorted(borders_per_patch[ref_patch]))
250291
assert in_sides in shifts
251292

252293
for ref_pdname, ref_pd in ref_patch.patch_datas.items():
@@ -336,36 +377,41 @@ def get_periodic_list(patches, domain_box, n_ghosts):
336377
shift_patch(first_patch, domain_box.shape)
337378
sorted_patches.append(first_patch)
338379

380+
return sorted_patches
381+
382+
dbu = domain_box.upper
383+
339384
if dim == 2:
340385
sides = {
341-
"bottom": Box([0, 0], [domain_box.upper[0], 0]),
342-
"top": Box(
343-
[0, domain_box.upper[1]], [domain_box.upper[0], domain_box.upper[1]]
344-
),
345-
"left": Box([0, 0], [0, domain_box.upper[1]]),
346-
"right": Box(
347-
[domain_box.upper[0], 0], [domain_box.upper[0], domain_box.upper[1]]
348-
),
386+
"left": Box([0, 0], [0, dbu[1]]),
387+
"right": Box([dbu[0], 0], [dbu[0], dbu[1]]),
388+
"bottom": Box([0, 0], [dbu[0], 0]),
389+
"top": Box([0, dbu[1]], [dbu[0], dbu[1]]),
349390
}
350391

351-
shifts = periodicity_shifts(domain_box)
392+
else:
393+
sides = {
394+
"left": Box([0, 0, 0], [0, dbu[1], dbu[2]]),
395+
"right": Box([dbu[0], 0, 0], [dbu[0], dbu[1], dbu[2]]),
396+
"bottom": Box([0, 0, 0], [dbu[0], 0, dbu[2]]),
397+
"top": Box([0, dbu[1], 0], [dbu[0], dbu[1], dbu[2]]),
398+
"front": Box([0, 0, 0], [dbu[0], dbu[1], 0]),
399+
"back": Box([0, 0, dbu[2]], [dbu[0], dbu[1], dbu[2]]),
400+
}
352401

353-
def borders_per(box):
354-
return "".join(
355-
[key for key, side in sides.items() if box * side is not None]
356-
)
402+
shifts = periodicity_shifts(domain_box)
357403

358-
for patch in patches:
359-
in_sides = borders_per(boxm.grow(patch.box, n_ghosts))
404+
def borders_per(box):
405+
return "".join([key for key, side in sides.items() if box * side is not None])
360406

361-
if in_sides in shifts: # in_sides might be empty, so no borders
362-
for shift in shifts[in_sides]:
363-
patch_copy = copy(patch)
364-
shift_patch(patch_copy, shift)
365-
sorted_patches.append(patch_copy)
407+
for patch in patches:
408+
in_sides = "".join(sorted(borders_per(boxm.grow(patch.box, n_ghosts))))
366409

367-
if dim == 3:
368-
raise ValueError("not yet implemented")
410+
if in_sides in shifts: # in_sides might be empty, so no borders
411+
for shift in shifts[in_sides]:
412+
patch_copy = copy(patch)
413+
shift_patch(patch_copy, shift)
414+
sorted_patches.append(patch_copy)
369415

370416
return sorted_patches
371417

@@ -470,18 +516,7 @@ def level_ghost_boxes(hierarchy, quantities, levelNbrs=[], time=None):
470516
check_patches = patches
471517

472518
for gabox in ghostAreaBoxes:
473-
remaining = gabox - check_patches[0].box
474-
475-
for patch in check_patches[1:]:
476-
tmp = []
477-
remove = []
478-
for i, rem in enumerate(remaining):
479-
if rem * patch.box is not None:
480-
remove.append(i)
481-
tmp += rem - patch.box
482-
for rm in reversed(remove):
483-
del remaining[rm]
484-
remaining += tmp
519+
remaining = gabox - [p.box for p in check_patches]
485520

486521
if ilvl not in lvl_gaboxes:
487522
lvl_gaboxes[ilvl] = {}

0 commit comments

Comments
 (0)