def build_refinementboxes(domain_box, **kwargs): refinement_ratio = kwargs["refinement_ratio"] boxes = {} for ilvl, boxes_data in kwargs["refinement_boxes"].items(): level_number = int(ilvl.strip("L")) + 1 if level_number not in boxes: boxes[level_number] = [] assert isinstance(boxes_data, (list, dict)) if isinstance(boxes_data, list): for _, refinement_box in enumerate(boxes_data): refined_box = boxm.refine(refinement_box, refinement_ratio) boxes[level_number].append(refined_box) else: for boxname, lower_upper in boxes_data.items(): refinement_box = Box(lower_upper[0][0], lower_upper[1][0]) refined_box = boxm.refine(refinement_box, refinement_ratio) boxes[level_number].append(refined_box) # coarse level boxes are arbitrarily divided in 2 patches in the middle middle_cell = np.round(domain_box.upper / 2) lower_box = Box(0, middle_cell) upper_box = Box(middle_cell + 1, domain_box.upper) boxes[0] = [lower_box, upper_box] return boxes
def get_periodic_list(patches, domain_box, n_ghosts): """ given a list of patches and a domain box the function returns a list of patches sorted by origin where the first (resp. last) patch is the last (resp. first) patch of the sorted list, if that patch touches the upper (resp. lower) border of the domain """ assert len(patches) > 0 dim = patches[-1].box.ndim assert all([p.box.ndim == dim for p in patches]) from copy import copy sorted_patches = sorted(patches, key=lambda p: p.origin.all()) if dim == 1: # only two possible border patches, [0] and [-1] # copy before check as the list is modified in-place last_patch = copy(sorted_patches[-1]) first_patch = copy(sorted_patches[0]) if touch_domain_border(boxm.grow(sorted_patches[-1].box, n_ghosts), domain_box, "upper"): shift_patch(last_patch, -domain_box.shape) sorted_patches.insert(0, last_patch) if touch_domain_border(boxm.grow(sorted_patches[0].box, n_ghosts), domain_box, "lower"): shift_patch(first_patch, domain_box.shape) sorted_patches.append(first_patch) if dim == 2: sides = { "bottom":Box([0, 0], [domain_box.upper[0], 0]), "top": Box([0, domain_box.upper[1]], [domain_box.upper[0], domain_box.upper[1]]), "left" : Box([0, 0], [0, domain_box.upper[1]]), "right": Box([domain_box.upper[0], 0], [domain_box.upper[0], domain_box.upper[1]]) } shifts = periodicity_shifts(domain_box) def borders_per(box): return "".join([key for key, side in sides.items() if box * side is not None]) for patch in patches: in_sides = borders_per(boxm.grow(patch.box, n_ghosts)) if in_sides in shifts: # in_sides might be empty, so no borders for shift in shifts[in_sides]: patch_copy = copy(patch) shift_patch(patch_copy, shift) sorted_patches.append(patch_copy) if dim == 3: raise ValueError("not yet implemented") return sorted_patches
def build_boxes(domain_box, **kwargs): refinement_ratio = kwargs["refinement_ratio"] ndim = domain_box.ndim boxes = {} for ilvl, boxes_data in kwargs["refinement_boxes"].items(): level_number = int(ilvl.strip("L")) + 1 if level_number not in boxes: boxes[level_number] = [] assert isinstance(boxes_data, (list, dict)) if isinstance(boxes_data, list): for _, refinement_box in enumerate(boxes_data): refined_box = boxm.refine(refinement_box, refinement_ratio) boxes[level_number].append(refined_box) else: for boxname, box in boxes_data.items(): if isinstance(box, Box): refinement_box = Box(box.lower, box.upper) else: refinement_box = Box(box[0], box[1]) refined_box = boxm.refine(refinement_box, refinement_ratio) boxes[level_number].append(refined_box) # coarse level boxes are arbitrarily divided in 2 patches in the middle if ( "largest_patch_size" in kwargs and (kwargs["largest_patch_size"] == domain_box.upper + 1).all() ): boxes[0] = [Box(domain_box.lower, domain_box.upper + 1)] else: if ndim == 1: middle_cell = np.round(domain_box.upper / 2) lower_box = Box(0, middle_cell) upper_box = Box(middle_cell + 1, domain_box.upper) if ndim >= 2: middle_cell = np.round(domain_box.upper / 2) lower_box = Box([0] * ndim, middle_cell - 1) upper_box = Box(middle_cell, domain_box.upper) boxes[0] = [lower_box, upper_box] if ndim >= 2: boxes[0].append( Box([0, middle_cell[1]], [middle_cell[0] - 1, domain_box.upper[1]]) ) boxes[0].append( Box([middle_cell[0], 0], [domain_box.upper[0], middle_cell[1] - 1]) ) return boxes
def dump(ndim, path, quantity): origin = [0] * ndim coarseLayout = gridlayout.GridLayout( Box([0] * ndim, [39] * ndim), origin=origin, dl=[0.2] * ndim ) fineLayout = gridlayout.GridLayout( Box([18] * ndim, [37] * ndim), origin=origin, dl=[0.1] * ndim ) domainSize = coarseLayout.box.shape * coarseLayout.dl coarseCoords, fineCoords, is_primal = [], [], [] for direction in directions[:ndim]: coarseCoords += [coarseLayout.yeeCoordsFor(quantity, direction)] fineCoords += [fineLayout.yeeCoordsFor(quantity, direction)] is_primal += [gridlayout.yee_element_is_primal(quantity, direction)] lower = [10] * ndim upper = [15 + primal for primal in is_primal] coarseBox = Box(lower, np.asarray(upper) - 1) for fn_idx, function in enumerate(functionList): fine = function(fineCoords, domainSize) coarse = function(coarseCoords, domainSize) afterCoarse = np.copy(coarse) coarseField = FieldData(coarseLayout, quantity, data=coarse) fineField = FieldData(fineLayout, quantity, data=fine) coarsen(quantity, coarseField, fineField, coarseBox, fine, afterCoarse) if fn_idx == 0: fineData = np.copy(fine) coarseData = np.copy(coarse) afterCoarseData = np.copy(afterCoarse) else: fineData = np.vstack((fineData, fine)) coarseData = np.vstack((coarseData, coarse)) afterCoarseData = np.vstack((afterCoarseData, afterCoarse)) file_datas = { f"{quantity}_fine_original{ndim}d.txt": fineData, f"{quantity}_coarse_original{ndim}d.txt": coarseData, f"{quantity}_coarse_linear_coarsed_{ndim}d.txt": afterCoarseData, } for filename, data in file_datas.items(): np.savetxt(os.path.join(path, filename), data, delimiter=" ")
def test_particle_ghost_area_boxes(self): hierarchy = self.basic_hierarchy() expected = { 0: [Box(-1, -1), Box(33, 33), Box(32, 32), Box(65, 65)], 1: [Box(9, 9), Box(60, 60), Box(63, 63), Box(112, 112)], } gaboxes = ghost_area_boxes(hierarchy, "particles") particles = "particles" # same number of levels self.assertEqual(len(expected), len(gaboxes)) for ilvl, lvl in enumerate(hierarchy.patch_levels): qtyNbr = len(gaboxes[ilvl].keys()) self.assertEqual(qtyNbr, 1) key = list(gaboxes[ilvl].keys())[0] level_ghost_area_boxes = sum( # aggregate to single list [actual["boxes"] for actual in gaboxes[ilvl][key]], [] ) self.assertEqual(expected[ilvl], level_ghost_area_boxes)
def test_level_ghost_boxes(self): hierarchy = self.basic_hierarchy() expected = { 1: [ { "boxes": [ Box(9, 9), ] }, { "boxes": [ Box(60, 60), ] }, { "boxes": [ Box(63, 63), ] }, { "boxes": [ Box(112, 112), ] }, ] } lvl_gaboxes = level_ghost_boxes(hierarchy, "particles") for ilvl in range(1, len(hierarchy.patch_levels)): qtyNbr = len(lvl_gaboxes[ilvl].keys()) self.assertEqual(qtyNbr, 1) key = list(lvl_gaboxes[ilvl].keys())[0] for actual, exp in zip(lvl_gaboxes[ilvl][key], expected[ilvl]): act_boxes = actual["boxes"] exp_boxes = exp["boxes"] for act_box, exp_box in zip(act_boxes, exp_boxes): self.assertEqual(act_box, exp_box)
def domain_border_ghost_boxes(domain_box, patches): max_ghosts = max([ pd.ghosts_nbr.max() for pd in sum([list(patch.patch_datas.values()) for patch in patches], []) ]) ghost_box_width = max_ghosts - 1 if domain_box.ndim == 1: upper_x = domain_box.upper return { "left": Box(0, ghost_box_width), "right": Box(upper_x - ghost_box_width, upper_x), } elif domain_box.ndim == 2: upper_x, upper_y = domain_box.upper return { "bottom": Box(( 0, 0, ), (upper_x, ghost_box_width)), "top": Box((0, upper_y - ghost_box_width), (upper_x, upper_y)), "left": Box((0, 0), (ghost_box_width, upper_y)), "right": Box((upper_x - ghost_box_width, 0), (upper_x, upper_y)), } raise ValueError("Unhandeled dimension")
def test_touch_border(self): hierarchy = self.basic_hierarchy() self.assertFalse( touch_domain_border(Box(10, 20), hierarchy.domain_box, "upper") ) self.assertFalse( touch_domain_border(Box(10, 20), hierarchy.domain_box, "lower") ) self.assertTrue(touch_domain_border(Box(0, 20), hierarchy.domain_box, "lower")) self.assertTrue(touch_domain_border(Box(-5, 20), hierarchy.domain_box, "lower")) self.assertTrue(touch_domain_border(Box(-5, 70), hierarchy.domain_box, "lower")) self.assertTrue(touch_domain_border(Box(-5, 70), hierarchy.domain_box, "upper")) self.assertTrue(touch_domain_border(Box(40, 70), hierarchy.domain_box, "upper")) self.assertTrue(touch_domain_border(Box(40, 64), hierarchy.domain_box, "upper"))
class BoxTesT(unittest.TestCase): @data( (Box(10, 20), Box(10, 20)), (Box(-5, 5), Box(-5, 5)), (Box(-5, -2), Box(-5, -2)), (Box([10, 10], [20, 20]), Box([10, 10], [20, 20])), (Box([-5, -5], [5, 5]), Box([-5, -5], [5, 5])), (Box([-5, -5], [-2, -2]), Box([-5, -5], [-2, -2])), ) @unpack def test_box_equality(self, box1, box2): self.assertTrue(box1 == box2) @data( (Box(10, 20), Box(20, 41)), (Box(-5, 11), Box(-10, 23)), (Box(-5, -2), Box(-10, -3)), (Box(1, 1), Box(2, 3)), (Box([10, 10], [20, 20]), Box([20, 20], [41, 41])), (Box([-5, -5], [11, 11]), Box([-10, -10], [23, 23])), (Box([-5, -5], [-2, -2]), Box([-10, -10], [-3, -3])), (Box([1, 1], [1, 1]), Box([2, 2], [3, 3])), ) @unpack def test_box_refinement(self, coarse, exp_refined): actual_refined = boxm.refine(coarse, 2) self.assertEqual(actual_refined, exp_refined) @data( (Box(10, 20), Box(15, 30), Box(15, 20)), # upper intersection (Box(-5, 20), Box(5, 5), Box(5, 5)), # box2 within and 1 cell (Box(-5, -5), Box(-5, -5), Box(-5, -5)), # all negative & 1 cell (Box(-5, 10), Box(-10, -5), Box(-5, -5)), # negative and box2 lower inter. (Box(10, 11), Box(11, 12), Box(11, 11)), # positive upper cell inter (Box(10, 11), Box(14, 15), None), # no inter # upper intersection (Box([10, 10], [20, 20]), Box([15, 15], [30, 30]), Box([15, 15], [20, 20])), # box2 within and 1 cell (Box([-5, -5], [20, 20]), Box([5, 5], [5, 5]), Box([5, 5], [5, 5])), # all negative & 1 cell (Box([-5, -5], [-5, -5]), Box([-5, -5], [-5, -5]), Box([-5, -5], [-5, -5])), # negative and box2 lower inter. (Box([-5, -5], [10, 10]), Box([-10, -10], [-5, -5]), Box([-5, -5], [-5, -5])), # positive upper cell inter (Box([10, 10], [11, 11]), Box([11, 11], [12, 12]), Box([11, 11], [11, 11])), # no inter (Box1D(10, 11), Box1D(14, 15), None), (Box2D(10, 11), Box2D(14, 15), None), (Box3D(10, 11), Box3D(14, 15), None), ) @unpack def test_intersection(self, box1, box2, exp_inter): self.assertEqual(box1 * box2, exp_inter) @data( (Box(10, 20), 5, Box(15, 25)), (Box(10, 20), -5, Box(5, 15)), (Box(10, 20), -15, Box(-5, 5)), (Box(-5, 20), 10, Box(5, 30)), (Box(-5, -5), -2, Box(-7, -7)), (Box([10, 10], [20, 20]), 5, Box([15, 15], [25, 25])), (Box([10, 10], [20, 20]), [5, 0], Box([15, 10], [25, 20])), (Box([10, 10], [20, 20]), [0, 5], Box([10, 15], [20, 25])), (Box([10, 10], [20, 20]), -5, Box([5, 5], [15, 15])), (Box([10, 10], [20, 20]), -15, Box([-5, -5], [5, 5])), (Box([-5, -5], [20, 20]), 10, Box([5, 5], [30, 30])), (Box([-5, -5], [-5, -5]), -2, Box([-7, -7], [-7, -7])), ) @unpack def test_shift(self, box, shift, expected): self.assertEqual(boxm.shift(box, shift), expected) @data( (Box(10, 20), 5, Box(5, 25)), (Box(-5, 20), 10, Box(-15, 30)), (Box(-5, -5), 2, Box(-7, -3)), (Box([10, 10], [20, 20]), 5, Box([5, 5], [25, 25])), (Box([-5, -5], [20, 20]), 10, Box([-15, -15], [30, 30])), (Box([-5, -5], [-5, -5]), 2, Box([-7, -7], [-3, -3])), ) @unpack def test_grow(self, box, size, expected): self.assertEqual(boxm.grow(box, [size] * box.dim()), expected) @data( (Box(10, 12)), (Box([10, 10], [12, 12])), ) def test_grow_neg_size_raises(self, box): with self.assertRaises(ValueError): boxm.grow(box, [-1] * box.dim()) @data( (Box1D(10, 29), Box1D(10, 25), [Box1D(26, 29)]), (Box1D(10, 29), Box1D(25, 29), [Box1D(10, 24)]), (Box1D(10, 29), Box1D(15, 19), [Box1D(10, 14), Box1D(20, 29)]), (Box1D(10, 29), Box1D(20, 29), [Box1D(10, 19)]), (Box(10, 30), Box(15, 25), [Box(10, 14), Box(26, 30) ]), # remove middle part (Box(10, 30), Box(5, 20), [ Box(21, 30), ]), # remove lower part (Box(10, 30), Box(15, 35), [ Box(10, 14), ]), # remove upper part (Box(10, 30), Box(5, 35), []), # remove all (Box(-10, 30), Box(-5, 25), [Box(-10, -6), Box(26, 30) ]), # remove middle part (Box(-10, 30), Box(-15, 20), [ Box(21, 30), ]), # remove lower part (Box(-10, 30), Box(15, 35), [ Box(-10, 14), ]), # remove upper part (Box(-10, 30), Box(-15, 35), []), # remove all (Box2D(10, 29), Box([10, 10], [25, 35]), [Box([26, 10], [29, 29])]), (Box2D(10, 29), Box([10, 10], [35, 25]), [Box([10, 26], [29, 29])]), ( Box2D(10, 29), Box2D(10, 25), [ # remove down left Box([26, 10], [29, 29]), # right Box([10, 26], [25, 29]), # up ]), ( Box2D(10, 29), Box2D(20, 29), [ # remove up right Box([10, 10], [19, 29]), # left Box([20, 10], [29, 19]) # down ]), ( Box2D(10, 29), Box2D(15, 25), [ # remove middle Box([10, 10], [14, 29]), # left Box([26, 10], [29, 29]), # right Box([15, 10], [25, 14]), # down Box([15, 26], [25, 29]) # up ]), ( Box2D(10, 29), Box2D(20, 25), [ # remove middle Box([10, 10], [19, 29]), # left Box([26, 10], [29, 29]), # right Box([20, 10], [25, 19]), # down Box([20, 26], [25, 29]) # up ]), ( Box2D(10, 29), Box([15, 10], [15, 25]), [ # enters one side Box([10, 10], [14, 29]), # left Box([16, 10], [29, 29]), # right Box([15, 26], [15, 29]) # up ]), ( Box3D(10, 29), Box3D(10, 15), [ # remove down left back Box([16, 10, 10], [29, 29, 29]), # right Box([10, 16, 10], [15, 29, 29]), # up Box([10, 10, 16], [15, 15, 29]), # front ]), ( Box3D(10, 29), Box3D(20, 25), [ # remove middle Box([10, 10, 10], [19, 29, 29]), # left Box([26, 10, 10], [29, 29, 29]), # right Box([20, 10, 10], [25, 19, 29]), # down Box([20, 26, 10], [25, 29, 29]), # up Box([20, 20, 10], [25, 25, 19]), # back Box([20, 20, 26], [25, 25, 29]), # front ]), ( Box3D(10, 29), Box([20, 10, 20], [20, 29, 20]), [ # remove middle Y block Box([10, 10, 10], [19, 29, 29]), # left Box([21, 10, 10], [29, 29, 29]), # right Box([20, 10, 10], [20, 29, 19]), # back Box([20, 10, 21], [20, 29, 29]), # front ]), ( Box3D(10, 29), Box([10, 10, 10], [10, 29, 29]), [ # remove left Box([11, 10, 10], [29, 29, 29]), # right ]), ( Box3D(10, 29), Box([29, 10, 10], [29, 29, 29]), [ # remove right Box([10, 10, 10], [28, 29, 29]), # left ]), ( Box3D(10, 29), Box([10, 10, 10], [29, 29, 10]), [ # remove back Box([10, 10, 11], [29, 29, 29]), # front ]), ( Box3D(10, 29), Box([10, 10, 29], [29, 29, 29]), [ # remove front Box([10, 10, 10], [29, 29, 28]), # back ]), ) @unpack def test_remove(self, box, to_remove, expected): remaining = boxm.remove(box, to_remove) self.assertEqual(expected, remaining) for i, k0 in enumerate(remaining): self.assertTrue(k0 in box) self.assertTrue(k0 not in to_remove) for k1 in remaining[i + 1:]: self.assertTrue(k0 not in k1) expectedCells, remainingCells = 0, 0 for exp, keep in zip(expected, remaining): expectedCells += exp.cells() remainingCells += keep.cells() self.assertTrue(expectedCells == remainingCells) self.assertTrue(box.cells() == remainingCells + (box * to_remove).cells()) @data((Box(10, 20), 10, True), (Box(10, 20), 11, True), (Box(10, 20), 20, True), (Box(-20, -10), -10, True), (Box(-20, -10), -20, True), (Box(-20, -10), -11, True), (Box(-20, -10), -9, False), (Box(10, 20), Box(10, 11), True), (Box(10, 20), Box(10, 10), True), (Box(10, 20), Box(15, 20), True), (Box(10, 20), Box(20, 20), True), (Box(10, 20), Box(20, 21), False), (Box(10, 20), Box(20, 21), False), (Box([10, 10], [20, 20]), [10, 10], True), (Box([10, 10], [20, 20]), [11, 11], True), (Box([10, 10], [20, 20]), [20, 20], True), (Box([-20, -20], [-10, -10]), [-10, -10], True), (Box([-20, -20], [-10, -10]), [-20, -20], True), (Box([-20, -20], [-10, -10]), [-11, -11], True), (Box([-20, -20], [-10, -10]), [-9, -9], False), (Box([10, 10], [20, 20]), Box([10, 10], [11, 11]), True), (Box([10, 10], [20, 20]), Box([10, 10], [10, 10]), True), (Box([10, 10], [20, 20]), Box([15, 15], [20, 20]), True), (Box( [10, 10], [20, 20]), Box([20, 20], [20, 20]), True), (Box2D( 10, 20), Box2D(21, 21), False), (Box3D(10, 20), Box3D( 20, 20), True), (Box3D(10, 20), Box3D(21, 21), False)) @unpack def test_in(self, box, element, expected): self.assertEqual(element in box, expected) @data((Box(33, 64), Box(-5, 69), Box(38, 69)), (Box( [33, 33], [64, 64]), Box([-5, -5], [69, 69]), Box([38, 38], [69, 69]))) @unpack def test_amr_to_local(self, box, ref_box, expected): self.assertEqual(boxm.amr_to_local(box, ref_box), expected)
def build_patch_datas(domain_box, boxes, **kwargs): quantities = kwargs["quantities"] origin = kwargs["origin"] interp_order = kwargs["interp_order"] domain_size = kwargs["domain_size"] cell_width = kwargs["cell_width"] refinement_ratio = kwargs["refinement_ratio"] skip_particles = False if "particles" in quantities: del quantities[quantities.index("particles")] else: skip_particles = True domain_layout = GridLayout(domain_box, origin, cell_width, interp_order) coarse_particles = Particles(box=domain_box) # copy domain particles and put them in ghost cells particle_ghost_nbr = domain_layout.particleGhostNbr(interp_order) box_extend = particle_ghost_nbr - 1 upper_slct_box = Box(domain_box.upper - box_extend, domain_box.upper) lower_slct_box = Box(domain_box.lower, domain_box.lower + box_extend) if not skip_particles: coarse_particles = Particles(box=domain_box) upper_cell_particles = coarse_particles.select(upper_slct_box) lower_cell_particles = coarse_particles.select(lower_slct_box) coarse_particles.add( upper_cell_particles.shift_icell(-domain_box.shape())) coarse_particles.add( lower_cell_particles.shift_icell(domain_box.shape())) patch_datas = {} for ilvl, lvl_box in boxes.items(): lvl_cell_width = cell_width / (refinement_ratio**ilvl) if not skip_particles: if ilvl == 0: lvl_particles = coarse_particles else: level_domain_box = boxm.refine(domain_box, refinement_ratio) lvl_ghost_domain_box = boxm.grow( level_domain_box, domain_layout.particleGhostNbr(interp_order)) lvl_particles = Particles(box=lvl_ghost_domain_box) if ilvl not in patch_datas: patch_datas[ilvl] = [] for box in lvl_box: ghost_box = boxm.grow(box, 5) origin = box.lower * lvl_cell_width layout = GridLayout(box, origin, lvl_cell_width, interp_order) datas = { qty: globals()[qty](ghost_box, layout, domain_size) for qty in quantities } if not skip_particles: datas["particles"] = lvl_particles.select(ghost_box) boxed_patch_datas = {} for qty_name, data in datas.items(): if qty_name == 'particles': pdata = ParticleData(layout, data, "pop_name") else: pdata = FieldData(layout, qty_name, data) boxed_patch_datas[qty_name] = pdata patch_datas[ilvl].append(boxed_patch_datas) return patch_datas
def test_overlaps(self): hierarchy = self.basic_hierarchy() expected = { 0: # Middle overlap, for all quantities [ {"box": Box(28, 38), "offset": (0, 0)}, {"box": Box(28, 37), "offset": (0, 0)}, {"box": Box(28, 37), "offset": (0, 0)}, {"box": Box(28, 37), "offset": (0, 0)}, {"box": Box(28, 38), "offset": (0, 0)}, {"box": Box(28, 38), "offset": (0, 0)}, {"box": Box(32, 33), "offset": (0, 0)}, # left side overlap with periodicity, for all quantities {"box": Box(-5, 5), "offset": (0, -65)}, # right side overlap with periodicity, for all quantities {"box": Box(60, 70), "offset": (65, 0)}, {"box": Box(-5, 4), "offset": (0, -65)}, {"box": Box(60, 69), "offset": (65, 0)}, {"box": Box(-5, 4), "offset": (0, -65)}, {"box": Box(60, 69), "offset": (65, 0)}, {"box": Box(-5, 4), "offset": (0, -65)}, {"box": Box(60, 69), "offset": (65, 0)}, {"box": Box(-5, 5), "offset": (0, -65)}, {"box": Box(60, 70), "offset": (65, 0)}, {"box": Box(-5, 5), "offset": (0, -65)}, {"box": Box(60, 70), "offset": (65, 0)}, {"box": Box(-1, 0), "offset": (0, -65)}, {"box": Box(64, 65), "offset": (65, 0)}, ], 1: [ # level 1 {"box": Box(59, 65), "offset": (0, 0)}, {"box": Box(59, 64), "offset": (0, 0)}, {"box": Box(59, 64), "offset": (0, 0)}, {"box": Box(59, 64), "offset": (0, 0)}, {"box": Box(59, 65), "offset": (0, 0)}, {"box": Box(59, 65), "offset": (0, 0)}, ], } overlaps = hierarchy_overlaps(hierarchy) for ilvl, lvl in enumerate(hierarchy.patch_levels): self.assertEqual(len(expected[ilvl]), len(overlaps[ilvl])) for exp, actual in zip(expected[ilvl], overlaps[ilvl]): act_box = actual["box"] act_offset = actual["offset"] exp_box = exp["box"] exp_offset = exp["offset"] self.assertEqual(act_box, exp_box) self.assertEqual(act_offset, exp_offset)
class GeometryTest(unittest.TestCase): def setup_hierarchy(self, dim, interp_order, nbr_cells, refinement_boxes, **kwargs): domain_size = np.asarray([1.0] * dim) return build_hierarchy( nbr_cells=nbr_cells, origin=np.asarray([0.0] * dim), interp_order=interp_order, domain_size=domain_size, cell_width=domain_size / nbr_cells, refinement_boxes=refinement_boxes, **kwargs ) # used for tests without ddt hierarchy overrides def basic_hierarchy(self): dim, interp_order, nbr_cells = (1, 1, 65) refinement_boxes = {"L0": {"B0": [(5,), (29,)], "B1": [(32,), (55,)]}} return self.setup_hierarchy(dim, interp_order, nbr_cells, refinement_boxes) def test_overlaps(self): hierarchy = self.basic_hierarchy() expected = { 0: # Middle overlap, for all quantities [ {"box": Box(28, 38), "offset": (0, 0)}, {"box": Box(28, 37), "offset": (0, 0)}, {"box": Box(28, 37), "offset": (0, 0)}, {"box": Box(28, 37), "offset": (0, 0)}, {"box": Box(28, 38), "offset": (0, 0)}, {"box": Box(28, 38), "offset": (0, 0)}, {"box": Box(32, 33), "offset": (0, 0)}, # left side overlap with periodicity, for all quantities {"box": Box(-5, 5), "offset": (0, -65)}, # right side overlap with periodicity, for all quantities {"box": Box(60, 70), "offset": (65, 0)}, {"box": Box(-5, 4), "offset": (0, -65)}, {"box": Box(60, 69), "offset": (65, 0)}, {"box": Box(-5, 4), "offset": (0, -65)}, {"box": Box(60, 69), "offset": (65, 0)}, {"box": Box(-5, 4), "offset": (0, -65)}, {"box": Box(60, 69), "offset": (65, 0)}, {"box": Box(-5, 5), "offset": (0, -65)}, {"box": Box(60, 70), "offset": (65, 0)}, {"box": Box(-5, 5), "offset": (0, -65)}, {"box": Box(60, 70), "offset": (65, 0)}, {"box": Box(-1, 0), "offset": (0, -65)}, {"box": Box(64, 65), "offset": (65, 0)}, ], 1: [ # level 1 {"box": Box(59, 65), "offset": (0, 0)}, {"box": Box(59, 64), "offset": (0, 0)}, {"box": Box(59, 64), "offset": (0, 0)}, {"box": Box(59, 64), "offset": (0, 0)}, {"box": Box(59, 65), "offset": (0, 0)}, {"box": Box(59, 65), "offset": (0, 0)}, ], } overlaps = hierarchy_overlaps(hierarchy) for ilvl, lvl in enumerate(hierarchy.patch_levels): self.assertEqual(len(expected[ilvl]), len(overlaps[ilvl])) for exp, actual in zip(expected[ilvl], overlaps[ilvl]): act_box = actual["box"] act_offset = actual["offset"] exp_box = exp["box"] exp_offset = exp["offset"] self.assertEqual(act_box, exp_box) self.assertEqual(act_offset, exp_offset) def test_touch_border(self): hierarchy = self.basic_hierarchy() self.assertFalse( touch_domain_border(Box(10, 20), hierarchy.domain_box, "upper") ) self.assertFalse( touch_domain_border(Box(10, 20), hierarchy.domain_box, "lower") ) self.assertTrue(touch_domain_border(Box(0, 20), hierarchy.domain_box, "lower")) self.assertTrue(touch_domain_border(Box(-5, 20), hierarchy.domain_box, "lower")) self.assertTrue(touch_domain_border(Box(-5, 70), hierarchy.domain_box, "lower")) self.assertTrue(touch_domain_border(Box(-5, 70), hierarchy.domain_box, "upper")) self.assertTrue(touch_domain_border(Box(40, 70), hierarchy.domain_box, "upper")) self.assertTrue(touch_domain_border(Box(40, 64), hierarchy.domain_box, "upper")) def test_particle_ghost_area_boxes(self): hierarchy = self.basic_hierarchy() expected = { 0: [Box(-1, -1), Box(33, 33), Box(32, 32), Box(65, 65)], 1: [Box(9, 9), Box(60, 60), Box(63, 63), Box(112, 112)], } gaboxes = ghost_area_boxes(hierarchy, "particles") particles = "particles" # same number of levels self.assertEqual(len(expected), len(gaboxes)) for ilvl, lvl in enumerate(hierarchy.patch_levels): qtyNbr = len(gaboxes[ilvl].keys()) self.assertEqual(qtyNbr, 1) key = list(gaboxes[ilvl].keys())[0] level_ghost_area_boxes = sum( # aggregate to single list [actual["boxes"] for actual in gaboxes[ilvl][key]], [] ) self.assertEqual(expected[ilvl], level_ghost_area_boxes) def test_level_ghost_boxes(self): hierarchy = self.basic_hierarchy() expected = { 1: [ { "boxes": [ Box(9, 9), ] }, { "boxes": [ Box(60, 60), ] }, { "boxes": [ Box(63, 63), ] }, { "boxes": [ Box(112, 112), ] }, ] } lvl_gaboxes = level_ghost_boxes(hierarchy, "particles") for ilvl in range(1, len(hierarchy.patch_levels)): qtyNbr = len(lvl_gaboxes[ilvl].keys()) self.assertEqual(qtyNbr, 1) key = list(lvl_gaboxes[ilvl].keys())[0] for actual, exp in zip(lvl_gaboxes[ilvl][key], expected[ilvl]): act_boxes = actual["boxes"] exp_boxes = exp["boxes"] for act_box, exp_box in zip(act_boxes, exp_boxes): self.assertEqual(act_box, exp_box) def test_level_ghost_boxes_do_not_overlap_patch_interiors(self): hierarchy = self.basic_hierarchy() lvl_gaboxes = level_ghost_boxes(hierarchy, "particles") for ilvl in range(1, len(hierarchy.patch_levels)): qtyNbr = len(lvl_gaboxes[ilvl].keys()) self.assertEqual(qtyNbr, 1) key = list(lvl_gaboxes[ilvl].keys())[0] for pdatainfo in lvl_gaboxes[ilvl][key]: for box in pdatainfo["boxes"]: for patch in hierarchy.patch_levels[ilvl].patches: self.assertIsNone(patch.box * box) @data( ( { "L0": [ Box(0, 4), Box(15, 19), ] }, { 1: [ {"box": Box(-5, 5), "offset": (0, -40)}, {"box": Box(35, 45), "offset": (40, 0)}, ] }, ), ( { "L0": [ Box(1, 5), Box(14, 18), ] }, { 1: [ {"box": Box(-3, 3), "offset": (0, -40)}, {"box": Box(37, 43), "offset": (40, 0)}, ] }, ), ) @unpack def test_periodic_overlaps(self, refinement_boxes, expected): dim, interp_order, nbr_cells = (1, 1, 20) hierarchy = self.setup_hierarchy( dim, interp_order, nbr_cells, refinement_boxes, quantities="Bx" ) overlaps = hierarchy_overlaps(hierarchy) for ilvl, lvl in enumerate(hierarchy.patch_levels): if ilvl not in expected: continue self.assertEqual(len(expected[ilvl]), len(overlaps[ilvl])) for exp, actual in zip(expected[ilvl], overlaps[ilvl]): self.assertEqual(actual["box"], exp["box"]) self.assertEqual(actual["offset"], exp["offset"]) @data( ( { "L0": [ Box(0, 4), Box(15, 19), ] }, { 1: [ Box(-10, -1), Box(0, 9), Box(30, 39), Box(40, 49), ] }, ), ( { "L0": [ Box(1, 5), Box(14, 18), ] }, { 1: [ Box(-12, -3), Box(2, 11), Box(28, 37), Box(42, 51), ] }, ), ) @unpack def test_periodic_list(self, refinement_boxes, expected): dim, interp_order, nbr_cells = (1, 1, 20) hierarchy = self.setup_hierarchy( dim, interp_order, nbr_cells, refinement_boxes, quantities="Bx" ) for ilvl in range(1, len(hierarchy.patch_levels)): refined_domain_box = hierarchy.refined_domain_box(ilvl) n_ghosts = ( hierarchy.patch_levels[ilvl].patches[0].patch_datas["Bx"].ghosts_nbr ) patches = get_periodic_list( hierarchy.patch_levels[ilvl].patches, refined_domain_box, n_ghosts ) periodic_boxes = [patch.box for patch in patches] for ref_box_i, ref_box in enumerate(periodic_boxes): for cmp_box in periodic_boxes[ref_box_i + 1 :]: self.assertTrue(ref_box * cmp_box == None) self.assertEqual(expected[ilvl], periodic_boxes) @data( ( { "L0": [ Box(0, 4), Box(15, 19), ] }, { 1: [ Box(10, 14), Box(25, 29), ] }, ), ( { "L0": [ Box(1, 5), Box(14, 18), ] }, { 1: [ Box(-2, 1), Box(12, 16), Box(23, 27), Box(38, 41), ] }, ), ( { "L0": [ Box(5, 9), Box(10, 14), ] }, { 1: [ Box(5, 9), Box(30, 34), ] }, ), ) @unpack def test_level_ghostboxes(self, refinement_boxes, expected): dim, interp_order, nbr_cells = (1, 1, 20) hierarchy = self.setup_hierarchy( dim, interp_order, nbr_cells, refinement_boxes, quantities="Bx" ) lvl_gaboxes = level_ghost_boxes(hierarchy, "Bx") for ilvl in range(1, len(hierarchy.patch_levels)): qtyNbr = len(lvl_gaboxes[ilvl].keys()) self.assertEqual(qtyNbr, 1) key = list(lvl_gaboxes[ilvl].keys())[0] ghost_area_boxes = sum( # aggregate to single list [actual["boxes"] for actual in lvl_gaboxes[ilvl][key]], [] ) self.assertEqual(expected[ilvl], ghost_area_boxes) def test_field_data_select(self): dim, interp_order, nbr_cells = (1, 1, 20) for qty in ["Bx", "By"]: hierarchy = self.setup_hierarchy( dim, interp_order, nbr_cells, {}, quantities=qty ) pdata = hierarchy.level(0).patches[0].patch_datas[qty] lower_gb, upper_gb = sorted( pdata.ghost_box - pdata.box, key=lambda box: box.lower.all() ) lower_ds, upper_ds = pdata[lower_gb], pdata[upper_gb] qty_is_primal = yee_element_is_primal(qty) assert lower_ds.shape[0] == pdata.ghosts_nbr[0] + qty_is_primal assert upper_ds.shape[0] == pdata.ghosts_nbr[0] + qty_is_primal np.testing.assert_array_equal(lower_ds, pdata.dataset[: lower_ds.shape[0]]) np.testing.assert_array_equal(upper_ds, pdata.dataset[-upper_ds.shape[0] :])
class ParticleLevelGhostGeometryTest(AGeometryTest): @data( ( # no patch ghost on level 1 { "L0": [Box2D(5, 9), Box2D(14, 19)] }, { 1: [ Box([9, 9], [9, 20]), Box([20, 9], [20, 20]), Box([10, 9], [19, 9]), Box([10, 20], [19, 20]), Box([27, 27], [27, 40]), Box([40, 27], [40, 40]), Box([28, 27], [39, 27]), Box([28, 40], [39, 40]), ] }, ), ( # top right of gabox0 overlaps bottom left gabox1 { "L0": [Box2D(5, 9), Box2D(10, 11)] }, { 1: [ Box([9, 9], [9, 20]), Box([20, 9], [20, 19]), Box([10, 9], [19, 9]), Box([10, 20], [19, 20]), Box([19, 20], [19, 24]), Box([24, 19], [24, 24]), Box([20, 19], [23, 19]), Box([20, 24], [23, 24]), ] }, ), ( # right side of gabox0 overlaps left side of gabox1 { "L0": [Box2D(2, 9), Box([10, 1], [14, 10])] }, { 1: [ Box([3, 3], [3, 20]), Box([4, 3], [19, 3]), Box([4, 20], [19, 20]), Box([19, 1], [19, 3]), Box([19, 20], [19, 22]), Box([30, 1], [30, 22]), Box([20, 1], [29, 1]), Box([20, 22], [29, 22]), ] }, ), ( { # right side of gabox0 overlaps left of gabox1/gabox2 # left side of gabox3 overlaps right of gabox1/gabox2 "L0": [ Box([0, 0], [4, 19]), Box([10, 0], [14, 19]), Box([5, 2], [9, 6]), Box([5, 12], [9, 16]), ] }, { 1: [ Box([-1, -1], [-1, 40]), Box([10, -1], [10, 3]), Box([10, 14], [10, 23]), Box([10, 34], [10, 40]), Box([0, -1], [9, -1]), Box([0, 40], [9, 40]), Box([19, -1], [19, 3]), Box([19, 14], [19, 23]), Box([19, 34], [19, 40]), Box([30, -1], [30, 40]), Box([20, -1], [29, -1]), Box([20, 40], [29, 40]), Box([10, 3], [19, 3]), Box([10, 14], [19, 14]), Box([10, 23], [19, 23]), Box([10, 34], [19, 34]), ] }, ), ) @unpack def test_level_ghost_boxes(self, refinement_boxes, expected): print("ParticleLevelGhostGeometryTest.test_level_ghost_boxes") dim, interp_order, nbr_cells = (2, 1, [20] * 2) hierarchy = self.setup_hierarchy(dim, interp_order, nbr_cells, refinement_boxes, quantities="particles") lvl_gaboxes = level_ghost_boxes(hierarchy, "particles") for ilvl in range(1, len(hierarchy.patch_levels)): self.assertEqual(len(lvl_gaboxes[ilvl].keys()), 1) key = list(lvl_gaboxes[ilvl].keys())[0] ghost_area_box_list = sum( # aggregate to single list [actual["boxes"] for actual in lvl_gaboxes[ilvl][key]], []) self.assertEqual(expected[ilvl], ghost_area_box_list) fig = hierarchy.plot_2d_patches( ilvl, collections=[ { "boxes": ghost_area_box_list, "facecolor": "yellow", }, { "boxes": [p.box for p in hierarchy.level(ilvl).patches], "facecolor": "grey", }, ], title="".join([ str(box) + "," for box in refinement_boxes["L" + str(ilvl - 1)] ]), ) fig.savefig( f"{type(self).__name__}_lvl_{ilvl}_{self._testMethodName}.png") @data( ( { # no level 1 }, { 0: [ Box([0, 0], [9, 9]), Box([0, 10], [9, 19]), Box([10, 0], [19, 9]), Box([10, 10], [19, 19]), Box([20, 0], [29, 9]), Box([0, 20], [9, 29]), Box([20, 20], [29, 29]), Box([-10, 10], [-1, 19]), Box([10, -10], [19, -1]), Box([-10, -10], [-1, -1]), Box([20, 10], [29, 19]), Box([0, -10], [9, -1]), Box([20, -10], [29, -1]), Box([-10, 0], [-1, 9]), Box([10, 20], [19, 29]), Box([-10, 20], [-1, 29]), ] }, ), ( { "L0": [ Box([0, 0], [4, 4]), Box([15, 15], [19, 19]), Box([15, 0], [19, 4]), Box([0, 15], [4, 19]), ] }, { 1: [ Box([0, 0], [9, 9]), Box([30, 0], [39, 9]), Box([0, 30], [9, 39]), Box([30, 30], [39, 39]), Box([40, 0], [49, 9]), Box([0, 40], [9, 49]), Box([40, 40], [49, 49]), Box([-10, 30], [-1, 39]), Box([30, -10], [39, -1]), Box([-10, -10], [-1, -1]), Box([-10, 0], [-1, 9]), Box([30, 40], [39, 49]), Box([-10, 40], [-1, 49]), Box([40, 30], [49, 39]), Box([0, -10], [9, -1]), Box([40, -10], [49, -1]), ] }, ), ( { "L0": [ Box([1, 1], [5, 5]), Box([14, 14], [18, 18]), Box([14, 1], [18, 5]), Box([1, 14], [5, 18]), ] }, { # NO lvl 1 PERIODIC PARTICLE GHOST BOX }, ), ) @unpack def test_patch_periodicity_copy(self, refinement_boxes, expected): print("ParticleLevelGhostGeometryTest.test_patch_periodicity_copy") dim, interp_order, nbr_cells = (2, 1, [20] * 2) hierarchy = self.setup_hierarchy(dim, interp_order, nbr_cells, refinement_boxes, quantities="particles") for ilvl, lvl in hierarchy.levels().items(): domain_box = hierarchy.level_domain_box(ilvl) qtyNbr = len(lvl.patches[0].patch_datas.keys()) self.assertEqual(qtyNbr, 1) pop_name = list(lvl.patches[0].patch_datas.keys())[0] n_ghosts = lvl.patches[0].patch_datas[pop_name].ghosts_nbr periodic_list = get_periodic_list(lvl.patches, domain_box, n_ghosts) box_list = [p.box for p in periodic_list] if ilvl in expected: self.assertEqual(expected[ilvl], box_list) fig = hierarchy.plot_2d_patches( ilvl, collections=[ { "boxes": [p.box for p in periodic_list], "facecolor": "grey", }, ], ) fig.savefig( f"{type(self).__name__}_lvl_{ilvl}_{self._testMethodName}.png")
class GeometryTest(AGeometryTest): @data( ( { # no level 1 }, { 0: [ { "box": Box([5, 5], [15, 14]), "offset": ([0, 0], [0, 0]) }, { "box": Box([-5, 5], [15, 14]), "offset": ([0, 0], [0, 0]) }, { "box": Box([5, -5], [15, 14]), "offset": ([0, 0], [0, 0]) }, { "box": Box([5, 5], [15, 24]), "offset": ([0, 0], [0, 0]) }, { "box": Box([5, 5], [25, 14]), "offset": ([0, 0], [0, 0]) }, { "box": Box([5, 5], [15, 14]), "offset": ([0, 0], [0, 0]) }, { "box": Box([-5, 5], [5, 14]), "offset": ([0, 0], [-20, 0]) }, { "box": Box([15, 5], [25, 14]), "offset": ([20, 0], [0, 0]) }, { "box": Box([-5, -5], [5, 14]), "offset": ([0, 0], [-20, 0]) }, { "box": Box([15, -5], [25, 14]), "offset": ([20, 0], [0, 0]) }, { "box": Box([5, -5], [15, 4]), "offset": ([0, 0], [0, -20]) }, { "box": Box([5, 15], [15, 24]), "offset": ([0, 20], [0, 0]) }, { "box": Box([-5, -5], [15, 4]), "offset": ([0, 0], [0, -20]) }, { "box": Box([-5, 15], [15, 24]), "offset": ([0, 20], [0, 0]) }, { "box": Box([-5, -5], [5, 4]), "offset": ([0, 0], [-20, -20]) }, { "box": Box([15, 15], [25, 24]), "offset": ([20, 20], [0, 0]) }, { "box": Box([15, 5], [25, 24]), "offset": ([0, 0], [20, 0]) }, { "box": Box([-5, 5], [5, 24]), "offset": ([-20, 0], [0, 0]) }, { "box": Box([5, 15], [25, 24]), "offset": ([0, 0], [0, 20]) }, { "box": Box([5, -5], [25, 4]), "offset": ([0, -20], [0, 0]) }, { "box": Box([-5, 5], [5, 14]), "offset": ([0, 0], [-20, 0]) }, { "box": Box([15, 5], [25, 14]), "offset": ([20, 0], [0, 0]) }, { "box": Box([5, 15], [15, 24]), "offset": ([0, 0], [0, 20]) }, { "box": Box([5, -5], [15, 4]), "offset": ([0, -20], [0, 0]) }, { "box": Box([-5, 15], [5, 24]), "offset": ([0, 0], [-20, 20]) }, { "box": Box([15, -5], [25, 4]), "offset": ([20, -20], [0, 0]) }, ] }, ), ( { "L0": [ Box2D(0, 4), Box([0, 15], [4, 19]), Box([15, 0], [19, 4]), Box2D(15, 19), ] }, { 1: [ # level 0 same as previous { "box": Box([-5, -5], [5, 14]), "offset": ([0, 0], [-40, 0]) }, { "box": Box([35, -5], [45, 14]), "offset": ([40, 0], [0, 0]) }, { "box": Box([-5, -5], [15, 4]), "offset": ([0, 0], [0, -40]) }, { "box": Box([-5, 35], [15, 44]), "offset": ([0, 40], [0, 0]) }, { "box": Box([-5, -5], [5, 4]), "offset": ([0, 0], [-40, -40]) }, { "box": Box([35, 35], [45, 44]), "offset": ([40, 40], [0, 0]) }, { "box": Box([-5, 25], [5, 44]), "offset": ([0, 0], [-40, 0]) }, { "box": Box([35, 25], [45, 44]), "offset": ([40, 0], [0, 0]) }, { "box": Box([-5, 35], [5, 44]), "offset": ([0, 0], [-40, 40]) }, { "box": Box([35, -5], [45, 4]), "offset": ([40, -40], [0, 0]) }, { "box": Box([25, -5], [45, 4]), "offset": ([0, 0], [0, -40]) }, { "box": Box([25, 35], [45, 44]), "offset": ([0, 40], [0, 0]) }, ] }, ), ) @unpack def test_overlaps(self, refinement_boxes, expected): print("GeometryTest.test_overlaps") test_id = self._testMethodName.split("_")[-1] dim, interp_order, nbr_cells = (2, 1, [20] * 2) hierarchy = self.setup_hierarchy(dim, interp_order, nbr_cells, refinement_boxes, quantities="Bx") level_overlaps = hierarchy_overlaps(hierarchy) for ilvl, lvl in enumerate(hierarchy.patch_levels): if ilvl not in expected: continue self.assertEqual(len(expected[ilvl]), len(level_overlaps[ilvl])) for exp, actual in zip(expected[ilvl], level_overlaps[ilvl]): self.assertEqual(actual["box"], exp["box"]) self.assertTrue((np.asarray(actual["offset"]) == np.asarray( exp["offset"])).all()) if 1 in level_overlaps: fig = hierarchy.plot_2d_patches( 1, [p.box for p in hierarchy.level(1).patches]) fig.savefig("hierarchy_2d_lvl_" + str(1) + "_simple_test_" + str(test_id) + ".png") @data([ { "box": Box([-5, -5], [6, 25]), "offset": ([0, 0], [-20, 0]) }, { "box": Box([15, -5], [26, 25]), "offset": ([20, 0], [0, 0]) }, { "box": Box([-5, -5], [26, 5]), "offset": ([0, 0], [0, -20]) }, { "box": Box([-5, 15], [26, 25]), "offset": ([0, 20], [0, 0]) }, { "box": Box([-5, -5], [6, 5]), "offset": ([0, 0], [-20, -20]) }, { "box": Box([15, 15], [26, 25]), "offset": ([20, 20], [0, 0]) }, { "box": Box([15, -5], [26, 25]), "offset": ([0, 0], [20, 0]) }, { "box": Box([-5, -5], [6, 25]), "offset": ([-20, 0], [0, 0]) }, { "box": Box([-5, 15], [26, 25]), "offset": ([0, 0], [0, 20]) }, { "box": Box([-5, -5], [26, 5]), "offset": ([0, -20], [0, 0]) }, { "box": Box([15, 15], [26, 25]), "offset": ([0, 0], [20, 20]) }, { "box": Box([-5, -5], [6, 5]), "offset": ([-20, -20], [0, 0]) }, { "box": Box([15, -5], [26, 5]), "offset": ([0, 0], [20, -20]) }, { "box": Box([-5, 15], [6, 25]), "offset": ([-20, 20], [0, 0]) }, { "box": Box([-5, 15], [6, 25]), "offset": ([0, 0], [-20, 20]) }, { "box": Box([15, -5], [26, 5]), "offset": ([20, -20], [0, 0]) }, ]) def test_large_patchoverlaps(self, expected): print(f"GeometryTest.{self._testMethodName}") test_id = self._testMethodName.split("_")[-1] dim, interp_order, nbr_cells = (2, 1, [20] * 2) hierarchy = self.setup_hierarchy(dim, interp_order, nbr_cells, {}, quantities="Bx", largest_patch_size=20) level_overlaps = hierarchy_overlaps(hierarchy) ilvl = 0 lvl = hierarchy.level(ilvl) overlap_boxes = [] self.assertEqual(len(expected), len(level_overlaps[ilvl])) for exp, actual in zip(expected, level_overlaps[ilvl]): self.assertEqual(actual["box"], exp["box"]) self.assertTrue((np.asarray(actual["offset"]) == np.asarray( exp["offset"])).all()) overlap_boxes += [actual["box"]] fig = hierarchy.plot_2d_patches( ilvl, collections=[ { "boxes": overlap_boxes, "facecolor": "yellow", }, { "boxes": [p.box for p in hierarchy.level(ilvl).patches], "facecolor": "grey", }, ], ) fig.savefig(f"hierarchy_2d_lvl_0_large_patch_test_{test_id}.png") def test_touch_border(self): print("GeometryTest.test_touch_border") hierarchy = super().basic_hierarchy() domain_box = hierarchy.domain_box self.assertFalse( touch_domain_border(Box2D(10, 14), domain_box, "upper")) self.assertFalse( touch_domain_border(Box2D(10, 14), domain_box, "lower")) self.assertTrue(touch_domain_border(Box2D(0, 14), domain_box, "lower")) self.assertTrue(touch_domain_border(Box2D(-5, 14), domain_box, "lower")) self.assertTrue(touch_domain_border(Box2D(-5, 10), domain_box, "lower")) self.assertTrue(touch_domain_border(Box2D(-5, 30), domain_box, "upper")) self.assertTrue(touch_domain_border(Box2D(10, 30), domain_box, "upper")) self.assertTrue(touch_domain_border(Box2D(10, 24), domain_box, "upper")) def test_particle_ghost_area_boxes(self): print("GeometryTest.test_particle_ghost_area_boxes") hierarchy = super().basic_hierarchy() expected = { 0: [ { "boxes": [ Box([-1, -1], [-1, 10]), Box([10, -1], [10, 10]), Box([0, -1], [9, -1]), Box([0, 10], [9, 10]), ] }, { "boxes": [ Box([9, 9], [9, 20]), Box([20, 9], [20, 20]), Box([10, 9], [19, 9]), Box([10, 20], [19, 20]), ] }, { "boxes": [ Box([-1, 9], [-1, 20]), Box([10, 9], [10, 20]), Box([0, 9], [9, 9]), Box([0, 20], [9, 20]), ] }, { "boxes": [ Box([9, -1], [9, 10]), Box([20, -1], [20, 10]), Box([10, -1], [19, -1]), Box([10, 10], [19, 10]), ] }, ], 1: [ { "boxes": [ Box([9, 9], [9, 20]), Box([20, 9], [20, 20]), Box([10, 9], [19, 9]), Box([10, 20], [19, 20]), ] }, { "boxes": [ Box([27, 27], [27, 40]), Box([40, 27], [40, 40]), Box([28, 27], [39, 27]), Box([28, 40], [39, 40]), ] }, ], } gaboxes = ghost_area_boxes(hierarchy, "particles") particles = "particles" self.assertEqual(len(expected), len(gaboxes)) for ilvl, lvl in enumerate(hierarchy.patch_levels): self.assertEqual(len(gaboxes[ilvl][particles]), len(expected[ilvl])) for act_pdata, exp_pdata in zip(gaboxes[ilvl][particles], expected[ilvl]): self.assertEqual(len(exp_pdata["boxes"]), len(act_pdata["boxes"])) for exp_box in exp_pdata["boxes"]: self.assertTrue(exp_box in act_pdata["boxes"]) def test_particle_level_ghost_boxes_do_not_overlap_patch_interiors(self): print( "GeometryTest.test_particle_level_ghost_boxes_do_not_overlap_patch_interiors" ) hierarchy = super().basic_hierarchy() lvl_gboxes = level_ghost_boxes(hierarchy, "particles") assert len(lvl_gboxes) > 0 for ilvl, pdatainfos in lvl_gboxes.items(): assert len(pdatainfos) > 0 for particles_id, gaboxes_list in pdatainfos.items(): assert len(gaboxes_list) > 0 for pdatainfo in gaboxes_list: for box in pdatainfo["boxes"]: for patch in hierarchy.patch_levels[ilvl].patches: self.assertIsNone(patch.box * box)
class SimulatorRefineBoxInputs(unittest.TestCase): def __init__(self, *args, **kwargs): super(SimulatorRefineBoxInputs, self).__init__(*args, **kwargs) self.simulator = None def dup(dic): dic = NoOverwriteDict(dic) dic.update(diags.copy()) dic.update( {"diags_fn": lambda model: dump_all_diags(model.populations)}) return dic """ The first set of boxes "B0": [(10,), (14,)] Are configured to force there to be a single patch on L0 This creates a case with MPI that there are an unequal number of Patches across MPI domains. This case must be handled and not hang due to collective calls not being handled properly. """ valid1D = [ dup({ "cells": [65], "refinement_boxes": { "L0": { "B0": [(10, ), (14, )] } } }), dup({ "cells": [65], "refinement_boxes": { "L0": { "B0": [(5, ), (55, )] } } }), dup({ "cells": [65], "refinement_boxes": { "L0": { "B0": Box(5, 55) } } }), dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 55)] } }), dup({ "cells": [65], "refinement_boxes": { 0: [Box(5, 55)] } }), dup({ "cells": [65], "refinement_boxes": { 0: [Box(0, 55)] } }), dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 14), Box(15, 25)] } }), dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 25)], "L1": [Box(12, 48)], "L2": [Box(60, 64)] } }), dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 25)], "L1": [Box(12, 48)] } }), dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 25)], "L1": [Box(20, 30)] } }), dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 25)], "L1": [Box(11, 49)] }, "nesting_buffer": 1 }), dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 25)], "L1": [Box(10, 50)] } }), dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 25)], "L1": [Box(15, 49)] } }), dup({ "cells": [65], "refinement_boxes": None, "smallest_patch_size": 20, "largest_patch_size": 20, "nesting_buffer": 10 }), ] invalid1D = [ # finer box outside lower dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 24)], "L1": [Box(9, 30)] } }), # finer box outside upper dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 24)], "L1": [Box(15, 50)] } }), # overlapping boxes dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 15), Box(15, 25)] } }), # box.upper outside domain dup({ "cells": [55], "refinement_boxes": { "L0": { "B0": [(5, ), (65, )] } } }), # largest_patch_size > smallest_patch_size dup({ "smallest_patch_size": 100, "largest_patch_size": 64, }), # refined_particle_nbr doesn't exist dup({"refined_particle_nbr": 1}), # L2 box incompatible with L1 box due to nesting buffer dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 25)], "L1": [Box(11, 49)] }, "nesting_buffer": 2 }), # negative nesting buffer dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 25)], "L1": [Box(11, 49)] }, "nesting_buffer": -1 }), # too large nesting buffer dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 25)], "L1": [Box(11, 49)] }, "nesting_buffer": 33 }), dup({ "cells": [65], "refinement_boxes": None, "largest_patch_size": 20, "nesting_buffer": 46 }), ] def tearDown(self): if self.simulator is not None: self.simulator.reset() def _do_dim(self, dim, input, valid: bool = False): for interp in range(1, 4): try: self.simulator = Simulator( populate_simulation(dim, interp, **input)) self.simulator.initialize() self.assertTrue(valid) self.simulator.dump(self.simulator.currentTime(), self.simulator.timeStep()) self.simulator = None except ValueError as e: self.assertTrue(not valid) @data(*valid1D) def test_1d_valid(self, input): self._do_dim(1, input, True) @data(*invalid1D) def test_1d_invalid(self, input): self._do_dim(1, input)
def test_particle_ghost_area_boxes(self): print("GeometryTest.test_particle_ghost_area_boxes") hierarchy = super().basic_hierarchy() expected = { 0: [ { "boxes": [ Box([-1, -1], [-1, 10]), Box([10, -1], [10, 10]), Box([0, -1], [9, -1]), Box([0, 10], [9, 10]), ] }, { "boxes": [ Box([9, 9], [9, 20]), Box([20, 9], [20, 20]), Box([10, 9], [19, 9]), Box([10, 20], [19, 20]), ] }, { "boxes": [ Box([-1, 9], [-1, 20]), Box([10, 9], [10, 20]), Box([0, 9], [9, 9]), Box([0, 20], [9, 20]), ] }, { "boxes": [ Box([9, -1], [9, 10]), Box([20, -1], [20, 10]), Box([10, -1], [19, -1]), Box([10, 10], [19, 10]), ] }, ], 1: [ { "boxes": [ Box([9, 9], [9, 20]), Box([20, 9], [20, 20]), Box([10, 9], [19, 9]), Box([10, 20], [19, 20]), ] }, { "boxes": [ Box([27, 27], [27, 40]), Box([40, 27], [40, 40]), Box([28, 27], [39, 27]), Box([28, 40], [39, 40]), ] }, ], } gaboxes = ghost_area_boxes(hierarchy, "particles") particles = "particles" self.assertEqual(len(expected), len(gaboxes)) for ilvl, lvl in enumerate(hierarchy.patch_levels): self.assertEqual(len(gaboxes[ilvl][particles]), len(expected[ilvl])) for act_pdata, exp_pdata in zip(gaboxes[ilvl][particles], expected[ilvl]): self.assertEqual(len(exp_pdata["boxes"]), len(act_pdata["boxes"])) for exp_box in exp_pdata["boxes"]: self.assertTrue(exp_box in act_pdata["boxes"])
def _test_field_coarsening_via_subcycles(self, dim, interp_order, refinement_boxes, **kwargs): print("test_field_coarsening_via_subcycles for dim/interp : {}/{}". format(dim, interp_order)) from tests.amr.data.field.coarsening.test_coarsen_field import coarsen from pyphare.pharein import global_vars time_step_nbr = 3 diag_outputs = f"subcycle_coarsening/{dim}/{interp_order}/{self.ddt_test_id()}" datahier = self.getHierarchy( interp_order, refinement_boxes, "eb", cells=60, diag_outputs=diag_outputs, time_step=0.001, extra_diag_options={"fine_dump_lvl_max": 10}, time_step_nbr=time_step_nbr, largest_patch_size=30, ndim=dim, **kwargs) lvl_steps = global_vars.sim.level_time_steps print("LEVELSTEPS === ", lvl_steps) assert len(lvl_steps) > 1, "this test makes no sense with only 1 level" finestTimeStep = lvl_steps[-1] secondFinestTimeStep = lvl_steps[-2] finest_level_step_nbr = global_vars.sim.level_step_nbr[-1] uniqTimes = set([0]) for step in range(1, finest_level_step_nbr + 1): checkTime = datahier.format_timestamp(finestTimeStep * step) self.assertIn(checkTime, datahier.times()) uniqTimes.add(checkTime) self.assertEqual(len(uniqTimes), len(datahier.time_hier.items())) syncSteps = global_vars.sim.level_step_nbr[ -2] # ignore finest subcycles # FIX THIS AFTER NO MORE REGRIDS # SEE: https://github.com/PHAREHUB/PHARE/issues/400 assert syncSteps % time_step_nbr == 0 # perfect division startStep = int( syncSteps / time_step_nbr) + 1 # skip first coarsest step due to issue 400 for step in range(startStep, syncSteps + 1): checkTime = datahier.format_timestamp(secondFinestTimeStep * step) self.assertIn(checkTime, datahier.times()) nLevels = datahier.levelNbr(checkTime) self.assertGreaterEqual(nLevels, 2) levelNbrs = datahier.levelNbrs(checkTime) finestLevelNbr = max(levelNbrs) coarsestLevelNbr = min(levelNbrs) for coarseLevelNbr in range(coarsestLevelNbr, finestLevelNbr): coarsePatches = datahier.level(coarseLevelNbr, checkTime).patches finePatches = datahier.level(coarseLevelNbr + 1, checkTime).patches for coarsePatch in coarsePatches: for finePatch in finePatches: lvlOverlap = boxm.refine(coarsePatch.box, 2) * finePatch.box if lvlOverlap is not None: for EM in ["E", "B"]: for xyz in ["x", "y", "z"]: qty = f"{EM}{xyz}" coarse_pd = coarsePatch.patch_datas[qty] fine_pd = finePatch.patch_datas[qty] coarseBox = boxm.coarsen(lvlOverlap, 2) coarse_pdDataset = coarse_pd.dataset[:] fine_pdDataset = fine_pd.dataset[:] coarseOffset = coarseBox.lower - coarse_pd.layout.box.lower dataBox_lower = coarseOffset + coarse_pd.layout.nbrGhostFor( qty) dataBox = Box( dataBox_lower, dataBox_lower + coarseBox.shape - 1) afterCoarse = np.copy(coarse_pdDataset) # change values that should be updated to make failure obvious assert (dim < 3) # update if dim == 1: afterCoarse[dataBox. lower[0]:dataBox.upper[0] + 1] = -144123 if dim == 2: afterCoarse[ dataBox.lower[0]:dataBox.upper[0] + 1, dataBox.lower[1]:dataBox.upper[1] + 1] = -144123 coarsen(qty, coarse_pd, fine_pd, coarseBox, fine_pdDataset, afterCoarse) np.testing.assert_allclose( coarse_pdDataset, afterCoarse, atol=1e-16, rtol=0)
class SimulatorValidation(unittest.TestCase): def __init__(self, *args, **kwargs): super(SimulatorValidation, self).__init__(*args, **kwargs) self.simulator = None def tearDown(self): if self.simulator is not None: self.simulator.reset() def _do_dim(self, dim, input, valid: bool = False): for interp in range(1, 4): try: self.simulator = Simulator( populate_simulation(dim, interp, **input)) self.simulator.initialize() self.assertTrue(valid) self.simulator = None except ValueError as e: self.assertTrue(not valid) """ The first set of boxes "B0": [(10,), (14,)] Are configured to force there to be a single patch on L0 This creates a case with MPI that there are an unequal number of Patches across MPI domains. This case must be handled and not hang due to collective calls not being handled properly. """ valid1D = [ dup({ "cells": [65], "refinement_boxes": { "L0": { "B0": [(10, ), (14, )] } } }), dup({ "cells": [65], "refinement_boxes": { "L0": { "B0": [(5, ), (55, )] } } }), dup({ "cells": [65], "refinement_boxes": { "L0": { "B0": Box(5, 55) } } }), dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 55)] } }), dup({ "cells": [65], "refinement_boxes": { 0: [Box(5, 55)] } }), dup({ "cells": [65], "refinement_boxes": { 0: [Box(0, 55)] } }), dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 14), Box(15, 25)] } }), dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 25)], "L1": [Box(12, 48)], "L2": [Box(60, 64)] } }), dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 25)], "L1": [Box(12, 48)] } }), dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 25)], "L1": [Box(20, 30)] } }), dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 25)], "L1": [Box(11, 49)] }, "nesting_buffer": 1 }), dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 25)], "L1": [Box(10, 50)] } }), dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 25)], "L1": [Box(15, 49)] } }), dup({ "cells": [65], "refinement_boxes": None, "smallest_patch_size": 20, "largest_patch_size": 20, "nesting_buffer": 10 }), # finer box is within set of coarser boxes dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 9), Box(10, 15)], "L1": [Box(11, 29)] } }), ] invalid1D = [ # finer box outside lower dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 24)], "L1": [Box(9, 30)] } }), # finer box outside upper dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 24)], "L1": [Box(15, 50)] } }), # overlapping boxes dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 15), Box(15, 25)] } }), # box.upper outside domain dup({ "cells": [55], "refinement_boxes": { "L0": { "B0": [(5, ), (65, )] } } }), # largest_patch_size > smallest_patch_size dup({ "smallest_patch_size": 100, "largest_patch_size": 64, }), # refined_particle_nbr doesn't exist dup({"refined_particle_nbr": 1}), # L2 box incompatible with L1 box due to nesting buffer dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 25)], "L1": [Box(11, 49)] }, "nesting_buffer": 2 }), # negative nesting buffer dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 25)], "L1": [Box(11, 49)] }, "nesting_buffer": -1 }), # too large nesting buffer dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 25)], "L1": [Box(11, 49)] }, "nesting_buffer": 33 }), dup({ "cells": [65], "refinement_boxes": None, "largest_patch_size": 20, "nesting_buffer": 46 }), # finer box is not within set of coarser boxes dup({ "cells": [65], "refinement_boxes": { "L0": [Box(5, 9), Box(11, 15)], "L1": [Box(11, 29)] } }), ] @data(*valid1D) def test_1d_valid(self, input): self._do_dim(1, input, True) @data(*invalid1D) def test_1d_invalid(self, input): self._do_dim(1, input) valid2D = [ dup({ "cells": [65, 65], "refinement_boxes": { "L0": [Box2D(5, 55)] } }), dup({ "smallest_patch_size": None, "largest_patch_size": None }), dup({ "smallest_patch_size": (10, 10), "largest_patch_size": (20, 20) }), dup({ "smallest_patch_size": [10, 10], "largest_patch_size": [20, 20] }), dup({ "smallest_patch_size": (10, 10), "largest_patch_size": None }), dup({ "smallest_patch_size": None, "largest_patch_size": (20, 20) }), dup({"smallest_patch_size": (10, 10)}), dup({"largest_patch_size": (20, 20)}), dup({ "smallest_patch_size": 10, "largest_patch_size": (20, 20) }), dup({ "smallest_patch_size": (10, 10), "largest_patch_size": 20 }), dup({ "smallest_patch_size": [10, 10], "largest_patch_size": (20, 20) }), dup({ "smallest_patch_size": (10, 10), "largest_patch_size": [20, 20] }), dup({ "cells": [65, 65], "refinement_boxes": None, "smallest_patch_size": 20, "largest_patch_size": 20, "nesting_buffer": 10 }), dup({ "cells": [65, 65], "refinement_boxes": { "L0": { "B0": Box2D(5, 55) } } }), dup({ "cells": [65, 65], "refinement_boxes": { "L0": [Box2D(5, 55)] } }), dup({ "cells": [65, 65], "refinement_boxes": { 0: [Box2D(5, 55)] } }), dup({ "cells": [65, 65], "refinement_boxes": { 0: [Box2D(0, 55)] } }), dup({ "cells": [65, 65], "refinement_boxes": { "L0": [Box2D(5, 14), Box2D(15, 25)] } }), dup({ "cells": [65, 65], "refinement_boxes": { "L0": [Box2D(5, 25)], "L1": [Box2D(12, 48)], "L2": [Box2D(60, 64)] } }), dup({ "cells": [65, 65], "refinement_boxes": { "L0": [Box2D(5, 25)], "L1": [Box2D(12, 48)] } }), dup({ "cells": [65, 65], "refinement_boxes": { "L0": [Box2D(5, 25)], "L1": [Box2D(20, 30)] } }), dup({ "cells": [65, 65], "refinement_boxes": { "L0": [Box2D(5, 25)], "L1": [Box2D(11, 49)] }, "nesting_buffer": 1 }), dup({ "cells": [65, 65], "refinement_boxes": { "L0": [Box2D(5, 25)], "L1": [Box2D(10, 50)] } }), dup({ "cells": [65, 65], "refinement_boxes": { "L0": [Box2D(5, 25)], "L1": [Box2D(15, 49)] } }), ] invalid2D = [ # finer box outside lower dup({ "cells": [65, 65], "refinement_boxes": { "L0": [Box2D(5, 24)], "L1": [Box2D(9, 30)] } }), # finer box outside lower dup({ "cells": [65, 65], "refinement_boxes": { "L0": [Box2D(5, 24)], "L1": [Box2D(9, 30)] } }), # finer box outside upper dup({ "cells": [65, 65], "refinement_boxes": { "L0": [Box2D(5, 24)], "L1": [Box2D(15, 50)] } }), # overlapping boxes dup({ "cells": [65, 65], "refinement_boxes": { "L0": [Box2D(5, 15), Box2D(15, 25)] } }), # box.upper outside domain dup({ "cells": [55, 55], "refinement_boxes": { "L0": { "B0": Box2D( 5, 65, ) } } }), # largest_patch_size > smallest_patch_size dup({ "smallest_patch_size": 100, "largest_patch_size": 64, }), # refined_particle_nbr doesn't exist dup({"refined_particle_nbr": 1}), # L2 box incompatible with L1 box due to nesting buffer dup({ "cells": [65, 65], "refinement_boxes": { "L0": [Box2D(5, 25)], "L1": [Box2D(11, 49)] }, "nesting_buffer": 2 }), # negative nesting buffer dup({ "cells": [65, 65], "refinement_boxes": { "L0": [Box2D(5, 25)], "L1": [Box2D(11, 49)] }, "nesting_buffer": -1 }), # too large nesting buffer dup({ "cells": [65, 65], "refinement_boxes": { "L0": [Box2D(5, 25)], "L1": [Box2D(11, 49)] }, "nesting_buffer": 33 }), dup({ "cells": [65, 65], "refinement_boxes": None, "largest_patch_size": 20, "nesting_buffer": 46 }), # finer box is not within set of coarser boxes dup({ "cells": [65, 65], "refinement_boxes": { "L0": [Box2D(5, 9), Box2D(11, 15)], "L1": [Box2D(11, 29)] } }), ] @data(*valid2D) def test_2d_valid(self, input): self._do_dim(2, input, True) @data(*invalid2D) def test_2d_invalid(self, input): self._do_dim(2, input)