class AdvanceTest(AdvanceTestBase): @data( *per_interp({}), *per_interp({"L0": [Box2D(10, 19)]}), *per_interp({"L0": [Box2D(5, 9), Box2D(10, 14)]}), ) @unpack def test_overlapped_particledatas_have_identical_particles( self, interp_order, refinement_boxes): self._test_overlapped_particledatas_have_identical_particles( ndim, interp_order, refinement_boxes, ppc=ppc, cells=40, largest_patch_size=20) @data(*interp_orders) def test_L0_particle_number_conservation(self, interp): self._test_L0_particle_number_conservation(ndim, interp, ppc=ppc) @data( *per_interp(({ "L0": { "B0": Box2D(10, 14) } })), ) @unpack def test_domain_particles_on_refined_level(self, interp_order, refinement_boxes): self._test_domain_particles_on_refined_level(ndim, interp_order, refinement_boxes, nbr_part_per_cell=ppc)
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"))
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)
class AdvanceTest(AdvanceTestBase): @data( *per_interp({}), *per_interp({"L0": [Box2D(10, 19)]}), *per_interp({"L0": [Box2D(8, 20)]}), ) @unpack def test_overlaped_fields_are_equal(self, interp_order, refinement_boxes): print(f"{self._testMethodName}_{ndim}d") time_step_nbr = 3 time_step = 0.001 diag_outputs = f"phare_overlaped_fields_are_equal_{ndim}_{self.ddt_test_id()}" datahier = self.getHierarchy(interp_order, refinement_boxes, "eb", diag_outputs=diag_outputs, time_step=time_step, time_step_nbr=time_step_nbr, ndim=ndim, nbr_part_per_cell=ppc) self._test_overlaped_fields_are_equal(datahier, time_step_nbr, time_step) @data( *per_interp({}), *per_interp({"L0": [Box2D(10, 19)]}), ) @unpack def test_overlaped_fields_are_equal_with_min_max_patch_size_of_max_ghosts( self, interp_order, refinement_boxes): print(f"{self._testMethodName}_{ndim}d") time_step_nbr = 3 time_step = 0.001 from pyphare.pharein.simulation import check_patch_size diag_outputs = f"phare_overlaped_fields_are_equal_with_min_max_patch_size_of_max_ghosts/{ndim}/{interp_order}/{self.ddt_test_id()}" largest_patch_size, smallest_patch_size = check_patch_size( ndim, interp_order=interp_order, cells=[60] * ndim) datahier = self.getHierarchy(interp_order, refinement_boxes, "eb", diag_outputs=diag_outputs, smallest_patch_size=smallest_patch_size, largest_patch_size=smallest_patch_size, time_step=time_step, time_step_nbr=time_step_nbr, ndim=ndim, nbr_part_per_cell=ppc) self._test_overlaped_fields_are_equal(datahier, time_step_nbr, time_step) @data( *per_interp(({ "L0": { "B0": Box2D(10, 14) } })), *per_interp(({ "L0": { "B0": Box2D(10, 14), "B1": Box2D(15, 19) } })), *per_interp(({ "L0": { "B0": Box2D(6, 23) } })), *per_interp(({ "L0": { "B0": Box2D(2, 12), "B1": Box2D(13, 25) } })), *per_interp(({ "L0": { "B0": Box2D(5, 20) }, "L1": { "B0": Box2D(15, 19) } })), *per_interp(({ "L0": { "B0": Box2D(5, 20) }, "L1": { "B0": Box2D(12, 38) }, "L2": { "B0": Box2D(30, 52) } })), ) @unpack def test_field_coarsening_via_subcycles(self, interp_order, refinement_boxes): print(f"{self._testMethodName}_{ndim}d") self._test_field_coarsening_via_subcycles(ndim, interp_order, refinement_boxes, dl=.3) @data( # only supports a hierarchy with 2 levels # *per_interp(({"L0": [Box2D(0, 4)]})), # fails? *per_interp(({ "L0": [Box2D(10, 14)] })), *per_interp(({ "L0": [Box2D(0, 4), Box2D(10, 14)] })), *per_interp(({ "L0": [Box2D(0, 4), Box2D(5, 9), Box2D(10, 14)] })), *per_interp(({ "L0": [Box2D(20, 24)] })), # *per_interp(({"L0": [Box2D(30, 34)]})), # fails? ) @unpack def test_field_level_ghosts_via_subcycles_and_coarser_interpolation( self, interp_order, refinement_boxes): self._test_field_level_ghosts_via_subcycles_and_coarser_interpolation( ndim, interp_order, refinement_boxes)
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)
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 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")
def basic_hierarchy(self): dim, interp_order, nbr_cells = (2, 1, [20] * 2) refinement_boxes = {"L0": {"B0": Box2D(5, 9), "B1": Box2D(14, 19)}} return self.setup_hierarchy(dim, interp_order, nbr_cells, refinement_boxes)
class InitializationTest(InitializationTest): @data(*interp_orders) def test_nbr_particles_per_cell_is_as_provided(self, interp_order): print(f"{self._testMethodName}_{ndim}d") self._test_nbr_particles_per_cell_is_as_provided(ndim, interp_order) @data( *per_interp(({ "L0": { "B0": Box2D(10, 14) } })), *per_interp(({ "L0": { "B0": Box2D(10, 14) }, "L1": { "B0": Box2D(22, 26) } })), *per_interp(({ "L0": { "B0": Box2D(2, 6), "B1": Box2D(7, 11) } })), ) @unpack def test_levelghostparticles_have_correct_split_from_coarser_particle( self, interp_order, refinement_boxes): print(f"\n{self._testMethodName}_{ndim}d") now = self.datetime_now() self._test_levelghostparticles_have_correct_split_from_coarser_particle( self.getHierarchy( interp_order, refinement_boxes, "particles", ndim=ndim, cells=30, nbr_part_per_cell=ppc, diag_outputs= f"phare_outputs/test_levelghost/{ndim}/{interp_order}/{self.ddt_test_id()}", )) print( f"\n{self._testMethodName}_{ndim}d took {self.datetime_diff(now)} seconds" ) @data( *per_interp(({ "L0": { "B0": Box2D(10, 14) } })), *per_interp(({ "L0": { "B0": Box2D(5, 20) }, "L1": { "B0": Box2D(15, 35) } })), *per_interp(({ "L0": { "B0": Box2D(2, 12), "B1": Box2D(13, 25) } })), ) @unpack def test_domainparticles_have_correct_split_from_coarser_particle( self, interp_order, refinement_boxes): print(f"\n{self._testMethodName}_{ndim}d") now = self.datetime_now() self._test_domainparticles_have_correct_split_from_coarser_particle( ndim, interp_order, refinement_boxes, nbr_part_per_cell=ppc) print( f"\n{self._testMethodName}_{ndim}d took {self.datetime_diff(now)} seconds" ) @data({ "cells": 40, "smallest_patch_size": 20, "largest_patch_size": 20, "nbr_part_per_cell": ppc }) def test_no_patch_ghost_on_refined_level_case(self, simInput): print(f"\n{self._testMethodName}_{ndim}d") now = self.datetime_now() self._test_patch_ghost_on_refined_level_case(ndim, False, **simInput) print( f"\n{self._testMethodName}_{ndim}d took {self.datetime_diff(now)} seconds" ) @data({"cells": 40, "interp_order": 1, "nbr_part_per_cell": ppc}) def test_has_patch_ghost_on_refined_level_case(self, simInput): print(f"\n{self._testMethodName}_{ndim}d") from pyphare.pharein.simulation import check_patch_size diag_outputs = f"phare_overlaped_fields_are_equal_with_min_max_patch_size_of_max_ghosts_{ndim}_{self.ddt_test_id()}" _, smallest_patch_size = check_patch_size(ndim, **simInput) simInput["smallest_patch_size"] = smallest_patch_size simInput["largest_patch_size"] = smallest_patch_size now = self.datetime_now() self._test_patch_ghost_on_refined_level_case(ndim, True, **simInput) print( f"\n{self._testMethodName}_{ndim}d took {self.datetime_diff(now)} seconds" )