From e781867edfd8525234442d68c08344251703124e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Franck=20P=C3=A9rignon?= <franck.perignon@imag.fr>
Date: Mon, 6 Oct 2014 14:22:01 +0200
Subject: [PATCH] Fix last bugs in subsets. All tests ok except python
 advection and penalization (--> obstacles obsolete)

---
 HySoP/hysop/domain/subsets/control_box.py    | 113 ++++++++++
 HySoP/hysop/domain/subsets/submesh.py        | 172 +++++++++++++++
 HySoP/hysop/domain/tests/test_control_box.py | 213 +++++++++++++++++++
 3 files changed, 498 insertions(+)
 create mode 100644 HySoP/hysop/domain/subsets/control_box.py
 create mode 100644 HySoP/hysop/domain/subsets/submesh.py
 create mode 100644 HySoP/hysop/domain/tests/test_control_box.py

diff --git a/HySoP/hysop/domain/subsets/control_box.py b/HySoP/hysop/domain/subsets/control_box.py
new file mode 100644
index 000000000..1821ac8a8
--- /dev/null
+++ b/HySoP/hysop/domain/subsets/control_box.py
@@ -0,0 +1,113 @@
+"""
+@file control_box.py
+Define a volume of control inside a domain (volume + all faces)
+"""
+from parmepy.domain.subsets.boxes import SubBox
+import numpy as np
+import parmepy.tools.numpywrappers as npw
+
+
+class ControlBox(SubBox):
+    """
+    Build a sub-domain, box-shaped
+    ==> define set of indices inside this domain (ind member)
+    and set of indices belonging to surfaces of this domain (slices members).
+    Useful to define control volume to perform integration.
+    """
+
+    def __init__(self, **kwds):
+        """
+        Build the volume of control
+        @param origin : coordinates of the lowest point in the sub-domain
+        @param lengths : lengths of box sides.
+        """
+        super(ControlBox, self).__init__(**kwds)
+
+        # We must have a real box, not a plane ...
+        assert len(self.s_dir) == len(self.length)
+
+        self.surf = [None] * len(self.length) * 2
+
+    def discretize(self, topo):
+        # Create the mesh for the whole box
+        super(ControlBox, self).discretize(topo)
+        # Create a mesh for each side
+        dim = topo.domain.dimension
+        ilist = np.arange(dim)
+        for direction in xrange(dim):
+            ndir = np.where(ilist == direction)[0]
+            length = self.real_length[topo].copy()
+            length[ndir] = 0.0
+            orig = self.real_orig[topo].copy()
+            self.surf[2 * direction] = \
+                SubBox(origin=orig, length=length, parent=self._parent)
+            orig = self.real_orig[topo].copy()
+            orig[ndir] += self.real_length[topo][ndir]
+            self.surf[2 * direction + 1] = \
+                SubBox(origin=orig, length=length, parent=self._parent)
+
+        for s in self.surf:
+            s.discretize(topo)
+
+    def _check_boundaries(self, surf, topo):
+        """
+        Special care when some boundaries of the control box are on the
+        upper boundaries of the domain.
+        Remind that for periodic bc, such surfaces does not really
+        exists in the parent mesh.
+        """
+        return surf.mesh[topo].check_boundaries()
+
+    def integrate_on_faces(self, field, topo, list_dir,
+                           component=0, root=None):
+        """
+        @param[in] field : a parmepy continuous field
+        @param[in] topo : a parmepy.mpi.topology.Cartesian topology
+        @param[in] root : root process used for mpi reduction. If None
+        reduction is done on all processes from topo.
+        @param[in] list_dir : list of surfaces on which we must integrate.
+        0 : normal to xdir, lower surf,
+        1 : normal to xdir, upper surf (in x dir)
+        2 : normal to ydir, lower surf, and so on.
+        the field must be integrated.
+        @return a numpy array, with res = sum
+        of the integrals of a component of field on all surf in list_dir
+        """
+        res = 0.
+        for ndir in list_dir:
+            surf = self.surf[ndir]
+            msg = 'Control Box error : surface out of bounds.'
+            assert self._check_boundaries(surf, topo), msg
+            res += surf.integrate_field_on_proc(field, topo, component)
+        if root is None:
+            return topo.comm.allreduce(res)
+        else:
+            return topo.comm.reduce(res, root=root)
+
+    def integrate_on_faces_allc(self, field, topo, list_dir, root=None):
+        """
+        @param[in] field : a parmepy continuous field
+        @param[in] topo : a parmepy.mpi.topology.Cartesian topology
+        @param[in] root : root process used for mpi reduction. If None
+        reduction is done on all processes from topo.
+        @param[in] list_dir : list of surfaces on which we must integrate.
+        0 : normal to xdir, lower surf,
+        1 : normal to xdir, upper surf (in x dir)
+        2 : normal to ydir, lower surf, and so on.
+        the field must be integrated.
+        @return a numpy array, with res[i] = sum
+        of the integrals of component i of field on all surf in list_dir
+        """
+        nbc = field.nbComponents
+        res = npw.zeros(nbc)
+        gres = npw.zeros(nbc)
+        for ndir in list_dir:
+            surf = self.surf[ndir]
+            assert self._check_boundaries(surf, topo)
+            for i in xrange(nbc):
+                res[i] += surf.integrate_field_on_proc(field, topo, i)
+        if root is None:
+            topo.comm.Allreduce(res, gres)
+        else:
+            topo.comm.Reduce(res, gres, root=root)
+        return gres
diff --git a/HySoP/hysop/domain/subsets/submesh.py b/HySoP/hysop/domain/subsets/submesh.py
new file mode 100644
index 000000000..b9867963d
--- /dev/null
+++ b/HySoP/hysop/domain/subsets/submesh.py
@@ -0,0 +1,172 @@
+"""
+@file submesh.py local cartesian grid.
+"""
+import parmepy.tools.numpywrappers as npw
+from parmepy.tools.parameters import Discretization
+import numpy as np
+from parmepy.tools.misc import utils
+
+
+class SubMesh(object):
+    """
+    A subset of a predefined (distributed) mesh.
+    """
+
+    def __init__(self, mesh, substart, subend):
+        """
+        @param mesh : the parent mesh
+        @param substart : indices in the global grid of mesh
+        of the lowest point of this submesh
+        @param subend : indices of the 'highest' point of this submesh,
+        in the global grid.
+        Warning : subend/substart are global values, that do not depend on the
+        mpi distribution of data.
+
+        \todo : a proper scheme to clarify all the notations for meshes
+        (global/local start end and so on).
+        """
+        # Note : all variables with 'global' prefix are related
+        # to the global grid, that is the mesh defined on the whole
+        # domain. These variables do not depend on the mpi distribution.
+
+        # ---
+        # Attributes relative to the global mesh
+
+        ## parent mesh
+        self.mesh = mesh
+        # dimension of the submesh
+        self._dim = self.mesh.domain.dimension
+
+        ## Index of the lowest point of the global submesh in the global grid
+        ## of its parent
+        self.substart = npw.asdimarray(substart)
+        ## Index of the 'highest' point of the global submesh
+        ## in the global grid of its parent
+        self.subend = npw.asdimarray(subend)
+
+        # position of the submesh in the global grid of its parent mesh.
+        global_position_in_parent = [slice(substart[d], self.subend[d] + 1)
+                                     for d in xrange(self._dim)]
+        hh = self.mesh.space_step
+        ## Coordinates of the lowest point of this submesh
+        self.global_origin = self.substart * hh + self.mesh.domain.origin
+        ## Length of this submesh
+        self.global_length = (self.subend - self.substart) * hh
+
+        # Warning : we must not overpass the parent global discretization.
+        gres = self.subend - self.substart + 1
+        # directions where length is 0, i.e. directions 'normal' to
+        # the submesh.
+        self._n_dir = np.where(gres == 1)[0]
+        ## discretization of the subset
+        ## Warning : at the time, no ghosts on the submesh!
+        self.discretization = Discretization(gres)
+        # Find which part of submesh is on the current process and
+        # find its computational points. Warning:
+        # the indices of computational points must be
+        # relative to the parent mesh local grid!
+        sl = utils.intersl(global_position_in_parent, self.mesh.position)
+        # Bool to check if this process holds the end (in any direction)
+        # of the domain. Useful for proper integration on this subset.
+        self.is_last = [False, ] * self._dim
+
+        ## Check if a part of the submesh is present on the current proc.
+        self.on_proc = sl is not None
+
+        if self.on_proc:
+            # Is this mesh on the last process in some direction in the
+            # mpi grid of process?
+            self.is_last = np.asarray([self.subend[d] < sl[d].stop
+                                       for d in xrange(self._dim)])
+            ## position of the LOCAL submesh in the global grid
+            ## of the parent mesh
+            self.position_in_parent = [s for s in sl]
+            ## Indices of the points of the submesh, relative to
+            ## the LOCAL array
+            self.iCompute = self.mesh.convert2local(self.position_in_parent)
+
+            ## Resolution of the local submesh
+            self.resolution = [self.iCompute[d].stop - self.iCompute[d].start
+                               for d in xrange(self._dim)]
+
+            # Now, we must find which points must be used
+            # when we integrate on this submesh
+            stops = npw.asdimarray([self.iCompute[d].stop
+                                    for d in xrange(self._dim)])
+            # ilist = [d for d in ll if self.is_last[d]]
+            # stops[ilist] += 1
+            # when 'is_last', the last point must be removed for integration
+            stops[self.is_last] -= 1
+            # and finally, for direction where subset length is zero,
+            # we must increase stop, else integral will always be zero!
+            stops[self._n_dir] = npw.asdimarray([self.iCompute[d].start + 1
+                                                 for d in self._n_dir])
+
+            ## Same as self.iCompute but recomputed to be used
+            ## for integration on the submesh
+            self.ind4integ = [slice(self.iCompute[d].start,
+                                    stops[d]) for d in xrange(self._dim)]
+            start = [self.position_in_parent[d].start - self.substart[d]
+                     for d in xrange(self._dim)]
+            ## position of the LOCAL submesh in the global grid
+            ## of the submesh (not the grid of the parent mesh!)
+            self.position = [slice(start[d], start[d] + self.resolution[d])
+                             for d in xrange(self._dim)]
+        else:
+            self.position_in_parent = None
+            self.position = None
+            self.iCompute = [slice(0, 0), ] * self._dim
+            self.ind4integ = self.iCompute
+            self.resolution = [0, ] * self._dim
+
+        ## Coordinates of the points of the local submesh
+        self.coords = [None, ] * self._dim
+        self.coords[0] = self.mesh.coords[0][self.iCompute[0]]
+        if self._dim > 1:
+            self.coords[1] = self.mesh.coords[1][:, self.iCompute[1], ...]
+        if self._dim > 2:
+            self.coords[2] = self.mesh.coords[2][:, :, self.iCompute[2]]
+        self.coords = tuple(self.coords)
+        ## Same as self.coords but reduced for integration on the submesh
+        self.coords4int = [None, ] * self._dim
+        self.coords4int[0] = self.mesh.coords[0][self.ind4integ[0]]
+        if self._dim > 1:
+            self.coords4int[1] = self.mesh.coords[1][:, self.ind4integ[1], ...]
+        if self._dim > 2:
+            self.coords4int[2] = self.mesh.coords[2][:, :, self.ind4integ[2]]
+        self.coords4int = tuple(self.coords4int)
+
+    def check_boundaries(self):
+        """
+        Special care when some boundaries of the submesh are on the
+        upper boundaries of the parent mesh.
+        Remind that for periodic bc, such a point does not really
+        exists in the parent mesh.
+        """
+        # List of directions which are periodic
+        periodic_dir = self.mesh.domain.i_periodic_boundaries()
+        if len(periodic_dir) > 0:
+            ll = np.where(
+                self.subend ==
+                self.mesh.global_indices(self.mesh.domain.end))[0]
+            return (ll != self._n_dir).all()
+
+        return True
+
+    def global_resolution(self):
+        """
+        @return the resolution of the global grid (on the whole
+        domain, whatever the mpi distribution is).
+        """
+        return self.discretization.resolution
+
+    def cx(self):
+        return self.coords[0][:, ...]
+
+    def cy(self):
+        assert self._dim > 1
+        return self.coords[1][0, :, ...]
+
+    def cz(self):
+        assert self._dim > 2
+        return self.coords[2][0, 0, :]
diff --git a/HySoP/hysop/domain/tests/test_control_box.py b/HySoP/hysop/domain/tests/test_control_box.py
new file mode 100644
index 000000000..20d85b710
--- /dev/null
+++ b/HySoP/hysop/domain/tests/test_control_box.py
@@ -0,0 +1,213 @@
+from parmepy.domain.subsets.control_box import ControlBox
+from parmepy.domain.subsets.boxes import SubBox
+from parmepy.tools.parameters import Discretization
+from parmepy import Field, Box
+import numpy as np
+import math
+import parmepy.tools.numpywrappers as npw
+
+
+Nx = 128
+Ny = 96
+Nz = 102
+g = 2
+
+
+def v3d(res, x, y, z, t):
+    res[0][...] = 1.
+    res[1][...] = 1.
+    res[2][...] = 1.
+    return res
+
+
+def v2d(res, x, y, t):
+    res[0][...] = 1.
+    res[1][...] = 1.
+    return res
+
+
+def f_test(x, y, z):
+    return 1
+
+
+def f_test_2(x, y, z):
+    return math.cos(z)
+
+ldef = [1.1, 0.76, 1.0]
+discr3D = Discretization([Nx + 1, Ny + 1, Nz + 1], [g - 1, g - 2, g])
+discr2D = Discretization([Nx + 1, Ny + 1], [g - 1, g])
+orig = np.asarray([0.1, -0.3, 0.5])
+ldom = [math.pi * 2., math.pi * 2., math.pi * 2.]
+xdef = orig + 0.2
+
+
+def check_control_box(discr, xr, lr):
+    dim = len(discr.resolution)
+    dom = Box(dimension=dim, length=ldom[:dim],
+              origin=orig[:dim])
+    # Starting point and length of the subdomain
+    cb = ControlBox(origin=xr, length=lr, parent=dom)
+    assert (cb.length == lr).all()
+    assert (cb.origin == xr).all()
+    # discretization of the dom and of its subset
+    topo = dom.create_topology(discr)
+    cb.discretize(topo)
+    assert cb.mesh.values()[0].mesh == topo.mesh
+    assert cb.mesh.keys()[0] == topo
+    assert len(cb.surf) == dim * 2
+    for s in cb.surf:
+        assert isinstance(s, SubBox)
+        ll = s.real_length[topo]
+        assert len(np.where(ll == 0.0)) == 1
+    return topo, cb
+
+
+def test_cb_3D():
+    topo, cb = check_control_box(discr3D, xdef, ldef)
+    velo = Field(domain=topo.domain, isVector=True, formula=v3d, name='velo')
+    velo.discretize(topo)
+    velo.initialize(topo=topo)
+    i0 = cb.integrate_field_allc(velo, topo)
+    vref = np.prod(cb.real_length[topo])
+    assert (np.abs(i0 - vref) < 1e-6).all()
+    nbc = velo.nbComponents
+    sref = npw.zeros(nbc)
+    dirs = np.arange(nbc)
+    for i in xrange(nbc):
+        ilist = np.where(dirs != i)[0]
+        sref = np.prod(cb.real_length[topo][ilist])
+        isurf = cb.integrate_on_faces(velo, topo, [2 * i])
+        assert np.abs(isurf - sref) < 1e-6
+        isurf = cb.integrate_on_faces(velo, topo, [2 * i + 1])
+        assert np.abs(isurf - sref) < 1e-6
+
+
+def test_cb_3D_2():
+    topo, cb = check_control_box(discr3D, xdef, ldef)
+    velo = Field(domain=topo.domain, isVector=True, formula=v3d, name='velo')
+    velo.discretize(topo)
+    velo.initialize(topo=topo)
+    nbc = velo.nbComponents
+    sref = npw.zeros(nbc)
+    dirs = np.arange(nbc)
+    list_dir = np.arange(2 * nbc)
+    sref = 0
+    for i in xrange(nbc):
+        ilist = np.where(dirs != i)[0]
+        sref += np.prod(cb.real_length[topo][ilist])
+    sref *= 2.
+    isurf = cb.integrate_on_faces(velo, topo, list_dir)
+    assert np.abs(isurf - sref) < 1e-6
+
+
+def test_cb_3D_3():
+    topo, cb = check_control_box(discr3D, xdef, ldef)
+    velo = Field(domain=topo.domain, isVector=True, formula=v3d, name='velo')
+    velo.discretize(topo)
+    velo.initialize(topo=topo)
+    nbc = velo.nbComponents
+    sref = npw.zeros(nbc)
+    dirs = np.arange(nbc)
+    list_dir = np.arange(2 * nbc)
+    sref = 0
+    for i in xrange(nbc):
+        ilist = np.where(dirs != i)[0]
+        sref += np.prod(cb.real_length[topo][ilist])
+    sref *= 2.
+    isurf = cb.integrate_on_faces_allc(velo, topo, list_dir)
+    assert (np.abs(isurf - sref) < 1e-6).all()
+
+
+def test_cb_2D():
+    topo, cb = check_control_box(discr2D, xdef[:2], ldef[:2])
+    velo = Field(domain=topo.domain, isVector=True, formula=v2d, name='velo')
+    velo.discretize(topo)
+    velo.initialize(topo=topo)
+    i0 = cb.integrate_field_allc(velo, topo)
+    vref = np.prod(cb.real_length[topo])
+    assert (np.abs(i0 - vref) < 1e-6).all()
+    nbc = velo.nbComponents
+    sref = npw.zeros(nbc)
+    dirs = np.arange(nbc)
+    for i in xrange(nbc):
+        ilist = np.where(dirs != i)[0]
+        sref = np.prod(cb.real_length[topo][ilist])
+        isurf = cb.integrate_on_faces(velo, topo, [2 * i])
+        assert np.abs(isurf - sref) < 1e-6
+        isurf = cb.integrate_on_faces(velo, topo, [2 * i + 1])
+        assert np.abs(isurf - sref) < 1e-6
+
+
+def test_cb_2D_2():
+    topo, cb = check_control_box(discr2D, xdef[:2], ldef[:2])
+    velo = Field(domain=topo.domain, isVector=True, formula=v2d, name='velo')
+    velo.discretize(topo)
+    velo.initialize(topo=topo)
+    nbc = velo.nbComponents
+    sref = npw.zeros(nbc)
+    dirs = np.arange(nbc)
+    list_dir = np.arange(2 * nbc)
+    sref = 0
+    for i in xrange(nbc):
+        ilist = np.where(dirs != i)[0]
+        sref += np.prod(cb.real_length[topo][ilist])
+    sref *= 2.
+    isurf = cb.integrate_on_faces(velo, topo, list_dir)
+    assert np.abs(isurf - sref) < 1e-6
+
+
+def test_cb_2D_3():
+    topo, cb = check_control_box(discr2D, xdef[:2], ldef[:2])
+    velo = Field(domain=topo.domain, isVector=True, formula=v2d, name='velo')
+    velo.discretize(topo)
+    velo.initialize(topo=topo)
+    nbc = velo.nbComponents
+    sref = npw.zeros(nbc)
+    dirs = np.arange(nbc)
+    list_dir = np.arange(2 * nbc)
+    sref = 0
+    for i in xrange(nbc):
+        ilist = np.where(dirs != i)[0]
+        sref += np.prod(cb.real_length[topo][ilist])
+    sref *= 2.
+    isurf = cb.integrate_on_faces_allc(velo, topo, list_dir)
+    assert (np.abs(isurf - sref) < 1e-6).all()
+
+
+def test_cb_3D_fulldomain():
+    """
+    A cb defined on the whole domain.
+    Integrals on upper surfaces must fail,
+    because of periodic bc.
+    """
+    topo, cb = check_control_box(discr3D, orig, ldom)
+    velo = Field(domain=topo.domain, isVector=True, formula=v3d, name='velo')
+    velo.discretize(topo)
+    velo.initialize(topo=topo)
+    i0 = cb.integrate_field_allc(velo, topo)
+    vref = np.prod(cb.real_length[topo])
+    assert (np.abs(i0 - vref) < 1e-6).all()
+    nbc = velo.nbComponents
+    sref = npw.zeros(nbc)
+    dirs = np.arange(nbc)
+    for i in xrange(nbc):
+        ilist = np.where(dirs != i)[0]
+        sref = np.prod(cb.real_length[topo][ilist])
+        isurf = cb.integrate_on_faces(velo, topo, [2 * i])
+        assert np.abs(isurf - sref) < 1e-6
+        res = False
+        try:
+            isurf = cb.integrate_on_faces(velo, topo, [2 * i + 1])
+        except:
+            res = True
+        assert res
+
+# This may be useful to run mpi tests
+if __name__ == "__main__":
+    test_cb_3D()
+    test_cb_3D_2()
+    test_cb_3D_3()
+    test_cb_2D()
+    test_cb_2D_2()
+    test_cb_2D_3()
+    test_cb_3D_fulldomain()
-- 
GitLab