From c5609f04bbe0f4eb107385d9a051c2dcd6fb558d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Keck <Jean-Baptiste.Keck@imag.fr> Date: Tue, 14 Aug 2018 19:32:31 +0200 Subject: [PATCH] fixed varname issues in codegeb --- examples/taylor_green/taylor_green.py | 2 +- .../device/codegen/kernels/custom_symbolic.py | 14 +-- .../functions/custom_symbolic_function.py | 2 +- .../autotunable_kernels/custom_symbolic.py | 12 +-- hysop/core/memory/memory_request.py | 4 +- hysop/fields/cartesian_discrete_field.py | 20 +++- hysop/fields/continuous_field.py | 99 +++++++++++++------ hysop/fields/discrete_field.py | 65 ++++++++---- hysop/fields/field_requirements.py | 9 +- hysop/mesh/cartesian_mesh.py | 28 +++--- hysop/operator/base/derivative.py | 2 +- hysop/symbolic/field.py | 19 ++-- hysop/tools/parameters.py | 14 +++ hysop/topology/cartesian_descriptor.py | 23 +++-- hysop/topology/cartesian_topology.py | 29 +++--- 15 files changed, 233 insertions(+), 109 deletions(-) diff --git a/examples/taylor_green/taylor_green.py b/examples/taylor_green/taylor_green.py index 5d7731835..21115b59d 100644 --- a/examples/taylor_green/taylor_green.py +++ b/examples/taylor_green/taylor_green.py @@ -84,7 +84,7 @@ def compute(args): # Define parameters and field (time, timestep, velocity, vorticity, enstrophy) t, dt = TimeParameters(dtype=args.dtype) velo = VelocityField(domain=box, dtype=args.dtype) - vorti = VorticityField(domain=box, dtype=args.dtype) + vorti = VorticityField(velocity=velo) enstrophy = EnstrophyParameter(dtype=args.dtype) ### Build the directional operators diff --git a/hysop/backend/device/codegen/kernels/custom_symbolic.py b/hysop/backend/device/codegen/kernels/custom_symbolic.py index 3fcd9ae28..e20fe6851 100644 --- a/hysop/backend/device/codegen/kernels/custom_symbolic.py +++ b/hysop/backend/device/codegen/kernels/custom_symbolic.py @@ -242,8 +242,8 @@ class SymbolicCodegenContext(object): for dfield in dfields: field = dfield._field ctype = dfield.ctype - name = dfield.name.lower() - if (name == dfield.name): + name = dfield.var_name.lower() + if (name == dfield.var_name): name = '_'+name name = '{}_{{}}'.format(name) reads = read_counter.get(dfield, None) @@ -533,7 +533,7 @@ class CustomSymbolicKernelGenerator(KernelCodeGenerator): args = array_args.setdefault(obj, {}) strides = array_strides.setdefault(obj, {}) - mesh_info_name = '{}_mesh_info'.format(dfield.name) + mesh_info_name = '{}_mesh_info'.format(dfield.var_name) mesh_info = kernel_reqs['MeshInfoStruct'].build_codegen_variable( const=True, name=mesh_info_name) assert dfield not in mesh_infos @@ -544,7 +544,7 @@ class CustomSymbolicKernelGenerator(KernelCodeGenerator): continue if (dfield in di.write_counter) and di.write_counter[dfield][i]>0: continue - vname = dfield.name + '_' + str(i) + vname = dfield.var_name + '_' + str(i) (arg, stride) = OpenClArrayBackend.build_codegen_arguments(kargs, name=vname, known_vars=csc.known_vars, symbolic_mode=csc.symbolic_mode, storage=self._global, ctype=dfield.ctype, @@ -585,7 +585,7 @@ class CustomSymbolicKernelGenerator(KernelCodeGenerator): args = array_args.setdefault(dfield, {}) strides = array_strides.setdefault(dfield, {}) if (dfield not in mesh_infos): - mesh_info_name = '{}_mesh_info'.format(dfield.name) + mesh_info_name = '{}_mesh_info'.format(dfield.var_name) mesh_info = kernel_reqs['MeshInfoStruct'].build_codegen_variable( const=True, name=mesh_info_name) mesh_infos[dfield] = mesh_info_name @@ -593,7 +593,7 @@ class CustomSymbolicKernelGenerator(KernelCodeGenerator): for (i, count) in enumerate(counts): if (count==0): continue - vname = dfield.name + '_' + str(i) + vname = dfield.var_name + '_' + str(i) arg, arg_strides = OpenClArrayBackend.build_codegen_arguments(kargs, name=vname, known_vars=csc.known_vars, symbolic_mode=csc.symbolic_mode, @@ -750,6 +750,8 @@ class CustomSymbolicKernelGenerator(KernelCodeGenerator): for (array, array_data) in array_args.iteritems(): if isinstance(array, OpenClSymbolicArray): name = array.varname + elif isinstance(array, DiscreteScalarFieldView): + name = array.var_name else: name = array.name vindex = CodegenVectorClBuiltin(name+'_vid', itype, varray_dim, typegen=tg) diff --git a/hysop/backend/device/codegen/symbolic/functions/custom_symbolic_function.py b/hysop/backend/device/codegen/symbolic/functions/custom_symbolic_function.py index e0fb4712f..858b13992 100644 --- a/hysop/backend/device/codegen/symbolic/functions/custom_symbolic_function.py +++ b/hysop/backend/device/codegen/symbolic/functions/custom_symbolic_function.py @@ -35,7 +35,7 @@ class CustomSymbolicFunction(OpenClFunctionCodeGenerator): @classmethod def field_name(cls, field, index): - return cls.varname('{}_{}'.format(field.name, index)) + return cls.varname('{}_{}'.format(field.var_name, index)) @classmethod def array_name(cls, array): return cls.varname(array.varname) diff --git a/hysop/backend/device/opencl/autotunable_kernels/custom_symbolic.py b/hysop/backend/device/opencl/autotunable_kernels/custom_symbolic.py index 395f5887a..1f2f14495 100644 --- a/hysop/backend/device/opencl/autotunable_kernels/custom_symbolic.py +++ b/hysop/backend/device/opencl/autotunable_kernels/custom_symbolic.py @@ -120,14 +120,14 @@ class OpenClAutotunableCustomSymbolicKernel(OpenClAutotunableKernel): if isinstance(obj, di.IndexedCounterTypes): assert isinstance(obj, DiscreteScalarFieldView) dfield = expr_info.input_dfields[obj._field] - mesh_info_name = '{}_mesh_info'.format(dfield.name) + mesh_info_name = '{}_mesh_info'.format(dfield.var_name) mesh_info_vars[mesh_info_name] = self.mesh_info(mesh_info_name, dfield.mesh) for (i, count) in enumerate(counts): if (count==0): continue if (dfield in di.write_counter) and (di.write_counter[dfield][i]>0): continue - vname = dfield.name + '_' + str(i) + vname = dfield.var_name + '_' + str(i) kernel_args[vname+'_base'] = dfield.data[i].base_data target_stride_args[vname+'_strides'] = make_strides(dfield.data[i].strides, dfield.dtype) target_offset_args[vname+'_offset'] = make_offset(dfield.data[i].offset, dfield.dtype) @@ -154,12 +154,12 @@ class OpenClAutotunableCustomSymbolicKernel(OpenClAutotunableKernel): if isinstance(obj, di.IndexedCounterTypes): assert isinstance(obj, DiscreteScalarFieldView) dfield = expr_info.output_dfields[obj._field] - mesh_info_name = '{}_mesh_info'.format(dfield.name) + mesh_info_name = '{}_mesh_info'.format(dfield.var_name) mesh_info_vars[mesh_info_name] = self.mesh_info(mesh_info_name, dfield.mesh) for (i, count) in enumerate(counts): if (count==0): continue - vname = dfield.name + '_' + str(i) + vname = dfield.var_name + '_' + str(i) kernel_args[vname+'_base'] = dfield.data[i].base_data target_stride_args[vname+'_strides'] = make_strides(dfield.data[i].strides, dfield.dtype) target_offset_args[vname+'_offset'] = make_offset(dfield.data[i].offset, dfield.dtype) @@ -252,7 +252,7 @@ class OpenClAutotunableCustomSymbolicKernel(OpenClAutotunableKernel): continue if (dfield in di.write_counter) and (di.write_counter[dfield][i]>0): continue - vname = dfield.name + '_' + str(i) + vname = dfield.var_name + '_' + str(i) args_mapping[vname+'_base'] = (arg_index, cl.MemoryObjectHolder) arg_index += 1 if (not hardcode_arrays): @@ -286,7 +286,7 @@ class OpenClAutotunableCustomSymbolicKernel(OpenClAutotunableKernel): for (i, count) in enumerate(counts): if (count==0): continue - vname = dfield.name + '_' + str(i) + vname = dfield.var_name + '_' + str(i) args_mapping[vname+'_base'] = (arg_index, cl.MemoryObjectHolder) arg_index += 1 if (not hardcode_arrays): diff --git a/hysop/core/memory/memory_request.py b/hysop/core/memory/memory_request.py index 9b564f28d..ee0815c05 100644 --- a/hysop/core/memory/memory_request.py +++ b/hysop/core/memory/memory_request.py @@ -145,7 +145,7 @@ class MemoryRequest(object): @classmethod def cartesian_dfield_like(cls, name, dfield, nb_components=None, initial_values=None, dtype=None, - global_resolution=None, ghosts=None, + grid_resolution=None, ghosts=None, backend=None, is_read_only=None): from hysop.fields.cartesian_discrete_field import CartesianDiscreteScalarFieldView check_instance(dfield, CartesianDiscreteScalarFieldView) @@ -160,7 +160,7 @@ class MemoryRequest(object): (dfield, request, request_id) = dfield.tmp_dfield_like(name=name, backend=backend, nb_components=nb_components, initial_values=initial_values, dtype=dtype, - global_resolution=global_resolution, ghosts=ghosts, is_read_only=is_read_only) + grid_resolution=grid_resolution, ghosts=ghosts, is_read_only=is_read_only) return (dfield, request, request_id) diff --git a/hysop/fields/cartesian_discrete_field.py b/hysop/fields/cartesian_discrete_field.py index b1f6bf0c1..858b90f9d 100644 --- a/hysop/fields/cartesian_discrete_field.py +++ b/hysop/fields/cartesian_discrete_field.py @@ -864,7 +864,8 @@ class CartesianDiscreteScalarFieldView(CartesianDiscreteScalarFieldViewContainer return s - def clone(self, name=None, pretty_name=None, tstate=None): + def clone(self, name=None, pretty_name=None, + var_name=None, latex_name=None, tstate=None): """ Create a new temporary DiscreteScalarField and allocate it like the current object, possibly on a different backend. @@ -877,14 +878,20 @@ class CartesianDiscreteScalarFieldView(CartesianDiscreteScalarFieldViewContainer default_name='{}__{}'.format(self.name, self._dfield._clone_id) default_pname='{}__{}'.format(self.pretty_name, subscript(self._dfield._clone_id).encode('utf-8')) + default_vname='{}__{}'.format(self.var_name, self._dfield._clone_id) + default_lname='{}__{}'.format(self.latex_name, self._dfield._clone_id) self._dfield._clone_id += 1 tstate = first_not_None(tstate, self.topology_state) - name = first_not_None(name, default_name) - pretty_name = first_not_None(pretty_name, default_pname) + pretty_name = first_not_None(pretty_name, name, default_pname) + var_name = first_not_None(var_name, name, default_vname) + latex_name = first_not_None(latex_name, name, default_lname) + name = first_not_None(name, default_name) dfield = CartesianDiscreteScalarField(name=name, pretty_name=pretty_name, + latex_name=latex_name, + var_name=var_name, field=self._dfield._field, topology=self._dfield._topology, init_topology_state=tstate, @@ -894,9 +901,10 @@ class CartesianDiscreteScalarFieldView(CartesianDiscreteScalarFieldViewContainer return dfield def tmp_dfield_like(self, name, pretty_name=None, + var_name=None, latex_name=None, backend=None, is_read_only=None, initial_values=None, dtype=None, - global_resolution=None, ghosts=None, tstate=None, + grid_resolution=None, ghosts=None, tstate=None, lboundaries=None, rboundaries=None, register_discrete_field=False, **kwds): """ @@ -904,6 +912,7 @@ class CartesianDiscreteScalarFieldView(CartesianDiscreteScalarFieldViewContainer like the current object, possibly on a different backend. /!\ The returned discrete field is not allocated. """ + assert ('global_resolution' not in kwds), 'Specify grid_resolution instead.' tstate = first_not_None(tstate, self._topology_state) if (is_read_only is not None): tstate._is_read_only = is_read_only @@ -912,12 +921,13 @@ class CartesianDiscreteScalarFieldView(CartesianDiscreteScalarFieldViewContainer btopo = self._dfield._topology field = bfield.field_like(name=name, pretty_name=pretty_name, + latex_name=latex_name, var_name=var_name, initial_values=initial_values, dtype=dtype, lboundaries=lboundaries, rboundaries=rboundaries, register_object=register_discrete_field) topology = btopo.topology_like(backend=backend, - global_resolution=global_resolution, ghosts=ghosts, + grid_resolution=grid_resolution, ghosts=ghosts, lboundaries=lboundaries, rboundaries=rboundaries) dfield = TmpCartesianDiscreteScalarField(field=field, topology=topology, diff --git a/hysop/fields/continuous_field.py b/hysop/fields/continuous_field.py index 7ba53d130..2d3da93b0 100644 --- a/hysop/fields/continuous_field.py +++ b/hysop/fields/continuous_field.py @@ -41,7 +41,7 @@ class SymbolContainerI(object): class NamedObjectI(object): __metaclass__ = ABCMeta - def __new__(cls, name, pretty_name=None, **kwds): + def __new__(cls, name, pretty_name=None, latex_name=None, var_name=None, **kwds): """ Create an abstract named object that contains a symbolic value. name : string @@ -52,59 +52,86 @@ class NamedObjectI(object): kwds: dict Keywords arguments for base class. """ + + obj = super(NamedObjectI, cls).__new__(cls, **kwds) + obj.rename(name=name, pretty_name=pretty_name, + latex_name=latex_name, var_name=var_name) + return obj + + def rename(self, name, pretty_name=None, latex_name=None): + """Change the names of this object.""" check_instance(name, str) check_instance(pretty_name, (str,unicode), allow_none=True) - - pretty_name = first_not_None(pretty_name, name) + check_instance(latex_name, str, allow_none=True) if isinstance(pretty_name, unicode): pretty_name = pretty_name.encode('utf-8') check_instance(pretty_name, str) - obj = super(NamedObjectI, cls).__new__(cls, **kwds) - obj._name = name - obj._pretty_name = pretty_name - return obj - - @abstractmethod - def short_description(self): - """Short description of this field as a string.""" - pass - - @abstractmethod - def long_description(self): - """Long description of this field as a string.""" - pass + pretty_name = first_not_None(pretty_name, name) + latex_name = first_not_None(latex_name, name) + self._name = name + self._pretty_name = pretty_name + self._latex_name = latex_name + def _get_name(self): """Return the name of this field.""" return self._name def _get_pretty_name(self): """Return the pretty name of this field.""" return self._pretty_name + def _get_latex_name(self): + """Return the latex name of this field.""" + return self._latex_name def __str__(self): return self.long_description() - def rename(self, name, pretty_name=None): - """Change the name or pretty name of this object.""" - pretty_name = first_not_None(pretty_name, name) - check_instance(name, str) - self._name = name - if isinstance(pretty_name, unicode): - pretty_name = pretty_name.encode('utf-8') - check_instance(pretty_name, str) - self._pretty_name = pretty_name - return self - + @abstractmethod + def short_description(self): + """Short description of this field as a string.""" + pass + + @abstractmethod + def long_description(self): + """Long description of this field as a string.""" + pass + name = property(_get_name) pretty_name = property(_get_pretty_name) + latex_name = property(_get_latex_name) class NamedScalarContainerI(NamedObjectI, SymbolContainerI): + @property def ndim(self): """Number of dimensions of this this tensor.""" return 0 + + def _get_var_name(self): + """Return the variable name of this field.""" + return self._var_name + + def rename(self, name, pretty_name=None, + latex_name=None, var_name=None): + """Change the names of this object.""" + super(NamedScalarContainerI, self).rename(name=name, + pretty_name=pretty_name, latex_name=latex_name) + self.check_and_set_varname(first_not_None(var_name, self._name)) + + def check_and_set_varname(self, var_name): + check_instance(var_name, str, allow_none=True) + + msg='Invalid variable name {}.'.format(var_name) + if var_name[0] in tuple(str(x) for x in range(10)): + raise RuntimeError(msg) + for c in '/*+-=|&()[]{}-!?:;,\'"#$^%<>@': + if c in var_name: + raise RuntimeError(msg) + self._var_name = var_name + + var_name = property(_get_var_name) class NamedTensorContainerI(NamedObjectI, SymbolContainerI): @@ -115,6 +142,13 @@ class NamedTensorContainerI(NamedObjectI, SymbolContainerI): obj._contained_objects = contained_objects return obj + def rename(self, name, pretty_name=None, + latex_name=None, var_name=None): + """Change the names of this object.""" + assert (var_name is None), 'Tensor do not have variable names.' + super(NamedTensorContainerI, self).rename(name=name, + pretty_name=pretty_name, latex_name=latex_name) + @property def size(self): """Full size of this container as if it was a 1D tensor.""" @@ -670,6 +704,7 @@ class ScalarField(NamedScalarContainerI, FieldContainerI): obj = super(ScalarField, cls).__new__(cls, domain=domain, name=name, pretty_name=pretty_name, + var_name=var_name, latex_name=latex_name, tag_prefix='f', tagged_cls=ScalarField, **kwds) obj._dtype = dtype obj._initial_values = initial_values @@ -680,9 +715,7 @@ class ScalarField(NamedScalarContainerI, FieldContainerI): # Symbolic representation of this field from hysop.symbolic.field import SymbolicField - obj._symbol = SymbolicField(field=obj, - var_name=var_name, - latex_name=latex_name) + obj._symbol = SymbolicField(field=obj) # Dictionnary of all the discretizations of this field. # keys are hysop.topology.topology.Topology, @@ -732,6 +765,7 @@ class ScalarField(NamedScalarContainerI, FieldContainerI): check_instance(obj.is_tmp, bool) def field_like(self, name, pretty_name=None, + latex_name=None, var_name=None, domain=None, dtype=None, is_tmp=None, lboundaries=None, rboundaries=None, initial_values=None, **kwds): @@ -744,6 +778,7 @@ class ScalarField(NamedScalarContainerI, FieldContainerI): rboundaries = first_not_None(rboundaries, self.rboundaries) initial_values = first_not_None(initial_values, self.initial_values) return ScalarField(name=name, pretty_name=pretty_name, + var_name=var_name, latex_name=latex_name, domain=domain, dtype=dtype, is_tmp=is_tmp, lboundaries=lboundaries, rboundaries=rboundaries, initial_values=initial_values, **kwds) @@ -782,7 +817,7 @@ class ScalarField(NamedScalarContainerI, FieldContainerI): *topology tags: [{}] ''').format(self.full_tag, self.name, self.pretty_name, - self.symbol._var_name, self.symbol._latex_name, + self.var_name, self.latex_name, self.dim, self.dtype, self.lboundaries.tolist(), self.rboundaries.tolist(), self.initial_values, diff --git a/hysop/fields/discrete_field.py b/hysop/fields/discrete_field.py index 3205e2b35..4b026955f 100644 --- a/hysop/fields/discrete_field.py +++ b/hysop/fields/discrete_field.py @@ -453,6 +453,12 @@ class DiscreteScalarFieldView(DiscreteScalarFieldViewContainerI, TaggedObjectVie def _get_pretty_name(self): """Get the name of the discrete field.""" return self._dfield._pretty_name + def _get_latex_name(self): + """Get the latex name of the discrete field.""" + return self._dfield._latex_name + def _get_var_name(self): + """Get the latex name of the discrete field.""" + return self._dfield._var_name def _get_dtype(self): """Get the data type of the discrete field.""" @@ -512,14 +518,6 @@ class DiscreteScalarFieldView(DiscreteScalarFieldViewContainerI, TaggedObjectVie h ^= hash(self._topology_state) return h - @property - def name(self): - return self._dfield._name - - @property - def pretty_name(self): - return self._dfield._pretty_name - @property def symbol(self): return self._dfield._symbol @@ -534,6 +532,8 @@ class DiscreteScalarFieldView(DiscreteScalarFieldViewContainerI, TaggedObjectVie name = property(_get_name) pretty_name = property(_get_pretty_name) + latex_name = property(_get_latex_name) + var_name = property(_get_var_name) dtype = property(_get_dtype) initial_values = property(_get_initial_values) @@ -547,6 +547,7 @@ class DiscreteScalarFieldView(DiscreteScalarFieldViewContainerI, TaggedObjectVie memory_request = property(_get_memory_request) memory_request_id = property(_get_memory_request_id) + class DiscreteScalarField(NamedScalarContainerI, TaggedObject): """ Discrete representation of scalar or vector fields, @@ -569,7 +570,9 @@ class DiscreteScalarField(NamedScalarContainerI, TaggedObject): @debug def __new__(cls, field, topology, register_discrete_field=True, - name=None, pretty_name=None, **kwds): + name=None, pretty_name=None, + var_name=None, latex_name=None, + **kwds): """ Creates a discrete field for a given continuous field and topology. @@ -579,6 +582,17 @@ class DiscreteScalarField(NamedScalarContainerI, TaggedObject): The continuous field that is dicrerized. topology: :class:`~hysop.topology.topology.Topology` The topology where to allocate the discrete field. + name : string, optional + A name for the field. + pretty_name: string or unicode, optional. + A pretty name used for display whenever possible. + Defaults to name. + var_name: string, optional. + A variable name used for code generation. + This will be passed to the symbolic representation of this discrete field. + latex_name: string, optional. + A variable name used for latex generation. + This will be passed to the symbolic representation of this discrete field. kwds: dict Base class arguments. """ @@ -587,12 +601,20 @@ class DiscreteScalarField(NamedScalarContainerI, TaggedObject): check_instance(name, str, allow_none=True) check_instance(pretty_name, (str,unicode), allow_none=True) - _name, _pretty_name = cls.format_discrete_names(field.name, - field.pretty_name, topology) + _name, _pretty_name, _var_name, _latex_name = \ + cls.format_discrete_names(field.name, + field.pretty_name, + field.s._var_name, + field.s._latex_name, + topology) + pretty_name = first_not_None(pretty_name, name, _pretty_name) + var_name = first_not_None(var_name, name, _var_name) + latex_name = first_not_None(latex_name, name, _latex_name) name = first_not_None(name, _name) obj = super(DiscreteScalarField, cls).__new__(cls, name=name, pretty_name=pretty_name, + var_name=var_name, latex_name=latex_name, tag_prefix='df', **kwds) assert isinstance(obj, DiscreteScalarFieldView), 'DiscreteScalarFieldView not inherited.' @@ -615,19 +637,23 @@ class DiscreteScalarField(NamedScalarContainerI, TaggedObject): return obj @classmethod - def format_discrete_names(cls, name, pretty_name, topology): + def format_discrete_names(cls, name, pretty_name, var_name, latex_name, topology): from hysop.tools.sympy_utils import subscript if (topology is None): # Tensor discrete field names (topology is not unique) name = '{}*'.format(name) pretty_name = '{}*'.format(pretty_name) + latex_name = '{}'.format(latex_name) + var_name = None else: # Scalar discrete field names name = '{}_t{}'.format(name, topology.id) - pretty_name = '{}_{}{}'.format(pretty_name, + pretty_name = '{}.{}{}'.format(pretty_name, u'\u209c'.encode('utf-8'), subscript(topology.id).encode('utf-8')) - return (name, pretty_name) + var_name = var_name + '_t{}'.format(topology.id) + latex_name = latex_name + '.t_{{{}}}'.format(0) + return (name, pretty_name, var_name, latex_name) class DiscreteTensorField(NamedTensorContainerI, DiscreteScalarFieldViewContainerI, TaggedObject): @@ -649,7 +675,8 @@ class DiscreteTensorField(NamedTensorContainerI, DiscreteScalarFieldViewContaine discrete fields may be defined on different topologies. """ - def __new__(cls, field, dfields, name=None, pretty_name=None, **kwds): + def __new__(cls, field, dfields, name=None, + pretty_name=None, latex_name=None, **kwds): check_instance(field, TensorField) check_instance(dfields, npw.ndarray, dtype=object, values=DiscreteScalarFieldView) assert npw.array_equal(field.shape, dfields.shape) @@ -661,12 +688,14 @@ class DiscreteTensorField(NamedTensorContainerI, DiscreteScalarFieldViewContaine name=name, pretty_name=pretty_name, **kwds) - _name, _pretty_name = DiscreteScalarField.format_discrete_names(field.name, - field.pretty_name, None) + _name, _pretty_name, _, _latex_name = DiscreteScalarField.format_discrete_names( + field.name, field.pretty_name, None, field.latex_name, None) name = first_not_None(name, _name) pretty_name = first_not_None(pretty_name, _pretty_name) + latex_name = first_not_None(latex_name, _latex_name) - obj = super(DiscreteTensorField, cls).__new__(cls, name=name, pretty_name=pretty_name, + obj = super(DiscreteTensorField, cls).__new__(cls, name=name, + pretty_name=pretty_name, latex_name=latex_name, tag_prefix='tdf', tagged_cls=DiscreteTensorField, contained_objects=dfields, **kwds) obj._field = field diff --git a/hysop/fields/field_requirements.py b/hysop/fields/field_requirements.py index 18af537ab..176c3dff2 100644 --- a/hysop/fields/field_requirements.py +++ b/hysop/fields/field_requirements.py @@ -233,8 +233,15 @@ class DiscreteFieldRequirements(object): msg='{} Dimension mismatch between field and topology.\n field={}d, topology={}d.' msg=msg.format(self._header, self.field.dim, topology.domain.dim) raise RuntimeError(msg) + if (topology.grid_resolution != self.topology_descriptor.grid_resolution).any(): + msg='{} Grid resolution mismatch between requirement and topology.\n ' + msg+=' requirement={}\n topology={}' + msg=msg.format(self._header, + self.topology_descriptor.grid_resolution, + topology.grid_resolution) + raise RuntimeError(msg) if (topology.global_resolution != self.topology_descriptor.global_resolution).any(): - msg='{} Discretisation mismatch between requirement and topology.\n ' + msg='{} Global resolution mismatch between requirement and topology.\n ' msg+=' requirement={}\n topology={}' msg=msg.format(self._header, self.topology_descriptor.global_resolution, diff --git a/hysop/mesh/cartesian_mesh.py b/hysop/mesh/cartesian_mesh.py index b0a383b69..ca8baaf19 100644 --- a/hysop/mesh/cartesian_mesh.py +++ b/hysop/mesh/cartesian_mesh.py @@ -81,7 +81,7 @@ class CartesianMeshView(MeshView): def _get_grid_npoints(self): """ Effective size of the global mesh. - Corresponds to np.prod(self.global_resolution - topology.is_periodic). + Corresponds to np.prod(self.grid_resolution) """ return prod(self._mesh._grid_resolution) @@ -944,21 +944,25 @@ class CartesianMesh(CartesianMeshView, Mesh): dim = domain.dim discretization = topology._topology._discretization - assert (discretization.resolution>=1).all() + assert (discretization.grid_resolution>=1).all() assert (discretization.ghosts>=0).all() - ghosts = np.asintegerarray(discretization.ghosts).copy() - resolution = np.asintegerarray(discretization.resolution).copy() - space_step = npw.asrealarray(domain.length / (resolution - 1)) - del resolution - - global_resolution = npw.asintegerarray(topology.global_resolution).copy() - global_start = npw.asintegerarray(global_start).copy() + ghosts = np.asintegerarray(discretization.ghosts).copy() + grid_resolution = np.asintegerarray(discretization.grid_resolution).copy() + periodicity = np.asintegerarray(discretization.periodicity).copy() + global_resolution = np.asintegerarray(discretization.global_resolution).copy() + # /!\ now we add one point on each periodic axes because the user give grid_resolution + # and not global resolution as it used to be. + assert all(global_resolution == grid_resolution + periodicity) + space_step = npw.asrealarray(domain.length / (grid_resolution + periodicity - 1)) + + topo_global_resolution = npw.asintegerarray(topology.global_resolution).copy() + topo_grid_resolution = npw.asintegerarray(topology.grid_resolution).copy() + global_start = npw.asintegerarray(global_start).copy() + assert all(topo_global_resolution == global_resolution) + assert all(topo_grid_resolution == grid_resolution) assert global_start.size == dim assert (global_start>=0).all() - # Remove 1 point on each periodic axe because of periodicity - grid_resolution = global_resolution - discretization.periodicity - # global boundaries and local boundaries (global_lboundaries, global_rboundaries) = discretization.boundaries del discretization diff --git a/hysop/operator/base/derivative.py b/hysop/operator/base/derivative.py index a603cabe3..769247c03 100644 --- a/hysop/operator/base/derivative.py +++ b/hysop/operator/base/derivative.py @@ -86,7 +86,7 @@ class SpaceDerivativeBase(object): check_instance(derivative, int, minval=1) check_instance(direction, int, minval=0, maxval=F.dim-1) check_instance(variables, dict, keys=Field, values=CartesianTopologyDescriptors) - + assert 0 <= direction < F.dim, direction assert 1 <= derivative, derivative diff --git a/hysop/symbolic/field.py b/hysop/symbolic/field.py index 5475b1ee3..2ce88e9f0 100644 --- a/hysop/symbolic/field.py +++ b/hysop/symbolic/field.py @@ -200,14 +200,22 @@ class FieldExpressionBuilder(object): class FieldBase(FunctionBase): - def __new__(cls, field, idx=None, name=None, pretty_name=None, **kwds): + def __new__(cls, field, idx=None, + **kwds): + assert 'name' not in kwds + assert 'pretty_name' not in kwds + assert 'latex_name' not in kwds + assert 'var_name' not in kwds check_instance(field, (Field, DiscreteField)) assert (field.nb_components == 1) or (idx is not None), (field.nb_components, idx) index = first_not_None(idx, [0])[0] - name = first_not_None(name, field.name) - pretty_name = first_not_None(pretty_name, field.pretty_name) + name = field.name + pretty_name = field.pretty_name + var_name = field.var_name + latex_name = field.latex_name assert (0<=index<field.nb_components), index - obj = super(FieldBase, cls).__new__(cls, name=name, pretty_name=pretty_name, **kwds) + obj = super(FieldBase, cls).__new__(cls, name=name, pretty_name=pretty_name, + var_name=var_name, latex_name=latex_name, **kwds) obj._field = field obj._index = index return obj @@ -241,9 +249,8 @@ class SymbolicDiscreteField(FieldBase, Symbol): """ def __new__(cls, field, name=None, fn=None, **kwds): check_instance(field, DiscreteField) - name = first_not_None(name, field.name) return super(SymbolicDiscreteField, cls).__new__(cls, field=field, - name=name, fn=fn, **kwds) + fn=fn, **kwds) @classmethod def from_field(cls, field): diff --git a/hysop/tools/parameters.py b/hysop/tools/parameters.py index a208e73d8..c00400c3b 100755 --- a/hysop/tools/parameters.py +++ b/hysop/tools/parameters.py @@ -76,6 +76,7 @@ class CartesianDiscretization(namedtuple("CartesianDiscretization", """ A struct to handle discretization parameters: - a resolution (either a list of int or a numpy array of int) + resolution is GRID_RESOLUTION. GLOBAL_RESOLUTION is GRID_RESOLUTION + PERIODICITY. - number of points in the ghost-layer. One value per direction, list or array. Default = None (ie. no ghosts). - global boundary conditions that should be prescribed on the left and the @@ -135,6 +136,19 @@ class CartesianDiscretization(namedtuple("CartesianDiscretization", else: return (self.lboundaries == BoundaryCondition.PERIODIC) + @property + def grid_resolution(self): + """Effective grid resolution given by user.""" + return self.resolution + + @property + def global_resolution(self): + """ + Logical grid resolution (grid_resolution + periodicity). + Can only be fetched if boundaries have been specified. + """ + return self.grid_resolution + self.periodicity + def __eq__(self, other): if self.__class__ != other.__class__: return NotImplemented diff --git a/hysop/topology/cartesian_descriptor.py b/hysop/topology/cartesian_descriptor.py index 96e513a13..80a3315d3 100644 --- a/hysop/topology/cartesian_descriptor.py +++ b/hysop/topology/cartesian_descriptor.py @@ -35,10 +35,12 @@ class CartesianTopologyDescriptor(TopologyDescriptor): msg='No ghost allowed for a topology descriptor.' raise ValueError(msg) - global_resolution = cartesian_discretization.resolution + global_resolution = cartesian_discretization.global_resolution + grid_resolution = cartesian_discretization.grid_resolution lboundaries = cartesian_discretization.lboundaries rboundaries = cartesian_discretization.rboundaries + check_instance(grid_resolution, np.ndarray, size=domain.dim, minval=2) check_instance(global_resolution, np.ndarray, size=domain.dim, minval=2) check_instance(lboundaries, npw.ndarray, dtype=object, size=domain.dim, values=BoundaryCondition, @@ -50,10 +52,12 @@ class CartesianTopologyDescriptor(TopologyDescriptor): is_lperiodic = (lboundaries==BoundaryCondition.PERIODIC) is_rperiodic = (rboundaries==BoundaryCondition.PERIODIC) + assert all((grid_resolution + is_lperiodic) == global_resolution) + msg='Invalid boundary conditions {} vs {}.' msg=msg.format(lboundaries, rboundaries) assert not (is_lperiodic ^ is_rperiodic).any(), msg - + # compute space step space_step = npw.asrealarray(domain.length / (global_resolution - 1)) npw.set_readonly(space_step) @@ -63,8 +67,13 @@ class CartesianTopologyDescriptor(TopologyDescriptor): @property def global_resolution(self): - """Get the global global_resolution of the discretization.""" - return self._cartesian_discretization.resolution + """Get the global global_resolution of the discretization (logical grid_size).""" + return self._cartesian_discretization.global_resolution + + @property + def grid_resolution(self): + """Get the global grid resolution of the discretization (effective grid size).""" + return self._cartesian_discretization.grid_resolution @property def lboundaries(self): @@ -110,9 +119,9 @@ class CartesianTopologyDescriptor(TopologyDescriptor): return h def __str__(self): - return ':CartesianTopologyDescriptor: backend={}, domain={}, resolution={}, bc=[{}]'.format( + return ':CartesianTopologyDescriptor: backend={}, domain={}, grid_resolution={}, bc=[{}]'.format( self.backend, self.domain.full_tag, - self.global_resolution, + self.grid_resolution, ','.join(('{}/{}'.format( str(lb).replace('HOMOGENEOUS_','')[:3], str(rb).replace('HOMOGENEOUS_','')[:3]) @@ -181,7 +190,7 @@ class CartesianTopologyDescriptor(TopologyDescriptor): by operators on variables and solved during operator's method get_field_requirements(). """ - discretization = CartesianDiscretization(resolution=self.global_resolution, + discretization = CartesianDiscretization(resolution=self.grid_resolution, lboundaries=self.lboundaries, rboundaries=self.rboundaries, ghosts=ghosts) diff --git a/hysop/topology/cartesian_topology.py b/hysop/topology/cartesian_topology.py index 0c0ea7c13..6a64a61bc 100644 --- a/hysop/topology/cartesian_topology.py +++ b/hysop/topology/cartesian_topology.py @@ -237,8 +237,11 @@ class CartesianTopologyView(TopologyView): # ATTRIBUTE GETTERS def _get_global_resolution(self): - """Returns global resolution of the discretization.""" - return self._proc_transposed(self._topology._discretization.resolution) + """Returns global resolution of the discretization (logical grid size).""" + return self._proc_transposed(self._topology._discretization.global_resolution) + def _get_grid_resolution(self): + """Returns grid resolution of the discretization (effective grid size).""" + return self._proc_transposed(self._topology._discretization.grid_resolution) def _get_ghosts(self): """Returns ghosts of the discretization.""" return self._proc_transposed(self._topology._discretization.ghosts) @@ -335,6 +338,7 @@ class CartesianTopologyView(TopologyView): return np.where(self._get_is_periodic() == True)[0].astype(np.int32) global_resolution = property(_get_global_resolution) + grid_resolution = property(_get_grid_resolution) ghosts = property(_get_ghosts) comm = property(_get_comm) @@ -372,13 +376,13 @@ class CartesianTopologyView(TopologyView): Short version of long_description(). """ s='{}[domain={}, backend={}, pcoords={}, pshape={}, ' - s+='resolution={}, ghosts={}, bc=({})]' + s+='grid_resolution={}, ghosts={}, bc=({})]' s = s.format( self.full_tag, self.domain.domain.full_tag, self.backend.kind, self.proc_coords, self.proc_shape, - '[{}]'.format(','.join(str(s) for s in self.global_resolution)), + '[{}]'.format(','.join(str(s) for s in self.grid_resolution)), '[{}]'.format(','.join(str(g) for g in self.ghosts)), ','.join(('{}/{}'.format( str(lb).replace('HOMOGENEOUS_','')[:3], @@ -685,16 +689,17 @@ class CartesianTopology(CartesianTopologyView, Topology): check_instance(self.domain, BoxView) check_instance(self.mesh, CartesianMeshView) - def topology_like(self, backend=None, global_resolution=None, ghosts=None, + def topology_like(self, backend=None, grid_resolution=None, ghosts=None, lboundaries=None, rboundaries=None, mpi_params=None, cart_shape=None, **kwds): """Return a topology like this object, possibly altered.""" - global_resolution = first_not_None(global_resolution, self._discretization.resolution) + assert ('global_resolution' not in kwds), 'Specify grid_resolution instead.' + grid_resolution = first_not_None(grid_resolution, self._discretization.grid_resolution) ghosts = first_not_None(ghosts, self._discretization.ghosts) lboundaries = first_not_None(lboundaries, self._discretization.lboundaries) rboundaries = first_not_None(rboundaries, self._discretization.rboundaries) backend = first_not_None(backend, self._backend) - discretization = CartesianDiscretization(resolution=global_resolution, ghosts=ghosts, + discretization = CartesianDiscretization(resolution=grid_resolution, ghosts=ghosts, lboundaries=lboundaries, rboundaries=rboundaries) # find out the target mpi_params @@ -706,7 +711,7 @@ class CartesianTopology(CartesianTopologyView, Topology): mpi_params = backend.cl_env.mpi_params mpi_params = first_not_None(mpi_params, self.mpi_params) - if (self.domain.dim == global_resolution.size): + if (self.domain.dim == grid_resolution.size): cart_shape = first_not_None(cart_shape, self.proc_shape) return CartesianTopology(domain=self._domain, mpi_params=mpi_params, @@ -919,7 +924,7 @@ class CartesianTopology(CartesianTopologyView, Topology): def _compute_mesh(self, domain, discretization): assert isinstance(domain, Box) assert isinstance(discretization, CartesianDiscretization) - assert (self.domain_dim == discretization.resolution.size), \ + assert (self.domain_dim == discretization.grid_resolution.size), \ 'The resolution size differs from the domain dimension.' proc_coords = self._proc_coords @@ -929,8 +934,10 @@ class CartesianTopology(CartesianTopologyView, Topology): domain_dim = domain.dim periodicity = discretization.periodicity - # Remove 1 point on each periodic axe because of periodicity - computational_grid_resolution = discretization.resolution - periodicity + # /!\ Now we assume that the user gives us the grid resolutionn + # and not the global_resolution as it used to be. + # We do not remove 1 point on each periodic axe because of periodicity + computational_grid_resolution = discretization.grid_resolution # Number of "computed" points (i.e. excluding ghosts). pts_noghost = npw.dim_zeros((domain_dim)) -- GitLab