Beispiel #1
0
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
Beispiel #2
0
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
Beispiel #3
0
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
Beispiel #4
0
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=" ")
Beispiel #5
0
    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)
Beispiel #6
0
    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)
Beispiel #7
0
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")
Beispiel #8
0
    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"))
Beispiel #9
0
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)
Beispiel #10
0
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
Beispiel #11
0
    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)
Beispiel #12
0
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] :])
Beispiel #13
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")
Beispiel #14
0
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)
Beispiel #15
0
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)
Beispiel #16
0
    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"])
Beispiel #17
0
    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)
Beispiel #18
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)