From 784600301a595a7114378c07d7b7c31a9fae2261 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Keck <Jean-Baptiste.Keck@imag.fr> Date: Sun, 14 Apr 2019 16:06:38 +0200 Subject: [PATCH] broken advection --- .../backend/device/codegen/base/variables.py | 10 +- .../device/codegen/functions/polynomial.py | 2 +- .../codegen/kernels/directional_advection.py | 478 ++++++++---------- .../autotunable_kernels/advection_dir.py | 52 +- .../operator/directional/advection_dir.py | 7 +- .../operator/directional/advection_dir.py | 5 +- hysop/fields/cartesian_discrete_field.py | 16 +- hysop/fields/continuous_field.py | 14 +- hysop/numerics/interpolation/polynomial.py | 3 +- hysop/operator/base/advection_dir.py | 55 +- .../operator/tests/test_bilevel_advection.py | 30 +- .../tests/test_directional_advection.py | 38 +- hysop/tools/parameters.py | 2 +- 13 files changed, 348 insertions(+), 364 deletions(-) diff --git a/hysop/backend/device/codegen/base/variables.py b/hysop/backend/device/codegen/base/variables.py index e10575633..2e84e7fae 100644 --- a/hysop/backend/device/codegen/base/variables.py +++ b/hysop/backend/device/codegen/base/variables.py @@ -528,11 +528,11 @@ class CodegenArray(CodegenVariable): _svalue = [None]*s0 for d in xrange(s0): _name = '{}_{}'.format(name, d) - _value = value[d] - _svalue = svalue[d] - val, sval = CodegenArray.initialize_rec(_name, typegen, + dvalue = value[d] + dsvalue = svalue[d] + val, sval = CodegenArray._initialize_rec(_name, typegen, storage, ctype, const, volatile, - _shape, _sshape, _value, _svalue, + _shape, _sshape, dvalue, dsvalue, _ptr_level, _ptr_restrict, _ptr_const, _ptr_volatile, symbolic_mode) var = CodegenArray(name=_name, typegen=typegen, @@ -1031,7 +1031,7 @@ class CodegenStruct(CodegenVariable): if __name__ == '__main__': - from hysop.backend.device.codegen.base.test import test_typegen + from hysop.backend.device.codegen.base.test import _test_typegen as test_typegen tg = test_typegen('float') print':: ARRAYS ::' diff --git a/hysop/backend/device/codegen/functions/polynomial.py b/hysop/backend/device/codegen/functions/polynomial.py index 1cb592148..78b1a689d 100644 --- a/hysop/backend/device/codegen/functions/polynomial.py +++ b/hysop/backend/device/codegen/functions/polynomial.py @@ -95,7 +95,7 @@ if __name__ == '__main__': tg = _test_typegen('float') pf = PolynomialFunction(tg,'float',4, - [0,1,2,3,4,0,0,0,5,6,7,8,9,0], 'test_poly', True) + [0,1,2,3,4,0,0,0,5,6,7,8,9,0], 'test_poly', 'x', True) pf.edit() print pf.per_work_statistics() diff --git a/hysop/backend/device/codegen/kernels/directional_advection.py b/hysop/backend/device/codegen/kernels/directional_advection.py index 43bef696b..aba77d14c 100644 --- a/hysop/backend/device/codegen/kernels/directional_advection.py +++ b/hysop/backend/device/codegen/kernels/directional_advection.py @@ -1,4 +1,5 @@ import contextlib +import itertools as it from contextlib import contextmanager from hysop.deps import math, operator, hashlib @@ -32,6 +33,7 @@ from hysop.backend.device.codegen.functions.runge_kutta import RungeKuttaFunc from hysop.backend.device.codegen.functions.advection_rhs import DirectionalAdvectionRhsFunction from hysop.numerics.odesolvers.runge_kutta import ExplicitRungeKutta +from hysop.numerics.interpolation.polynomial import PolynomialInterpolator, PolynomialSubgridInterpolator from hysop.backend.device.opencl import cl, clCharacterize from hysop.backend.device.opencl.opencl_env import OpenClEnvironment @@ -43,41 +45,69 @@ from hysop.fields.discrete_field import DiscreteScalarFieldView class DirectionalAdvectionKernelGenerator(KernelCodeGenerator): @staticmethod - def codegen_name(ftype, is_cached, rk_scheme, nparticles, min_ghosts, - relative_velocity, bilevel, **kargs): + def codegen_name(ftype, is_cached, rk_scheme, nparticles, cache_ghosts, + relative_velocity, bilevel, interpolator, **kargs): cache = '' if is_cached: - cache = 'cached' - if bilevel is not None: - cache += str(bilevel[-1]) - cache += '_' - return 'directional_{}advection_{}_{}{}p_{}g__{}'.format(cache,rk_scheme.name(), - ftype[0],nparticles,min_ghosts,abs(hash(relative_velocity))) + cache = 'cached_' + if (bilevel is not None): + bilevel='' + if interpolator: + bilevel += ['linear','cubic','quintic','septic','nonic','unknown'][min((interpolator.interpolator.deg[0]-1)//2, 5)] + bilevel += '_fdc{}'.format(interpolator.interpolator.fd[0]) + else: + bilevel += 'legacy_linear' + bilevel+='_bilevel_' + else: + bilevel='' + return '{}directional_{}advection_{}_{}{}p_{}g__{}'.format( + bilevel, cache, + rk_scheme.name(), + ftype[0], nparticles, cache_ghosts, + abs(hash(relative_velocity))) def __init__(self, typegen, work_dim, ftype, is_cached, rk_scheme, vboundary, nparticles, relative_velocity, + velocity_cfl, grid_ratio, offset_by_xmin = False, - min_ghosts = 0, use_short_circuit = None, unroll_loops = None, symbolic_mode = False, debug_mode = False, tuning_mode = False, known_vars = None, - is_bilevel = None): - + polynomial_interpolator = None): # <= overrides legacy linear interpolator with custom interpolator for known grid ratios assert work_dim>0 and work_dim<=3 assert nparticles in [1,2,4,8,16] assert isinstance(relative_velocity, (str,float)) check_instance(vboundary[0],BoundaryCondition) check_instance(vboundary[1],BoundaryCondition) check_instance(rk_scheme, ExplicitRungeKutta) - - if (is_bilevel is not None) and (nparticles>1): - msg='Bilevel support with multiple particles at a time has not been implemented yet.' - raise NotImplementedError(msg) + check_instance(grid_ratio, np.ndarray, minval=1.0) + check_instance(polynomial_interpolator, PolynomialSubgridInterpolator, allow_none=True) + + assert grid_ratio.dtype in (np.float32, np.float64) + grid_ratio = grid_ratio[::-1] # <= we pass from hysop representation ZYX to opencl XYZ + inverse_grid_ratio = 1.0/grid_ratio + integer_grid_ratio = np.ceil(grid_ratio).astype(np.int32) + + is_bilevel = (grid_ratio>1.0).any() + velocity_cache_ghosts = self.advection_cache_ghosts(velocity_cfl, grid_ratio[0]) + + if is_bilevel and (is_cached is False): + msg='Bilevel advection only works if cache is enabled.' + raise RuntimeError(msg) + + if (polynomial_interpolator is not None): + if is_bilevel: + print 'LOL' + assert (integer_grid_ratio == grid_ratio).all() + assert (integer_grid_ratio[::-1] == polynomial_interpolator.gr).all() + else: + msg='A polynomial_interpolator has been supplied but without specifying grid_ratio.' + raise RuntimeError(msg) known_vars = known_vars or dict() @@ -91,9 +121,9 @@ class DirectionalAdvectionKernelGenerator(KernelCodeGenerator): is_periodic = (vboundary[0]==BoundaryCondition.PERIODIC \ and vboundary[1]==BoundaryCondition.PERIODIC) - assert (is_periodic and not is_cached) or min_ghosts>0 + assert (is_periodic and not is_cached) or velocity_cache_ghosts>0 - cache_size_known = ('local_size' in known_vars or is_bilevel is not None) + cache_size_known = ('local_size' in known_vars) _global = OpenClCodeGenerator.default_keywords['global'] _local = OpenClCodeGenerator.default_keywords['local'] @@ -108,8 +138,8 @@ class DirectionalAdvectionKernelGenerator(KernelCodeGenerator): itype = 'int' name = DirectionalAdvectionKernelGenerator.codegen_name( - ftype, is_cached, rk_scheme, nparticles, min_ghosts, - relative_velocity, is_bilevel) + ftype, is_cached, rk_scheme, nparticles, velocity_cache_ghosts, + relative_velocity, is_bilevel, polynomial_interpolator) kernel_reqs = self.build_requirements( typegen, work_dim, itype, ftype, is_cached, @@ -143,34 +173,42 @@ class DirectionalAdvectionKernelGenerator(KernelCodeGenerator): self.is_periodic = is_periodic self.is_cached = is_cached self.is_bilevel = is_bilevel - self.min_ghosts = min_ghosts + self.grid_ratio = grid_ratio + self.integer_grid_ratio = integer_grid_ratio + self.inverse_grid_ratio = inverse_grid_ratio + self.velocity_cache_ghosts = velocity_cache_ghosts self.tuning_mode = tuning_mode self.offset_by_xmin = offset_by_xmin self.relative_velocity = relative_velocity self.use_short_circuit = use_short_circuit self.unroll_loops = unroll_loops + self.polynomial_interpolator = polynomial_interpolator self.gencode() + self.edit() @classmethod - def advec_ghosts(cls, velocity_cfl): - """Return the minimal numbers of ghosts required on the lasr axe of the velocity grid.""" - return int(1+math.floor(velocity_cfl)) + def advection_ghosts(cls, velocity_cfl): + """Return the minimal numbers of ghosts required on the last axe of the velocity grid.""" + assert velocity_cfl > 0.0, 'cfl <= 0.0' + return int(math.floor(velocity_cfl)) @classmethod - def min_ghosts(cls, work_dim, velocity_cfl): - """Return the minimal numbers of ghosts required on the velocity grid.""" - assert velocity_cfl > 0.0, 'cfl <= 0.0' - ghosts = [0]*work_dim - ghosts[-1] = cls.advec_ghosts(velocity_cfl) - return np.asarray(ghosts, dtype=np.int32) + def advection_cache_ghosts(cls, velocity_cfl, grid_ratio): + assert isinstance(grid_ratio, float) + assert grid_ratio >= 1.0 + linear_interpolation_ghosts = 1 + return linear_interpolation_ghosts+int(math.floor(grid_ratio*velocity_cfl)) @classmethod - def min_wg_size(cls, work_dim, velocity_cfl): - """Return the minimum workgroup size.""" - ghosts = cls.min_ghosts(work_dim, velocity_cfl) - return np.asarray(2*ghosts+1, dtype=np.int32).copy() + def min_velocity_ghosts(cls, work_dim, velocity_cfl, grid_ratio, periodicity): + """Return the minimal numbers of ghosts required on the velocity grid, includint interpolation ghosts.""" + interpolation_ghosts = 1 + ghosts = periodicity * (grid_ratio > 1.0) + ghosts += interpolation_ghosts + ghosts[-1] += cls.advection_ghosts(velocity_cfl) + return np.asarray(ghosts, dtype=np.int32) def required_workgroup_cache_size(self, local_work_size): """Return a tuple of required (static,dynamic,total) cache bytes per workgroup.""" @@ -183,8 +221,8 @@ class DirectionalAdvectionKernelGenerator(KernelCodeGenerator): sc,dc = 0,0 if is_cached: - count = self.nparticles*local_work_size[0]+2*self.min_ghosts - if 'local_size' in self.known_vars: + count = self.nparticles*local_work_size[0]+2*self.velocity_cache_ghosts + if ('local_size' in self.known_vars): assert (self.known_vars['local_size'] == local_work_size[:work_dim]).all() sc += count else: @@ -196,13 +234,6 @@ class DirectionalAdvectionKernelGenerator(KernelCodeGenerator): return (sc,dc,tc) - def required_workgroup_velocity_cache_size(self): - ftype = self.ftype - flt_bytes = self.typegen.FLT_BYTES[ftype] - c = self.is_bilevel[-1]+2*self.min_ghosts - c *= flt_bytes - return c - def build_requirements(self,typegen,work_dim,itype,ftype,is_cached,rk_scheme, vboundary,relative_velocity,nparticles,force_symbolic,storage,is_periodic,known_vars): tg=typegen @@ -216,20 +247,12 @@ class DirectionalAdvectionKernelGenerator(KernelCodeGenerator): mesh_info_struct = MeshInfoStruct(typegen=typegen, vsize=vsize) reqs['MeshInfoStruct'] = mesh_info_struct - field_names = ('V','P') - global_field_infos = GlobalFieldInfos(typegen=typegen, field_names=field_names, - workdim=work_dim, vsize=nparticles) - reqs['GlobalFieldInfos'] = global_field_infos - - self.field_names = field_names - self.field_infos = global_field_infos.build_codegen_variable(name='field_infos') - advection_rhs = DirectionalAdvectionRhsFunction(typegen=typegen, work_dim=work_dim, ftype=ftype, is_cached=is_cached, boundary=vboundary[0], nparticles=nparticles, relative_velocity=relative_velocity, ptr_restrict=True, - itype=itype, field_infos=self.field_infos) + itype=itype) used_vars = RungeKuttaFunction._default_used_vars.copy() used_vars['y']='X' @@ -291,9 +314,10 @@ class DirectionalAdvectionKernelGenerator(KernelCodeGenerator): vboundary = s.vboundary storage = s.storage nparticles = s.nparticles - min_ghosts = s.min_ghosts - field_infos = s.field_infos + velocity_cache_ghosts = s.velocity_cache_ghosts tuning_mode = s.tuning_mode + is_bilevel = s.is_bilevel + polynomial_interpolator = s.polynomial_interpolator symbolic_mode = s.symbolic_mode use_short_circuit = s.use_short_circuit @@ -322,44 +346,43 @@ class DirectionalAdvectionKernelGenerator(KernelCodeGenerator): velocity, velocity_strides = s.velocity, s.velocity_strides position, position_strides = s.position, s.position_strides - has_bilevel = (self.is_bilevel is not None) - - if has_bilevel: - assert is_cached and cache_size_known, \ - "In bilevel, velocity must be cached " + str(is_cached) + " " + str(cache_size_known) - - compute_grid_size = position_mesh_info['local_mesh']['compute_resolution'].view( + p_grid_size = position_mesh_info['local_mesh']['resolution'].view( + 'p_grid_size', slice(0, work_dim), const=True) + p_compute_grid_size = position_mesh_info['local_mesh']['compute_resolution'].view( 'p_compute_grid_size', slice(0, work_dim), const=True) - if has_bilevel: - v_grid_size = velocity_mesh_info['local_mesh']['resolution'].view( + p_grid_ghosts = position_mesh_info['ghosts'].view( + 'p_grid_ghosts', slice(0,work_dim), const=True) + p_dx = position_mesh_info['dx'].view('p_dx', slice(0,work_dim), const=True) + p_inv_dx = position_mesh_info['inv_dx'].view('p_inv_dx', slice(0,work_dim), const=True) + + v_grid_size = velocity_mesh_info['local_mesh']['resolution'].view( 'v_grid_size', slice(0, work_dim), const=True) - - P_grid_ghosts = position_mesh_info['ghosts'].view( - 'P_grid_ghosts', slice(0,work_dim), const=True) - V_grid_ghosts = velocity_mesh_info['ghosts'].view( - 'V_grid_ghosts', slice(0,work_dim), const=True) - - dx = position_mesh_info['dx'].view('p_dx', slice(0,work_dim), const=True) - inv_dx = position_mesh_info['inv_dx'].view('p_inv_dx', slice(0,work_dim), const=True) + v_compute_grid_size = velocity_mesh_info['local_mesh']['compute_resolution'].view( + 'v_compute_grid_size', slice(0, work_dim), const=True) + v_grid_ghosts = velocity_mesh_info['ghosts'].view( + 'v_grid_ghosts', slice(0,work_dim), const=True) v_dx = velocity_mesh_info['dx'].view('v_dx', slice(0,work_dim), const=True) v_inv_dx = velocity_mesh_info['inv_dx'].view('v_inv_dx', slice(0,work_dim), const=True) - xmin = CodegenVariable(name='xmin', ctype=ftype, typegen=tg, const=True, + p_xmin = CodegenVariable(name='p_xmin', ctype=ftype, typegen=tg, const=True, init='{} + {}*{}'.format(position_mesh_info['local_mesh']['xmin'][0], - P_grid_ghosts[0], dx[0])) + p_grid_ghosts[0], p_dx[0])) + v_xmin = CodegenVariable(name='v_xmin', ctype=ftype, typegen=tg, const=True, + init='{} + {}*{}'.format(velocity_mesh_info['local_mesh']['xmin'][0], + v_grid_ghosts[0], v_dx[0])) position_gid = CodegenVectorClBuiltin('P_gid', itype, work_dim, typegen=tg) velocity_gid = CodegenVectorClBuiltin('V_gid', itype, work_dim, typegen=tg) - - if has_bilevel: - velocity_gid_pos = CodegenVectorClBuiltin('V_pos', ftype, work_dim, typegen=tg) + if is_bilevel: + velocity_p = CodegenVectorClBuiltin('V_pos', ftype, work_dim, typegen=tg) velocity_h = CodegenVectorClBuiltin('Vh', ftype, work_dim, typegen=tg) - velocity_ix = CodegenVariable(name='V_gid_x', ctype=itype, typegen=tg, const=True) - line_offset_for_v = CodegenVariable(name='line_offset_for_v', ctype=itype, typegen=tg, const=False) runge_kutta = self.reqs['runge_kutta'] - - advec_ghosts = CodegenVariable('V_advec_ghosts', itype, typegen=tg, value=min_ghosts) + + grid_ratio = CodegenVectorClBuiltin('grid_ratio', ftype, work_dim, typegen=tg, value=self.grid_ratio) + igrid_ratio = CodegenVectorClBuiltin('igrid_ratio', ftype, work_dim, typegen=tg, value=self.inverse_grid_ratio) + V_cache_ghosts = CodegenVariable('V_cache_ghosts', itype, typegen=tg, init=velocity_cache_ghosts) + line_offset = CodegenVariable(name='line_offset', ctype=itype, typegen=tg, const=True) line_work = CodegenVariable(name='line_work', ctype=itype, typegen=tg, const=True) @@ -379,16 +402,8 @@ class DirectionalAdvectionKernelGenerator(KernelCodeGenerator): V_cache_width = CodegenVariable('V_cache_width', itype, typegen=tg, const=True) if cache_size_known: - if has_bilevel: - # if bilevel, velocity cache is the entire local line - # WARNING : is_bilevel is HySoP velocity resolution (using transposition state) - # whereas 'local_size' is a OpenCL size - # if bilevel, velocity cache is the entire local line - L = s.is_bilevel[-1] - velocity_cache_full_length = True - else: - L = s.known_vars['local_size'][0] - Vc_shape = (nparticles*L+2*min_ghosts,) + L = s.known_vars['local_size'][0] + Vc_shape = (nparticles*L+2*velocity_cache_ghosts,) Vc = CodegenArray(name='Vc',dim=1,ctype=ftype,typegen=tg, shape=Vc_shape, storage='__local') del L @@ -403,7 +418,16 @@ class DirectionalAdvectionKernelGenerator(KernelCodeGenerator): kmax = CodegenVariable('kmax', itype, tg, const=True, init='({Sx}+{npart}*{Lx}-1)/({npart}*{Lx})'.format( - Sx=compute_grid_size[0], npart=npart(),Lx=local_size[0])) + Sx=p_compute_grid_size[0], npart=npart(),Lx=local_size[0])) + + if polynomial_interpolator: + pi = polynomial_interpolator + weights = polynomial_interpolator.Wr.reshape(pi.gr+pi.n) + interpolation_weights = CodegenArray(name='interpolation_weights',ctype=ftype, dim=2*work_dim, + value=weights, typegen=tg, storage='__constant', const=True) + widx = CodegenVectorClBuiltin('widx', itype, work_dim, typegen=tg) + with self._codeblock_('global_scope_constants'): + interpolation_weights.declare(s) @contextmanager def _work_iterate_(i): @@ -415,9 +439,9 @@ class DirectionalAdvectionKernelGenerator(KernelCodeGenerator): else: fval = global_id[i] gsize = global_size[i] - N = '{Sx}'.format(Sx=compute_grid_size[i]) - position_ghosts = P_grid_ghosts[i] - velocity_ghosts = V_grid_ghosts[i] + N = '{Sx}'.format(Sx=p_compute_grid_size[i]) + position_ghosts = p_grid_ghosts[i] + velocity_ghosts = v_grid_ghosts[i] with s._for_('int {i}={fval}; {i}<{N}; {i}+={gsize}'.format( i='kji'[i], fval=fval, gsize=gsize,N=N), @@ -429,37 +453,25 @@ class DirectionalAdvectionKernelGenerator(KernelCodeGenerator): is_active.declare(al, align=True, init='({k}*{ls}+{lid} <= ({gs}+{npart}-1)/{npart})'.format( npart=npart, lid=local_id[0], k='kji'[0], - gs=compute_grid_size[0], ls=local_size[0])) + gs=p_compute_grid_size[0], ls=local_size[0])) s.jumpline() with s._align_() as al: line_offset.declare(al, align=True, init='{}*{}*{}'.format( 'kji'[0], local_size[0], npart)) line_work.declare(al, align=True, init='({} ? {}-{} : {}*{})'.format( - is_last, compute_grid_size[0], line_offset, local_size[0], npart)) - if is_cached and not has_bilevel: + is_last, p_compute_grid_size[0], line_offset, local_size[0], npart)) + if is_cached: al.jumpline() V_cache_width.declare(al, align=True, init='{} + 2*{}'.format( - line_work, advec_ghosts)) - s.jumpline() - - for k in s.field_names: - v = field_infos[k] - v0 = '{}.idx.IX'.format(v) - v1 = '{}.pos.X'.format(v) - mi = s.vars['{}_mesh_info'.format(k)] - s.append('{} = {} + {} + {};'.format(v0, mi['start'][0], line_offset, - poffset)) - s.append('{} = {} + convert_{}({})*{};'.format(v1, - mi['global_mesh']['xmin'][0], - pvtype, v0, mi['dx'][0])) + line_work, V_cache_ghosts)) s.jumpline() position_gid.affect(i=0, codegen=s, init='{} + {}'.format( line_offset, position_ghosts)) - if not has_bilevel: + if not is_bilevel: velocity_gid.affect(i=0, codegen=s, init='{} + {} - {}'.format( - line_offset, velocity_ghosts, advec_ghosts)) + line_offset, velocity_ghosts, V_cache_ghosts)) line_velocity_offset = ' + '.join('{}*{}'.format(velocity_gid[j], velocity_strides[j]) for j in xrange(work_dim-1, -1, -1)) @@ -468,74 +480,18 @@ class DirectionalAdvectionKernelGenerator(KernelCodeGenerator): with s._align_() as al: line_position.declare(al, init='{} + {}'.format(position, line_position_offset), align=True) - if (s.is_bilevel is None): + if not is_bilevel: line_velocity.declare(al, init='{} + {}'.format(velocity, line_velocity_offset), align=True) else: - for k in s.field_names: - v = field_infos[k] - d = DirectionLabels[i] - v0 = '{}.idx.I{}'.format(v, d) - v1 = '{}.pos.{}'.format(v, d) - mi = s.vars['{}_mesh_info'.format(k)] - s.append('{} = {} + {};'.format(v0, mi['start'][i], 'kji'[i])) - s.append('{} = {} + {}*{};'.format(v1, mi['global_mesh']['xmin'][i], - v0, mi['dx'][i])) - s.jumpline() - s.append('{} = {} + {};'.format(position_gid[i], 'kji'[i], position_ghosts)) - if has_bilevel: - s.append('{} = ({} * {}) * {};'.format( - velocity_gid_pos[i], 'kji'[i], dx[i], v_inv_dx[i])) - s.append('{} = convert_int_rtn({});'.format(velocity_gid[i], velocity_gid_pos[i])) - s.append('{} = {} - convert_{}({});'.format( - velocity_h[i], velocity_gid_pos[i], part_ftype, velocity_gid[i])) - if i==1: - if work_dim==3: - line_velocity_offset = '({}+{})*{}+({}+{})*{}+{}-{}'.format( - velocity_gid[2],V_grid_ghosts[2], velocity_strides[2], - velocity_gid[1],V_grid_ghosts[1], velocity_strides[1], - V_grid_ghosts[0], advec_ghosts) - elif work_dim==2: - line_velocity_offset = '({}+{})*{}+{}-{}'.format( - velocity_gid[1],V_grid_ghosts[1], velocity_strides[1], - V_grid_ghosts[0], advec_ghosts) - s.jumpline() - with s._align_() as al: - line_velocity.declare(al, init='{} + {}'.format(velocity, line_velocity_offset), align=True) - line_offset_for_v.declare(al, init='0') - s.jumpline() - if velocity_cache_full_length: - s.comment("Load velocity in cache with linear interpolation") - with s._for_('int {i}={fval}; {i}<{N}; {i}+={gsize}'.format( - i=velocity_ix, fval=local_id[0], gsize=local_size[0],N=Vc_shape[0]), - unroll=False): - is_first = True - if work_dim==3: - for ii,jj in [(ii,jj) for ii in range(2) for jj in range(2)]: - s.append('{}[{}] {} ({}{})*({}{})*{};'.format( - Vc, velocity_ix, " =" if is_first else "+=", - "1.0-" if ii==0 else " ", velocity_h[2], - "1.0-" if jj==0 else " ", velocity_h[1], - s.vload(n=nparticles, ptr=line_velocity, - offset='({})*{}+({})*{}+{}*{}'.format( - ii, velocity_strides[2], - jj, velocity_strides[1], - velocity_ix, velocity_strides[0])))) - is_first = False - elif work_dim==2: - for ii in range(2): - s.append('{}[{}] {} ({}{})*{};'.format( - Vc, velocity_ix, " =" if is_first else "+=", - "1.0-" if ii==0 else " ", velocity_h[1], - s.vload(n=nparticles,ptr=line_velocity, - offset='({})*{}+{}*{}'.format( - ii, velocity_strides[1], - velocity_ix, velocity_strides[0])))) - is_first = False - else: - raise RuntimeError("Bilevel advection 1D need further developments") - s.barrier(_local=True) - s.jumpline() + if is_bilevel: + s.append('{} = {} * {} + {};'.format( + velocity_p[i], 'kji'[i], igrid_ratio[i], v_grid_ghosts[i])) + s.append('{} = convert_int_rtn({});'.format(velocity_gid[i], velocity_p[i])) + s.append('{} = {} - {};'.format( + velocity_h[i], velocity_p[i], velocity_gid[i])) + if polynomial_interpolator: + s.append('{} = (int)(round({}/{}));'.format(widx[i], velocity_h[i], p_dx[i])) else: s.append('{} = {} + {};'.format(velocity_gid[i], 'kji'[i], velocity_ghosts)) yield ctx @@ -544,8 +500,6 @@ class DirectionalAdvectionKernelGenerator(KernelCodeGenerator): nested_loops = [_work_iterate_(i) for i in xrange(work_dim-1,-1,-1)] with s._kernel_(): - field_infos.declare(s) - s.jumpline() with s._align_() as al: global_id.declare(al,align=True,const=True) local_id.declare(al,align=True,const=True) @@ -554,19 +508,20 @@ class DirectionalAdvectionKernelGenerator(KernelCodeGenerator): s.jumpline() with s._align_() as al: - compute_grid_size.declare(al,align=True) - if has_bilevel: - v_grid_size.declare(al,align=True) - P_grid_ghosts.declare(al,align=True) - V_grid_ghosts.declare(al,align=True) + p_grid_size.declare(al,align=True) + v_grid_size.declare(al,align=True) + p_compute_grid_size.declare(al,align=True) + v_compute_grid_size.declare(al,align=True) + p_grid_ghosts.declare(al,align=True) + v_grid_ghosts.declare(al,align=True) al.jumpline() - dx.declare(al, align=True) - inv_dx.declare(al,align=True) - if has_bilevel: - v_dx.declare(al, align=True) - v_inv_dx.declare(al,align=True) - s.jumpline() - xmin.declare(al,align=True) + p_dx.declare(al, align=True) + v_dx.declare(al, align=True) + p_inv_dx.declare(al,align=True) + v_inv_dx.declare(al,align=True) + al.jumpline() + p_xmin.declare(al,align=True) + v_xmin.declare(al,align=True) s.jumpline() with s._align_() as al: @@ -576,7 +531,10 @@ class DirectionalAdvectionKernelGenerator(KernelCodeGenerator): if is_cached: with s._align_() as al: - advec_ghosts.declare(al, const=True, align=True) + if is_bilevel: + grid_ratio.declare(al, const=True, align=True) + igrid_ratio.declare(al, const=True, align=True) + V_cache_ghosts.declare(al, const=True, align=True) s.jumpline() with s._align_() as al: @@ -590,13 +548,15 @@ class DirectionalAdvectionKernelGenerator(KernelCodeGenerator): kmax.declare(s) s.decl_aligned_vars(position_gid, velocity_gid) - if has_bilevel: - s.decl_aligned_vars(velocity_gid_pos, velocity_h) + if is_bilevel: + s.decl_aligned_vars(velocity_p, velocity_h) + if polynomial_interpolator: + s.decl_aligned_vars(widx) with contextlib.nested(*nested_loops): s.jumpline() - if is_cached and not has_bilevel: + if is_cached and not is_bilevel: if tuning_mode: loop = 'int {i}={Lx}; {i}<{N}; {i}+={gsize}'.format( i='idx', N=V_cache_width, @@ -611,89 +571,80 @@ class DirectionalAdvectionKernelGenerator(KernelCodeGenerator): s.append(code) code = 'wait_group_events(1, &event);' s.append(code) - s.jumpline() - elif has_bilevel and not velocity_cache_full_length: + elif is_bilevel: # We must load velocity cache line on the fly s.comment("Load velocity in cache with linear interpolation") - mesh_ratio = 1 - s_mesh_size = position_mesh_info['global_mesh']['compute_resolution'].value[0] - v_mesh_size = velocity_mesh_info['global_mesh']['compute_resolution'].value[0] - if (s_mesh_size%v_mesh_size==0): - mesh_ratio = s_mesh_size / v_mesh_size - with s._if_('k%{}==0'.format(mesh_ratio)): - s.append('{} = convert_int_rtn(convert_{}({}+1)*{}*{});'.format(line_offset_for_v, ftype, line_offset, dx[0], v_inv_dx[0])) - with s._for_('int {i}={fval}; {i}<{N} && ({i}+{o})<{Nv}; {i}+={gsize}'.format( - i=velocity_ix, fval=local_id[0], gsize=local_size[0], - N=Vc_shape[0],o=line_offset_for_v, Nv=v_grid_size[0]), - unroll=False): - if work_dim==3: - is_first = True - for ii,jj in [(ii,jj) for ii in range(2) for jj in range(2)]: - s.append('{}[{}] {} ({}{})*({}{})*{};'.format( - Vc, velocity_ix, " =" if is_first else "+=", - "1.0-" if ii==0 else " ",velocity_h[2], - "1.0-" if jj==0 else " ",velocity_h[1], - s.vload(n=nparticles,ptr=line_velocity,offset='({})*{}+({})*{}+({}+{})*{}'.format( - ii, velocity_strides[2], - jj, velocity_strides[1], - line_offset_for_v, - velocity_ix, velocity_strides[0])))) - is_first = False - elif work_dim==2: - for ii in range(2): - s.append('{}[{}] {} ({}{})*{};'.format( - Vc, velocity_ix, " =" if is_first else "+=", - "1.0-" if jj==0 else " ",velocity_h[1], - s.vload(n=nparticles,ptr=line_velocity,offset='({}+{})*{}+({}+{})*{}'.format( - jj, velocity_strides[1], - line_offset_for_v, - velocity_ix, velocity_strides[0])))) - is_first = False - s.barrier(_local=True) - s.jumpline() + loop = 'int {i}={Lx}; {i}<{N}; {i}+={gsize}'.format( + i='idx', N=V_cache_width, + Lx=local_id[0], gsize=local_size[0]) + with s._for_(loop): + s.append('{} = ({}+{}-{})*{} + {};'.format( + velocity_p[i], line_offset, 'idx', V_cache_ghosts, igrid_ratio[i], v_grid_ghosts[i])) + s.append('{} = convert_int_rtn({});'.format(velocity_gid[i], velocity_p[i])) + s.append('{} = {} - {};'.format( + velocity_h[i], velocity_p[i], velocity_gid[i])) + is_first = True + if polynomial_interpolator: + s.append('{} = (int)(round({}/{}));'.format(widx[i], velocity_h[i], p_dx[i])) + for idx in np.ndindex(*polynomial_interpolator.n): + idx = idx[::-1] + offset = '+'.join('({}{:+})*{}'.format(gidi, idxi-gi, vsi) + for (gidi,idxi,gi,vsi) in zip(velocity_gid, idx, polynomial_interpolator.ghosts, velocity_strides)) + value = '{}[{}]'.format(velocity, offset) + weight = '{}{}{}'.format(interpolation_weights, + ''.join('[{}]'.format(widx[ii]) for ii in range(work_dim)[::-1]), + ''.join('[{}]'.format(ii) for ii in idx[::-1])) + rhs = '{}*{}'.format(weight, value) + lhs = '{}[{}]'.format(Vc, 'idx') + code = '{} {} {};'.format(lhs, ' =' if is_first else '+=', rhs) + s.append(code) + is_first=False + else: + for idx in it.product(range(2), repeat=work_dim): + idx = idx[::-1] + offset = '+'.join('({}+{})*{}'.format(gidi, idxi, vsi) + for (gidi,idxi,vsi) in zip(velocity_gid, idx, velocity_strides)) + value = '{}[{}]'.format(velocity, offset) + weight = '*'.join('({}{})'.format('1.0-' if (idxi==0) else ' ', vhi) + for (idxi, vhi) in zip(idx, velocity_h)) + rhs = '{}*{}'.format(weight, value) + lhs = '{}[{}]'.format(Vc, 'idx') + code = '{} {} {};'.format(lhs, ' =' if is_first else '+=', rhs) + s.append(code) + is_first=False + s.barrier(_local=True) + s.jumpline() with s._if_(is_active()): pid.declare(s, init='{} + {}*{} + {}'.format(line_offset, local_id[0], npart, poffset)) X.declare(s, init='convert_{}(min({},{}-1))*{}'.format(part_ftype, pid, - compute_grid_size[0], dx[0])); + p_compute_grid_size[0], p_dx[0])); s.jumpline() - # Modify for bilevel interpolation : in this case, the line - # offset is set to 0 because cache length equals local resolution - if has_bilevel: - rk_args = { - 'X': X, - 'dt': dt, - 'inv_dx': v_inv_dx[0], - 'line_offset': '0' if velocity_cache_full_length else line_offset_for_v, - 'field_infos': '&{}'.format(field_infos), - } - else: - rk_args = { - 'X': X, - 'dt': dt, - 'inv_dx': inv_dx[0], - 'line_offset': line_offset, - 'field_infos': '&{}'.format(field_infos), + rk_args = { + 'X': X, + 'dt': dt, + 'inv_dx': p_inv_dx[0], + 'line_offset': line_offset, } if is_cached: - rk_args['line_velocity'] = Vc + rk_args['line_velocity'] = Vc else: - rk_args['line_velocity'] = line_velocity - rk_args['line_velocity'] = '{}+{}'.format(rk_args['line_velocity'], min_ghosts) + rk_args['line_velocity'] = line_velocity + rk_args['line_velocity'] = '{}+{}'.format(rk_args['line_velocity'], V_cache_ghosts) if is_periodic: - rk_args['line_width'] = compute_grid_size[0] + rk_args['line_width'] = p_compute_grid_size[0] call = runge_kutta(**rk_args) code = '{} = {};'.format(X(), call) s.append(code) if s.offset_by_xmin: - s.append('{} += {};'.format(X(), xmin)) + s.append('{} += {};'.format(X(), p_xmin)) s.jumpline() s.vstore_if(cond=is_last, - scalar_cond=lambda i: '{} < {}'.format(pid[i], compute_grid_size[0]), + scalar_cond=lambda i: '{} < {}'.format(pid[i], p_compute_grid_size[0]), n=nparticles, ptr=line_position, offset=local_id[0], data=X, offset_is_ftype=False, use_short_circuit=use_short_circuit) @@ -703,7 +654,7 @@ if __name__ == '__main__': from hysop.backend.device.opencl import cl from hysop.backend.device.codegen.base.test import _test_mesh_info, _test_typegen - work_dim=3 + work_dim=2 ghosts=(0,0,0) vresolution=(128,64,32) presolution=(256,128,64) @@ -714,17 +665,22 @@ if __name__ == '__main__': (_,vmesh_info) = _test_mesh_info('velocity_mesh_info',tg,work_dim,ghosts,vresolution) (_,pmesh_info) = _test_mesh_info('position_mesh_info',tg,work_dim,ghosts,presolution) + grid_ratio = np.asarray([2.0,3.0,4.0][:work_dim], dtype=np.float64) + PI = PolynomialInterpolator(dim=work_dim, deg=3, fd=2, verbose=False) + polynomial_interpolator = PI.generate_subgrid_interpolator(grid_ratio=tuple(grid_ratio.astype(int))) + dak = DirectionalAdvectionKernelGenerator(typegen=tg, ftype=tg.fbtype, work_dim=work_dim, rk_scheme=ExplicitRungeKutta('Euler'), vboundary=(BoundaryCondition.PERIODIC,BoundaryCondition.PERIODIC), is_cached=True, - min_ghosts=8, + velocity_cfl=1.42, symbolic_mode=True, relative_velocity=0.66, - tuning_mode = True, - #nparticles=4, # MONOLEVEL TEST - nparticles=1, is_bilevel=(256,64,32), # BILEVEL TEST + tuning_mode = False, + nparticles=4, + grid_ratio=grid_ratio, + polynomial_interpolator = polynomial_interpolator, known_vars=dict( V_mesh_info=vmesh_info, P_mesh_info=pmesh_info, diff --git a/hysop/backend/device/opencl/autotunable_kernels/advection_dir.py b/hysop/backend/device/opencl/autotunable_kernels/advection_dir.py index 654ec2be8..f5268ea2c 100644 --- a/hysop/backend/device/opencl/autotunable_kernels/advection_dir.py +++ b/hysop/backend/device/opencl/autotunable_kernels/advection_dir.py @@ -2,6 +2,7 @@ from hysop.tools.numpywrappers import npw from hysop.tools.types import check_instance from hysop.constants import AutotunerFlags, DirectionLabels, BoundaryCondition from hysop.numerics.odesolvers.runge_kutta import ExplicitRungeKutta +from hysop.numerics.interpolation.polynomial import PolynomialSubgridInterpolator from hysop.backend.device.opencl import cl, clTools from hysop.backend.device.codegen.kernels.directional_advection import DirectionalAdvectionKernelGenerator @@ -13,7 +14,8 @@ class OpenClAutotunableDirectionalAdvectionKernel(OpenClAutotunableKernel): def autotune(self, direction, time_integrator, velocity_cfl, velocity, position, relative_velocity, - hardcode_arrays, is_bilevel, **kwds): + hardcode_arrays, grid_ratio, + polynomial_interpolator, **kwds): """Autotune this kernel with specified configuration.""" precision = self.typegen.dtype @@ -31,11 +33,18 @@ class OpenClAutotunableDirectionalAdvectionKernel(OpenClAutotunableKernel): raise ValueError(msg) if not isinstance(velocity_cfl, float) or velocity_cfl<=0.0: raise ValueError('Invalid velocity_cfl value.') + check_instance(polynomial_interpolator, PolynomialSubgridInterpolator) + + check_instance(grid_ratio, npw.ndarray, minval=1.0, allow_none=True) + is_bilevel = (grid_ratio>1.0).any() + periodicity = velocity.periodicity.astype(npw.int32) + + + min_velocity_ghosts = DirectionalAdvectionKernelGenerator.min_velocity_ghosts(dim, velocity_cfl, grid_ratio, periodicity) + velocity_cache_ghosts = DirectionalAdvectionKernelGenerator.advection_cache_ghosts(velocity_cfl, grid_ratio[-1]) - min_ghosts = DirectionalAdvectionKernelGenerator.min_ghosts(dim, velocity_cfl) - cache_ghosts = min_ghosts[-1] - self.check_cartesian_field(velocity, min_ghosts=min_ghosts, nb_components=1) - if (is_bilevel is not None): + self.check_cartesian_field(velocity, min_ghosts=min_velocity_ghosts, nb_components=1) + if is_bilevel: self.check_cartesian_field(position, nb_components=1, compute_resolution=position.compute_resolution) else: @@ -53,10 +62,10 @@ class OpenClAutotunableDirectionalAdvectionKernel(OpenClAutotunableKernel): assert isinstance(Vr, (str,float)) ftype = clTools.dtype_to_ctype(precision) - name = 'directional_advection{}_{}__{}_{}_{}g__{}'.format( + name = 'directional_advection{}_{}__{}_{}__{}'.format( "" if (is_bilevel is None) else "_bilevel_linear", DirectionLabels[direction], - time_integrator.name(), ftype, cache_ghosts, abs(hash(Vr))) + time_integrator.name(), ftype, abs(hash(Vr))) vboundaries = (velocity.global_lboundaries[-1], velocity.global_rboundaries[-1]) @@ -94,13 +103,16 @@ class OpenClAutotunableDirectionalAdvectionKernel(OpenClAutotunableKernel): position.mesh) return super(OpenClAutotunableDirectionalAdvectionKernel, self).autotune(name=name, - rk_scheme=time_integrator, kernel_args=kernel_args, cache_ghosts=cache_ghosts, + rk_scheme=time_integrator, kernel_args=kernel_args, + cache_ghosts=velocity_cache_ghosts, velocity_cfl=velocity_cfl, vboundaries=vboundaries, precision=precision, ftype=ftype, mesh_info_vars=mesh_info_vars, work_dim=dim, work_size=position.compute_resolution[::-1], hardcode_arrays=hardcode_arrays, known_args=known_args, offset_dtype=offset_dtype, strides_dtype=strides_dtype, - isolation_params=isolation_params, Vr=Vr, is_bilevel=is_bilevel, + isolation_params=isolation_params, Vr=Vr, + grid_ratio=grid_ratio, is_bilevel=is_bilevel, + polynomial_interpolator=polynomial_interpolator, **kwds) @@ -148,10 +160,7 @@ class OpenClAutotunableDirectionalAdvectionKernel(OpenClAutotunableKernel): max_workitem_workload = [1,16,16] nparticles_options = [1,2,4,8,16] - if (extra_kwds['is_bilevel'] is not None): - # Currently bilevel codegen is not vectorized - nparticles_options = [1] - max_workitem_workload = [1,1,1] + if extra_kwds['is_bilevel']: caching_options = [True] extra_kwds['max_work_load'] = max_workitem_workload[:work_dim] @@ -190,7 +199,9 @@ class OpenClAutotunableDirectionalAdvectionKernel(OpenClAutotunableKernel): cache_ghosts = extra_kwds['cache_ghosts'] known_args = extra_kwds['known_args'] Vr = extra_kwds['Vr'] - is_bilevel = extra_kwds['is_bilevel'] + grid_ratio = extra_kwds['grid_ratio'] + velocity_cfl = extra_kwds['velocity_cfl'] + polynomial_interpolator = extra_kwds['polynomial_interpolator'] ## Get compile time OpenCL known variables known_vars = super(OpenClAutotunableDirectionalAdvectionKernel, self).generate_kernel_src( @@ -213,27 +224,24 @@ class OpenClAutotunableDirectionalAdvectionKernel(OpenClAutotunableKernel): rk_scheme=rk_scheme, symbolic_mode=self.symbolic_mode, ftype=ftype, - min_ghosts=cache_ghosts, + velocity_cfl=velocity_cfl, tuning_mode=tuning_mode, - is_bilevel=extra_kwds['is_bilevel'], + grid_ratio=grid_ratio, + polynomial_interpolator=polynomial_interpolator, known_vars=known_vars, **extra_parameters) kernel_name = codegen.name kernel_src = str(codegen) - ## Check if cache would fit if (local_work_size is not None): self.check_cache(codegen.required_workgroup_cache_size(local_work_size)[2]) - if (is_bilevel is not None): - self.check_cache(codegen.required_workgroup_velocity_cache_size()) return (kernel_name, kernel_src) def hash_extra_kwds(self, extra_kwds): """Hash extra_kwds dictionnary for caching purposes.""" - kwds = ('rk_scheme', 'ftype', 'work_dim', 'Vr', - 'vboundaries', 'cache_ghosts', 'work_size', - 'known_args') + kwds = ('rk_scheme', 'ftype', 'work_dim', 'Vr', 'grid_ratio', + 'vboundaries', 'cache_ghosts', 'work_size', 'known_args') return self.custom_hash(*tuple(extra_kwds[kwd] for kwd in kwds), mesh_info_vars=extra_kwds['mesh_info_vars']) diff --git a/hysop/backend/device/opencl/operator/directional/advection_dir.py b/hysop/backend/device/opencl/operator/directional/advection_dir.py index 51cf8b3b8..0ada2c118 100644 --- a/hysop/backend/device/opencl/operator/directional/advection_dir.py +++ b/hysop/backend/device/opencl/operator/directional/advection_dir.py @@ -96,15 +96,12 @@ class OpenClDirectionalAdvection(DirectionalAdvectionBase, OpenClDirectionalOper kwds['velocity'] = self.dvelocity kwds['position'] = self.dposition kwds['relative_velocity'] = self.relative_velocity - - if (self.is_bilevel is None): - kwds['is_bilevel'] = None - else: - kwds['is_bilevel'] = self.is_bilevel + kwds['grid_ratio'] = self.discrete_grid_ratio kwds['direction'] = self.splitting_direction kwds['velocity_cfl'] = self.velocity_cfl kwds['time_integrator'] = self.time_integrator + kwds['polynomial_interpolator'] = self.subgrid_interpolator (advec_kernel, args_dict) = kernel.autotune(force_verbose=self._force_autotuner_verbose, force_debug=self._force_autotuner_debug, hardcode_arrays=True, **kwds) diff --git a/hysop/backend/host/python/operator/directional/advection_dir.py b/hysop/backend/host/python/operator/directional/advection_dir.py index f746d9bb6..2a2584eed 100644 --- a/hysop/backend/host/python/operator/directional/advection_dir.py +++ b/hysop/backend/host/python/operator/directional/advection_dir.py @@ -71,7 +71,10 @@ class PythonDirectionalAdvection(DirectionalAdvectionBase, HostDirectionalOperat if self.is_inplace: self.dstmp = work.get_buffer(self, 'stmp', handle=True) - assert (self.is_bilevel is None), "Python bilevel advection has not been implemented yet." + if self.is_bilevel: + msg="Python bilevel advection has not been implemented yet." + raise NotImplementedError(msg) + self._prepare_apply() def _prepare_apply(self): diff --git a/hysop/fields/cartesian_discrete_field.py b/hysop/fields/cartesian_discrete_field.py index a7457025f..7330ae255 100644 --- a/hysop/fields/cartesian_discrete_field.py +++ b/hysop/fields/cartesian_discrete_field.py @@ -967,12 +967,11 @@ class CartesianDiscreteScalarFieldView(CartesianDiscreteScalarFieldViewContainer def clone(self, name=None, pretty_name=None, var_name=None, latex_name=None, tstate=None): """ - Create a new temporary DiscreteScalarField and allocate it + Create a DiscreteScalarField and allocate it like the current object, possibly on a different backend. This should only be used for debugging and testing purpose. - The generated discrete field is not registered to the continuous - field. + The generated discrete field is registered to a cloned continuous field. """ from hysop.tools.sympy_utils import subscript default_name='{}__{}'.format(self.name, self._dfield._clone_id) @@ -988,14 +987,19 @@ class CartesianDiscreteScalarFieldView(CartesianDiscreteScalarFieldViewContainer latex_name = first_not_None(latex_name, name, default_lname) name = first_not_None(name, default_name) + field = self._dfield._field + field = field.field_like(name=field.name+'__{}'.format(self._dfield._clone_id)) + + topology = self._dfield._topology.topology_like() + dfield = CartesianDiscreteScalarField(name=name, pretty_name=pretty_name, latex_name=latex_name, var_name=var_name, - field=self._dfield._field, - topology=self._dfield._topology, + field=field, + topology=topology, init_topology_state=tstate, - register_discrete_field=False) + register_discrete_field=True) dfield.copy(self) return dfield diff --git a/hysop/fields/continuous_field.py b/hysop/fields/continuous_field.py index fec70928e..7f1ba42b6 100644 --- a/hysop/fields/continuous_field.py +++ b/hysop/fields/continuous_field.py @@ -74,9 +74,9 @@ class FieldContainerI(TaggedObject): @abstractmethod def tmp_like(self, name, **kwds): - """Create a temporaty field like self, possibly altered.""" + """Create a temporary field like self, possibly altered.""" pass - + @abstractmethod def fields(self): """Return all unique scalar fields contained in this field container.""" @@ -759,14 +759,14 @@ class ScalarField(NamedScalarContainerI, FieldContainerI): check_instance(topology_state, TopologyState) if (topology not in self.discrete_fields): - field = topology.discretize(self) - check_instance(field, DiscreteField) + dfield = topology.discretize(self) + check_instance(dfield, DiscreteField) if not self.is_tmp: assert (topology in self.discrete_fields) - assert (self.discrete_fields[topology] is field) + assert (self.discrete_fields[topology] is dfield) else: - field = self.discrete_fields[topology] - return field.view(topology_state) + dfield = self.discrete_fields[topology] + return dfield.view(topology_state) def _get_dtype(self): """Return the default allocation dtype of this ScalarField.""" diff --git a/hysop/numerics/interpolation/polynomial.py b/hysop/numerics/interpolation/polynomial.py index 70e83e44b..486c47628 100644 --- a/hysop/numerics/interpolation/polynomial.py +++ b/hysop/numerics/interpolation/polynomial.py @@ -62,6 +62,7 @@ class PolynomialInterpolator(object): spi = str(pi) if spi.startswith('LINEAR'): deg=1 + fd = 2 elif spi.startswith('CUBIC'): deg=3 elif spi.startswith('QUINTIC'): @@ -520,7 +521,7 @@ class PolynomialSubgridInterpolator(object): Same data type as W. """ check_instance(interpolator, PolynomialInterpolator) - check_instance(grid_ratio, tuple, size=interpolator.dim, minval=1) + check_instance(grid_ratio, tuple, values=int, size=interpolator.dim, minval=1) p = interpolator.p n = interpolator.n diff --git a/hysop/operator/base/advection_dir.py b/hysop/operator/base/advection_dir.py index 0b21c7a4c..0e67f8803 100644 --- a/hysop/operator/base/advection_dir.py +++ b/hysop/operator/base/advection_dir.py @@ -7,6 +7,7 @@ from hysop.fields.continuous_field import Field, ScalarField from hysop.numerics.remesh.remesh import RemeshKernel from hysop.numerics.odesolvers.runge_kutta import ExplicitRungeKutta, Euler, RK2, RK3, RK4 +from hysop.numerics.interpolation.polynomial import PolynomialInterpolation, PolynomialInterpolator from hysop.topology.cartesian_descriptor import CartesianTopologyDescriptors from hysop.core.memory.memory_request import MemoryRequest @@ -17,13 +18,13 @@ class DirectionalAdvectionBase(object): __default_method = { TimeIntegrator: Euler, - Interpolation: Interpolation.LINEAR, + Interpolation: PolynomialInterpolation.LINEAR, #Interpolation.LINEAR, Remesh: Remesh.L2_1, } __available_methods = { TimeIntegrator: InstanceOf(ExplicitRungeKutta), - Interpolation: Interpolation.LINEAR, + Interpolation: (InstanceOf(Interpolation), InstanceOf(PolynomialInterpolation)), Remesh: (InstanceOf(Remesh), InstanceOf(RemeshKernel)), } @@ -201,13 +202,31 @@ class DirectionalAdvectionBase(object): s_dx = s_topo.mesh.space_step scalar_cfl = velocity_cfl * (v_dx[ndir] / s_dx[ndir]) + assert (v_dx>=s_dx).all() + grid_ratio = v_dx/s_dx + is_bilevel = (grid_ratio>1.0).any() + + if is_bilevel: + interp = self.interp + if isinstance(interp, PolynomialInterpolation): + assert (grid_ratio == grid_ratio.astype(npw.int32)).all(), grid_ratio + polynomial_interpolator = PolynomialInterpolator.build_interpolator( + pi=interp, dim=self.velocity.dim) + elif isinstance(interp, Interpolation): + assert interp is Interpolation.LINEAR # legacy linear interpolator + polynomial_interpolator = None + else: + raise NotImplementedError(interp) + else: + polynomial_interpolator = None + advection_ghosts = self.velocity_advection_ghosts(velocity_cfl) - min_velocity_ghosts = npw.integer_zeros(v_dx.shape) - has_bilevel = any(v_dx != s_dx) - if has_bilevel: - # add one ghost in each direction for nd interpolation - min_velocity_ghosts[...] = 1 - min_velocity_ghosts[ndir] = advection_ghosts + + min_velocity_ghosts = self.velocity.periodicity * (grid_ratio > 1.0).astype(npw.int32) + min_velocity_ghosts += 1 + if (polynomial_interpolator is not None): + min_velocity_ghosts += polynomial_interpolator.ghosts + min_velocity_ghosts[ndir] += advection_ghosts v_requirements.min_ghosts = npw.maximum(v_requirements.min_ghosts, min_velocity_ghosts).astype(HYSOP_INTEGER) scalar_advection_ghosts = self.velocity_advection_ghosts(scalar_cfl) @@ -243,13 +262,16 @@ class DirectionalAdvectionBase(object): self.advection_ghosts = advection_ghosts self.scalar_advection_ghosts = scalar_advection_ghosts self.remesh_ghosts = remesh_ghosts + self.grid_ratio = grid_ratio + self.is_bilevel = is_bilevel + self.polynomial_interpolator = polynomial_interpolator return requirements @classmethod def velocity_advection_ghosts(cls, velocity_cfl): assert velocity_cfl > 0.0, 'cfl <= 0.0' - return int(1+npw.floor(velocity_cfl)) + return int(npw.ceil(velocity_cfl)) @classmethod def scalars_out_cache_ghosts(cls, scalar_cfl, remesh_kernel): @@ -272,14 +294,21 @@ class DirectionalAdvectionBase(object): dadvected_fields_out = {ofield: self.output_discrete_fields[ofield] for ofield in self.advected_fields_out} + grid_ratio = dadvected_fields_in.values()[0].topology_state.transposed(self.grid_ratio) + assert (grid_ratio == (dvelocity.space_step / dadvected_fields_in.values()[0].space_step)).all() + + self.dvelocity = dvelocity self.dadvected_fields_in = dadvected_fields_in self.dadvected_fields_out = dadvected_fields_out + self.discrete_grid_ratio = grid_ratio - self.is_bilevel = None - if any(self.dvelocity.compute_resolution != \ - self.dadvected_fields_out.values()[0].compute_resolution): - self.is_bilevel = self.dvelocity.compute_resolution + if (self.polynomial_interpolator is not None) and self.is_bilevel: + subgrid_interpolator = self.polynomial_interpolator.generate_subgrid_interpolator( + grid_ratio=tuple(grid_ratio.astype(int))) + self.subgrid_interpolator = subgrid_interpolator + else: + self.subgrid_interpolator = None @debug def get_work_properties(self): diff --git a/hysop/operator/tests/test_bilevel_advection.py b/hysop/operator/tests/test_bilevel_advection.py index 9e5297c2d..d72b27ddd 100644 --- a/hysop/operator/tests/test_bilevel_advection.py +++ b/hysop/operator/tests/test_bilevel_advection.py @@ -40,7 +40,7 @@ class TestBilevelAdvectionOperator(object): size_min=None, size_max=None): assert dim > 0 - shape = npw.asarray((16,32,64)) + shape = npw.asarray((16,32,64))[:dim] npw.random.shuffle(shape) shape = tuple(shape.tolist()) shape_s = tuple(2*s for s in shape) @@ -74,20 +74,19 @@ class TestBilevelAdvectionOperator(object): Vin=Vin, Sin=Sin, Sout=Sout, velocity_cfl=velocity_cfl) @classmethod - def __velocity_init(cls, data, coords, axes): - for i,d in enumerate(data): - if i in axes: - d[...] = +1.0 - else: - d[...] = 0.0 + def __velocity_init(cls, data, coords, component, axes): + if component in axes: + data[...] = +1.0 + else: + data[...] = 0.0 @classmethod - def __scalar_init(cls, data, coords, offsets=None): - offsets = first_not_None(offsets, (0.0,)*len(coords[0])) - for i,(d,coord) in enumerate(zip(data, coords)): - d[...] = 1.0/(i+1) - for (c, o) in zip(coord, offsets): - d[...] *= npw.cos(c+o) + def __scalar_init(cls, data, coords, component, offsets=None): + offsets = first_not_None(offsets, (0.0,)*len(coords)) + assert len(coords)==len(offsets) + data[...] = 1.0/(component+1) + for (c, o) in zip(coords, offsets): + data[...] *= npw.cos(c+o) def _test_one(self, time_integrator, remesh_kernel, shape, shape_s, dim, @@ -114,7 +113,7 @@ class TestBilevelAdvectionOperator(object): implementations = DirectionalAdvection.implementations().keys() implementations += Advection.implementations().keys() implementations = list(set(implementations)) - assert ref_impl in implementations + assert (ref_impl in implementations) implementations.remove(ref_impl) implementations = [ref_impl] + implementations @@ -269,9 +268,6 @@ class TestBilevelAdvectionOperator(object): self._test(dim=3, is_inplace=True) print - def test_3D(self): - self._test(dim=3, is_inplace=True) - if __name__ == '__main__': import hysop diff --git a/hysop/operator/tests/test_directional_advection.py b/hysop/operator/tests/test_directional_advection.py index 05e81ccff..b424ab569 100644 --- a/hysop/operator/tests/test_directional_advection.py +++ b/hysop/operator/tests/test_directional_advection.py @@ -81,20 +81,19 @@ class TestDirectionalAdvectionOperator(object): Vin=Vin, Sin=Sin, Sout=Sout, velocity_cfl=velocity_cfl) @classmethod - def __velocity_init(cls, data, coords, axes): - for i,d in enumerate(data): - if i in axes: - d[...] = +1.0 - else: - d[...] = 0.0 + def __velocity_init(cls, data, coords, component, axes): + if component in axes: + data[...] = +1.0 + else: + data[...] = 0.0 @classmethod - def __scalar_init(cls, data, coords, offsets=None): - offsets = first_not_None(offsets, (0.0,)*len(coords[0])) - for i,(d,coord) in enumerate(zip(data, coords)): - d[...] = 1.0/(i+1) - for (c, o) in zip(coord, offsets): - d[...] *= npw.cos(c+o) + def __scalar_init(cls, data, coords, component, offsets=None): + offsets = first_not_None(offsets, (0.0,)*len(coords)) + assert len(coords)==len(offsets) + data[...] = 1.0/(component+1) + for (c, o) in zip(coords, offsets): + data[...] *= npw.cos(c+o) def _test_one(self, time_integrator, remesh_kernel, shape, dim, @@ -115,7 +114,7 @@ class TestDirectionalAdvectionOperator(object): # Use optimal timestep, ||Vi||_inf is 1 on a per-axis basis dt = ScalarParameter('dt', initial_value=npw.nan) - dt.value = (0.99 * velocity_cfl) / (max(shape)-1) + dt.value = (0.95 * velocity_cfl) / (max(shape)-1) ref_impl = Implementation.OPENCL implementations = DirectionalAdvection.implementations().keys() @@ -212,6 +211,8 @@ class TestDirectionalAdvectionOperator(object): if is_ref: dxk = -Vi*(k+0)*dt() + dsref[0].sdata[...] = npw.nan + dsref[1].sdata[...] = npw.inf dsref.initialize(self.__scalar_init, offsets=dxk.tolist()) d = dsout.distance(dsref, p=2) if npw.any(d > 1e-1): @@ -272,13 +273,6 @@ class TestDirectionalAdvectionOperator(object): sys.stdout.flush() raise - # def test_advec_1D_out_of_place(self): - # self._test(dim=1, is_inplace=False) - # def test_advec_2D_out_of_place(self): - # self._test(dim=2, is_inplace=False) - # def test_advec_3D_out_of_place(self): - # self._test(dim=3, is_inplace=False) - def test_advec_1D_inplace(self): self._test(dim=1, is_inplace=True) def test_advec_2D_inplace(self): @@ -290,10 +284,6 @@ class TestDirectionalAdvectionOperator(object): self.test_advec_1D_inplace() self.test_advec_2D_inplace() self.test_advec_3D_inplace() - - #self.test_advec_1D_out_of_place() - #self.test_advec_2D_out_of_place() - #self.test_advec_3D_out_of_place() print diff --git a/hysop/tools/parameters.py b/hysop/tools/parameters.py index c00400c3b..900a0f943 100755 --- a/hysop/tools/parameters.py +++ b/hysop/tools/parameters.py @@ -106,7 +106,7 @@ class CartesianDiscretization(namedtuple("CartesianDiscretization", lboundaries = npw.empty(shape=(resolution.size,), dtype=object) lboundaries[...] = BoundaryCondition.PERIODIC rboundaries = lboundaries.copy() - + check_instance(lboundaries, npw.ndarray, dtype=object, size=resolution.size, values=BoundaryCondition, allow_none=True) -- GitLab