From 5f219872f53e5882f39b5d16e928a6aa9bbb2590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franck=20P=C3=A9rignon?= <franck.perignon@imag.fr> Date: Mon, 23 May 2016 14:47:10 +0200 Subject: [PATCH] Update differential operations: internal work array management, tests and docs --- .../users_guide/differential_operations.rst | 8 + hysop/numerics/differential_operations.py | 751 +++++++++--- hysop/numerics/tests/test_diffOp.py | 222 ---- .../tests/test_differential_operations.py | 1015 +++++++++++++++++ 4 files changed, 1641 insertions(+), 355 deletions(-) create mode 100644 docs/sphinx/users_guide/differential_operations.rst delete mode 100755 hysop/numerics/tests/test_diffOp.py create mode 100755 hysop/numerics/tests/test_differential_operations.py diff --git a/docs/sphinx/users_guide/differential_operations.rst b/docs/sphinx/users_guide/differential_operations.rst new file mode 100644 index 000000000..94552b4ce --- /dev/null +++ b/docs/sphinx/users_guide/differential_operations.rst @@ -0,0 +1,8 @@ +.. _differential_operations: + +.. currentmodule:: hysop.numerics.differential_operations + +Vector calculous based on finite differences +-------------------------------------------- + +To be done ... diff --git a/hysop/numerics/differential_operations.py b/hysop/numerics/differential_operations.py index 67559774c..c6ba55445 100755 --- a/hysop/numerics/differential_operations.py +++ b/hysop/numerics/differential_operations.py @@ -5,11 +5,18 @@ * :class:`~hysop.numerics.differential_operations.Curl`, * :class:`~hysop.numerics.differential_operations.DivRhoV`, * :class:`~hysop.numerics.differential_operations.DivWV`, +* :class:`~hysop.numerics.differential_operations.Laplacian`, * :class:`~hysop.numerics.differential_operations.GradS`, * :class:`~hysop.numerics.differential_operations.GradV`, * :class:`~hysop.numerics.differential_operations.GradVxW`, * :class:`~hysop.numerics.differential_operations.DivAdvection`, -* :class:`~hysop.numerics.differential_operations.Laplacian`, +* :class:`~hysop.numerics.differential_operations.Strain`, +* :class:`~hysop.numerics.differential_operations.StrainCriteria`, +* :class:`~hysop.numerics.differential_operations.MaxDiagGradV`, +* :class:`~hysop.numerics.differential_operations.StretchLike`, +* :class:`~hysop.numerics.differential_operations.DiagAndStretch`, +* :class:`~hysop.numerics.differential_operations.StrainCriteria`, +* :class:`~hysop.numerics.differential_operations.StrainAndStretch`, * :class:`~hysop.numerics.differential_operations.DifferentialOperation`, (abstract base class). @@ -25,6 +32,8 @@ For example : * a = DivRhoV(c,d), d a vector field, a and c scalar fields. d is a list of 3 arrays and a and c lists of 1 array. +For practical examples of use, see test_differential_operations.py file. + """ from hysop.constants import debug, XDIR, YDIR, ZDIR from abc import ABCMeta @@ -46,7 +55,7 @@ class DifferentialOperation(object): # return object.__new__(cls, *args, **kw) @debug - def __init__(self, topo, indices=None, reduce_output_shape=None, + def __init__(self, topo, indices=None, reduce_output_shape=False, method=None, work=None): """ Parameters @@ -81,8 +90,6 @@ class DifferentialOperation(object): * If work = None, some work arrays will be allocated internally. Else, you must provide a list of lwk arrays of shape topo.mesh.resolution, - with lwk = ClassName.get_work_length(), ClassName being Curl, DivV ... - or any other DifferentialOperation. """ self._dim = topo.domain.dimension @@ -93,49 +100,68 @@ class DifferentialOperation(object): self.method = method if indices is None: indices = topo.mesh.compute_index - reduce_output_shape = False - self._indices = indices + self.in_indices = indices + # True if work has to be done for this set + # of indices on the current proc. + self._on_proc = Utils.is_on_proc(indices) self.fd_scheme = self._init_fd_method(topo, reduce_output_shape) self.output_indices = self.fd_scheme.output_indices - self._work = self._set_work_arrays(work, topo, reduce_output_shape) + self._work = self._set_work_arrays(topo, reduce_output_shape, work) - def _set_work_arrays(self, work, topo, reduce_output_shape): - """Check and allocate internal work buffers. + def _set_work_arrays(self, topo, reduce_output_shape, rwork=None): + """Check and/or allocate internal work buffers. """ - lwk = self.get_work_length() + wk_prop = self.get_work_properties(topo, self.in_indices)['rwork'] + if wk_prop is None: + return [] if reduce_output_shape: - shape = self.fd_scheme.result_shape + subshape = np.asarray([self.in_indices[i].stop - + self.in_indices[i].start + for i in xrange(self._dim)]) else: - shape = tuple(topo.mesh.resolution) - if work is None: - work = [] - for _ in xrange(lwk): - work.append(npw.asrealarray(shape)) - else: - msg = 'Wrong input for work arrays.' - assert isinstance(work, list), msg - assert len(work) == lwk, msg - for wk in work: - assert wk.shape == shape - return work + subshape = topo.mesh.resolution + subshape = tuple(subshape) + return WorkSpaceTools.check_work_array(len(wk_prop), subshape, rwork) @staticmethod - def get_work_length(): + def get_work_properties(topo, indices=None): + """Default : no work vector """ - Compute the number of required work arrays for this method. + return DifferentialOperation._find_work(0, topo, indices) + + @staticmethod + def _find_work(lwork, topo, indices=None): + """Internal function to find the shape of work vector + for a given topo and a set of indices """ - return 0 + if lwork == 0: + return {'rwork': None, 'iwork': None} + + if indices is not None: + if not Utils.is_on_proc(indices): + shape = (0, ) * topo.domain.dimension + else: + shape = np.prod(tuple([indices[i].stop - indices[i].start + for i in xrange(len(indices))])) + else: + shape = np.prod(topo.mesh.resolution) + return {'rwork': [(shape, ), ] * lwork, 'iwork': None} def _init_fd_method(self, topo, reduce_output_shape): """Build the finite difference scheme """ msg = 'FD scheme Not yet implemented for this operation.' assert self.method.__mro__[0] in self._authorized_methods, msg + if not self._on_proc: + empty_set = [slice(0, 0), ] * self._dim + return self.method(topo.mesh.space_step, + empty_set) + fd_scheme = self.method(topo.mesh.space_step, - self._indices, + self.in_indices, reduce_output_shape) msg = 'Ghost layer is too small for the chosen FD scheme.' - required_ghost_layer = fd_scheme.minimal_ghost_layer + required_ghost_layer = fd_scheme.ghosts_layer_size assert (topo.ghosts() >= required_ghost_layer).all(), msg return fd_scheme @@ -151,19 +177,22 @@ class Curl(DifferentialOperation): """Curl of a vector field """ super(Curl, self).__init__(**kwds) - assert len(self._indices) > 1 + assert len(self.in_indices) > 1 # connect to fd function call self.fcall = self._central - if len(self._indices) == 2: + if len(self.in_indices) == 2: # a 'fake' curl for 2D case self.fcall = self._central_2d @staticmethod - def get_work_length(): - return 1 + def get_work_properties(topo, indices=None): + return Curl._find_work(Curl._lwork, topo, indices) def __call__(self, variable, result): - return self.fcall(variable, result) + if self._on_proc: + return self.fcall(variable, result) + else: + return result def _central(self, variable, result): """ 3D Curl @@ -239,13 +268,83 @@ class DivRhoV(DifferentialOperation): Default method : FDC4 """ + self._in_gh = None + self._wk_indices = None + self._fd_work = None super(DivRhoV, self).__init__(**kwds) - #self.fcall = self._central4_caa - self.fcall = self._central4 + + def _init_fd_method(self, topo, reduce_output_shape): + """Build the finite difference scheme + """ + msg = 'FD scheme Not yet implemented for this operation.' + assert self.method.__mro__[0] in self._authorized_methods, msg + # for this operation, the fd scheme uses + # work array as input, which means that work + # array must include ghost points. This is done + # thanks to self.wk_indices + if not self._on_proc: + empty_set = [slice(0, 0), ] * self._dim + self._fd_work = self.method(topo.mesh.space_step, + empty_set) + self._in_gh = empty_set + self._wk_indices = empty_set + return self._fd_work + + gh = self.method.ghosts_layer_size + ref = self.in_indices + self._in_gh = [slice(ref[i].start - gh, ref[i].stop + gh) + for i in xrange(self._dim)] + for sl in self._in_gh: + assert sl.start >= 0 + assert sl.stop >= 0 + wk_shape = tuple([self._in_gh[i].stop - self._in_gh[i].start + for i in xrange(self._dim)]) + self._wk_indices = [slice(gh, wk_shape[i] - gh) + for i in xrange(self._dim)] + if reduce_output_shape: + iout = [slice(0, wk_shape[i] - 2 * gh) + for i in xrange(self._dim)] + else: + iout = None + fd_scheme = self.method(topo.mesh.space_step, + self._wk_indices, + indices_out=iout) + self._fd_work = self.method(topo.mesh.space_step, + self._wk_indices) + msg = 'Ghost layer is too small for the chosen FD scheme.' + required_ghost_layer = fd_scheme.ghosts_layer_size + assert (topo.ghosts() >= required_ghost_layer).all(), msg + return fd_scheme + + def _set_work_arrays(self, topo, reduce_output_shape, rwork=None): + """Check and allocate internal work buffers. + """ + wk_prop = self.get_work_properties(topo, self.in_indices)['rwork'] + if wk_prop is None: + return [] + subshape = np.asarray([self.in_indices[i].stop - + self.in_indices[i].start + for i in xrange(self._dim)]) + if self._on_proc: + subshape += 2 * self.fd_scheme.ghosts_layer_size + subshape = tuple(subshape) + + return WorkSpaceTools.check_work_array(len(wk_prop), subshape, rwork) @staticmethod - def get_work_length(): - return 2 + def get_work_properties(topo, indices=None): + # fd scheme will be applied on work so it needs ghost points. + if indices is not None: + if not Utils.is_on_proc(indices): + shape = (0, ) * topo.domain.dimension + else: + shape = np.asarray([indices[i].stop - indices[i].start + for i in xrange(len(indices))]) + shape += 2 * topo.ghosts() + else: + shape = np.asarray(topo.mesh.resolution).copy() + shape = np.prod(shape) + return {'rwork': [(shape,), ] * 2, 'iwork': None} def __call__(self, var1, scal, result): """Apply operation @@ -267,7 +366,10 @@ class DivRhoV(DifferentialOperation): assert len(result) == len(scal) for i in xrange(len(var1)): assert var1[i] is not result - return self.fcall(var1, scal, result) + if self._on_proc: + return self._central4(var1, scal, result) + else: + return result def _central4(self, var1, scal, result): """ @@ -282,45 +384,23 @@ class DivRhoV(DifferentialOperation): # Result does not need initialisation to zero. # d/dx (scal * var1x), saved into result - np.multiply(scal[0], var1[XDIR], self._work[0]) - self.fd_scheme.compute(self._work[0], XDIR, result[0]) - # other components (if any) saved into _work[0] and added into result - # d/dy (scal * var1y), saved in work and added into result - # d/dz(scal * var1z), saved in work and added into result (if 3D) - for cdir in xrange(1, len(var1)): - np.multiply(scal[0], var1[cdir], self._work[0]) - self.fd_scheme.compute(self._work[0], cdir, self._work[1]) - result[0][self.output_indices] += \ - self._work[1][self.output_indices] - - return result - - def _central4_caa(self, var1, scal, result): - """ - Compute central finite difference scheme, order 4. - Use fd_scheme.compute_and_add. - """ - # _work[0] is used as temporary space - # for computation - # div computations are accumulate into result. - # result does not need initialisation to zero. - - # d/dx (scal * var1x), saved into result - np.multiply(scal[0][self._indices], var1[XDIR][self._indices], - self._work[0][self.output_indices]) - self.fd_scheme.compute(self._work[0], XDIR, result[0]) + np.multiply(scal[0][self._in_gh], + var1[XDIR][self._in_gh], self._work[0]) + result[0] = self.fd_scheme.compute(self._work[0], XDIR, result[0]) # other components (if any) saved into _work[0] and added into result # d/dy (scal * var1y), saved in work and added into result # d/dz(scal * var1z), saved in work and added into result (if 3D) for cdir in xrange(1, len(var1)): - np.multiply(scal[0][self._indices], var1[cdir][self._indices], - self._work[0][self.output_indices]) - self.fd_scheme.compute_and_add(self._work[0], cdir, result[0]) + np.multiply(scal[0][self._in_gh], + var1[cdir][self._in_gh], self._work[0]) + self._work[1] = self._fd_work.compute(self._work[0], cdir, + self._work[1]) + result[0][self.output_indices] += self._work[1][self._wk_indices] return result -class DivWV(DifferentialOperation): +class DivWV(DivRhoV): """ Computes nabla.(W.Vx, W.Vy, W.Vz), W and V some vector fields. @@ -333,21 +413,13 @@ class DivWV(DifferentialOperation): Parameters ---------- - fd_optim : bool, optional - if 'CAA', compute result += div(rhoV) else - result = div(rhoV), which is the default. **kwds : parameters for base class Default method : FDC4 """ super(DivWV, self).__init__(**kwds) - self.fcall = self._central4 - # set div(scal.var1) operator - self.div = DivRhoV(**kwds) - - @staticmethod - def get_work_length(): - return DivRhoV.get_work_length() + msg = 'Implemented only in 3D.' + assert self._dim == 3, msg def __call__(self, var1, var2, result): """Apply operation @@ -365,16 +437,11 @@ class DivWV(DifferentialOperation): list of numpy arrays """ - assert len(result) == len(var1) - return self.fcall(var1, var2, result) - - def _central4(self, var1, var2, result): - # Note FP var1[dir] and var2[dir] must be different from result. - # This is checked inside divV call. - - for cdir in xrange(len(var1)): - result[cdir:cdir + 1] = self.div(var1, var2[cdir:cdir + 1], - result=result[cdir:cdir + 1]) + assert len(result) == len(var2) + if self._on_proc: + for cdir in xrange(len(var2)): + result[cdir:cdir + 1] = self._central4( + var1, var2[cdir:cdir + 1], result=result[cdir:cdir + 1]) return result @@ -386,8 +453,8 @@ class Laplacian(DifferentialOperation): _lwork = 1 @staticmethod - def get_work_length(): - return 0 + def get_work_properties(topo, indices=None): + return Laplacian._find_work(Laplacian._lwork, topo, indices) def __call__(self, var, result): """"Apply laplacian @@ -403,10 +470,12 @@ class Laplacian(DifferentialOperation): numpy array """ assert len(var) == len(result) - for d in xrange(len(var)): - self.fd_scheme.compute(var[d], 0, result[d]) - for cdir in xrange(1, self._dim): - self.fd_scheme.compute_and_add(var[d], cdir, result[d]) + if self._on_proc: + for d in xrange(len(var)): + self.fd_scheme.compute(var[d], 0, result[d]) + for cdir in xrange(1, self._dim): + self.fd_scheme.compute_and_add(var[d], cdir, result[d], + self._work[0]) return result @@ -426,9 +495,10 @@ class GradS(DifferentialOperation): in/out result """ assert len(result) == self._dim - for cdir in xrange(self._dim): - # d/dcdir (scal), saved in data[cdir] - self.fd_scheme.compute(scal[0], cdir, result[cdir]) + if self._on_proc: + for cdir in xrange(self._dim): + # d/dcdir (scal), saved in data[cdir] + self.fd_scheme.compute(scal[0], cdir, result[cdir]) return result @@ -438,10 +508,6 @@ class GradV(DifferentialOperation): """ _authorized_methods = [FDC4, FDC2] - def __init__(self, **kwds): - super(GradV, self).__init__(**kwds) - self.grad = GradS(**kwds) - def __call__(self, var1, result): """Apply gradient @@ -454,10 +520,13 @@ class GradV(DifferentialOperation): """ nbc = len(var1) assert len(result) == nbc * self._dim - for cdir in xrange(self._dim): - i1 = cdir * nbc - i2 = i1 + nbc - result[i1:i2] = self.grad(var1[cdir:cdir + 1], result[i1:i2]) + if self._on_proc: + for cdir in xrange(self._dim): + pos = nbc * cdir + for ddir in xrange(self._dim): + # d/dddir (v[cdir]), saved in result[pos] + self.fd_scheme.compute(var1[cdir], ddir, result[pos]) + pos += 1 return result @@ -470,8 +539,8 @@ class GradVxW(DifferentialOperation): _lwork = 2 @staticmethod - def get_work_length(): - return 2 + def get_work_properties(topo, indices=None): + return DifferentialOperation._find_work(GradVxW._lwork, topo, indices) def __call__(self, var1, var2, result, diagnostics): """Apply gradient, with central finite difference scheme. @@ -487,29 +556,33 @@ class GradVxW(DifferentialOperation): In/out param, overwritten """ assert len(result) == len(var1) - nbc = len(var1) - diagnostics[:] = 0.0 - for comp in xrange(nbc): - result[comp][...] = 0.0 - self._work[1][...] = 0.0 - for cdir in xrange(self._dim): - # self._work = d/dcdir (var1_comp) - self.fd_scheme.compute(var1[comp], cdir, self._work[0]) - # some diagnostics ... - if cdir == comp: - tmp = np.max(abs(self._work[0])) - diagnostics[0] = max(diagnostics[0], tmp) - np.add(abs(self._work[0]), self._work[1], self._work[1]) - - # compute self._work = self._work.var2[cdir] - np.multiply(self._work[0][self.output_indices], - var2[cdir][self._indices], - self._work[0][self.output_indices]) - # sum to obtain nabla(var_comp) . var2, saved into result[comp] - npw.add(result[comp][self.output_indices], - self._work[0][self.output_indices], - result[comp][self.output_indices]) - diagnostics[1] = max(diagnostics[1], np.max(self._work[1])) + if self._on_proc: + diagnostics[:] = 0. + nbc = len(var1) + for comp in xrange(nbc): + result[comp][...] = 0.0 + self._work[1][...] = 0.0 + for cdir in xrange(self._dim): + # self._work = d/dcdir (var1_comp) + self.fd_scheme.compute(var1[comp], cdir, self._work[0]) + # some diagnostics ... + if cdir == comp: + tmp = np.max(abs(self._work[0][self.output_indices])) + diagnostics[0] = max(diagnostics[0], tmp) + np.add(abs(self._work[0]), self._work[1], self._work[1]) + + # compute self._work = self._work.var2[cdir] + np.multiply(self._work[0][self.output_indices], + var2[cdir][self.in_indices], + self._work[0][self.output_indices]) + # sum to obtain nabla(var_comp) . var2, + # saved into result[comp] + npw.add(result[comp][self.output_indices], + self._work[0][self.output_indices], + result[comp][self.output_indices]) + diagnostics[1] = max( + diagnostics[1], np.max(self._work[1][self.output_indices])) + return result, diagnostics @@ -520,8 +593,9 @@ class DivAdvection(DifferentialOperation): _lwork = 3 @staticmethod - def get_work_length(): - return 3 + def get_work_properties(topo, indices=None): + return DifferentialOperation._find_work(DivAdvection._lwork, + topo, indices) def __call__(self, var1, result): """Apply divergence, with central finite difference scheme. @@ -535,6 +609,8 @@ class DivAdvection(DifferentialOperation): """ assert len(result) == 1 nbc = len(var1) + if not self._on_proc: + return result assert nbc == 3, 'Only 3D case is implemented.' # Compute diff(var[dir], dir), saved in work[dir] for d in xrange(nbc): @@ -591,3 +667,412 @@ class DivAdvection(DifferentialOperation): result[0][self.output_indices] *= 2.0 return result + + +class Strain(DifferentialOperation): + """Compute 0.5(grad U + grad U^T), U a vector field + """ + _authorized_methods = [FDC4, FDC2] + + def __init__(self, **kwds): + super(Strain, self).__init__(**kwds) + if self._dim == 3: + self._i1 = [0, 0, 1] + self._i2 = [1, 2, 2] + elif self._dim == 2: + self._i1 = [0, ] + self._i2 = [1, ] + + def __call__(self, var1, result): + """Compute strain + + Parameters + ---------- + var1 : list of numpy arrays + the input vector field + result : list of numpy arrays + in/out result + + result = [diagonal components, extra-diag comp] + + For example in 3D : + + result = [Strain00, Strain11, Strain22, Strain01, Strain02, Strain12] + """ + nbc = len(var1) + assert len(result) == self._dim * (self._dim + 1) / 2 + if not self._on_proc: + return result + + pos = nbc + # Extra diagonal terms. result[0] is used as temporary + # work array. + for i1, i2 in zip(self._i1, self._i2): + self.fd_scheme.compute(var1[i1], i2, result[pos]) + self.fd_scheme.compute_and_add(var1[i2], i1, result[pos], + result[0]) + np.multiply(result[pos], 0.5, result[pos]) + pos += 1 + # Then diagonal terms + for cdir in xrange(self._dim): + self.fd_scheme.compute(var1[cdir], cdir, result[cdir]) + + return result + + +class StrainCriteria(DifferentialOperation): + """For Strain = 0.5(grad U + grad U^T), U a vector field + compute max(sum_j abs(Strain_i,j)) + + Warning : this is a local (mpi) max computation, since input array var1 + is also a local array. + """ + _authorized_methods = [FDC4, FDC2] + + def __init__(self, **kwds): + super(StrainCriteria, self).__init__(**kwds) + if self._dim == 3: + self._i1 = [0, 0, 1] + self._i2 = [1, 2, 2] + self._pos = [0, 1, 2] + self._adds = [[0, 1], [0, 2], [1, 2]] + elif self._dim == 2: + self._i1 = [0, ] + self._i2 = [1, ] + self._pos = [0] + self._adds = [[0, ], [0, ]] + + @staticmethod + def get_work_properties(topo, indices=None): + dime = topo.domain.dimension + if dime == 2: + lwork = 2 + elif dime == 3: + lwork = 4 + + return DifferentialOperation._find_work(lwork, topo, indices) + + def __call__(self, var1, result): + """Compute criteria + + Parameters + ---------- + var1 : list of numpy arrays + the input vector field + result : numpy array + criteria values for each direction. + + """ + work = self._work + assert len(result) == self._dim + if not self._on_proc: + return result + + # First, compute extra-diagonal terms + for i1, i2, ipos in zip(self._i1, self._i2, self._pos): + self.fd_scheme.compute(var1[i1], i2, work[ipos]) + self.fd_scheme.compute_and_add(var1[i2], i1, work[ipos], + work[-1]) + np.multiply(work[ipos], 0.5, work[ipos]) + np.abs(work[ipos], work[ipos]) + + # Then add those terms to diagonal term and compute max + for idiag in xrange(self._dim): + self.fd_scheme.compute(var1[idiag], idiag, work[-1]) + np.abs(work[-1], work[-1]) + for i1 in self._adds[idiag]: + np.add(work[i1], work[-1], work[-1]) + if self._on_proc: + result[idiag] = np.max(work[-1][self.output_indices]) + else: + result[idiag] = 0.0 + return result + + +class MaxDiagGradV(DifferentialOperation): + """Maximum of each diagonal term (abs) of the Gradient of a vector field + """ + _authorized_methods = [FDC4, FDC2] + _lwork = 1 + + @staticmethod + def get_work_properties(topo, indices=None): + return DifferentialOperation._find_work(MaxDiagGradV._lwork, + topo, indices) + + def __call__(self, var1, result): + """Compute max + + Parameters + ---------- + var1 : list of numpy arrays + the input vector field + result : numpy array + in/out result + """ + assert len(result) == self._dim + if not self._on_proc: + return result + + for cdir in xrange(self._dim): + # d/dcdir (var1[cdir]) saved in work + self._work[0] = self.fd_scheme.compute(var1[cdir], + cdir, self._work[0]) + np.abs(self._work[0], self._work[0]) + if self._on_proc: + result[cdir] = np.max(self._work[0][self.output_indices]) + + return result + + +class StretchLike(DifferentialOperation): + """Maximum of the sum of each 'line' (abs) of the Gradient of a vector field + + Warning: local (mpi) maximum. + """ + _authorized_methods = [FDC4, FDC2] + _lwork = 2 + + @staticmethod + def get_work_properties(topo, indices=None): + return DifferentialOperation._find_work(StretchLike._lwork, + topo, indices) + + def __call__(self, var1, result): + """Compute max + + Parameters + ---------- + var1 : list of numpy arrays + the input vector field + result : numpy array + in/out result + + result[i] = max(sum_j abs(diff(u_i, j)) for i = 0..dim + """ + assert len(result) == self._dim + result[...] = 0. + if not self._on_proc: + return result + for cdir in xrange(self._dim): + self._work[1][...] = 0.0 + for ddir in xrange(self._dim): + # d/dddir (var1[cdir]) saved in work + self._work[0] = self.fd_scheme.compute(var1[cdir], + ddir, self._work[0]) + np.abs(self._work[0], self._work[0]) + np.add(self._work[0], self._work[1], self._work[1]) + if self._on_proc: + result[cdir] = np.max(self._work[1][self.output_indices]) + + return result + + +class DiagAndStretch(DifferentialOperation): + """Maximum of the sum of each 'line' of abs of the Gradient of a vector field + and max of each abs of 'diagonal' block of the gradient. + + Warning : this is a local (mpi) max computation, since input array var1 + is also a local array. + """ + _authorized_methods = [FDC4, FDC2] + _lwork = 2 + + @staticmethod + def get_work_properties(topo, indices=None): + return DifferentialOperation._find_work(DiagAndStretch._lwork, + topo, indices) + + def __call__(self, var1, result): + """Compute max + + Parameters + ---------- + var1 : list of numpy arrays + the input vector field + result : numpy array + in/out result + + result[i] = max(abs(diff(u_i,i))) for i = 0..dim + result[dim:...] = max(sum_j abs(diff(u_i, j)) for i = 0..dim + """ + assert len(result) == 2 * self._dim + result[...] = 0. + + if not self._on_proc: + return result + + pos = 0 + for cdir in xrange(self._dim): + self._work[1][...] = 0.0 + for ddir in xrange(self._dim): + # d/dcdir (var1[cdir]) saved in work + self._work[0] = self.fd_scheme.compute(var1[cdir], + ddir, self._work[0]) + np.abs(self._work[0], self._work[0]) + if cdir == ddir and self._on_proc: + result[pos] = np.max(self._work[0][self.output_indices]) + pos += 1 + np.add(self._work[0], self._work[1], self._work[1]) + if self._on_proc: + result[self._dim + cdir] = np.max( + self._work[1][self.output_indices]) + + return result + + +class StrainAndStretch(DifferentialOperation): + """For Strain = 0.5(grad U + grad U^T), U a vector field + compute max(sum_j abs(Strain_i,j)) and maximum of the sum + of each 'line' (abs) of the Gradient of U. + + Warning : this is a local (mpi) max computation, since input array var1 + is also a local array. + + result = [ max(sum_j(|strain_0,j|), ...], + max(sum_j(|u0,j|), max(sum_j(|u1,j|)], ...] + """ + _authorized_methods = [FDC4, FDC2] + + def __init__(self, **kwds): + super(StrainAndStretch, self).__init__(**kwds) + if self._dim == 3: + self._fcall = self._call_3d + elif self._dim == 2: + self._fcall = self._call_2d + + @staticmethod + def get_work_properties(topo, indices=None): + dime = topo.domain.dimension + if dime == 2: + lwork = 4 + elif dime == 3: + lwork = 6 + + return DifferentialOperation._find_work(lwork, topo, indices) + + def __call__(self, var1, result): + """Compute criteria + + Parameters + ---------- + var1 : list of numpy arrays + the input vector field + result : numpy array + criteria values for each direction. + + """ + if self._on_proc: + return self._fcall(var1, result) + else: + return result + + def _call_3d(self, var1, result): + work = self._work + assert len(result) == 2 * self._dim + # work[0] = abs(eps_x,x) + self.fd_scheme.compute(var1[XDIR], XDIR, work[0]) + np.abs(work[0], work[0]) + + # work[1] = diff(u(x), i2) + # work[2] = abs(work[1]) + # work[3] = diff(u(x), i3) + # work[4] = abs(work[3]) + self.fd_scheme.compute(var1[XDIR], YDIR, work[1]) + np.abs(work[1], work[2]) + self.fd_scheme.compute(var1[XDIR], ZDIR, work[3]) + np.abs(work[3], work[4]) + # max(sum_j(abs(diff(u(x),j)))) + np.add(work[2], work[4], work[2]) + np.add(work[2], work[0], work[2]) + result[3] = np.max(work[2][self.output_indices]) + # free: p[2, 4, 5] + # work[5] = diff(u(y), x) + self.fd_scheme.compute(var1[YDIR], XDIR, work[5]) + # ... used to compute work[1] = abs(eps_x,y) + np.add(work[1], work[5], work[1]) + np.multiply(work[1], 0.5, work[1]) + np.abs(work[1], work[1]) + # work[4] = diff(u(z), x) + self.fd_scheme.compute(var1[ZDIR], XDIR, work[4]) + # ... used to compute work[2] = abs(eps_x,z) + np.add(work[4], work[3], work[2]) + np.multiply(work[2], 0.5, work[2]) + np.abs(work[2], work[2]) + # At this point, + # work[p[0, 1, 2] = abs([epsxx, epsxy, epsxz]) + # compute max(epsxx + epsxy + epsxz) + np.add(work[1], work[0], work[0]) + np.add(work[2], work[0], work[0]) + result[0] = np.max(work[0][self.output_indices]) + # work[4] = abs(diff(u(z), x)) + # work[5] = abs(diff(u(y), x)) + np.abs(work[4], work[4]) + np.abs(work[5], work[5]) + # free : p[0, 3] + # work[0] = abs(eps_y,y) + self.fd_scheme.compute(var1[YDIR], YDIR, work[0]) + np.abs(work[0], work[0]) + np.add(work[0], work[1], work[1]) + np.add(work[0], work[5], work[5]) + # work[0] = abs(diff(u(y), z)) + self.fd_scheme.compute(var1[YDIR], ZDIR, work[3]) + np.abs(work[3], work[0]) + np.add(work[0], work[5], work[5]) + result[4] = np.max(work[5][self.output_indices]) + # free : p[0, 5] + # work[0] = diff(u(i1), i2) + # work[5] = abs(diff(u(i1), i2)) + self.fd_scheme.compute(var1[ZDIR], YDIR, work[0]) + np.abs(work[0], work[5]) + # ... used to compute work[0] = abs(eps_yz) + np.add(work[0], work[3], work[0]) + np.multiply(work[0], 0.5, work[0]) + np.abs(work[0], work[0]) + # compute max(epsyy + epsxy + epsyz) + np.add(work[1], work[0], work[1]) + result[1] = np.max(work[1][self.output_indices]) + np.add(work[2], work[0], work[2]) + # work[0] = abs(diff(uzz) + self.fd_scheme.compute(var1[2], 2, work[0]) + np.abs(work[0], work[0]) + np.add(work[2], work[0], work[2]) + result[2] = np.max(work[2][self.output_indices]) + np.add(work[0], work[4], work[0]) + np.add(work[0], work[5], work[0]) + result[5] = np.max(work[0][self.output_indices]) + # ouf! + + return result + + def _call_2d(self, var1, result): + work = self._work + assert len(result) == 2 * self._dim + # work[0] = abs(eps_x,x) + self.fd_scheme.compute(var1[XDIR], XDIR, work[0]) + np.abs(work[0], work[0]) + + # work[1] = diff(u(x), y) + # work[2] = abs(diff(u(x), y)) + # work[3] = diff(u(y), x) + self.fd_scheme.compute(var1[XDIR], YDIR, work[1]) + np.abs(work[1], work[2]) + self.fd_scheme.compute(var1[YDIR], XDIR, work[3]) + np.add(work[0], work[2], work[2]) + result[2] = np.max(work[2][self.output_indices]) + # work[1] = epsxy + np.add(work[1], work[3], work[1]) + np.multiply(work[1], 0.5, work[1]) + np.abs(work[1], work[1]) + np.add(work[0], work[1], work[0]) + result[0] = np.max(work[0][self.output_indices]) + # work[0] = abs(eps_y,y) + self.fd_scheme.compute(var1[YDIR], YDIR, work[0]) + np.abs(work[0], work[0]) + # work[2] = abs(uy,x) + np.abs(work[3], work[2]) + np.add(work[2], work[0], work[2]) + result[3] = np.max(work[2][self.output_indices]) + np.add(work[0], work[1], work[0]) + result[0] = np.max(work[0][self.output_indices]) + return result diff --git a/hysop/numerics/tests/test_diffOp.py b/hysop/numerics/tests/test_diffOp.py deleted file mode 100755 index 555ea48a3..000000000 --- a/hysop/numerics/tests/test_diffOp.py +++ /dev/null @@ -1,222 +0,0 @@ -# -*- coding: utf-8 -*- -from hysop import Field, Box -import numpy as np -import hysop.numerics.differential_operations as diffop -import hysop.tools.numpywrappers as npw -from hysop.tools.parameters import Discretization -import math as m -pi = m.pi -cos = np.cos -sin = np.sin - - -def computeVel(res, x, y, z, t): - res[0][...] = sin(x) * cos(y) * cos(z) - res[1][...] = - cos(x) * sin(y) * cos(z) - res[2][...] = 0. - return res - - -def computeVort(res, x, y, z, t): - res[0][...] = - cos(x) * sin(y) * sin(z) - res[1][...] = - sin(x) * cos(y) * sin(z) - res[2][...] = 2. * sin(x) * sin(y) * cos(z) - return res - - -def laplacian_func(res, x, y, z, t): - res[0][...] = 3 * cos(x) * sin(y) * sin(z) - res[1][...] = 3 * sin(x) * cos(y) * sin(z) - res[2][...] = -6. * sin(x) * sin(y) * cos(z) - return res - - -def computeVel2(res, x, y, t): - res[0][...] = sin(x) * cos(y) - res[1][...] = - cos(x) * sin(y) - return res - - -def computeVort2(res, x, y, t): - res[0][...] = 2. * sin(x) * sin(y) - return res - - -def analyticalDivWV(res, x, y, z, t): - res[0][...] = - sin(y) * cos(y) * sin(z) * cos(z) * \ - (- sin(x) * sin(x) + cos(x) * cos(x)) + \ - 2. * cos(x) * cos(x) * sin(z) * cos(z) * sin(y) * cos(y) - res[1][...] = - 2. * cos(y) * cos(y) * sin(z) * cos(z) * sin(x) * cos(x) +\ - sin(x) * cos(x) * sin(z) * cos(z) * (cos(y) * cos(y) - sin(y) * sin(y)) - res[2][...] = 0. - return res - - -def analyticalDivWV2D(res, x, y, t): - res[0][...] = 0. - return res - -def analyticalGradVxW(res, x, y, z, t): - res[0][...] = - sin(y) * cos(y) * sin(z) * cos(z) - res[1][...] = sin(x) * cos(x) * sin(z) * cos(z) - res[2][...] = 0. - return res - - -def analyticalDivStressTensor(res, x, y, z, t): - res[0][...] = - 3. * sin(x) * cos(y) * cos(z) - res[1][...] = 3. * cos(x) * sin(y) * cos(z) - res[2][...] = 0. - return res - -def analyticalDivAdvection(res, x, y, z, t): - res[0][...] = - cos(z) * cos(z) * (cos(x) * cos(x) - sin(x) * sin(x)) - \ - cos(z) * cos(z) * (cos(y) * cos(y) - sin(y) * sin(y)) - return res - - -Nx = 128 -Ny = 110 -Nz = 162 -g = 2 -discr3D = Discretization([Nx + 1, Ny + 1, Nz + 1], [g, g, g]) -discr2D = Discretization([Nx + 1, Ny + 1], [g, g]) -ldom = npw.asrealarray([pi * 2., ] * 3) -xdom = [0., 0., 0.] - - -def init(discr, vform, wform): - dim = len(discr.resolution) - dom = Box(dimension=dim, length=ldom[:dim], - origin=xdom[:dim]) - topo = dom.create_topology(discr) - work = [] - shape = tuple(topo.mesh.resolution) - for _ in xrange(3): - work.append(npw.zeros(shape)) - velo = Field(domain=dom, formula=vform, - name='Velocity', is_vector=True) - vorti = Field(domain=dom, formula=wform, - name='Vorticity', is_vector=dim == 3) - vd = velo.discretize(topo) - wd = vorti.discretize(topo) - velo.initialize(topo=topo) - vorti.initialize(topo=topo) - return vd, wd, topo, work - - -# Init 2D and 3D -v3d, w3d, topo3, work3 = init(discr3D, computeVel, computeVort) -v2d, w2d, topo2, work2 = init(discr2D, computeVel2, computeVort2) -ic3 = topo3.mesh.compute_index -ic2 = topo2.mesh.compute_index - - -def build_op(op_class, len_result, topo, work): - lwk = op_class.get_work_length() - op = op_class(topo=topo, work=work[:lwk]) - memshape = tuple(topo.mesh.resolution) - result = [npw.zeros(memshape) for _ in xrange(len_result)] - return op, result - - -def test_curl(): - op, result = build_op(diffop.Curl, 3, topo3, work3) - result = op(v3d.data, result) - rtol = np.max(topo3.mesh.space_step ** 2) - for i in xrange(3): - assert np.allclose(w3d.data[i][ic3], result[i][ic3], rtol=rtol) - - -def test_curl_2d(): - op, result = build_op(diffop.Curl, 1, topo2, work2) - result = op(v2d.data, result) - rtol = np.max(topo3.mesh.space_step ** 2) - assert np.allclose(w2d.data[0][ic2], result[0][ic2], rtol=rtol) - - -def test_laplacian(): - ref = Field(domain=topo3.domain, formula=laplacian_func, - name='Analytical', is_vector=True) - rd = ref.discretize(topo3) - ref.initialize(topo=topo3) - - op, result = build_op(diffop.Laplacian, 3, topo3, work3) - result = op(w3d.data, result) - tol = np.max(topo3.mesh.space_step ** 2) - for i in xrange(3): - assert np.allclose(rd.data[i][ic3], result[i][ic3], rtol=tol) - - -def test_div_rho_v_2d(): - # Reference field - ref = Field(domain=topo2.domain, formula=analyticalDivWV2D, - name='Analytical', is_vector=False) - rd = ref.discretize(topo2) - ref.initialize(topo=topo2) - op, result = build_op(diffop.DivRhoV, 1, topo2, work2) - tol = np.max(topo2.mesh.space_step ** 4) - result = op(v2d.data, w2d.data[0:1], result) - # Numerical VS analytical - assert np.allclose(rd[0][ic2], result[0][ic2], atol=tol) - - -def test_div_rho_v(): - # Reference field - ref = Field(domain=topo3.domain, formula=analyticalDivWV, - name='Analytical', is_vector=True) - rd = ref.discretize(topo3) - ref.initialize(topo=topo3) - op, result = build_op(diffop.DivRhoV, 3, topo3, work3) - tol = np.max(topo3.mesh.space_step ** 4) - for i in xrange(3): - result[i:i + 1] = op(v3d.data, w3d.data[i:i + 1], result[i:i + 1]) - # Numerical VS analytical - assert np.allclose(rd[i][ic3], result[i][ic3], atol=tol) - - -def test_divwv(): - # Reference field - ref = Field(domain=topo3.domain, formula=analyticalDivWV, - name='Analytical', is_vector=True) - rd = ref.discretize(topo3) - ref.initialize(topo=topo3) - op, result = build_op(diffop.DivWV, 3, topo3, work3) - result = op(v3d.data, w3d.data, result) - - # Numerical VS analytical - tol = np.max(topo3.mesh.space_step ** 2) - for i in xrange(3): - assert np.allclose(rd[i][ic3], result[i][ic3], atol=tol) - - -def test_grad_vxw(): - # Reference field - ref = Field(domain=topo3.domain, formula=analyticalGradVxW, - name='Analytical', is_vector=True) - rd = ref.discretize(topo3) - ref.initialize(topo=topo3) - op, result = build_op(diffop.GradVxW, 3, topo3, work3) - diag = npw.zeros(2) - result, diag = op(v3d.data, w3d.data, result, diag) - - # Numerical VS analytical - tol = np.max(topo3.mesh.space_step ** 2) - for i in xrange(3): - assert np.allclose(rd[i][ic3], result[i][ic3], atol=tol) - - -# test for RHS of Pressure-Poisson equation -def test_div_advection(): - # Reference scalar field - ref = Field(domain=topo3.domain, formula=analyticalDivAdvection, - name='Analytical') - rd = ref.discretize(topo3) - ref.initialize(topo=topo3) - op, result = build_op(diffop.DivAdvection, 1, topo3, work3) - result = op(v3d.data, result) - - # Numerical VS analytical - errx = (topo3.domain.length[0] / (Nx - 1)) ** 4 - assert np.allclose(rd[0][ic3], result[0][ic3], rtol=errx) - diff --git a/hysop/numerics/tests/test_differential_operations.py b/hysop/numerics/tests/test_differential_operations.py new file mode 100755 index 000000000..35139d349 --- /dev/null +++ b/hysop/numerics/tests/test_differential_operations.py @@ -0,0 +1,1015 @@ +# -*- coding: utf-8 -*- +from hysop import Field, Box +import numpy as np +import hysop.numerics.differential_operations as diffop +import hysop.tools.numpywrappers as npw +from hysop.tools.parameters import Discretization +from hysop.domain.subsets import SubBox +import math as m +pi = m.pi +cos = np.cos +sin = np.sin + + +def compute_vel(res, x, y, z, t): + res[0][...] = sin(x) * cos(y) * cos(z) + res[1][...] = - cos(x) * sin(y) * cos(z) + res[2][...] = 0. + return res + + +def compute_vort(res, x, y, z, t): + res[0][...] = - cos(x) * sin(y) * sin(z) + res[1][...] = - sin(x) * cos(y) * sin(z) + res[2][...] = 2. * sin(x) * sin(y) * cos(z) + return res + + +def compute_vel2(res, x, y, t): + res[0][...] = sin(x) * cos(y) + res[1][...] = - cos(x) * sin(y) + return res + + +def compute_vort2(res, x, y, t): + res[0][...] = 2. * sin(x) * sin(y) + return res + + +def analyticalDivRhoV(res, x, y, z, t): + res[0][...] = cos(y) * cos(z) * sin(y) * sin(z) + return res + + +def analyticalDivRhoV2D(res, x, y, t): + res[0][...] = 0. + return res + + +def analyticalDivWV(res, x, y, z, t): + res[0][...] = - cos(y) * cos(z) * sin(y) * sin(z) + res[1][...] = cos(x) * cos(z) * sin(z) * sin(x) + res[2][...] = 0. + return res + + +def analyticalGradVxW(res, x, y, z, t): + res[0][...] = - sin(y) * cos(y) * sin(z) * cos(z) + res[1][...] = sin(x) * cos(x) * sin(z) * cos(z) + res[2][...] = 0. + return res + + +def analyticalDivStressTensor(res, x, y, z, t): + res[0][...] = - 3. * sin(x) * cos(y) * cos(z) + res[1][...] = 3. * cos(x) * sin(y) * cos(z) + res[2][...] = 0. + return res + +def analyticalDivAdvection(res, x, y, z, t): + res[0][...] = - cos(z) * cos(z) * (cos(x) * cos(x) - sin(x) * sin(x)) - \ + cos(z) * cos(z) * (cos(y) * cos(y) - sin(y) * sin(y)) + return res + + +Nx = 88 +Ny = 50 +Nz = 64 +g = 2 +discr3D = Discretization([Nx + 1, Ny + 1, Nz + 1], [g, g, g]) +discr2D = Discretization([Nx + 1, Ny + 1], [g, g]) +ldom = npw.asrealarray([2., pi, 3.5]) +xdom = [0., -0.3, 0.4] + + +def init(discr, vform, wform): + dim = len(discr.resolution) + dom = Box(dimension=dim, length=ldom[:dim], + origin=xdom[:dim]) + topo = dom.create_topology(discr) + work = [] + shape = tuple(topo.mesh.resolution) + subsize = np.prod(shape) + for _ in xrange(6): + work.append(npw.zeros(subsize,)) + velo = Field(domain=dom, formula=vform, + name='Velocity', is_vector=True) + vorti = Field(domain=dom, formula=wform, + name='Vorticity', is_vector=dim == 3) + vd = velo.discretize(topo) + wd = vorti.discretize(topo) + velo.initialize(topo=topo) + vorti.initialize(topo=topo) + return vd, wd, topo, work + + +# Init 2D and 3D +v3d, w3d, topo3, work3 = init(discr3D, compute_vel, compute_vort) +v2d, w2d, topo2, work2 = init(discr2D, compute_vel2, compute_vort2) + + +def build_op(op_class, len_result, topo, work=None, reduced=False, + reduce_output=False): + if reduced: + # computations will be done on a subset of the domain + sl = topo.domain.length * 0.5 + orig = topo.domain.origin + 0.1 * topo.domain.length + subbox = SubBox(parent=topo.domain, origin=orig, length=sl) + indices = subbox.discretize(topo)[0] + if reduce_output: + outputshape = subbox.mesh[topo].resolution + else: + outputshape = np.asarray(topo.mesh.resolution).copy() + else: + indices = None + if reduce_output: + outputshape = np.asarray(topo.mesh.resolution).copy() + gh = topo.ghosts() + outputshape -= 2 * gh + outputshape = tuple(outputshape) + else: + outputshape = tuple(topo.mesh.resolution) + + if work is not None: + wk_prop = op_class.get_work_properties(topo, indices) + if wk_prop['rwork'] is not None: + lwork = len(wk_prop['rwork']) + else: + lwork = 0 + op = op_class(topo=topo, indices=indices, work=work[:lwork], + reduce_output_shape=reduce_output) + else: + op = op_class(topo=topo, indices=indices, + reduce_output_shape=reduce_output) + result = [npw.zeros(outputshape) for _ in xrange(len_result)] + return op, result + + +def assert_curl(wdim, topo, velo, vorti, work=None, reduced=False, + reduce_output=False): + op, result = build_op(diffop.Curl, wdim, topo, work, reduced, + reduce_output) + ic_out = op.output_indices + ic_in = op.in_indices + result = op(velo.data, result) + atol = np.max(topo.mesh.space_step ** 2) + for i in xrange(wdim): + assert np.allclose(vorti.data[i][ic_in], result[i][ic_out], atol=atol) + + +def test_curl(): + # 2d + assert_curl(1, topo2, v2d, w2d) + # 2d work + assert_curl(1, topo2, v2d, w2d, work2) + # 2d reduced + assert_curl(1, topo2, v2d, w2d, reduced=True) + # 2d reduced and work + assert_curl(1, topo2, v2d, w2d, work2, reduced=True) + # 2d reduce output + assert_curl(1, topo2, v2d, w2d, reduce_output=True) + # 2d reduce output and work + assert_curl(1, topo2, v2d, w2d, work2, reduce_output=True) + # 2d reduce output, on subset, with work + assert_curl(1, topo2, v2d, w2d, work2, reduced=True, reduce_output=True) + # 3d + assert_curl(3, topo3, v3d, w3d) + # 3d work + assert_curl(3, topo3, v3d, w3d, work3) + # 3d reduced + assert_curl(3, topo3, v3d, w3d, reduced=True) + # 3d reduced and work + assert_curl(3, topo3, v3d, w3d, work3, reduced=True) + # 3d reduce output + assert_curl(3, topo3, v3d, w3d, reduce_output=True) + # 3d reduce output and work + assert_curl(3, topo3, v3d, w3d, work3, reduce_output=True) + # 3d, work, reduce output, on subset + assert_curl(3, topo3, v3d, w3d, work3, reduced=True, reduce_output=True) + + +def assert_div_rho_v(topo, velo, scal, formula, work=None, reduced=False, + reduce_output=False): + ref = Field(domain=topo.domain, formula=formula, + name='Analytical', is_vector=False) + rd = ref.discretize(topo) + ref.initialize(topo=topo) + op, result = build_op(diffop.DivRhoV, 1, topo, work, reduced, + reduce_output) + ic_out = op.output_indices + ic_in = op.in_indices + tol = np.max(topo.mesh.space_step ** 4) + result = op(velo.data, [scal], result) + assert np.allclose(rd[0][ic_in], result[0][ic_out], atol=tol) + + +def test_div_rho_v(): + # 2d + assert_div_rho_v(topo2, v2d, w2d[0], analyticalDivRhoV2D) + # 2d work + assert_div_rho_v(topo2, v2d, w2d[0], analyticalDivRhoV2D, work2) + # 2d reduced + assert_div_rho_v(topo2, v2d, w2d[0], analyticalDivRhoV2D, reduced=True) + # 2d reduced and work + assert_div_rho_v(topo2, v2d, w2d[0], analyticalDivRhoV2D, work2, + reduced=True) + # 2d reduce output + assert_div_rho_v(topo2, v2d, w2d[0], analyticalDivRhoV2D, + reduce_output=False) + # 2d reduce output and work + assert_div_rho_v(topo2, v2d, w2d[0], analyticalDivRhoV2D, work2, + reduce_output=False) + # 2d reduce output, on subset, with work + assert_div_rho_v(topo2, v2d, w2d[0], analyticalDivRhoV2D, work2, + reduce_output=True, reduced=True) + # 3d + assert_div_rho_v(topo3, v3d, w3d[0], analyticalDivRhoV) + # 3d work + assert_div_rho_v(topo3, v3d, w3d[0], analyticalDivRhoV, work3) + # 3d reduced + assert_div_rho_v(topo3, v3d, w3d[0], analyticalDivRhoV, reduced=True) + # 3d reduced and work + assert_div_rho_v(topo3, v3d, w3d[0], analyticalDivRhoV, work3, + reduced=True) + # 3d reduce output + assert_div_rho_v(topo3, v3d, w3d[0], analyticalDivRhoV, + reduce_output=False) + # 3d reduce output and work + assert_div_rho_v(topo3, v3d, w3d[0], analyticalDivRhoV, work3, + reduce_output=False) + # 3d, work, reduce output, on subset + assert_div_rho_v(topo3, v3d, w3d[0], analyticalDivRhoV, work3, + reduce_output=False, reduced=True) + + +def assert_div_w_v(wdim, topo, velo, vorti, formula, work=None, reduced=False, + reduce_output=False): + # Reference field + ref = Field(domain=topo.domain, formula=formula, + name='Analytical', is_vector=True) + rd = ref.discretize(topo) + ref.initialize(topo=topo) + op, result = build_op(diffop.DivWV, wdim, topo, work, reduced, + reduce_output) + result = op(vorti.data, velo.data, result) + ic_out = op.output_indices + ic_in = op.in_indices + + # Numerical VS analytical + tol = np.max(topo.mesh.space_step ** 2) + for i in xrange(wdim): + assert np.allclose(rd[i][ic_in], result[i][ic_out], atol=tol) + + +def test_div_w_v(): + # 3d + assert_div_w_v(3, topo3, v3d, w3d, analyticalDivWV) + # 3d work + assert_div_w_v(3, topo3, v3d, w3d, analyticalDivWV, work3) + # 3d reduced + assert_div_w_v(3, topo3, v3d, w3d, analyticalDivWV, reduced=True) + # 3d reduced and work + assert_div_w_v(3, topo3, v3d, w3d, analyticalDivWV, work3, + reduced=True) + # 3d reduce output + assert_div_w_v(3, topo3, v3d, w3d, analyticalDivWV, reduce_output=True) + # 3d reduce output and work + assert_div_w_v(3, topo3, v3d, w3d, analyticalDivWV, work3, + reduce_output=True) + # 3d, work, reduce output, on subset + assert_div_w_v(3, topo3, v3d, w3d, analyticalDivWV, work3, + reduce_output=True, reduced=True) + + +def assert_laplacian(wdim, topo, vorti, formula, work=None, reduced=False, + reduce_output=False): + ref = Field(domain=topo.domain, formula=formula, + name='Analytical', is_vector=True) + rd = ref.discretize(topo) + ref.initialize(topo=topo) + op, result = build_op(diffop.Laplacian, wdim, topo, work, reduced, + reduce_output) + ic_out = op.output_indices + ic_in = op.in_indices + result = op(vorti.data, result) + tol = np.max(topo.mesh.space_step ** 2) + for i in xrange(wdim): + assert np.allclose(rd.data[i][ic_in], result[i][ic_out], rtol=tol) + + +def laplacian_func(res, x, y, z, t): + res[0][...] = 3 * cos(x) * sin(y) * sin(z) + res[1][...] = 3 * sin(x) * cos(y) * sin(z) + res[2][...] = -6. * sin(x) * sin(y) * cos(z) + return res + + +def laplacian_func_2d(res, x, y, t): + res[0][...] = -4. * sin(x) * sin(y) + return res + + +def test_laplacian(): + # 2d + assert_laplacian(1, topo2, w2d, laplacian_func_2d) + # 2d work + assert_laplacian(1, topo2, w2d, laplacian_func_2d, work2) + # 2d reduced + assert_laplacian(1, topo2, w2d, laplacian_func_2d, reduced=True) + # 2d reduced and work + assert_laplacian(1, topo2, w2d, laplacian_func_2d, work2, + reduced=True) + # 2d reduce output + assert_laplacian(1, topo2, w2d, laplacian_func_2d, reduce_output=True) + # 2d reduce output and work + assert_laplacian(1, topo2, w2d, laplacian_func_2d, work2, + reduce_output=True) + # 2d, work, reduce output, on subset + assert_laplacian(1, topo2, w2d, laplacian_func_2d, work2, + reduce_output=True, reduced=True) + + # 3d + assert_laplacian(3, topo3, w3d, laplacian_func) + # 3d work + assert_laplacian(3, topo3, w3d, laplacian_func, work3) + + # 3d reduced + assert_laplacian(3, topo3, w3d, laplacian_func, reduced=True) + # 3d reduced and work + assert_laplacian(3, topo3, w3d, laplacian_func, work3, + reduced=True) + # 3d reduce output + assert_laplacian(3, topo3, w3d, laplacian_func, reduce_output=True) + # 3d reduce output and work + assert_laplacian(3, topo3, w3d, laplacian_func, work3, + reduce_output=True) + # 3d, work, reduce output, on subset + assert_laplacian(3, topo3, w3d, laplacian_func, work3, + reduce_output=True, reduced=True) + + +def assert_grad_s(topo, scal, formula, work=None, reduced=False, + reduce_output=False): + # Reference field + ref = Field(domain=topo.domain, formula=formula, + name='Analytical', is_vector=True) + rd = ref.discretize(topo) + ref.initialize(topo=topo) + op, result = build_op(diffop.GradS, topo.domain.dimension, + topo, work, reduced, reduce_output) + ic_out = op.output_indices + ic_in = op.in_indices + result = op([scal], result) + # Numerical VS analytical + tol = np.max(topo.mesh.space_step ** 4) + for i in xrange(topo.domain.dimension): + assert np.allclose(rd[i][ic_in], result[i][ic_out], atol=tol) + + +def grad_s_func_2d(res, x, y, t): + res[0][...] = cos(x) * cos(y) + res[1][...] = -sin(x) * sin(y) + return res + + +def grad_s_func_3d(res, x, y, z, t): + res[0][...] = cos(x) * cos(y) * cos(z) + res[1][...] = -sin(x) * sin(y) * cos(z) + res[2][...] = -sin(x) * cos(y) * sin(z) + return res + + +def test_grad_s(): + # 2d + assert_grad_s(topo2, v2d[0], grad_s_func_2d) + # 2d work + assert_grad_s(topo2, v2d[0], grad_s_func_2d, work2) + # 2d reduced + assert_grad_s(topo2, v2d[0], grad_s_func_2d, reduced=True) + # 2d reduced and work + assert_grad_s(topo2, v2d[0], grad_s_func_2d, work2, + reduced=True) + # 2d reduce output + assert_grad_s(topo2, v2d[0], grad_s_func_2d, + reduce_output=True) + # 2d reduce output and work + assert_grad_s(topo2, v2d[0], grad_s_func_2d, work2, + reduce_output=True) + # 2d reduce output, on subset, with work + assert_grad_s(topo2, v2d[0], grad_s_func_2d, work2, + reduce_output=True, reduced=True) + # 3d + assert_grad_s(topo3, v3d[0], grad_s_func_3d) + # 3d work + assert_grad_s(topo3, v3d[0], grad_s_func_3d, work3) + # 3d reduced + assert_grad_s(topo3, v3d[0], grad_s_func_3d, reduced=True) + # 3d reduced and work + assert_grad_s(topo3, v3d[0], grad_s_func_3d, work3, + reduced=True) + # 3d reduce output + assert_grad_s(topo3, v3d[0], grad_s_func_3d, reduce_output=True) + # 3d reduce output and work + assert_grad_s(topo3, v3d[0], grad_s_func_3d, work3, + reduce_output=True) + # 3d, work, reduce output, on subset + assert_grad_s(topo3, v3d[0], grad_s_func_3d, work3, + reduce_output=True, reduced=True) + + +def assert_grad_v(topo, velo, formula, work=None, reduced=False, + reduce_output=False): + # Reference field + resdim = topo.domain.dimension ** 2 + ref = Field(domain=topo.domain, formula=formula, + name='Analytical', nb_components=resdim) + rd = ref.discretize(topo) + ref.initialize(topo=topo) + op, result = build_op(diffop.GradV, resdim, topo, work, reduced, + reduce_output) + result = op(velo.data, result) + ic_out = op.output_indices + ic_in = op.in_indices + # Numerical VS analytical + tol = np.max(topo.mesh.space_step ** 4) + for i in xrange(resdim): + assert np.allclose(rd[i][ic_in], result[i][ic_out], atol=tol) + + +def grad_v_func_2d(res, x, y, t): + res[0][...] = cos(x) * cos(y) + res[1][...] = -sin(x) * sin(y) + res[2][...] = sin(x) * sin(y) + res[3][...] = -cos(x) * cos(y) + return res + + +def grad_v_func_3d(res, x, y, z, t): + res[0][...] = cos(x) * cos(y) * cos(z) + res[1][...] = -sin(x) * sin(y) * cos(z) + res[2][...] = -sin(x) * cos(y) * sin(z) + res[3][...] = sin(x) * sin(y) * cos(z) + res[4][...] = - cos(x) * cos(y) * cos(z) + res[5][...] = cos(x) * sin(y) * sin(z) + res[6][...] = 0. + res[7][...] = 0. + res[8][...] = 0. + return res + + +def test_grad_v(): + # 2d + assert_grad_v(topo2, v2d, grad_v_func_2d) + # 2d work + assert_grad_v(topo2, v2d, grad_v_func_2d, work2) + # 2d reduced + assert_grad_v(topo2, v2d, grad_v_func_2d, reduced=True) + # 2d reduced and work + assert_grad_v(topo2, v2d, grad_v_func_2d, work2, + reduced=True) + # 2d reduce output + assert_grad_v(topo2, v2d, grad_v_func_2d, + reduce_output=True) + # 2d reduce output and work + assert_grad_v(topo2, v2d, grad_v_func_2d, work2, + reduce_output=True) + # 2d reduce output, on subset, with work + assert_grad_v(topo2, v2d, grad_v_func_2d, work2, + reduce_output=True, reduced=True) + # 3d + assert_grad_v(topo3, v3d, grad_v_func_3d) + # 3d work + assert_grad_v(topo3, v3d, grad_v_func_3d, work3) + # 3d reduced + assert_grad_v(topo3, v3d, grad_v_func_3d, reduced=True) + # 3d reduced and work + assert_grad_v(topo3, v3d, grad_v_func_3d, work3, + reduced=True) + # 3d reduce output + assert_grad_v(topo3, v3d, grad_v_func_3d, reduce_output=True) + # 3d reduce output and work + assert_grad_v(topo3, v3d, grad_v_func_3d, work3, + reduce_output=True) + # 3d, work, reduce output, on subset + assert_grad_v(topo3, v3d, grad_v_func_3d, work3, + reduce_output=True, reduced=True) + + +def assert_grad_vxw(topo, velo, vorti, work=None, reduced=False, + reduce_output=False): + # Reference field + ref = Field(domain=topo.domain, formula=analyticalGradVxW, + name='Analytical', is_vector=True) + rd = ref.discretize(topo) + ref.initialize(topo=topo) + op, result = build_op(diffop.GradVxW, velo.nb_components, topo, + work, reduced, reduce_output) + diag = npw.zeros(2) + result, diag = op(velo.data, vorti.data, result, diag) + # Numerical VS analytical + ic_out = op.output_indices + ic_in = op.in_indices + tol = np.max(topo3.mesh.space_step ** 2) + for i in xrange(topo.domain.dimension): + assert np.allclose(rd[i][ic_in], result[i][ic_out], atol=tol) + + +def test_grad_vxw(): + # 3d + assert_grad_vxw(topo3, v3d, w3d) + # 3d work + assert_grad_vxw(topo3, v3d, w3d, work3) + # 3d reduced + assert_grad_vxw(topo3, v3d, w3d, reduced=True) + # 3d reduced and work + assert_grad_vxw(topo3, v3d, w3d, work3, + reduced=True) + # 3d reduce output + assert_grad_vxw(topo3, v3d, w3d, reduce_output=True) + # 3d reduce output and work + assert_grad_vxw(topo3, v3d, w3d, work3, + reduce_output=True) + # 3d, work, reduce output, on subset + assert_grad_vxw(topo3, v3d, w3d, work3, + reduce_output=True, reduced=True) + + +# test for RHS of Pressure-Poisson equation +def assert_div_advection(topo, velo, work=None, reduced=False, + reduce_output=False): + # Reference scalar field + ref = Field(domain=topo.domain, formula=analyticalDivAdvection, + name='Analytical') + rd = ref.discretize(topo) + ref.initialize(topo=topo) + op, result = build_op(diffop.DivAdvection, 1, topo, work, reduced, + reduce_output) + result = op(velo.data, result) + ic_out = op.output_indices + ic_in = op.in_indices + + # Numerical VS analytical + tol = np.max(topo.mesh.space_step ** 4) + assert np.allclose(rd[0][ic_in], result[0][ic_out], rtol=tol) + + +def test_div_advection(): + # 3d + assert_div_advection(topo3, v3d) + # 3d work + assert_div_advection(topo3, v3d, work3) + # 3d reduced + assert_div_advection(topo3, v3d, reduced=True) + # 3d reduced and work + assert_div_advection(topo3, v3d, work3, + reduced=True) + # 3d reduce output + assert_div_advection(topo3, v3d, reduce_output=True) + # 3d reduce output and work + assert_div_advection(topo3, v3d, work3, + reduce_output=True) + # 3d, work, reduce output, on subset + assert_div_advection(topo3, v3d, work3, + reduce_output=True, reduced=True) + + +def assert_strain(topo, velo, formula, work=None, reduced=False, + reduce_output=False): + # Reference scalar field + resdim = topo.domain.dimension * (topo.domain.dimension + 1) / 2 + ref = Field(domain=topo.domain, formula=formula, + name='Analytical', nb_components=resdim) + rd = ref.discretize(topo) + ref.initialize(topo=topo) + op, result = build_op(diffop.Strain, resdim, topo, work, reduced, + reduce_output) + result = op(velo.data, result) + + ic_out = op.output_indices + ic_in = op.in_indices + # Numerical VS analytical + tol = np.max(topo.mesh.space_step ** 4) + for d in xrange(resdim): + assert np.allclose(rd[d][ic_in], result[d][ic_out], atol=tol) + + +def strain_f_2d(res, x, y, t): + res[0][...] = cos(x) * cos(y) + res[1][...] = - cos(x) * cos(y) + res[2][...] = 0. + return res + + +def strain_f_3d(res, x, y, z, t): + res[0][...] = cos(x) * cos(y) * cos(z) + res[1][...] = - cos(x) * cos(y) * cos(z) + res[2][...] = 0. + res[3][...] = 0. + res[4][...] = -0.5 * sin(x) * cos(y) * sin(z) + res[5][...] = 0.5 * cos(x) * sin(y) * sin(z) + return res + + +def test_strain(): + # 2d + assert_strain(topo2, v2d, strain_f_2d) + # 2d work + assert_strain(topo2, v2d, strain_f_2d, work2) + # 2d reduced + assert_strain(topo2, v2d, strain_f_2d, reduced=True) + # 2d reduced and work + assert_strain(topo2, v2d, strain_f_2d, work2, + reduced=True) + # 2d reduce output + assert_strain(topo2, v2d, strain_f_2d, + reduce_output=True) + # 2d reduce output and work + assert_strain(topo2, v2d, strain_f_2d, work2, + reduce_output=True) + # 2d reduce output, on subset, with work + assert_strain(topo2, v2d, strain_f_2d, work2, + reduce_output=True, reduced=True) + # 3d + assert_strain(topo3, v3d, strain_f_3d) + # 3d work + assert_strain(topo3, v3d, strain_f_3d, work3) + # 3d reduced + assert_strain(topo3, v3d, strain_f_3d, reduced=True) + # 3d reduced and work + assert_strain(topo3, v3d, strain_f_3d, work3, + reduced=True) + # 3d reduce output + assert_strain(topo3, v3d, strain_f_3d, reduce_output=True) + # 3d reduce output and work + assert_strain(topo3, v3d, strain_f_3d, work3, + reduce_output=True) + # 3d, work, reduce output, on subset + assert_strain(topo3, v3d, strain_f_3d, work3, + reduce_output=True, reduced=True) + + +def compute_max(field, ic): + """Compute max of sum_i (abs(field_i[ic])) for i in len(field) + """ + temp = npw.zeros_like(field[0]) + for d in xrange(len(field)): + temp[ic] += np.abs(field[d][ic]) + return np.max(temp) + + +def assert_strain_criteria(topo, velo, formula, work=None, reduced=False, + reduce_output=False): + # Reference scalar field + resdim = topo.domain.dimension * (topo.domain.dimension + 1) / 2 + ref = Field(domain=topo.domain, formula=formula, + name='Analytical', nb_components=resdim) + rd = ref.discretize(topo) + ref.initialize(topo=topo) + op, result = build_op(diffop.StrainCriteria, 0, topo, work, reduced, + reduce_output) + result = npw.zeros(topo.domain.dimension) + result = op(velo.data, result) + ic_in = op.in_indices + + dd = topo.domain.dimension + if dd == 3: + ll = [None, ] * dd + ll[0] = [rd[0], rd[3], rd[4]] + ll[1] = [rd[1], rd[3], rd[5]] + ll[2] = [rd[2], rd[4], rd[5]] + elif dd == 2: + ll = [None, ] * dd + ll[0] = [rd[0], rd[2]] + ll[1] = [rd[1], rd[2]] + + for d in xrange(dd): + refmax = compute_max(ll[d], ic_in) + assert np.allclose(refmax, result[d]) + + +def test_strain_criteria(): + # 2d + assert_strain_criteria(topo2, v2d, strain_f_2d) + # 2d work + assert_strain_criteria(topo2, v2d, strain_f_2d, work2) + # 2d reduced + assert_strain_criteria(topo2, v2d, strain_f_2d, reduced=True) + # 2d reduced and work + assert_strain_criteria(topo2, v2d, strain_f_2d, work2, + reduced=True) + # 2d reduce output + assert_strain_criteria(topo2, v2d, strain_f_2d, + reduce_output=True) + # 2d reduce output and work + assert_strain_criteria(topo2, v2d, strain_f_2d, work2, + reduce_output=True) + # 2d reduce output, on subset, with work + assert_strain_criteria(topo2, v2d, strain_f_2d, work2, + reduce_output=True, reduced=True) + # 3d + assert_strain_criteria(topo3, v3d, strain_f_3d) + # 3d work + assert_strain_criteria(topo3, v3d, strain_f_3d, work3) + # 3d reduced + assert_strain_criteria(topo3, v3d, strain_f_3d, reduced=True) + # 3d reduced and work + assert_strain_criteria(topo3, v3d, strain_f_3d, work3, + reduced=True) + # 3d reduce output + assert_strain_criteria(topo3, v3d, strain_f_3d, reduce_output=True) + # 3d reduce output and work + assert_strain_criteria(topo3, v3d, strain_f_3d, work3, + reduce_output=True) + # 3d, work, reduce output, on subset + assert_strain_criteria(topo3, v3d, strain_f_3d, work3, + reduce_output=True, reduced=True) + + +def assert_max_diag_grad(topo, velo, formula, work=None, reduced=False, + reduce_output=False): + # Reference field + ref = Field(domain=topo.domain, formula=formula, + name='Analytical', is_vector=True) + rd = ref.discretize(topo) + ref.initialize(topo=topo) + op, result = build_op(diffop.MaxDiagGradV, 0, topo, work, reduced, + reduce_output) + result = npw.zeros(topo.domain.dimension) + result = op(velo.data, result) + ic_in = op.in_indices + for d in xrange(topo.domain.dimension): + refmax = compute_max([rd[d]], ic_in) + assert np.allclose(refmax, result[d]) + + +def diag_grad_func_2d(res, x, y, t): + res[0][...] = cos(x) * cos(y) + res[1][...] = -cos(x) * cos(y) + return res + + +def diag_grad_func_3d(res, x, y, z, t): + res[0][...] = cos(x) * cos(y) * cos(z) + res[1][...] = - cos(x) * cos(y) * cos(z) + res[2][...] = 0. + return res + + +def test_max_diag_grad(): + # 2d + assert_max_diag_grad(topo2, v2d, diag_grad_func_2d) + # 2d work + assert_max_diag_grad(topo2, v2d, diag_grad_func_2d, work2) + # 2d reduced + assert_max_diag_grad(topo2, v2d, diag_grad_func_2d, reduced=True) + # 2d reduced and work + assert_max_diag_grad(topo2, v2d, diag_grad_func_2d, work2, + reduced=True) + # 2d reduce output + assert_max_diag_grad(topo2, v2d, diag_grad_func_2d, + reduce_output=True) + # 2d reduce output and work + assert_max_diag_grad(topo2, v2d, diag_grad_func_2d, work2, + reduce_output=True) + # 2d reduce output, on subset, with work + assert_max_diag_grad(topo2, v2d, diag_grad_func_2d, work2, + reduce_output=True, reduced=True) + # 3d + assert_max_diag_grad(topo3, v3d, diag_grad_func_3d) + # 3d work + assert_max_diag_grad(topo3, v3d, diag_grad_func_3d, work3) + # 3d reduced + assert_max_diag_grad(topo3, v3d, diag_grad_func_3d, reduced=True) + # 3d reduced and work + assert_max_diag_grad(topo3, v3d, diag_grad_func_3d, work3, + reduced=True) + # 3d reduce output + assert_max_diag_grad(topo3, v3d, diag_grad_func_3d, reduce_output=True) + # 3d reduce output and work + assert_max_diag_grad(topo3, v3d, diag_grad_func_3d, work3, + reduce_output=True) + # 3d, work, reduce output, on subset + assert_max_diag_grad(topo3, v3d, diag_grad_func_3d, work3, + reduce_output=True, reduced=True) + + +def assert_stretch_like(topo, velo, formula, work=None, reduced=False, + reduce_output=False): + # Reference scalar field + resdim = topo.domain.dimension ** 2 + ref = Field(domain=topo.domain, formula=formula, + name='Analytical', nb_components=resdim) + rd = ref.discretize(topo) + ref.initialize(topo=topo) + op, result = build_op(diffop.StretchLike, 0, topo, work, reduced, + reduce_output) + result = npw.zeros(topo.domain.dimension) + result = op(velo.data, result) + ic_in = op.in_indices + + dd = topo.domain.dimension + for d in xrange(dd): + pos = d * dd + refmax = compute_max(rd[pos: pos + dd], ic_in) + assert np.allclose(refmax, result[d]) + + +def test_stretch_like(): + # 2d + assert_stretch_like(topo2, v2d, grad_v_func_2d) + # 2d work + assert_stretch_like(topo2, v2d, grad_v_func_2d, work2) + # 2d reduced + assert_stretch_like(topo2, v2d, grad_v_func_2d, reduced=True) + # 2d reduced and work + assert_stretch_like(topo2, v2d, grad_v_func_2d, work2, + reduced=True) + # 2d reduce output + assert_stretch_like(topo2, v2d, grad_v_func_2d, + reduce_output=True) + # 2d reduce output and work + assert_stretch_like(topo2, v2d, grad_v_func_2d, work2, + reduce_output=True) + # 2d reduce output, on subset, with work + assert_stretch_like(topo2, v2d, grad_v_func_2d, work2, + reduce_output=True, reduced=True) + # 3d + assert_stretch_like(topo3, v3d, grad_v_func_3d) + # 3d work + assert_stretch_like(topo3, v3d, grad_v_func_3d, work3) + # 3d reduced + assert_stretch_like(topo3, v3d, grad_v_func_3d, reduced=True) + # 3d reduced and work + assert_stretch_like(topo3, v3d, grad_v_func_3d, work3, + reduced=True) + # 3d reduce output + assert_stretch_like(topo3, v3d, grad_v_func_3d, reduce_output=True) + # 3d reduce output and work + assert_stretch_like(topo3, v3d, grad_v_func_3d, work3, + reduce_output=True) + # 3d, work, reduce output, on subset + assert_stretch_like(topo3, v3d, grad_v_func_3d, work3, + reduce_output=True, reduced=True) + + +def assert_diag_and_stretch(topo, velo, formula, work=None, reduced=False, + reduce_output=False): + # Reference scalar field + resdim = topo.domain.dimension ** 2 + ref = Field(domain=topo.domain, formula=formula, + name='Analytical', nb_components=resdim) + rd = ref.discretize(topo) + ref.initialize(topo=topo) + op, result = build_op(diffop.DiagAndStretch, 0, topo, work, reduced, + reduce_output) + result = npw.zeros(2 * topo.domain.dimension) + result = op(velo.data, result) + ic_in = op.in_indices + + dd = topo.domain.dimension + ll = [rd[i * (dd + 1)] for i in xrange(dd)] + + for d in xrange(dd): + refmax = compute_max([ll[d]], ic_in) + assert np.allclose(refmax, result[d]) + pos = d * dd + refmax = compute_max(rd[pos: pos + dd], ic_in) + assert np.allclose(refmax, result[d + dd]) + + +def test_diag_and_stretch(): + # 2d + assert_diag_and_stretch(topo2, v2d, grad_v_func_2d) + # 2d work + assert_diag_and_stretch(topo2, v2d, grad_v_func_2d, work2) + # 2d reduced + assert_diag_and_stretch(topo2, v2d, grad_v_func_2d, reduced=True) + # 2d reduced and work + assert_diag_and_stretch(topo2, v2d, grad_v_func_2d, work2, + reduced=True) + # 2d reduce output + assert_diag_and_stretch(topo2, v2d, grad_v_func_2d, + reduce_output=True) + # 2d reduce output and work + assert_diag_and_stretch(topo2, v2d, grad_v_func_2d, work2, + reduce_output=True) + # 2d reduce output, on subset, with work + assert_diag_and_stretch(topo2, v2d, grad_v_func_2d, work2, + reduce_output=True, reduced=True) + # 3d + assert_diag_and_stretch(topo3, v3d, grad_v_func_3d) + # # 3d work + assert_diag_and_stretch(topo3, v3d, grad_v_func_3d, work3) + # 3d reduced + assert_diag_and_stretch(topo3, v3d, grad_v_func_3d, reduced=True) + # 3d reduced and work + assert_diag_and_stretch(topo3, v3d, grad_v_func_3d, work3, + reduced=True) + # 3d reduce output + assert_diag_and_stretch(topo3, v3d, grad_v_func_3d, reduce_output=True) + # 3d reduce output and work + assert_diag_and_stretch(topo3, v3d, grad_v_func_3d, work3, + reduce_output=True) + # 3d, work, reduce output, on subset + assert_diag_and_stretch(topo3, v3d, grad_v_func_3d, work3, + reduce_output=True, reduced=True) + + +def assert_strain_stretch_criteria(topo, velo, formula1, formula2, work=None, + reduced=False, reduce_output=False): + # Reference scalar field + resdim = topo.domain.dimension * (topo.domain.dimension + 1) / 2 + graddim = topo.domain.dimension ** 2 + refstrain = Field(domain=topo.domain, formula=formula2, + name='Strain', nb_components=resdim) + refgrad = Field(domain=topo.domain, formula=formula1, + name='Gradl', nb_components=graddim) + + rd = refstrain.discretize(topo) + gd = refgrad.discretize(topo) + refstrain.initialize(topo) + refgrad.initialize(topo=topo) + op, result = build_op(diffop.StrainAndStretch, 0, topo, work, reduced, + reduce_output) + result = npw.zeros(2 * topo.domain.dimension) + result = op(velo.data, result) + ic_in = op.in_indices + + dd = topo.domain.dimension + if dd == 3: + ll = [None, ] * dd + ll[0] = [rd[0], rd[3], rd[4]] + ll[1] = [rd[1], rd[3], rd[5]] + ll[2] = [rd[2], rd[4], rd[5]] + elif dd == 2: + ll = [None, ] * dd + ll[0] = [rd[0], rd[2]] + ll[1] = [rd[1], rd[2]] + + if dd == 3: + for d in xrange(dd): + refmaxstr = compute_max(ll[d], ic_in) + assert np.allclose(refmaxstr, result[d]) + pos = d * dd + refmaxgrad = compute_max(gd[pos: pos + dd], ic_in) + assert np.allclose(refmaxgrad, result[d + dd]) + + +def test_strain_stretch(): + # 2d + assert_strain_stretch_criteria(topo2, v2d, grad_v_func_2d, strain_f_2d) + # 2d work + assert_strain_stretch_criteria(topo2, v2d, grad_v_func_2d, strain_f_2d, + work2) + # 2d reduced + assert_strain_stretch_criteria(topo2, v2d, grad_v_func_2d, strain_f_2d, + reduced=True) + # 2d reduced and work + assert_strain_stretch_criteria(topo2, v2d, grad_v_func_2d, strain_f_2d, + work2, reduced=True) + # 2d reduce output + assert_strain_stretch_criteria(topo2, v2d, grad_v_func_2d, strain_f_2d, + reduce_output=True) + # 2d reduce output and work + assert_strain_stretch_criteria(topo2, v2d, grad_v_func_2d, strain_f_2d, + work2, reduce_output=True) + # 2d reduce output, on subset, with work + assert_strain_stretch_criteria(topo2, v2d, grad_v_func_2d, strain_f_2d, + work2, reduce_output=True, reduced=True) + # 3d + assert_strain_stretch_criteria(topo3, v3d, grad_v_func_3d, strain_f_3d) + # 3d work + assert_strain_stretch_criteria(topo3, v3d, grad_v_func_3d, strain_f_3d, + work3) + # 3d reduced + assert_strain_stretch_criteria(topo3, v3d, grad_v_func_3d, strain_f_3d, + reduced=True) + # 3d reduced and work + assert_strain_stretch_criteria(topo3, v3d, grad_v_func_3d, strain_f_3d, + work3, reduced=True) + # 3d reduce output + assert_strain_stretch_criteria(topo3, v3d, grad_v_func_3d, strain_f_3d, + reduce_output=True) + # 3d reduce output and work + assert_strain_stretch_criteria(topo3, v3d, grad_v_func_3d, strain_f_3d, + work3, reduce_output=True) + # 3d, work, reduce output, on subset + assert_strain_stretch_criteria(topo3, v3d, grad_v_func_3d, strain_f_3d, + work3, reduce_output=True, reduced=True) + +if __name__ == "__main__": + test_curl() + test_div_rho_v() + test_div_w_v() + test_laplacian() + test_grad_s() + test_grad_v() + test_grad_vxw() + test_div_advection() + test_strain() + test_strain_criteria() + test_max_diag_grad() + test_stretch_like() + test_diag_and_stretch() + test_strain_criteria() + test_strain_stretch() -- GitLab