From b1edbf009a39c92e4a13bdb6a97a640b695799f9 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Keck <Jean-Baptiste.Keck@imag.fr> Date: Thu, 1 Jun 2017 17:34:49 +0200 Subject: [PATCH] topology descriptors --- .../backend/device/opencl/opencl_operator.py | 61 ++++--- .../operator/directional/advection_dir.py | 5 +- .../backend/host/fortran/operator/poisson.py | 4 +- hysop/core/graph/computational_graph.py | 61 +++++-- hysop/core/graph/computational_node.py | 92 +++++----- hysop/core/graph/computational_operator.py | 88 ++++++--- hysop/core/graph/continuous.py | 52 ++---- hysop/core/mpi/topology.py | 29 ++- hysop/core/mpi/topology_descriptor.py | 158 ++++++++++++++-- hysop/fields/field_requirements.py | 168 ++++++++++-------- hysop/numerics/splitting/strang.py | 4 +- hysop/old/operator.old/computational.py | 4 +- hysop/old/operator.old/continuous.py | 2 +- hysop/old/operator.old/curlAndDiffusion.py | 4 +- hysop/old/operator.old/redistribute.py | 4 +- hysop/operator/diffusion.py | 4 +- hysop/operator/directional/advection_dir.py | 4 +- hysop/operator/directional/stretching_dir.py | 4 +- hysop/operator/poisson.py | 4 +- hysop/operator/redistribute.py | 7 +- hysop/problem.py | 2 + hysop/tools/types.py | 6 +- 22 files changed, 506 insertions(+), 261 deletions(-) diff --git a/hysop/backend/device/opencl/opencl_operator.py b/hysop/backend/device/opencl/opencl_operator.py index 2679c1f46..27506a5ae 100644 --- a/hysop/backend/device/opencl/opencl_operator.py +++ b/hysop/backend/device/opencl/opencl_operator.py @@ -7,14 +7,16 @@ discrete operators working on the OpenCl backend. """ from abc import ABCMeta +from hysop.constants import Precision, Backend from hysop.tools.decorators import debug from hysop.tools.types import check_instance, first_not_None -from hysop.constants import Precision, Backend -from hysop.core.graph.computational_operator import ComputationalGraphOperator from hysop.backend.device.codegen.structs.mesh_info import MeshInfoStruct -from hysop.backend.device.opencl.opencl_tools import get_or_create_opencl_env from hysop.backend.device.kernel_config import KernelConfig +from hysop.backend.device.opencl.opencl_tools import get_or_create_opencl_env +from hysop.backend.device.opencl.opencl_env import OpenClEnvironment from hysop.core.mpi.topology import Topology +from hysop.core.mpi.topology_descriptor import TopologyDescriptor +from hysop.core.graph.computational_operator import ComputationalGraphOperator class OpenClOperator(ComputationalGraphOperator): """ @@ -42,10 +44,13 @@ class OpenClOperator(ComputationalGraphOperator): KernelConfig: user build options, defines, precision and autotuner configuration """ + super(OpenClOperator, self).__init__(**kwds) + + cl_env = cl_env or get_or_create_opencl_env() + check_instance(cl_env, OpenClEnvironment) + self.cl_env = cl_env - self.cl_env = cl_env or get_or_create_opencl_env() - def supported_backends(self): """ return the backends that this operator's topologies can support. @@ -75,16 +80,16 @@ class OpenClOperator(ComputationalGraphOperator): else: from hysop.backend.device.opencl.opencl_tools import convert_precision precision = convert_precision(precision) - + self.precision = precision self.autotuner_config = autotuner_config - - @debug - def post_initialize(self): - super(OpenClOperator, self).post_initialize() - self._check_cl_env() + self._initialize_cl_build_options(kernel_config.user_build_options) self._initialize_cl_size_constants(kernel_config.user_size_constants) + + def check(self): + super(OpenClOperator, self).check() + self._check_cl_env() @debug def handle_field_requirements(self): @@ -101,14 +106,30 @@ class OpenClOperator(ComputationalGraphOperator): Keys are continuous fields and values are of type hysop.fields.field_requirement.DiscreteFieldRequirements - Default is Backend.HOST, no min or max ghosts, Basis.CARTESIAN and no specific + Default is Backend.OPENCL, no min or max ghosts, Basis.CARTESIAN and no specific transposition state for each input and output variables. """ + + # by default we create OPENCL (gpu) TopologyDescriptors + for field, topo_descriptor in self.input_vars.iteritems(): + topo_descriptor = TopologyDescriptor.build_descriptor( + backend=Backend.OPENCL, + operator=self, + field=field, + handle=topo_descriptor, + cl_env=self.cl_env) + self.input_vars[field] = topo_descriptor + + for field, topo_descriptor in self.output_vars.iteritems(): + topo_descriptor = TopologyDescriptor.build_descriptor( + backend=Backend.OPENCL, + operator=self, + field=field, + handle=topo_descriptor, + cl_env=self.cl_env) + self.output_vars[field] = topo_descriptor + super(OpenClOperator, self).handle_field_requirements() - for req in self.input_field_requirements.values(): - req.set_backend(Backend.OPENCL) - for req in self.output_field_requirements.values(): - req.set_backend(Backend.OPENCL) @debug def discretize(self): @@ -136,20 +157,18 @@ class OpenClOperator(ComputationalGraphOperator): @debug def _check_cl_env(self): """ - Check if all topologies are on OpenCL backend and check that all opencl environments match. - Sets attribute cl_env. + Check if all topologies are on OpenCL backend and check that all opencl environments + match. """ topo = (self.input_vars.values()+self.output_vars.values())[0] assert isinstance(topo, Topology) assert topo.backend.kind == Backend.OPENCL - ref_env = topo.cl_env + ref_env = self.cl_env for topo in set(self.input_vars.values()+self.output_vars.values()): assert isinstance(topo, Topology) assert topo.backend.kind == Backend.OPENCL assert topo.cl_env == ref_env - - self.cl_env = ref_env @debug def _initialize_cl_build_options(self, user_options): diff --git a/hysop/backend/device/opencl/operator/directional/advection_dir.py b/hysop/backend/device/opencl/operator/directional/advection_dir.py index 14c13eead..c30376e23 100644 --- a/hysop/backend/device/opencl/operator/directional/advection_dir.py +++ b/hysop/backend/device/opencl/operator/directional/advection_dir.py @@ -1,9 +1,10 @@ -from hysop import Field, TopologyDescriptor +from hysop import Field from hysop.tools.numpywrappers import npw from hysop.constants import BoundaryCondition from hysop.tools.decorators import debug from hysop.tools.types import check_instance +from hysop.core.mpi.topology_descriptor import CartesianDescriptors from hysop.core.graph.graph import not_initialized, initialized, discretized, ready from hysop.backend.device.kernel_config import KernelConfig @@ -71,7 +72,7 @@ class OpenClDirectionalAdvection(OpenClDirectionalOperator): check_instance(velocity, Field) check_instance(advected_fields, list, values=Field) check_instance(advected_fields_out, list, values=Field) - check_instance(variables, dict, keys=Field, values=TopologyDescriptor) + check_instance(variables, dict, keys=Field, values=CartesianDescriptors) assert (len(advected_fields)==len(advected_fields_out)) input_vars = { velocity: variables[velocity] } diff --git a/hysop/backend/host/fortran/operator/poisson.py b/hysop/backend/host/fortran/operator/poisson.py index 2ba2eb7d5..a4439a9f9 100644 --- a/hysop/backend/host/fortran/operator/poisson.py +++ b/hysop/backend/host/fortran/operator/poisson.py @@ -3,7 +3,7 @@ from hysop.tools.types import check_instance, InstanceOf from hysop.tools.decorators import debug from hysop.tools.numpywrappers import npw from hysop.fields.continuous import Field -from hysop.core.mpi.topology_descriptor import TopologyDescriptor +from hysop.core.mpi.topology_descriptor import CartesianDescriptors from hysop.constants import FieldProjection from hysop.backend.host.fortran.operator.fortran_fftw import fftw2py, FortranFFTWOperator @@ -40,7 +40,7 @@ class PoissonFFTW(FortranFFTWOperator): check_instance(velocity, Field) check_instance(vorticity, Field) - check_instance(variables, dict, keys=Field, values=TopologyDescriptor) + check_instance(variables, dict, keys=Field, values=CartesianDescriptors) assert velocity.domain is vorticity.domain, 'only one domain is supported' assert variables[velocity] is variables[vorticity], 'only one topology is supported' diff --git a/hysop/core/graph/computational_graph.py b/hysop/core/graph/computational_graph.py index 482972148..ad8ecf920 100644 --- a/hysop/core/graph/computational_graph.py +++ b/hysop/core/graph/computational_graph.py @@ -67,7 +67,7 @@ class ComputationalGraph(ComputationalGraphNode): def topology_report(self): def print_topo(topo): return 'topology_id={}, task_id={}, shape={}, ghosts={}'.format( - topo.get_id(), topo.task_id(), topo.shape, topo.ghosts()) + topo.get_id(), topo.task_id(), topo.shape, topo.ghosts) ss = '== ComputationalGraph {} topology report =='.format(self.name) for (backend,topologies) in self.get_topologies().iteritems(): @@ -107,7 +107,6 @@ class ComputationalGraph(ComputationalGraphNode): return method = super(ComputationalGraph, self).initialize(topgraph_method) - self._build_topologies() self._build_graph(outputs_are_inputs=outputs_are_inputs) self._init_base(**self._kwds) @@ -120,28 +119,60 @@ class ComputationalGraph(ComputationalGraphNode): input_field_requirements = self.input_field_requirements output_field_requirements = self.output_field_requirements for node in self.nodes: + if __DEBUG__: + print + print '{} INITIALIZE'.format(node.name) node.initialize(self.method) + if __DEBUG__: + print 'NODE INPUT REQUIREMENTS: {}' + print '==========================' + print for ifield,ireqs in node.input_field_requirements.iteritems(): - ireqs = ireqs if isinstance(ireqs, MultiFieldRequirements) \ - else MultiFieldRequirements(ireqs) + if not isinstance(ireqs, MultiFieldRequirements): + _ireqs = ireqs + ireqs = MultiFieldRequirements(ifield) + ireqs.update(_ireqs) if ifield in input_field_requirements: + print 'ADD IN {}'.format(ireqs.nrequirements()) input_field_requirements[ifield].update(ireqs) else: + print 'FIRST IN' input_field_requirements[ifield] = ireqs + print 'INPUT OPERATOR {}'.format(node.name), + print input_field_requirements[ifield].nrequirements() for ofield,oreqs in node.output_field_requirements.iteritems(): - oreqs = oreqs if isinstance(oreqs, MultiFieldRequirements) - else MultiFieldRequirements(oreqs) + if not isinstance(oreqs, MultiFieldRequirements): + _oreqs = oreqs + oreqs = MultiFieldRequirements(ofield) + oreqs.update(_oreqs) if ofield in output_field_requirements: + print 'ADD OUT {}'.format(oreqs.nrequirements()) output_field_requirements[ofield].update(oreqs) else: + print 'FIRST OUT' output_field_requirements[ofield] = oreqs + print 'OUTPUT OPERATOR {}'.format(node.name), + print output_field_requirements[ofield].nrequirements() @debug def _build_topologies(self): - self.input_field_requirements.build_topologies() - self.output_field_requirements.build_topologies() + for field_reqs in self.input_field_requirements.values(): + assert isinstance(field_reqs, MultiFieldRequirements) + print 'FIELD {}: building {} requirements.'.format(field_reqs.field.name, field_reqs.nrequirements()) + field_reqs.build_topologies() + for field_reqs in self.output_field_requirements.values(): + assert isinstance(field_reqs, MultiFieldRequirements) + field_reqs.build_topologies() + print + print 'AFTER BUILD' + for node in self.nodes: + if isinstance(node, ComputationalGraphNode): + print node.name + print node.input_vars.values() + print node.output_vars.values() + print @staticmethod def _op_info(op, jmp=False): @@ -195,8 +226,10 @@ class ComputationalGraph(ComputationalGraphNode): field_write_nodes = {} field_read_nodes = {} for field in continuous_variables: - field_write_nodes[field] = {} # dictionnary topology -> node that wrote this topo - field_read_nodes[field] = {} # dictionnary topology -> list of nodes that reads field:topo + # dictionnary topology -> node that wrote this topology + field_write_nodes[field] = {} + # dictionnary topology -> list of nodes that reads field:topo + field_read_nodes[field] = {} for node_id,node in enumerate(self.nodes): node_ops = [] @@ -211,9 +244,9 @@ class ComputationalGraph(ComputationalGraphNode): subgraph_ops = subgraph.vertex_properties['operators'] node_properties = None for nid in node_ordering: - node = subgraph.vertex(nid) - op = subgraph_ops[node] - node_vertices.append(node) + _node = subgraph.vertex(nid) + op = subgraph_ops[_node] + node_vertices.append(_node) node_ops.append(op) for prop_name,vp in subgraph.vertex_properties.iteritems(): if prop_name not in vertex_properties: @@ -240,7 +273,7 @@ class ComputationalGraph(ComputationalGraphNode): vertex_properties['operators'][opnode] = op if from_subgraph: - level = subgraph.vertex_properties['op_data'][oldvertex].current_level + level = node.level + 1 for enp in extra_node_props: vertex_properties[enp][opnode] = subgraph.vp[enp][oldvertex] else: diff --git a/hysop/core/graph/computational_node.py b/hysop/core/graph/computational_node.py index b0ff354a9..34560069d 100644 --- a/hysop/core/graph/computational_node.py +++ b/hysop/core/graph/computational_node.py @@ -10,14 +10,14 @@ from hysop.tools.types import InstanceOf, to_set, check_instance from hysop.fields.continuous import Field from hysop.core.graph.graph import not_implemented, wraps,\ not_initialized, initialized, discretized, ready -from hysop.core.graph.continuous import Operator -from hysop.core.mpi.topology_descriptor import TopologyDescriptor +from hysop.core.graph.continuous import OperatorBase +from hysop.core.mpi.topology import Topology from hysop.tools.decorators import debug def base_initialized(f): assert callable(f) @wraps(f) - def check(*args,**kargs): + def _check(*args,**kargs): self = args[0] msg = 'Cannot call {}.{}() on node \'{}\' because {}'\ .format(self.__class__.__name__,f.__name__,self.name,'{}') @@ -25,9 +25,9 @@ def base_initialized(f): reason='this self._init_base() has not been called yet.' raise RuntimeError(msg.format(reason)) return f(*args,**kargs) - return check + return _check -class ComputationalGraphNode(Operator): +class ComputationalGraphNode(OperatorBase): """ Interface of an abstract computational graph node. """ @@ -168,24 +168,12 @@ class ComputationalGraphNode(Operator): """ Initialize base class and check everything. """ - variables = {} - for io_vars in (self.input_vars, self.output_vars): - for field,topo in io_vars.iteritems(): - if (field in variables): - if (not topo is variables[field]): - variables[field].add(topo) - else: - print topo.__class__ - variables[field] = set([topo]) - - super(ComputationalGraphNode, self).__init__( - variables = variables, - **kwds) + ifields = set(self.input_vars.keys()) + ofields = set(self.output_vars.keys()) + fields = list(ifields.union(ofields)) + super(ComputationalGraphNode, self).__init__(fields=fields, **kwds) self._base_initialized = True - self._check_variables() - self._check_topologies() - self._check_support() - + @debug def _check_method(self, user_method): """ @@ -218,27 +206,35 @@ class ComputationalGraphNode(Operator): msg+='\n possible values are {}.'.format(available_methods[k]) raise ValueError(msg) return method - + + @debug + @base_initialized + def check(self): + """ + Check if node was correctly initialized. + By default this checks variables, topologies and support. + """ + self._check_variables() + self._check_topologies() + self._check_support() + @debug @base_initialized def _check_variables(self): """ Check input and output variables. - Called automatically in ComputationalGraphNode.post_initialize() - """ - variables = self.variables - for (k,v) in variables.iteritems(): - if not isinstance(k,Field): - msg = 'Given key is not a continuous Field (got a {}).' - raise ValueError(msg.format(k.__class__)) - if not isinstance(v, set): - msg = 'Given value is not a set of topologies (got a {}).' - raise ValueError(msg.format(v.__class__)) - else: - for t in v: - if not isinstance(t,TopologyDescriptor): - msg = 'Given value is not a topology (got a {}).' - raise ValueError(msg.format(t.__class__)) + Called automatically in ComputationalGraphNode.check() + """ + for variables in [self.input_vars, self.output_vars]: + for (k,v) in variables.iteritems(): + if not isinstance(k,Field): + msg = 'Given key is not a continuous Field (got a {}).' + raise TypeError(msg.format(k.__class__)) + if not isinstance(v, Topology): + msg='Expected a Topology instance but got a {}.'.format(topo.__class__) + msg+='\nAll topologies are expected to be set after ' + msg+='ComputationalGraph.handle_field_requirements() has been called.' + raise TypeError(msg) @debug @base_initialized @@ -250,23 +246,25 @@ class ComputationalGraphNode(Operator): _has_multiple_field_topologies Sets the following attributes: _multi_topo_fields (list of field that have at least two different topologies) - Called automatically in ComputationalGraphNode.post_initialize() + Called automatically in ComputationalGraphNode.check() """ is_distributed = self.mpi_params.size > 1 has_multiple_topologies = False has_multiple_field_topologies = False multi_topo_fields = set() - assert self.variables, 'no variables in {}'.format(self.__class__) - topo_ref = list(self.variables.values()[0])[0] - for field,topologies in self.variables.iteritems(): - if len(topologies)>1: - multi_topo_fields.add(field) - has_multiple_field_topologies = True - for topo in topologies: + topo_ref = (self.input_vars.values()+self.output_vars.values())[0] + for variables in [self.input_vars, self.output_vars]: + for field, topo in variables.iteritems(): if topo != topo_ref: has_multiple_topologies = True + for ifield in self.input_vars: + if ifield in self.output_vars and \ + self.input_vars[ifield] != self.output_vars[ifield]: + multi_topo_fields.add(ifield) + has_multiple_field_topologies = True + self._is_distributed = is_distributed self._has_multiple_topologies = has_multiple_topologies self._has_multiple_field_topologies = has_multiple_field_topologies @@ -280,7 +278,7 @@ class ComputationalGraphNode(Operator): See ComputationalGraphNode.supports_multiple_topologies() ComputationalGraphNode.supports_multiple_field_topologies() ComputationalGraphNode.supports_mpi() - Called automatically in ComputationalGraphNode.post_initialize() + Called automatically in ComputationalGraphNode.check() """ cls = self.__class__ if (self._has_multiple_field_topologies) and \ diff --git a/hysop/core/graph/computational_operator.py b/hysop/core/graph/computational_operator.py index 5eb4f6019..543fbe10f 100644 --- a/hysop/core/graph/computational_operator.py +++ b/hysop/core/graph/computational_operator.py @@ -4,6 +4,7 @@ from hysop.tools.decorators import debug from hysop.core.graph.computational_node import ComputationalGraphNode from hysop.core.graph.graph import initialized, discretized from hysop.core.memory.memory_request import OperatorMemoryRequests +from hysop.core.mpi.topology_descriptor import TopologyDescriptor from hysop.fields.field_requirements import DiscreteFieldRequirements from abc import ABCMeta @@ -82,6 +83,7 @@ class ComputationalGraphOperator(ComputationalGraphNode): handle_field_requirements() -> self.input_field_requirements it set self._initialized is set post_intialize() + check() (requires self.initialized to be set) discretize() (requires self.initialized to be set) self.discretized is set get_work_properties() (requires self.discretized to be set) @@ -133,7 +135,7 @@ class ComputationalGraphOperator(ComputationalGraphNode): self.output_discrete_fields = {} self.input_field_requirements = {} self.output_field_requirements = {} - + @debug def handle_field_requirements(self): """ @@ -142,6 +144,7 @@ class ComputationalGraphOperator(ComputationalGraphNode): 1) min and max ghosts for each input and output variables 2) allowed splitting directions for cartesian topologies 3) required local and global transposition state, if any. + 4) required space (fourier, physical, ...) and more They are stored in self.input_field_requirements and self.output_field_requirements. @@ -152,14 +155,38 @@ class ComputationalGraphOperator(ComputationalGraphNode): Default is Backend.HOST, no min or max ghosts, Basis.CARTESIAN and no specific transposition state for each input and output variables. """ - for field,topology_descriptor in self.input_vars.iteritems(): - req = DiscreteFieldRequirements(self, self.input_vars, field, - backend=Backend.HOST) + + # by default we create HOST (cpu) TopologyDescriptors + for field,topo_descriptor in self.input_vars.iteritems(): + topo_descriptor = TopologyDescriptor.build_descriptor( + backend=Backend.HOST, + operator=self, + field=field, + handle=topo_descriptor) + self.input_vars[field] = topo_descriptor + + for field,topo_descriptor in self.output_vars.iteritems(): + topo_descriptor = TopologyDescriptor.build_descriptor( + backend=Backend.HOST, + operator=self, + field=field, + handle=topo_descriptor) + self.output_vars[field] = topo_descriptor + + # and we use default DiscreteFieldRequirements (ie. no min ghosts, no max ghosts, + # can_split set to True in all directions, Basis.CARTESIAN, TranspositionState.XYZ). + for field, topo_descriptor in self.input_vars.iteritems(): + req = DiscreteFieldRequirements(self, self.input_vars, field) self.input_field_requirements[field] = req - for field,topology_descriptor in self.output_vars.iteritems(): - req = DiscreteFieldRequirements(self, self.output_vars, field, - backend=Backend.HOST) + + for field, topo_descriptor in self.output_vars.iteritems(): + req = DiscreteFieldRequirements(self, self.output_vars, field) self.output_field_requirements[field] = req + + print '{} HANDLE FIELD REQUIREMENT'.format(self.name) + print len(self.input_field_requirements.values()) + print len(self.output_field_requirements.values()) + print @debug @initialized @@ -192,28 +219,39 @@ class ComputationalGraphOperator(ComputationalGraphNode): no extra buffers. """ return OperatorMemoryRequests(self) - + def supported_backends(self): """ Return the backends that this operator's topologies can support as a set. By default all operators support only Backend.HOST. """ return set([Backend.HOST]) + + @debug + @initialized + def check(self): + """ + Check if node was correctly initialized. + This calls ComputationalGraphNode.check() and then checks backend + by comparing all variables topology backend to set.supported_backends(). + """ + super(ComputationalGraphOperator, self).check() + self._check_backend() + + def _check_backend(self): + """ + Checks backend support and topologies. + """ + topologies_per_backend = self.get_topologies() + supported_backends = self.supported_backends() + for (backend, topologies) in topologies_per_backend.iteritems(): + if backend.kind not in supported_backends: + msg='\n\nOperator {} topology backend mismatch:'.format(self.name) + msg+='\n -> got topologies defined on backend {}.'.format(backend) + msg+='\n *bad topology ids were {}'.format([t.get_id() for t in topologies]) + msg+='\n -> this operator only supports the following backends:' + msg+='\n *'+'\n *'.join([str(b) for b in supported_backends]) + for t in topologies: + print '\n',t + raise RuntimeError(msg) - # def _check_support(self): - # """ - # Calls ComputationalGraphNode._check_support() and checks backend support. - # """ - # super(ComputationalGraphOperator,self)._check_support() - # topologies_per_backend = self.get_topologies() - # supported_backends = self.supported_backends() - # for (backend, topologies) in topologies_per_backend.iteritems(): - # if backend.kind not in supported_backends: - # msg='\n\nOperator {} topology backend mismatch:'.format(self.name) - # msg+='\n -> got topologies defined on backend {}.'.format(backend) - # msg+='\n *bad topology ids were {}'.format([t.get_id() for t in topologies]) - # msg+='\n -> this operator only supports the following backends:' - # msg+='\n *'+'\n *'.join([str(b) for b in supported_backends]) - # for t in topologies: - # print '\n',t - # raise RuntimeError(msg) diff --git a/hysop/core/graph/continuous.py b/hysop/core/graph/continuous.py index 562b6553f..4422f5d72 100755 --- a/hysop/core/graph/continuous.py +++ b/hysop/core/graph/continuous.py @@ -8,60 +8,42 @@ from hysop.tools.parameters import MPIParams from hysop.tools.decorators import debug from hysop.tools.types import check_instance from hysop.fields.continuous import Field -from hysop.core.mpi.topology_descriptor import TopologyDescriptor import hysop.tools.io_utils as io -class Operator(object): +class OperatorBase(object): """Abstract interface to continuous operators. """ __metaclass__ = ABCMeta @debug - def __new__(cls, *args, **kw): - return object.__new__(cls, *args, **kw) - - @debug - def __init__(self, variables=None, + def __init__(self, fields, mpi_params=None, io_params=None, **kwds): """ Parameters ---------- - variables : dictionnary. See Notes for details. + field: list of Field + fields on which this operator operates mpi_params : :class:`hysop.tools.parameters.MPIParams` mpi config for the operator (comm, task ...) io_params : :class:`hysop.tools.io_utils.IOParams` file i/o config (filename, format ...) - Notes - ----- - Variables is a dictionnary. - The elements of the list or the keys of the dict - are :class:`hysop.fields.continuous.Fields`. - - The values of the dict can be either - :class:`hysop.core.mpi.topology.Topology` - or a set of topologies. - Attributes ---------- - variables: dict - :class:`~hysop.fields.continuous.Continuous` with their topologies as a set. - One field can expose multiple topologies as a set. + fields: list of Field io_params: IOParams mpi_params: MPIParams """ - super(Operator,self).__init__(**kwds) - + super(OperatorBase,self).__init__(**kwds) + + check_instance(fields, list, values=Field) check_instance(io_params, IOParams, allow_none=True) check_instance(mpi_params, MPIParams, allow_none=True) - check_instance(variables, dict, keys=Field) - for var in variables.values(): - check_instance(var, set, values=TopologyDescriptor) - - self.variables = variables + + self.fields = fields self.io_params = io_params self.mpi_params = mpi_params @@ -101,7 +83,7 @@ class Operator(object): def _set_domain_and_tasks(self): """ - Initialize the mpi context, depending on local variables, domain + Initialize the mpi context, depending on local fields, domain and so on. Notes @@ -111,13 +93,13 @@ class Operator(object): during construction (__init__). """ # When this function is called, the operator must at least - # have one variable. - assert len(self.variables) > 0 - self.domain = self.variables.keys()[0].domain + # have one field. + assert len(self.fields) > 0 + self.domain = self.fields[0].domain - # Check if all variables have the same domain - for v in self.variables: - assert v.domain is self.domain, 'All variables of the operator\ + # Check if all fields have the same domain + for field in self.fields: + assert field.domain is self.domain, 'All fields of the operator\ must be defined on the same domain.' # Set/check mpi context diff --git a/hysop/core/mpi/topology.py b/hysop/core/mpi/topology.py index a3524aba7..cd2ac5d1a 100644 --- a/hysop/core/mpi/topology.py +++ b/hysop/core/mpi/topology.py @@ -25,6 +25,12 @@ class Topology(object): # Counter of topology.Topology instances to set a unique id for each topology. __ids = count(0) + + @debug + def __new__(cls, mpi_params, domain, backend, **kwds): + obj = object.__new__(cls, **kwds) + obj.__id = cls.__ids.next() + return obj def __init__(self, mpi_params, domain, backend=Backend.HOST , **kargs): super(Topology,self).__init__(**kargs) @@ -52,13 +58,13 @@ class Topology(object): elif backend == Backend.OPENCL: from hysop.backend.device.opencl.opencl_tools import get_or_create_opencl_env from hysop.core.arrays.all import OpenClArrayBackend - cl_env = kargs.pop('cl_env',None) \ - or get_or_create_opencl_env(mpi_params.comm) + cl_env = kargs.pop('cl_env',None) \ + or get_or_create_opencl_env(mpi_params.comm) self.cl_env = cl_env assert cl_env.comm == mpi_params.comm - queue = kargs.pop('queue',None) - allocator = kargs.pop('allocator',None) + queue = kargs.pop('queue', None) + allocator = kargs.pop('allocator', None) backend = OpenClArrayBackend(cl_env=cl_env,queue=queue,allocator=allocator) else: msg = 'Unsupported backend {}.'.format(backend) @@ -107,8 +113,11 @@ class Cartesian(Topology): """ @debug - def __new__(cls, *args, **kw): - return object.__new__(cls, *args, **kw) + def __new__(cls, domain, discretization, dim=None, mpi_params=None, + isperiodic=None, cutdir=None, shape=None, mesh=None, + cartesian_topology=None, **kargs): + obj = super(Cartesian,cls).__new__(cls, *args, **kw) + return obj @debug def __init__(self, domain, discretization, dim=None, mpi_params=None, @@ -175,7 +184,8 @@ class Cartesian(Topology): self.cutdir = None # mpi communicator (cartesian topo) self.comm = None - + # discretization used to build this topology + self.discretization = discretization if cartesian_topology is None: self._build_mpi_topo(shape, cutdir, dim, isperiodic) else: @@ -225,7 +235,7 @@ class Cartesian(Topology): # ===== 5 - Final setup ==== - # The topology is register into domain list. + # The topology is registered into domain list. # If it already exists (in the sense of the comparison operator # of the present class) then isNew is turned to false # and the present instance will be destroyed. @@ -406,10 +416,11 @@ class Cartesian(Topology): """ return self.mpi_params.comm - def ghosts(self): + def get_ghosts(self): """returns ghost layer width. """ return self.mesh.discretization.ghosts + ghosts = property(get_ghosts) def task_id(self): """returns id of the task that owns this topology diff --git a/hysop/core/mpi/topology_descriptor.py b/hysop/core/mpi/topology_descriptor.py index acc624d11..5edf49f55 100644 --- a/hysop/core/mpi/topology_descriptor.py +++ b/hysop/core/mpi/topology_descriptor.py @@ -1,15 +1,147 @@ +from abc import ABCMeta, abstractmethod +from hysop.deps import np +from hysop.tools.types import check_instance +from hysop.tools.numpywrappers import npw +from hysop.constants import Backend, BoundaryCondition from hysop.tools.parameters import Discretization -from hysop.core.mpi.topology import Topology - -TopologyDescriptor = (Topology, Discretization,) - -def get_discretization(self, topology_descriptor): - if isinstance(topology_descriptor, Topology): - return self.topology_descriptor.mesh.discretization - elif isinstance(topology_descriptor, Discretization): - return self.topology_descriptor - else: - msg='{} Could not extract discretization from topology descriptor of class {}.' - msg=msg.format(self.header, self.topology_descriptor.__class__) - raise TypeError(msg) +from hysop.core.mpi.topology import Topology, Cartesian +from hysop.fields.continuous import Field + +class TopologyDescriptor(object): + """ + Describes how a topology should be built. + kargs allows for backend specific variables. + """ + __metaclass__ = ABCMeta + + def __init__(self, mpi_params, domain, backend, **kargs): + super(TopologyDescriptor, self).__init__() + self.mpi_params = mpi_params + self.domain = domain + self.backend=backend + self.extra_kargs = kargs + + @staticmethod + def build_descriptor(backend, operator, field, handle, **kargs): + if isinstance(handle, Topology): + # handle is already a Topology, so we return it. + return handle + elif isinstance(handle, TopologyDescriptor): + # handle is already a TopologyDescriptor, so we return it. + return handle + elif isinstance(handle, CartesianDescriptors): + return CartesianDescriptor.build_descriptor(backend, operator, + field, handle, **kargs) + else: + msg='Unknown handle of class {} to build a TopologyDescriptor.' + msg=msg.format(handle.__class__) + raise TypeError(msg) + + def choose_or_create_topology(self, known_topologies, **kargs): + """ + Returns a topology that is either taken from known_topologies, a set + of user specified topologies which are ensured to be compatible + with the current TopologyDescriptor, or created from the descriptor + if choose_topology() returns None. + """ + check_instance(known_topologies, list, values=Topology) + topo = self.choose_topology(known_topologies, **kargs) or \ + self.create_topology(**kargs) + return topo + + @abstractmethod + def choose_topology(self, known_topologies, **kargs): + pass + + @abstractmethod + def create_topology(self, **kargs): + pass + + def dim(self): + return self.domain.dimension + + def is_periodic(self): + is_lperiodic = [lboundary==BoundaryCondition.PERIODIC + for lboundary in self.domain.boundaries[0]] + is_rperiodic = [rboundary==BoundaryCondition.PERIODIC + for rboundary in self.domain.boundaries[1]] + assert len(is_lperiodic)==self.dim() + assert len(is_rperiodic)==self.dim() + assert is_lperiodic == is_rperiodic + return npw.asintegerarray(is_lperiodic) + + +class CartesianDescriptor(TopologyDescriptor): + """ + Describes how a Cartesian topology should be built. + kargs allows for backend specific variables. + """ + def __init__(self, mpi_params, domain, backend, discretization, **kargs): + super(CartesianDescriptor, self).__init__(mpi_params=mpi_params, + domain=domain, backend=backend, **kargs) + # boundaries are stored in domain + self.discretization = discretization + + @staticmethod + def build_descriptor(backend, operator, field, handle, **kargs): + from hysop.core.graph.computational_operator import ComputationalGraphOperator + check_instance(backend, Backend) + check_instance(operator, ComputationalGraphOperator) + check_instance(field, Field) + check_instance(handle, CartesianDescriptors) + msg='A Cartesian topology descriptor should not contain any ghosts, ' + msg+='they will be determined during the handle_field_requirements() in the ' + msg+=' operator initialization step to minimize the number of topologies created.' + msg+='\nIf you want to impose a specific topology, you can directly pass a ' + msg+='Cartesian instance into operator\'s input or output variables dictionnary instead.' + if isinstance(handle, Discretization): + if handle.ghosts.sum() > 0: + raise ValueError(msg) + msg='mpi_params has not been set in operator {}.'.format(operator.name) + if not hasattr(operator, 'mpi_params'): + raise RuntimeError(msg) + return CartesianDescriptor( + backend=backend, + domain=field.domain, + mpi_params=operator.mpi_params, + discretization=handle, + **kargs) + elif isinstance(handle, CartesianDescriptor): + if handle.discretization.ghosts.sum() > 0: + raise ValueError(msg) + return handle + else: + # handle is a Cartesian instance, ghosts can be imposed by user here + return handle + + def choose_topology(self, known_topologies, **kargs): + """ + Find optimal topology parameters from known_topologies. + If None is returned, create_topology will be called instead. + """ + pass + + def create_topology(self, cutdir, ghosts): + """ + Build a topology with the current TopologyDescriptor. + Free parameters are cutdir and ghosts which are imposed + by operators on variables and solved during operator's + method handle_field_requirements(). + """ + is_periodic = self.is_periodic() + if self.mpi_params.size==1: + #local periodic computation, we remove ghosts. + ghosts *= (~is_periodic) + discretization = Discretization(self.discretization.resolution, ghosts) + return Cartesian(domain=self.domain, discretization=discretization, + mpi_params=self.mpi_params, isperiodic=is_periodic, + cutdir=cutdir) + + +CartesianDescriptors = (Cartesian, CartesianDescriptor, Discretization) +""" +Instance of those types can be used to create a CartesianDescriptor. +Thus they can be passed in the variables of each operator supporting +Cartesian topologies. +""" diff --git a/hysop/fields/field_requirements.py b/hysop/fields/field_requirements.py index e443ab5a1..24f319279 100644 --- a/hysop/fields/field_requirements.py +++ b/hysop/fields/field_requirements.py @@ -1,15 +1,23 @@ from hysop.deps import np, it +from hysop.constants import Basis, transposition_states from hysop.tools.types import to_list, check_instance -from hysop.constants import Backend, Basis, transposition_states +from hysop.tools.numpywrappers import npw from hysop.fields.continuous import Field from hysop.fields.discrete import DiscreteField +from hysop.core.mpi.topology import Topology from hysop.core.mpi.topology_descriptor import TopologyDescriptor from hysop.core.graph.computational_node import ComputationalGraphNode + +def can_convert_basis_inplace(lbasis, rbasis): + return False + +def can_transpose_inplace(ltranspose, rtranspose): + return False + class DiscreteFieldRequirements(object): - def __init__(self, operator, variables, - field, backend, + def __init__(self, operator, variables, field, min_ghosts=None, max_ghosts=None, can_split=None, @@ -21,27 +29,20 @@ class DiscreteFieldRequirements(object): check_instance(operator, ComputationalGraphNode) check_instance(field, Field) - check_instance(variables, dict, keys=Field, values=TopologyDescriptor) + check_instance(variables, dict, keys=Field, values=(Topology,TopologyDescriptor)) self._operator = operator self._field = field self._variables = variables self._topology_descriptor = variables[field] - self._header = '::{}[{}] requirements::\n'.format(operator.name, field.name) + self.header = '::{}[{}] requirements::\n'.format(operator.name, field.name) - self.backend = backend self.min_ghosts = min_ghosts self.max_ghosts = max_ghosts self.can_split = can_split self.required_basis = required_basis self.required_transposition_state = required_transposition_state - def get_backend(self): - return self._backend - def set_backend(self, backend): - check_instance(backend, Backend) - self._backend = backend - def get_required_transposition_state(self): return self._required_transposition_state def set_required_transposition_state(self, tstate): @@ -76,78 +77,73 @@ class DiscreteFieldRequirements(object): assert self.can_split.size == self.workdim def get_work_dim(self): - return self._field.dimension + return self._topology_descriptor.domain.dimension + def get_operator(self): + return self._operator + def get_field(self): + return self._field + def get_variables(self): + return self._variables + def get_topology_descriptor(self): + return self._topology_descriptor - backend = property(get_backend, set_backend) can_split = property(get_can_split, set_can_split) min_ghosts = property(get_min_ghosts, set_min_ghosts) max_ghosts = property(get_max_ghosts, set_max_ghosts) required_basis = property(get_required_basis, set_required_basis) required_transposition_state = property(get_required_transposition_state, set_required_transposition_state) - workdim = property(get_work_dim) + workdim = property(get_work_dim) + operator = property(get_operator) + field = property(get_field) + variables = property(get_variables) + topology_descriptor = property(get_topology_descriptor) - def is_compatible_with(self, other, check_backend=True): + def is_compatible_with(self, other): assert self.field == other.field, 'field mismatch.' - assert self.workdim == other.workdim, 'workdim mismatch.' - assert self.discretization() == other.discretization(), 'discretization mismatch.' - if isinstance(other, FieldRequirements): + if isinstance(other, DiscreteFieldRequirements): others=[other] elif isinstance(other, MultiFieldRequirements): - if self.discretization() in other.requirements.keys(): - others=other.requirements[self.discretization()] + if self.topology_descriptor in other.requirements.keys(): + others=other.requirements[self.topology_descriptor] else: return True else: msg='Unknown type {}.'.format(other.__class__) for other in others: - if check_backend and (other.backend != self.backend): + assert self.workdim == other.workdim, 'workdim mismatch.' + assert self.topology_descriptor == other.topology_descriptor, \ + 'topology_descriptor mismatch.' + if (other.max_ghosts < self.min_ghosts).any(): return False - if other.max_ghosts < self.min_ghosts: + if (other.min_ghosts > self.max_ghosts).any(): return False - if other.min_ghosts > self.max_ghosts: + if not (other.can_split * self.can_split).any(): return False if (other.required_basis != self.required_basis) and \ - not self.can_convert_basis_inplace( + not can_convert_basis_inplace( other.required_basis, self.required_basis): return False if (other.required_transposition_state != self.required_transposition_state) and \ - not self.can_transpose_inplace( + not can_transpose_inplace( other.required_transposition_state, self.required_transposition_state): return False return True - def discretization(self): - return get_discretization(self.topology_descriptor) - def can_convert_basis_inplace(self): - return False - def can_transpose_inplace(self): - return False - - def choose_or_create_topology(self, known_topologies, ghosts): - assert not isinstance(self.variables[self.field], Topology) - return Cartesian(domain, self.discretization, - - def check_topology(self, topology=None): topology = topology or self.variables[self.field] check_instance(topology, Topology) - sid = self.requirement_identifier - if topology.dimension != self.field.dimension: + if topology.domain.dimension != self.field.dimension: msg='{} Dimension mismatch between field and topology.\n field={}d, topology={}d.' msg=msg.format(self.header, self.field.dimension, topology.dimension) raise RuntimeError(msg) - if topology.mesh.discretization != self.discretization(): + if topology.mesh.discretization != self.topology_descriptor.discretization: msg='{} Discretisation mismatch between requirement and topology.\n ' msg+='requirement={}, topology={}.' - msg=msg.format(self.header, self.discretization(), topology.mesh.discretization) - raise RuntimeError(msg) - if (topology.backend != self.Backend): - msg='{} Backend mismatch between requirement and topology.\n ' - msg+='requirement={}, topology={}.' - msg=msg.format(self.header, self.backend, topology.backend) + msg=msg.format(self.header, self.topology_descriptor, + topology.mesh.topology_descriptor) raise RuntimeError(msg) if (topology.ghosts < self.min_ghosts).any(): msg='{} min ghosts constraint was not met.\n min={}, actual={}.' @@ -158,6 +154,17 @@ class DiscreteFieldRequirements(object): msg=msg.format(self.header, self.max_ghosts, topology.ghosts) raise RuntimeError(msg) + def set_and_check_topology(self, topology): + """ + Check topology and replace a TopologyDescriptor by a Topology instance in + self.variables[self.field]. + """ + assert isinstance(topology, Topology) + assert not isinstance(self.variables[self.field], Topology) \ + or (self.variables[self.field] == topology) + self.check_topology(topology) + self.variables[self.field] = topology + def check_state(self, dfield): check_instance(dfield, DiscreteField) self.check_topology(dfield.topology) @@ -176,47 +183,62 @@ class MultiFieldRequirements(object): def __init__(self, field): self.field = field self.requirements = {} - - def update(self, *reqs): - for lreq in reqs: - if not isinstance(lreq, MultiFieldRequirements): - lreq = [lreq] - for req in lreq: - check_instance(req, FieldRequirements) - assert req.field == self.field - self.requirements.setdefault(req.discretization(), []).append(req) + self.built = False + + def nrequirements(self): + return sum(len(lreqs) for lreqs in self.requirements.values()) + + def update(self, *update_reqs): + for reqs in update_reqs: + if isinstance(reqs, MultiFieldRequirements): + reqs = reqs.requirements.values() + else: + reqs = [[reqs]] + for lreq in reqs: + for req in lreq: + check_instance(req, DiscreteFieldRequirements) + assert req.field == self.field + td_reqs = self.requirements.setdefault(req.topology_descriptor, []) + td_reqs.append(req) def build_topologies(self): - for compatible_reqs in self.split(): + if self.built: + return + for compatible_reqs in self._split(): compatible_reqs._build_compatible_topologies() + self.built = True def all_compatible(self): - for discretization in self.requirements.keys(): - requirements = self.requirements[discretization] + for topology_descriptor in self.requirements.keys(): + requirements = self.requirements[topology_descriptor] assert len(requirements)>0 - for req0, req1 in it.combinations(requirements, repeat=2): + for req0, req1 in it.combinations(requirements, 2): if not req0.is_compatible_with(req1): return False return True - def _split(self, **kargs): + def _split(self): sub_field_requirements = [] - for lreq in self.reqs.values(): + for lreq in self.requirements.values(): for req in lreq: - for multi_reqs in sub_field_requirement: - if req.is_compatible_with(multi_reqs, - no_backend_check=True): + ok = False + for multi_reqs in sub_field_requirements: + if req.is_compatible_with(multi_reqs): multi_reqs.update(req) - else: + ok=True + break + if not ok: new_group = MultiFieldRequirements(self.field) new_group.update(req) sub_field_requirements.append(new_group) + assert self.nrequirements() == sum(sf.nrequirements() for sf in sub_field_requirements) return sub_field_requirements def _build_compatible_topologies(self): assert self.all_compatible() - for discretization, reqs in self.requirements.iteritems(): - ghosts = np.asarray([0,0,0]) + for topology_descriptor, reqs in self.requirements.iteritems(): + ghosts = npw.integer_zeros(shape=(topology_descriptor.dim(),)) + can_split = npw.integer_ones(shape=(topology_descriptor.dim(),)) known_topologies = [] unknown_topologies = [] @@ -226,8 +248,14 @@ class MultiFieldRequirements(object): known_topologies.append(topology) else: ghosts = np.maximum(ghosts, req.min_ghosts) + can_split *= req.can_split unknown_topologies.append(req) + assert can_split.any() + for req in unknown_topologies: - req.choose_or_create_topology(known_topologies, ghosts) - req.check_topology() + topo = req.topology_descriptor.choose_or_create_topology(known_topologies, + ghosts=ghosts, cutdir=can_split) + known_topologies.append(topo) + req.set_and_check_topology(topo) + diff --git a/hysop/numerics/splitting/strang.py b/hysop/numerics/splitting/strang.py index e17c3945f..c4e442002 100644 --- a/hysop/numerics/splitting/strang.py +++ b/hysop/numerics/splitting/strang.py @@ -40,8 +40,8 @@ class StrangSplitting(DirectionalSplitting): splitting_dim = self.splitting_dim for op in self.directional_operators: op.generate(splitting_dim=splitting_dim) - for dir in self.directions: + for dir_ in self.directions: for op in self.directional_operators: - operators = op.get_direction(dir) + operators = op.get_direction(dir_) self.push_nodes(operators) diff --git a/hysop/old/operator.old/computational.py b/hysop/old/operator.old/computational.py index a76ca7537..3c798ffa5 100755 --- a/hysop/old/operator.old/computational.py +++ b/hysop/old/operator.old/computational.py @@ -3,14 +3,14 @@ """ from abc import ABCMeta, abstractmethod from hysop.constants import debug -from hysop.operator.continuous import Operator, opapply +from hysop.operator.continuous import OperatorBase, opapply from hysop.core.mpi.topology import Cartesian from hysop.tools.parameters import Discretization from hysop.tools.numpywrappers import npw from hysop import __FFTW_ENABLED__ -class Computational(Operator): +class Computational(OperatorBase): """ Abstract base class for computational operators. diff --git a/hysop/old/operator.old/continuous.py b/hysop/old/operator.old/continuous.py index 0a9205a5d..e6a3b3701 100755 --- a/hysop/old/operator.old/continuous.py +++ b/hysop/old/operator.old/continuous.py @@ -59,7 +59,7 @@ class Operator(object): the geometry on which this operator applies """ - super(Operator,self).__init__(**kwds) + super(OperatorBase,self).__init__(**kwds) # 1 ---- Variables setup ---- # List of hysop.continuous.Fields involved in the operator. diff --git a/hysop/old/operator.old/curlAndDiffusion.py b/hysop/old/operator.old/curlAndDiffusion.py index fe7172abf..6b9ffc207 100644 --- a/hysop/old/operator.old/curlAndDiffusion.py +++ b/hysop/old/operator.old/curlAndDiffusion.py @@ -5,7 +5,7 @@ Operator for diffusion problem. """ -from hysop.operator.continuous import Operator +from hysop.operator.continuous import OperatorBase try: from hysop.f2hysop import fftw2py except ImportError: @@ -15,7 +15,7 @@ from hysop.constants import debug from hysop.operator.continuous import opsetup -class CurlDiffusion(Operator): +class CurlDiffusion(OperatorBase): """ Diffusion operator \f{eqnarray*} diff --git a/hysop/old/operator.old/redistribute.py b/hysop/old/operator.old/redistribute.py index c81a2e859..2e5de956f 100644 --- a/hysop/old/operator.old/redistribute.py +++ b/hysop/old/operator.old/redistribute.py @@ -13,7 +13,7 @@ """ -from hysop.operator.continuous import Operator +from hysop.operator.continuous import OperatorBase from abc import ABCMeta, abstractmethod from hysop.core.mpi.topology import Cartesian from hysop.operator.computational import Computational @@ -22,7 +22,7 @@ from hysop.core.mpi.bridge import Bridge, BridgeOverlap, BridgeInter from hysop.constants import DirectionLabels, debug -class Redistribute(Operator): +class Redistribute(OperatorBase): """Abstract interface to redistribute operators """ diff --git a/hysop/operator/diffusion.py b/hysop/operator/diffusion.py index e38ea6567..b918b81fc 100644 --- a/hysop/operator/diffusion.py +++ b/hysop/operator/diffusion.py @@ -8,7 +8,7 @@ from hysop.tools.types import check_instance from hysop.tools.enum import EnumFactory from hysop.tools.decorators import debug from hysop.fields.continuous import Field -from hysop.core.mpi.topology_descriptor import TopologyDescriptor +from hysop.core.mpi.topology_descriptor import CartesianDescriptors from hysop.core.graph.computational_node_frontend import ComputationalGraphNodeFrontend from hysop.backend.host.fortran.operator.diffusion import DiffusionFFTW @@ -70,7 +70,7 @@ class Diffusion(ComputationalGraphNodeFrontend): check_instance(input_field, Field) check_instance(output_field, Field) - check_instance(variables, dict, keys=Field, values=TopologyDescriptor) + check_instance(variables, dict, keys=Field, values=CartesianDescriptors) check_instance(base_kwds, dict, keys=str) super(Diffusion, self).__init__(input_field=input_field, output_field=output_field, diff --git a/hysop/operator/directional/advection_dir.py b/hysop/operator/directional/advection_dir.py index 7e078d718..7c15bd403 100644 --- a/hysop/operator/directional/advection_dir.py +++ b/hysop/operator/directional/advection_dir.py @@ -12,7 +12,7 @@ from hysop.core.mpi.topology import Topology from hysop.operator.directional.directional import DirectionalOperatorFrontend from hysop.backend.device.opencl.operators import OpenClDirectionalAdvection -from hysop.core.mpi.topology_descriptor import TopologyDescriptor +from hysop.core.mpi.topology_descriptor import CartesianDescriptors class DirectionalAdvection(DirectionalOperatorFrontend): @@ -90,7 +90,7 @@ class DirectionalAdvection(DirectionalOperatorFrontend): check_instance(velocity, Field) check_instance(advected_fields, list, values=Field) check_instance(advected_fields_out, list, values=Field) - check_instance(variables, dict, keys=Field, values=TopologyDescriptor) + check_instance(variables, dict, keys=Field, values=CartesianDescriptors) check_instance(base_kwds, dict, keys=str) super(DirectionalAdvection, self).__init__(velocity=velocity, diff --git a/hysop/operator/directional/stretching_dir.py b/hysop/operator/directional/stretching_dir.py index 7e808a476..070dc1540 100644 --- a/hysop/operator/directional/stretching_dir.py +++ b/hysop/operator/directional/stretching_dir.py @@ -8,7 +8,7 @@ from hysop.constants import DirectionLabels, Implementation from hysop.tools.types import check_instance, to_tuple from hysop.tools.decorators import debug from hysop.fields.continuous import Field -from hysop.core.mpi.topology_descriptor import TopologyDescriptor +from hysop.core.mpi.topology_descriptor import CartesianDescriptors from hysop.operator.directional.directional import DirectionalOperatorFrontend from hysop.backend.device.opencl.operators import OpenClDirectionalStretching @@ -72,7 +72,7 @@ class DirectionalStretching(DirectionalOperatorFrontend): check_instance(velocity, Field) check_instance(vorticity, Field) check_instance(vorticity_out, Field) - check_instance(variables, dict, keys=Field, values=TopologyDescriptor) + check_instance(variables, dict, keys=Field, values=CartesianDescriptors) assert vorticity.domain.dimension==3 assert vorticity_out.domain.dimension==3 diff --git a/hysop/operator/poisson.py b/hysop/operator/poisson.py index c8ab0934e..ee12c8b1f 100644 --- a/hysop/operator/poisson.py +++ b/hysop/operator/poisson.py @@ -8,7 +8,7 @@ from hysop.tools.types import check_instance from hysop.tools.enum import EnumFactory from hysop.tools.decorators import debug from hysop.fields.continuous import Field -from hysop.core.mpi.topology_descriptor import TopologyDescriptor +from hysop.core.mpi.topology_descriptor import CartesianDescriptors from hysop.core.graph.computational_node_frontend import ComputationalGraphNodeFrontend from hysop.backend.host.fortran.operator.poisson import PoissonFFTW @@ -76,7 +76,7 @@ class Poisson(ComputationalGraphNodeFrontend): check_instance(velocity, Field) check_instance(vorticity, Field) - check_instance(variables, dict, keys=Field, values=TopologyDescriptor) + check_instance(variables, dict, keys=Field, values=CartesianDescriptors) check_instance(base_kwds, dict, keys=str) dim = velocity.domain.dimension diff --git a/hysop/operator/redistribute.py b/hysop/operator/redistribute.py index 1e57b2c3a..e63fecd76 100644 --- a/hysop/operator/redistribute.py +++ b/hysop/operator/redistribute.py @@ -12,7 +12,6 @@ from hysop.tools.types import check_instance, to_set from hysop.tools.decorators import debug from hysop.fields.continuous import Field from hysop.core.mpi.topology import Topology, Cartesian -from hysop.core.mpi.topology_descriptor import TopologyDescriptor from hysop.core.mpi.redistribute import RedistributeOperator, RedistributeIntra from hysop.core.graph.node_generator import ComputationalGraphNodeGenerator @@ -70,13 +69,13 @@ class Redistribute(ComputationalGraphNodeGenerator): check_instance(source_topos, dict, keys=Field, values=set) for v in source_topos.values(): - check_instance(v, set, values=TopologyDescriptor) + check_instance(v, set, values=Topology) # format target_topo to a dict(Field, Topology) if not isinstance(target_topo, dict): - check_instance(target_topo, TopologyDescriptor) + check_instance(target_topo, Topology) target_topo = dict(zip(variables, (target_topo,)*len(variables))) - check_instance(target_topo, dict, keys=Field, values=TopologyDescriptor) + check_instance(target_topo, dict, keys=Field, values=Topology) # format components to a dict(Field, set(int)|None) if not isinstance(components, dict): diff --git a/hysop/problem.py b/hysop/problem.py index 745b13316..8de18fda4 100644 --- a/hysop/problem.py +++ b/hysop/problem.py @@ -16,6 +16,8 @@ class Problem(ComputationalGraph): def build(self): vprint('\nInitializing problem...') self.initialize(outputs_are_inputs=True, topgraph_method=None) + vprint('\nChecking problem...') + self.check() vprint('\nDiscretizing problem...') self.discretize() vprint('\nGetting work properties...') diff --git a/hysop/tools/types.py b/hysop/tools/types.py index 05fff7c71..3d8ef1e69 100644 --- a/hysop/tools/types.py +++ b/hysop/tools/types.py @@ -46,8 +46,10 @@ def check_instance(val, cls, allow_none=False, **kargs): val_cls = kargs.pop('values') for k,v in val.iteritems(): if not isinstance(v,val_cls): - msg='Value contained in {} at key {} is not an instance of {}, got {}.' - msg=msg.format(cls.__name__, k, val_cls.__name__, v.__class__) + if hasattr(k, 'name'): + key=k.name + msg='Value contained in {} at key \'{}\' is not an instance of {}, got {}.' + msg=msg.format(cls.__name__, key, val_cls, v.__class__) raise TypeError(msg) if kargs: raise RuntimeError('Some arguments were not used ({}).'.format(kargs)) -- GitLab