diff --git a/examples/sediment_deposit/sediment_deposit.py b/examples/sediment_deposit/sediment_deposit.py index e5f827dc5d294e0ed53dc73bb6fc73ece8e09268..82c66c9a37183e53b6ad8716867bf7bb22148385 100644 --- a/examples/sediment_deposit/sediment_deposit.py +++ b/examples/sediment_deposit/sediment_deposit.py @@ -248,7 +248,7 @@ def compute(args): g = 1.0 Fext = SymbolicExternalForce(name='S', Fext=(0,-g*Ss), diffusion = {S: nu_S}) - external_force = SpectralExternalForce(vorticity=Ws, dt=dt, + external_force = SpectralExternalForce(vorticity=vorti, dt=dt, Fext=Fext, Finf=True, implementation=impl, variables={vorti: npts, S: npts}, diff --git a/hysop/backend/device/opencl/operator/external_force.py b/hysop/backend/device/opencl/operator/external_force.py index d5e10fb66ecca28ed4351b6693449379bc335b0c..64238517609fb787715242becdee0a8daadaaceb 100644 --- a/hysop/backend/device/opencl/operator/external_force.py +++ b/hysop/backend/device/opencl/operator/external_force.py @@ -4,7 +4,7 @@ from hysop.tools.numerics import float_to_complex_dtype from hysop.tools.numpywrappers import npw from hysop.tools.decorators import debug from hysop.tools.warning import HysopWarning -from hysop.operator.base.curl import SpectralExternalForceOperatorBase +from hysop.operator.base.external_force import SpectralExternalForceOperatorBase from hysop.backend.device.opencl.opencl_symbolic import OpenClSymbolic from hysop.core.graph.graph import op_apply from hysop.core.memory.memory_request import MemoryRequest @@ -22,82 +22,13 @@ class OpenClSpectralExternalForce(SpectralExternalForceOperatorBase, OpenClSymbo @debug def __init__(self, **kwds): - """ - What is computed in 2D: - FEXT_X = fft(Fext_X) - tmp = dFEXT/dy - FEXT_Y = fft(Fext_Y) - tmp -= dFEXT/dx - Fmin[0], Fmax[0], Finf[0] = min(tmp), max(tmp), max(abs(tmp)) - Wz += dt*tmp - What is computed in 3D: - FEXT_X = fft(Fext_X) - FEXT_Y = fft(Fext_Y) - FEXT_Z = fft(Fext_Z) - - tmp = - Fmin[0], Fmax[0], Finf[0] = min(tmp), max(tmp), max(abs(tmp)) - Wx += dt*tmp - - tmp = - Fmin[1], Fmax[1], Finf[1] = min(tmp), max(tmp), max(abs(tmp)) - Wy += dt*tmp - - tmp = - Fmin[1], Fmax[1], Finf[1] = min(tmp), max(tmp), max(abs(tmp)) - Wz += dt*tmp - """ super(OpenClSpectralExternalForce, self).__init__(**kwds) - assert (len(self.forward_transforms) % 2 == 0) - N = len(self.forward_transforms)//2 - assert len(self.K)==2*N - - kernel_names = () - for (i,(Ft,(tg,Ki))) in enumerate(zip(self.forward_transforms, self.K)): - Fhs = Ft.output_symbolic_array('F{}_hat'.format(i)) - - kname = 'filter_curl_{}d_{}'.format(Fhs.dim, i) - kernel_names += (kname,) - - is_complex = Ki.is_complex - Ki = tg._indexed_wave_numbers[Ki] - - if is_complex: - expr = ComplexMul(Ki, Fhs) - else: - expr = Ki*Fhs - - if (i<N): - expr = Assignment(Fhs, +expr) - else: - expr = Assignment(Fhs, -expr) - - self.require_symbolic_kernel(kname, expr) - - self._kernel_names = kernel_names - @debug def setup(self, work): super(OpenClSpectralExternalForce, self).setup(work) - curl_filters = () - for kname in self._kernel_names: - kernel, _ = self.symbolic_kernels[kname] - kernel = functools.partial(kernel, queue=self.cl_env.default_queue) - curl_filters += (kernel,) - self.curl_filters = curl_filters - self.exchange_ghosts = self.dFout.exchange_ghosts(build_launcher=True) - assert len(self.forward_transforms)==len(self.backward_transforms)==len(curl_filters) @op_apply def apply(self, **kwds): - """Solve the ExternalForce equation.""" super(OpenClSpectralExternalForce, self).apply(**kwds) - for (Ft,filter_curl,Bt) in zip(self.forward_transforms, - self.curl_filters, - self.backward_transforms): - evt = Ft() - evt = filter_curl() - evt = Bt() - if (self.exchange_ghosts is not None): - evt = self.exchange_ghosts() + diff --git a/hysop/operator/adapt_timestep.py b/hysop/operator/adapt_timestep.py index 820bd4946bce6c00d8c4afed5f0935bc4e6c5241..68fee1950cedf3c6ed917835882763e1fa596bc3 100755 --- a/hysop/operator/adapt_timestep.py +++ b/hysop/operator/adapt_timestep.py @@ -76,8 +76,7 @@ class TimestepCriteria(ComputationalGraphOperator): class ConstantTimestepCriteria(TimestepCriteria): @debug - def __init__(self, cst, parameter, criteria, - Finf=None, + def __init__(self, cst, parameter, Finf, name=None, pretty_name=None, **kwds): """ Initialize a ConstantTimestepCriteria. @@ -98,8 +97,10 @@ class ConstantTimestepCriteria(TimestepCriteria): kwds: dict Base class arguments. """ + if isinstance(cst, (int, long)): + cst = float(cst) assert (cst > 0.0), 'negative cst factor.' - check_instance(cst, (float, np.ndarray, list, tuple)) + check_instance(cst, (float, npw.ndarray, list, tuple)) check_instance(Finf, (ScalarParameter, TensorParameter)) if isinstance(Finf, ScalarParameter): assert isinstance(cst, float) @@ -107,19 +108,18 @@ class ConstantTimestepCriteria(TimestepCriteria): else: is_scalar = False if isinstance(cst, float): - cst = np.full(shape=Finf.shape, dtype=Finf.dtype, fill_value=cst) + cst = npw.full(shape=Finf.shape, dtype=Finf.dtype, fill_value=cst) if isinstance(cst, (list, tuple)): assert Finf.ndim == 1 - cst = np.asarray(cst) + cst = npw.asarray(cst) msg='Shape mismatch between parameter {} and cst {}.' msg=msg.format(Finf.shape, cst.shape) assert Finf.shape == cst.shape, msg - input_params[Finf.name] = Finf name = first_not_None(name, 'CST') pretty_name = first_not_None(pretty_name, name) super(ConstantTimestepCriteria, self).__init__(name=name, pretty_name=pretty_name, - input_params=input_params, + input_params={Finf.name: Finf}, output_params={parameter.name: parameter}, parameter=parameter, **kwds) self.cst = cst @@ -133,13 +133,13 @@ class ConstantTimestepCriteria(TimestepCriteria): if self.is_scalar: assert Finf >= 0 if (Finf == 0): - return np.inf + return npw.inf else: return cst/Finf else: assert Finf.min() >= 0 mask = (Finf!=0) - dt = np.zeros_like(cst, fill_value=np.inf) + dt = npw.zeros_like(cst, fill_value=npw.inf) dt[mask] = cst[mask] / Finf[mask] return dt.min() @@ -511,8 +511,7 @@ class AdaptiveTimeStep(ComputationalGraphNodeGenerator): name=param_name, pretty_name=param_pretty_name, basename=name) criteria = ConstantTimestepCriteria(cst=cst, Finf=Finf, - parameter=parameter, - name=name, pretty_name=pretty_name, **kwds) + parameter=parameter, name=name, pretty_name=pretty_name, **kwds) self._push_criteria(parameter.name, criteria) def push_cfl_criteria(self, cfl, Fmin=None, Fmax=None, Finf=None, diff --git a/hysop/operator/base/min_max.py b/hysop/operator/base/min_max.py index 0bbed36412c78c2f184581afe5b517c4810e1e79..964e2fcf83ed6d462ee1e3dca751ce362421cd3a 100644 --- a/hysop/operator/base/min_max.py +++ b/hysop/operator/base/min_max.py @@ -27,7 +27,7 @@ class MinMaxFieldStatisticsBase(object): @classmethod def build_parameters(cls, field, components, all_quiet, - Fmin, Fmax, Finf, pbasename, ppbasename): + Fmin, Fmax, Finf, pbasename, ppbasename, dtype=None): if ( ((Fmin is None) or (Fmin is False)) and ((Fmax is None) or (Fmax is False)) and ((Finf is None) or (Finf is False))): @@ -37,8 +37,10 @@ class MinMaxFieldStatisticsBase(object): msg+=' tensor parameter.' raise ValueError(msg) - pbasename = first_not_None(pbasename, field.name) - ppbasename = first_not_None(ppbasename, field.pretty_name.decode('utf-8')) + if (field is not None): + dtype = first_not_None(dtype, field.dtype) + pbasename = first_not_None(pbasename, field.name) + ppbasename = first_not_None(ppbasename, field.pretty_name.decode('utf-8')) def make_param(k, quiet): return TensorParameter(name=names[k], pretty_name=pretty_names[k], @@ -56,7 +58,9 @@ class MinMaxFieldStatisticsBase(object): 'Finf': u'|{}|\u208a'.format(ppbasename), } - components = to_tuple(first_not_None(components, range(field.nb_components))) + if (field is not None): + components = first_not_None(components, range(field.nb_components)) + components = to_tuple(components) nb_components = len(components) parameters = {} diff --git a/hysop/operator/external_force.py b/hysop/operator/external_force.py index 564a113a51001f0e02dd172317afeaadbe120471..bb77ad21c5bfa09be1ba8c136a8ae342c36d7c51 100644 --- a/hysop/operator/external_force.py +++ b/hysop/operator/external_force.py @@ -1,10 +1,19 @@ +import sympy as sm from abc import ABCMeta, abstractmethod -from hysop.fields -from hysop.symbolic.field import AppliedSymbolicField, SymbolicDiscreteField +from hysop.constants import Implementation +from hysop.fields.continuous_field import Field +from hysop.symbolic.field import AppliedSymbolicField, SymbolicField from hysop.symbolic.parameter import SymbolicScalarParameter +from hysop.parameters.tensor_parameter import TensorParameter from hysop.parameters.scalar_parameter import ScalarParameter from hysop.operator.min_max import MinMaxFieldStatisticsBase +from hysop.core.graph.computational_node_frontend import ComputationalGraphNodeFrontend +from hysop.topology.cartesian_descriptor import CartesianTopologyDescriptors +from hysop.tools.types import first_not_None, to_tuple, check_instance +from hysop.tools.sympy_utils import nabla +from hysop.tools.interface import NamedObjectI +from hysop.tools.decorators import debug class ExternalForce(NamedObjectI): __metaclass__ = ABCMeta @@ -16,18 +25,12 @@ class ExternalForce(NamedObjectI): self._dim = dim self._Fext = Fext - for f in self.input_fields: + for f in self.input_fields(): msg='Expected field dimension to be {} but got {} from field {}.' - msg=msg.format(dim, field.dim, field.short_description()) + msg=msg.format(dim, f.dim, f.short_description()) assert f.dim == dim, msg - @property - def name(self): - return self._diffusion - @property - def pretty_name(self): - return self._diffusion @property def Fext(self): return self._Fext @@ -52,18 +55,18 @@ class ExternalForce(NamedObjectI): pass -class SymbolicExternalForce(object): +class SymbolicExternalForce(ExternalForce): def __init__(self, name, Fext, diffusion=None, **kwds): """ Specify an external force as a tuple of symbolic expressions. 2D ExternalForce example: 1) Fext = -rho*g*e_y where rho is a field and g a constant - Fext = (0, -rho.s*g) + Fext = (0, -rho.s()*g) 2) Fext = (Rs*S+C)*e_y - Fext = (0, -Rs*S.s+C.s) + Fext = (0, -Rs*S.s()+C.s()) """ - check_instance(Fext, tuple) + Fext = to_tuple(Fext) dim = len(Fext) Fext = list(Fext) @@ -72,14 +75,15 @@ class SymbolicExternalForce(object): Fext[i] = 0 # curl(const) = 0 msg='Expression "{}" contains a SymbolicField, did you forget to apply it ?' msg=msg.format(e) - assert not e.atoms(SymbolicField), msg + if isinstance(e, sm.Basic): + assert not e.atoms(SymbolicField), msg Fext = tuple(Fext) super(SymbolicExternalForce, self).__init__(name=name, dim=dim, Fext=Fext, **kwds) diffusion = first_not_None(diffusion, {}) for (k,v) in diffusion.iteritems(): - assert k in self.input_fields + assert k in self.input_fields(), k.short_description() assert isinstance(v, ScalarParameter) self._diffusion = diffusion @@ -95,11 +99,44 @@ class SymbolicExternalForce(object): return set() def _extract_objects(self, obj_type): - objs = () + objs = set() for e in self.Fext: - objs += e.atoms(obj_type) + try: + objs.update(e.atoms(obj_type)) + except AttributeError: + pass return objs + def short_description(self): + return 'SymbolicExternalForce[name={}]'.format(self.name) + + def long_description(self): + sep = '\n *' + expressions = sep + sep.join('F{} = {}'.format(x,e) for (x,e) in zip('xyz',self.Fext)) + diffusion = sep + sep.join('{}: {}'.format(f.pretty_name, p.pretty_name) + for (f,p) in self.diffusion.iteritems()) + input_fields = ', '.join(f.pretty_name for f in self.input_fields()) + output_fields = ', '.join(f.pretty_name for f in self.output_fields()) + input_params = ', '.join(p.pretty_name for p in self.input_params()) + output_params = ', '.join(p.pretty_name for p in self.output_params()) + + ss = \ + '''SymbolicExternalForce: + name: {} + pretty_name: {} + expressions: {} + diffusion: {} + ----------------- + input_fields: {} + output_fields: {} + input_params: {} + output_params: {} + '''.format(self.name, self.pretty_name, + expressions, diffusion, + input_fields, output_fields, + input_params, output_params) + return ss + class SpectralExternalForce(ComputationalGraphNodeFrontend): """ @@ -120,10 +157,11 @@ class SpectralExternalForce(ComputationalGraphNodeFrontend): @debug def __init__(self, vorticity, Fext, dt, variables, - Fmin=None, Fmax=None, Finf=None, + Fmin=None, Fmax=None, Finf=None, + all_quiet=False, pbasename=None, ppbasename=None, implementation=None, base_kwds=None, **kwds): """ - Create an operator that computes the curl of a given input field Fext or a set of symbolic expressions. + Create an operator that computes the curl of a given input force field Fext. Only the following configurations are supported: dim nb_components | dim nb_components @@ -145,10 +183,15 @@ class SpectralExternalForce(ComputationalGraphNodeFrontend): All contained field have to live on the same domain. Fext: hysop.operator.external_force.ExternalForce Expression of the external force. - F...: TensorParameter or boolean, optional + dt: ScalarParameter + Timestep paramater. + F...: ScalarParameter, TensorParameter or boolean, optional TensorParameters should match the shape of tmp (see Notes). If set to True, the TensorParameter will be generated automatically. - Autogenerated TensorParameters that are not required by the user are set to be quiet. + all_quiet: bool + Force all autogenerated TensorParameter to be quiet. + By default, only the autogenerated TensorParameters that are not required + by the user are set to be quiet. pbasename: str, optional Parameters basename for created parameters. Defaults to 'curl_{}'.format(Fext.name). @@ -165,28 +208,42 @@ class SpectralExternalForce(ComputationalGraphNodeFrontend): Notes ----- - If Fext.dim == 2, Fext + If dim == 2, it is expected that: + vorticity has only one component + Fext has 2 components + Expected parameters are ScalarParameters or TensorParameters of shape (1,) + Else if dim == 3: + vorticity has 3 components + Fext has 3 components + Expected parameters are TensorParameter of shape (3,) """ base_kwds = first_not_None(base_kwds, {}) check_instance(vorticity, Field) check_instance(Fext, ExternalForce) + check_instance(dt, ScalarParameter) check_instance(variables, dict, keys=Field, values=CartesianTopologyDescriptors) # Pregenerate parameters so that we can directly store them in self. default_pbasename = 'curl_{}'.format(Fext.name) - default_ppbasename = '{}_{}'.format(nabla, Fext.pretty_name) + default_ppbasename = u'{}x{}'.format(nabla, Fext.pretty_name) pbasename = first_not_None(pbasename, default_pbasename) ppbasename = first_not_None(ppbasename, default_ppbasename) - parameters = MinMaxFieldStatisticsBase.build_parameters(field=field, - components=components, all_quiet=all_quiet, + parameters = MinMaxFieldStatisticsBase.build_parameters(field=vorticity, + components=None, dtype=None, all_quiet=all_quiet, Fmin=Fmin, Fmax=Fmax, Finf=Finf, pbasename=pbasename, ppbasename=ppbasename) (Fmin, Fmax, Finf) = tuple(parameters[k] for k in ('Fmin', 'Fmax', 'Finf')) - - super(Curl, self).__init__(Fin=Fin, Fout=Fout, variables=variables, - candidate_input_tensors=(Fin,), - candidate_output_tensors=(Fout,), + + check_instance(Fmin, TensorParameter, allow_none=True) + check_instance(Fmax, TensorParameter, allow_none=True) + check_instance(Finf, TensorParameter, allow_none=True) + + super(SpectralExternalForce, self).__init__(vorticity=vorticity, + Fext=Fext, dt=dt, variables=variables, + Fmin=Fmin, Fmax=Fmax, Finf=Finf, + candidate_input_tensors=(vorticity,), + candidate_output_tensors=(vorticity,), implementation=implementation, base_kwds=base_kwds, **kwds) diff --git a/hysop/tools/interface.py b/hysop/tools/interface.py index be755838b7aac7819c6e8ceb05f92a4865ea3c34..dcd1334ab0550d5c37fdd01b540812049136b94d 100644 --- a/hysop/tools/interface.py +++ b/hysop/tools/interface.py @@ -39,17 +39,18 @@ class NamedObjectI(object): latex_name=latex_name, var_name=var_name) return obj - def rename(self, name, pretty_name=None, latex_name=None): + def rename(self, name, pretty_name=None, latex_name=None, var_name=None): """Change the names of this object.""" check_instance(name, str) check_instance(pretty_name, (str,unicode), allow_none=True) 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) pretty_name = first_not_None(pretty_name, name) latex_name = first_not_None(latex_name, name) + + if isinstance(pretty_name, unicode): + pretty_name = pretty_name.encode('utf-8') + check_instance(pretty_name, str) self._name = name self._pretty_name = pretty_name