diff --git a/HySoP/CMake/FindFFTW.cmake b/CMake/FindFFTW.cmake similarity index 100% rename from HySoP/CMake/FindFFTW.cmake rename to CMake/FindFFTW.cmake diff --git a/HySoP/CMake/FindPythonFull.cmake b/CMake/FindPythonFull.cmake similarity index 100% rename from HySoP/CMake/FindPythonFull.cmake rename to CMake/FindPythonFull.cmake diff --git a/HySoP/CMake/FindPythonModule.cmake b/CMake/FindPythonModule.cmake similarity index 100% rename from HySoP/CMake/FindPythonModule.cmake rename to CMake/FindPythonModule.cmake diff --git a/HySoP/CMake/HySoPInstallSetup.cmake b/CMake/HySoPInstallSetup.cmake similarity index 100% rename from HySoP/CMake/HySoPInstallSetup.cmake rename to CMake/HySoPInstallSetup.cmake diff --git a/HySoP/CMake/HySoPTests.cmake b/CMake/HySoPTests.cmake similarity index 100% rename from HySoP/CMake/HySoPTests.cmake rename to CMake/HySoPTests.cmake diff --git a/HySoP/CMake/LibFindMacros.cmake b/CMake/LibFindMacros.cmake similarity index 100% rename from HySoP/CMake/LibFindMacros.cmake rename to CMake/LibFindMacros.cmake diff --git a/HySoP/CMake/MyTools.cmake b/CMake/MyTools.cmake similarity index 100% rename from HySoP/CMake/MyTools.cmake rename to CMake/MyTools.cmake diff --git a/HySoP/CMake/OutOfSourceBuild.cmake b/CMake/OutOfSourceBuild.cmake similarity index 100% rename from HySoP/CMake/OutOfSourceBuild.cmake rename to CMake/OutOfSourceBuild.cmake diff --git a/HySoP/CMake/PythonSetup.cmake b/CMake/PythonSetup.cmake similarity index 100% rename from HySoP/CMake/PythonSetup.cmake rename to CMake/PythonSetup.cmake diff --git a/HySoP/CMake/explore_python_config.py b/CMake/explore_python_config.py similarity index 100% rename from HySoP/CMake/explore_python_config.py rename to CMake/explore_python_config.py diff --git a/Docs/CMake b/Docs/CMake new file mode 120000 index 0000000000000000000000000000000000000000..2a271a9f072593cf0f7de2713322f58cdc41f6e5 --- /dev/null +++ b/Docs/CMake @@ -0,0 +1 @@ +../CMake \ No newline at end of file diff --git a/Docs/sphinx_sources/user_guide/domains.rst b/Docs/sphinx_sources/user_guide/domains.rst index 165b7e4573711fb91cb3e21c32775f7793599434..72404866b06cd7ad304593d7b4fb3405002a6938 100644 --- a/Docs/sphinx_sources/user_guide/domains.rst +++ b/Docs/sphinx_sources/user_guide/domains.rst @@ -10,10 +10,16 @@ Box-shaped domain :class:`~box.Box` +Data distribution +----------------- +.. toctree:: + :maxdepth: 2 + + topologies + How to define a subset ---------------------- - .. toctree:: :maxdepth: 2 diff --git a/Docs/sphinx_sources/user_guide/penalisation.rst b/Docs/sphinx_sources/user_guide/penalisation.rst index 89a3f85998a1487966edf5d7056ffce80f6689a8..7bd0ba197edff83269f3271e076040217329eadb 100644 --- a/Docs/sphinx_sources/user_guide/penalisation.rst +++ b/Docs/sphinx_sources/user_guide/penalisation.rst @@ -3,12 +3,14 @@ Penalisation ============ +.. currentmodule:: hysop.operator.penalization + :math:`u(X,t)` is the current velocity field, :math:`\omega(X,t)` is the current vorticity field, :math:`u_D` the velocity of the obstacle, dt the time step. Velocity penalisation --------------------- -:class:`hysop.operator.penalization.Penalization` +:class:`Penalization` Solves: @@ -41,7 +43,7 @@ After that, vorticity is computing as: Vorticity penalisation ---------------------- -:class:`hysop.operator.penalize_vorticity.PenalizeVorticity` +:class:`PenalizeVorticity` Solves: diff --git a/Docs/sphinx_sources/user_guide/subsets.rst b/Docs/sphinx_sources/user_guide/subsets.rst index 6e07ab2f3017a84a7ff70015a6b7edd573d2fb51..255b8dd846f80d01a0c8fedbb3c5d1dc9b65c308 100644 --- a/Docs/sphinx_sources/user_guide/subsets.rst +++ b/Docs/sphinx_sources/user_guide/subsets.rst @@ -3,9 +3,9 @@ Subsets and obstacles --------------------- -.. currentmodule:: hysop.domain.subsets +.. currentmodule:: hysop.domain -The class :class:`~subset.Subset` and its heirs allow user to define some subsets +The class :class:`subsets.Subset` and its heirs allow user to define some subsets of a predefined domain. This may be useful for: * define specific area(s) where an operator applies (penalisation for example) * reduce the size of i/o by writing hdf output only for a specific area. @@ -15,12 +15,12 @@ of a predefined domain. This may be useful for: Porous obstacles ^^^^^^^^^^^^^^^^ -:class:`~porous.BiPole` +:class:`porous.BiPole` .. image:: /figures/PolesExample.png :width: 400pt -:class:`~porous.QuadriPole` +:class:`porous.QuadriPole` .. image:: /figures/QuadriPoleExample.png :width: 400pt diff --git a/Docs/sphinx_sources/user_guide/topologies.rst b/Docs/sphinx_sources/user_guide/topologies.rst new file mode 100644 index 0000000000000000000000000000000000000000..de8d285fae6254f384e19870cdc61a51b1da783d --- /dev/null +++ b/Docs/sphinx_sources/user_guide/topologies.rst @@ -0,0 +1,15 @@ +.. _topologies: + +MPI topologies and space discretisation +======================================= + +.. currentmodule:: hysop.mpi + +:class:`topology.Cartesian` objects are used to described both the mpi grid layout +and the space discretisations (global and local to each mpi process). + +TODO : + +* describe the different ways to build topologies +* describe the way we define local and global grids. + diff --git a/HySoP/CMake b/HySoP/CMake new file mode 120000 index 0000000000000000000000000000000000000000..6126c513081cd9c08d0031ea93410931a228d572 --- /dev/null +++ b/HySoP/CMake @@ -0,0 +1 @@ +../CMake/ \ No newline at end of file diff --git a/HySoP/CMakeLists.txt b/HySoP/CMakeLists.txt index c5a4075d6bfdbbf1f88a9de2b72074275b87ac6f..e8fa1c138c9cbdd54671a5c4fd1788dfe3fd8f82 100644 --- a/HySoP/CMakeLists.txt +++ b/HySoP/CMakeLists.txt @@ -85,14 +85,14 @@ include(FindPythonModule) # - python packages - find_python_module(numpy REQUIRED) -find_python_module(scipy REQUIRED) +find_python_module(scipy) find_python_module(matplotlib) if(NOT matplotlib_FOUND) find_python_module(Gnuplot REQUIRED) endif() find_python_module(scitools) find_python_module(h5py REQUIRED) -find_python_module(sympy REQUIRED) +find_python_module(sympy) # --- OpenCL --- find_python_module(pyopencl REQUIRED) # --- MPI --- diff --git a/HySoP/hysop/domain/__init__.py b/HySoP/hysop/domain/__init__.py index 79b4773973e2ba2129afceeea2ba982ef6edc34f..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/HySoP/hysop/domain/__init__.py +++ b/HySoP/hysop/domain/__init__.py @@ -1,5 +0,0 @@ -## @package hysop.domain -# Physical domains descriptions and related tools. -# At the time : -# - Box for box-shaped domains -# - obstacle diff --git a/HySoP/hysop/domain/box.py b/HySoP/hysop/domain/box.py index b67c20794e30d513b349af7b91476146c539b62a..cbf6317fc5d6b267bb700bc9db88947335aaac6f 100644 --- a/HySoP/hysop/domain/box.py +++ b/HySoP/hysop/domain/box.py @@ -1,7 +1,4 @@ -""" -@file box.py -Box-shaped domains definition. - +"""Box-shaped domains definition. """ from hysop.domain.domain import Domain from hysop.constants import PERIODIC, debug @@ -9,10 +6,9 @@ import hysop.tools.numpywrappers as npw class Box(Domain): - """ - Box-shaped domain description. - @note FP : BC as parameter may be better? - @todo Have different BC + """ Box-shaped domain description. + + todo implement different boundary conditions typesKHave different BC """ @debug @@ -20,8 +16,12 @@ class Box(Domain): """ Create a Periodic Box from a dimension, length and origin. - @param length : Box length. Default [1.0, ...] - @param origin : Box minimum position. Default [0., ...] + Parameters + ----------- + length : list or numpy array of double + box sides lengthes. Default = [1.0, ...] + origin : list or numpy array of double + position of the lowest point of the box. Default [0., ...] Example: @@ -59,10 +59,6 @@ class Box(Domain): assert list(self.boundaries).count(PERIODIC) == self.dimension, msg def __str__(self): - """ - Informations display. - @return Informations - """ s = str(self.dimension) + \ "D box (parallelepipedic or rectangular) domain : \n" @@ -74,5 +70,5 @@ class Box(Domain): c1 = (self.length == other.length).all() c2 = (self.origin == other.origin).all() c3 = (self.boundaries == other.boundaries).all() - c4 = self.currentTask() == other.currentTask() + c4 = self.current_task() == other.current_task() return c1 and c2 and c3 and c4 diff --git a/HySoP/hysop/domain/control_box.py b/HySoP/hysop/domain/control_box.py index 2c3bd2214030c074db8de62de079d1017820892b..47d285e5a0fc0d522ceaaf583a6302fa13003f59 100644 --- a/HySoP/hysop/domain/control_box.py +++ b/HySoP/hysop/domain/control_box.py @@ -12,6 +12,9 @@ class ControlBox(SubBox): Useful to define control volume to perform integration. """ def __init__(self, **kwds): + """ + Same parameters as for :class:`hysop.domain.subsets.SubBox`. + """ super(ControlBox, self).__init__(**kwds) # We must have a real box, not a plane ... @@ -20,6 +23,9 @@ class ControlBox(SubBox): self.surf = [None] * len(self.length) * 2 def discretize(self, topo): + """Create a sub meshes for topo inside the control box and + on its faces. + """ # Create the mesh for the whole box super(ControlBox, self).discretize(topo) # Create a mesh for each side @@ -54,17 +60,26 @@ class ControlBox(SubBox): def integrate_on_faces(self, field, topo, list_dir, component=0, root=None): - """ - @param[in] field : a hysop continuous field - @param[in] topo : a hysop.mpi.topology.Cartesian topology - @param[in] root : root process used for mpi reduction. If None - reduction is done on all processes from topo. - @param[in] list_dir : list of surfaces on which we must integrate. - 0 : normal to xdir, lower surf, - 1 : normal to xdir, upper surf (in x dir) - 2 : normal to ydir, lower surf, and so on. - the field must be integrated. - @return a numpy array, with res = sum + """Integration of a field on one or more faces of the box + + Parameters + ---------- + field : :class:`~hysop.fields.continuous.Field` + a field to be integrated on the box + topo : :class:`~hysop.mpi.topology.Cartesian` + set mesh/topology used for integration + list_dir : list of int + indices of faces on which integration is required + 0 : normal to xdir, lower surf, + 1 : normal to xdir, upper surf (in x dir) + 2 : normal to ydir, lower surf, and so on. + component : int, optional + number of the field component to be integrated + root : int, optional + rank of the leading mpi process (to collect data) + If None reduction is done on all processes from topo. + + Returns a numpy array, with res = sum of the integrals of a component of field on all surf in list_dir """ res = 0. @@ -79,17 +94,24 @@ class ControlBox(SubBox): return topo.comm.reduce(res, root=root) def integrate_on_faces_allc(self, field, topo, list_dir, root=None): - """ - @param[in] field : a hysop continuous field - @param[in] topo : a hysop.mpi.topology.Cartesian topology - @param[in] root : root process used for mpi reduction. If None - reduction is done on all processes from topo. - @param[in] list_dir : list of surfaces on which we must integrate. - 0 : normal to xdir, lower surf, - 1 : normal to xdir, upper surf (in x dir) - 2 : normal to ydir, lower surf, and so on. - the field must be integrated. - @return a numpy array, with res[i] = sum + """Integration of a field on one or more faces of the box + + Parameters + ---------- + field : :class:`~hysop.fields.continuous.Field` + a field to be integrated on the box + topo : :class:`~hysop.mpi.topology.Cartesian` + set mesh/topology used for integration + list_dir : list of int + indices of faces on which integration is required + 0 : normal to xdir, lower surf, + 1 : normal to xdir, upper surf (in x dir) + 2 : normal to ydir, lower surf, and so on. + root : int, optional + rank of the leading mpi process (to collect data) + If None reduction is done on all processes from topo. + + Returns a numpy array, with res[i] = sum of the integrals of component i of field on all surf in list_dir """ nbc = field.nb_components diff --git a/HySoP/hysop/domain/domain.py b/HySoP/hysop/domain/domain.py index cecad94d3803abfac75443f1e9110db504e36697..6868c724dc1f8135af933fe003296014985120cb 100644 --- a/HySoP/hysop/domain/domain.py +++ b/HySoP/hysop/domain/domain.py @@ -1,7 +1,4 @@ -""" -@file domain.py - -Abstract interface for physical domains description. +"""Abstract interface for physical domains description. """ from abc import ABCMeta, abstractmethod @@ -24,21 +21,51 @@ class Domain(object): @debug @abstractmethod def __init__(self, dimension=3, proc_tasks=None, bc=None): - """ Constructor - @param dimension integer : domain dimension. - @param proc_tasks : task - mpi processes connection. + """ + + Parameters + ---------- + dimension : integer, optional + proc_tasks : list or array of int, optional + connectivity between mpi process id and task number. + See notes below. Default : all procs on task DEFAULT_TASK_ID. + bc : list or array of int, optional + type of boundary condtions. See notes below. + + + Attributes + ---------- + + dimension : int + topologies : dictionnary of :class:`~hysop.mpi.topology.Cartesian` + all topologies already built for this domain. + comm_task : mpi communicator + corresponds to the task that owns the current process. + boundaries : list or array of int + boundary conditions type. + + Notes + ----- + About boundary conditions + ========================== + At the time, only periodic boundary conditions are implemented. + Do not use this parameter (i.e. let default values) + + About MPI Tasks + ================ proc_tasks[n] = 12 means that task 12 owns proc n. + """ - ## Domain dimension. + # Domain dimension. self.dimension = dimension - ## A list of all the topologies defined on this domain. - ## Each topology is unique in the sense defined by - ## the comparison operator in the class hysop.mpi.topology.Cartesian. + # A list of all the topologies defined on this domain. + # Each topology is unique in the sense defined by + # the comparison operator in the class hysop.mpi.topology.Cartesian. self.topologies = {} - ## Connectivity between mpi tasks and proc numbers : - ## self.task_of_proc[i] returns the task which is bound to proc of - ## rank i in main_comm. + # Connectivity between mpi tasks and proc numbers : + # self.task_of_proc[i] returns the task which is bound to proc of + # rank i in main_comm. # Warning FP: rank in main_comm! We consider that each proc has # one and only one task. # Maybe we should change this and allow proc_tasks in subcomm @@ -53,19 +80,29 @@ class Domain(object): comm_s = main_comm.Split(color=proc_tasks[main_rank], key=main_rank) - ## The sub-communicator corresponding to the task that owns - ## the current process. + # The sub-communicator corresponding to the task that owns + # the current process. self.comm_task = comm_s - ## Boundary conditions type. - ## At the moment, only periodic conditions are allowed. + # Boundary conditions type. + # At the moment, only periodic conditions are allowed. self.boundaries = bc - def isOnTask(self, params): - """ - @param params : a hysop.mpi.MPIParams object + def is_on_task(self, params): + """Test if the current process corresponds to param task. + + :param params: :class:`~hysop.mpi.MPIParams` or int + description of the mpi setup or task number. or an int (task number) - @return true if params.task_id (or params if int) corresponds with - task_id of the current proc. + :return bool: + + Example : + :code: + dom.is_on_task(4) + mpi_params = MPIParams(task_id=4) + dom.is_on_task(mpi_params) + :code: + + returns True if the current process runs on task 4. """ if params.__class__ is MPIParams: return params.task_id == self._tasks_on_proc[main_rank] @@ -73,147 +110,151 @@ class Domain(object): return params == self._tasks_on_proc[main_rank] def tasks_on_proc(self, index): - """ - @param index: proc number - @return task number of mpi proc 'index' + """Get task number for a given mpi process + + :param index: int + proc number + :return int: """ return self._tasks_on_proc[index] def tasks_list(self): - """ - @return the connectivity between proc. numbers and their associated - task (numbers in main_comm). + """Get task/process number connectivity + + :return list or array of int: + such that res[proc_number] = task_id """ return self._tasks_on_proc - def currentTask(self): - """ - @return task number of the current proc. + def current_task(self): + """Get task number of the current mpi process. + + :return int: """ return self._tasks_on_proc[main_rank] def create_topology(self, discretization, dim=None, mpi_params=None, shape=None, cutdir=None): - """ - Create or return an existing hysop.mpi.topology. - - Either it gets the topology corresponding to the input arguments - if it exists (in the sense of the comparison operator defined in - hysop.mpi.topology.Cartesian) - or it creates a new topology and register it i n the topology list. - @param domain : the geometry; it must be a box. - @param discretization : a hysop.tools.parameters.Discretization - with: - - resolution = Number of points in the domain - in each direction. We assume that first point corresponds - to origin, and last point to boundary point, - whatever the boundary type is. - That is x[0] = domain.origin and - x[Discretization.resolution-1] = domain.Lengths_x. - - ghosts = number of points in the ghost layer - @param dim : dimension of the topology - @param mpi_params : a hysop.tools.parameters.MPIParams, with: - - comm : MPI communicator used to create this topology - (default = main_comm) - - task_id : id of the task that owns this topology. - @param isperiodic : periodicity of the topology (in each direction) - @param cutdir : array of bool, set cutdir[dir] = True if you want - to distribute data through direction dir. - @param shape : topology resolution - (i.e process layout in each direction). - @return the required topology. + """Create or return an existing :class:`~hysop.mpi.topology.Cartesian`. + + Parameters + ----------- + discretization : :class:`~hysop.tools.parameters.Discretization` + description of the global space discretization + (resolution and ghosts). + dim : int, optional + mpi topology dimension. + mpi_params : :class:`~hysop.tools.parameters.MPIParams`, optional + mpi setup (comm, task ...). + If None, comm = main_comm, task = DEFAULT_TASK_ID. + shape : list or array of int + mpi grid layout + cutdir : list or array of bool + set which directions must (may) be distributed, + cutdir[dir] = True to distribute data along dir. + + Returns + ------- + :class:`~hysop.mpi.topology.Cartesian` + Either it gets the topology corresponding to the input arguments + if it exists (in the sense of the comparison operator defined in + :class:`~hysop.mpi.topology.Cartesian`) + or it creates a new topology and register it in the topology dict. + """ # set task number - tid = self.currentTask() + tid = self.current_task() if mpi_params is None: mpi_params = MPIParams(comm=self.comm_task, task_id=tid) else: msg = 'Try to create a topology on a process that does not' msg += ' belong to the current task.' assert mpi_params.task_id == tid, msg - newTopo = Cartesian(self, discretization, dim=dim, - mpi_params=mpi_params, shape=shape, - cutdir=cutdir) - newid = newTopo.get_id() + new_topo = Cartesian(self, discretization, dim=dim, + mpi_params=mpi_params, shape=shape, + cutdir=cutdir) + newid = new_topo.get_id() return self.topologies[newid] def create_plane_topology_from_mesh(self, localres, global_start, cdir=None, **kwds): - """ - Create or return an existing hysop.mpi.topology. + """Create 1D topology from predifined :class:`~hysop.domain.mesh.Mesh`. Define a 'plane' (1D) topology for a given mesh resolution. This function is to be used when topo/discretization features come from an external routine (e.g. scales or fftw) - @param localres : local mesh resolution - @param global_start : global indices of the lowest point - of the local mesh. - @param cdir : direction of cutting (i.e. normal to mpi planes) - default = last if fortran order, first if C order. + + Parameters + ---------- + localres : list or array of int + local mesh resolution + global_start : list or array of int + indices in the global mesh of the lowest point of the local mesh. + cdir : int, optional + direction where data must be distributed in priority. + Default = last if fortran order, first if C order. + **kwds : other args + used in :class:`~hysop.mpi.topology.Cartesian` build. + """ - tid = self.currentTask() + tid = self.current_task() if 'mpi_params' not in kwds: kwds['mpi_params'] = MPIParams(comm=self.comm_task, task_id=tid) else: msg = 'Try to create a topology on a process that does not' msg += ' belong to the current task.' assert kwds['mpi_params'].task_id == tid, msg - newTopo = Cartesian.plane_precomputed(localres, global_start, cdir, - domain=self, **kwds) + new_topo = Cartesian.plane_precomputed(localres, global_start, cdir, + domain=self, **kwds) - newid = newTopo.get_id() + newid = new_topo.get_id() return self.topologies[newid] - def _checkTopo(self, newTopo): - """ - Return the id of the input topology + def _check_topo(self, new_topo): + """Returns the id of the input topology if it exists in the domain list, else return -1. - @param newTopo : the topology to check - @return id of the topology if present else -1. """ otherid = -1 for top in self.topologies.values(): - if newTopo == top: + if new_topo == top: otherid = top.get_id() break return otherid - def register(self, newTopo): - """ - Add a new topology in the list of this domain. + def register(self, new_topo): + """Add a new topology in the list of this domain. Do nothing if an equivalent topology is already in the list. - @param newTopo : the topology to be registered. - @return the id of the new registered topology + + :Returns the id of the new registered topology or of the corresponding 'old one' if it already exists. """ - otherid = self._checkTopo(newTopo) + otherid = self._check_topo(new_topo) if otherid < 0: - self.topologies[newTopo.get_id()] = newTopo - newTopo.isNew = True - newid = newTopo.get_id() + self.topologies[new_topo.get_id()] = new_topo + new_topo.isNew = True + newid = new_topo.get_id() else: # No registration - newTopo.isNew = False + new_topo.isNew = False newid = otherid return newid def remove(self, topo): - """ - Remove a topology from the list of this domain. + """Remove a topology from the list of this domain. Do nothing if the topology does not exist in the list. Warning : the object topo is not destroyed, only removed from self.topologies. - @param topo : the topology to be removed. - @return either the od of the removed topology + + Returns either the id of the removed topology or -1 if nothing is done. """ - otherid = self._checkTopo(topo) + otherid = self._check_topo(topo) if otherid >= 0: self.topologies.pop(otherid) return otherid - def printTopologies(self): + def print_topologies(self): """ Print all topologies of the domain. """ @@ -238,7 +279,7 @@ class Domain(object): def i_periodic_boundaries(self): """ - @return list of directions where + Returns the list of directions where boundaries are periodic """ return np.where(self.boundaries == PERIODIC)[0] diff --git a/HySoP/hysop/domain/mesh.py b/HySoP/hysop/domain/mesh.py index 616be81b42508c5530b63aa8a313ea1fcfce2f25..b3f26498ae42e286c2f19f221d5d7b0c17e75508 100644 --- a/HySoP/hysop/domain/mesh.py +++ b/HySoP/hysop/domain/mesh.py @@ -1,5 +1,4 @@ -""" -@file mesh.py local cartesian grid. +"""local (mpi process) cartesian grid. """ from hysop.constants import debug import hysop.tools.numpywrappers as npw @@ -63,12 +62,16 @@ class Mesh(object): @debug def __init__(self, parent, discretization, resolution, global_start): """ - @param parent : the geometry on which this mesh is defined - (it must be a rectangle or a rectangular cuboid) - @param discretization : hysop.tools.parameters.Discretization which - describes the global discretization of the domain - (resolution and ghost layer) - @param global_start + Parameters + ----------- + parent : :class:`hysop.domain.domain.Domain` + the geometry on which this mesh is defined + (it must be a rectangle or a rectangular cuboid) + discretization : :class:`hysop.tools.parameters.Discretization` + describes the global discretization of the domain + (resolution and ghost layer) + global_start : list or array of int + indices in the global mesh of the lowest point of the local mesh. """ # Note : all variables with 'global' prefix are related @@ -155,9 +158,11 @@ class Mesh(object): / self.space_step)) def is_inside(self, point): - """ - @param point: coordinates of a point - @return : true if the point is inside the local (mpi proc) mesh + """True if the point belongs to volume or surface + described by the mesh + + :param point: list or numpy array, + coordinates of a point """ point = npw.asrealarray(point) return ((point - self.origin) >= 0.).all() and ((self.end @@ -175,13 +180,12 @@ class Mesh(object): return s def convert2local(self, sl): - """ - Return the local mesh indices from - their global indices. - Ilocal = Iglobal - _global_start + ghost - @param sl: the list of slices (one per dir) for global indices + """convert indices from global mesh to local one + + :param sl: list of slices (one slice per dir) of global indices something like [(start_dir1, end_dir1), ...] - @return the same kind of list but in local coordinates + + Returns the same kind of list but in local coordinates """ tmp = utils.intersl(sl, self.position) if tmp is not None: @@ -192,26 +196,22 @@ class Mesh(object): return None def convert2global(self, sl): - """ - Return the global mesh indices from - their local indices. - Iglobal = Ilocal + _global_start - ghost - @param loc_index an array of size domain.dim*2 - with loc_index = [start_dir1, end_dir1, start_dir2, end_dir2, ...] - return an array of indices, same 'setup' as loc_index. + """convert indices from local mesh to global one + + :param sl: list of slices (one slice per dir) of local indices + something like [(start_dir1, end_dir1), ...] + + Returns the same kind of list but in global coordinates """ return [slice(sl[i].start - self._shift[i], sl[i].stop - self._shift[i]) for i in xrange(self._dim)] def __eq__(self, other): - """ - = operator for mesh. - We consider that 2 submeshes are equal if all the conditions - below are fulfilled: - - their global resolution is the same - - the ghost layer is the same - - their local resolution is the same + """True if meshes are equal. + Two meshes are equal if they have the same + global resolution, the same ghost layer and the same + local resolution is the same """ if self.__class__ != other.__class__: return False @@ -219,8 +219,7 @@ class Mesh(object): npw.equal(self.resolution, other.resolution).all() def start(self): - """ - @return indices of the lowest point of the local mesh + """Returns indices of the lowest point of the local mesh in the global grid """ return npw.asdimarray([p for p in @@ -228,9 +227,9 @@ class Mesh(object): for d in xrange(self._dim))]) def stop(self): - """ - @return indices + 1 of the 'highest' point of the local mesh + """Returns indices + 1 of the 'highest' point of the local mesh in the global grid. + Warning: made to be used in slices : slice(mesh.start()[d], mesh.stop()[d]) represents the points of the current process mesh, indices given @@ -241,8 +240,7 @@ class Mesh(object): for d in xrange(self._dim))]) def global_resolution(self): - """ - @return the resolution of the global grid (on the whole + """Returns the resolution of the global grid (on the whole domain, whatever the mpi distribution is). Warning : this depends on the type of boundary conditions. For periodic bc, this corresponds to the user-defined discretization diff --git a/HySoP/hysop/domain/porous.py b/HySoP/hysop/domain/porous.py index f57fe564374ede00bb0e3e43653a92c60131262f..5d7a5643b7da85fc99e6bc288aa037685d67dae7 100644 --- a/HySoP/hysop/domain/porous.py +++ b/HySoP/hysop/domain/porous.py @@ -1,4 +1,17 @@ -"""Pre-defined subsets as combination of basic geometries. +"""Porous (multi-layers) subsets: + +.. currentmodule hysop.domain.porous +* :class:`~Porous` +* :class:`~BiPole` +* :class:`~QuadriPole` +* :class:`~RingPole` +* :class:`~Ring` + +See also +-------- + +* :class:`~hysop.domain.subsets.Subset` for standard subsets. +* :ref:`subsets` in HySoP user guide. """ from hysop.domain.subsets import Subset, Sphere, HemiSphere @@ -19,7 +32,7 @@ class Porous(Subset): """ Parameters ----------- - source : :class:`~hysop.domain.subsets.subset.Subset` + source : :class:`~hysop.domain.subsets.Subset` geometry of the object. The only authorized sources are : Cylinder, Sphere, HemiSphere, HemiCylinder origin : list or array of coordinates @@ -27,7 +40,17 @@ class Porous(Subset): layers : list of real width of each layer (starting from outside layer) """ - super(Porous, self).__init__(**kwds) + # the external layer of the porous subset. + # Note FP : we need this envelop to set a proper + # indicator function for the porous subset, since this + # function is used in all the union, intersection ... + # methods. + # is_inside will return true for any point inside this + # envelop. + envelop = source(parent=kwds['parent'], origin=origin, + radius=sum(layers)) + assert 'chi' not in kwds + super(Porous, self).__init__(chi=envelop.get_chi(), **kwds) # Center position self.origin = npw.asrealarray(origin).copy() self.is_porous = True @@ -36,7 +59,6 @@ class Porous(Subset): assert isinstance(layers, list) # width of porous layers on the source self._layers = layers - self._box = None def discretize(self, topo): @@ -117,24 +139,22 @@ class BiPole(Porous): ---------- poles_thickness : real or list or array of real numbers thickness of layer(s) in direction given by poles_dir. - Use : - * poles_thickness = value or [value] if all poles have the - same width. - * poles_thickness = [v1, v2] to set bottom pole to v1 and top - pole to v2 + Use poles_thickness = value or [value] + if all poles have the same width. + Or, to set bottom pole to v1 and top pole to v2 + poles_thickness = [v1, v2]. poles_dir : int direction for which poles are 'computed'. Default = last dir. kwds : args for base class - Example: - -------- + Notes + ----- - >>> obst = BiPole(..., source=HemiSphere, - ... poles_thickness=[0.1, 0.2], poles_dir=[2] + An example to create poles in direction 2, i.e. z, + of thickness 0.2 on top and 0.1 at the bottom of an HemiSphere:: - will create poles in direction 2, i.e. z, - of thickness 0.2 on top and 0.1 at the - bottom of an HemiSphere. + obst = BiPole(..., source=HemiSphere, poles_thickness=[0.1, 0.2], + poles_dir=[2]) """ super(BiPole, self).__init__(**kwds) @@ -189,24 +209,22 @@ class QuadriPole(Porous): ---------- poles_thickness : real or list or array of real numbers thickness of layer(s) in direction given by poles_dir. - Use : - * poles_thickness = value or [v1, v2] if all poles in one direction - have the - same width. - * poles_thickness = [v1, v2, v3, v4] to set - bottom/top poles to v1/v2 in y dir - and bottom/top poles to v3/v4 in z dir. + Use poles_thickness = value or [v1, v2] + if all poles in one direction have the same width. + Or, to set bottom/top poles to v1/v2 in y dir and + bottom/top poles to v3/v4 in z dir, + poles_thickness = [v1, v2, v3, v4]. + kwds : args for base class - Example: - -------- + Notes + ----- - >>> obst = QuadriPole(source=HemiSphere, - ... poles_thickness=[0.1, 0.2]) + An example to create poles in direction 2, i.e. z, + of thickness 0.2 on top and 0.1 at the bottom, on an HemiSphere:: - will create poles in direction 2, i.e. z, - of thickness 0.2 on top and 0.1 at the - bottom, on an HemiSphere. + obst = QuadriPole(..., source=HemiSphere, + poles_thickness=[0.1, 0.2]) """ super(QuadriPole, self).__init__(**kwds) diff --git a/HySoP/hysop/domain/subsets.py b/HySoP/hysop/domain/subsets.py index 572620b8f2ae60aad420419403bfc66ce230a7ff..65baf2d022d0296f1a3c8250335b1a7c059b7739 100644 --- a/HySoP/hysop/domain/subsets.py +++ b/HySoP/hysop/domain/subsets.py @@ -1,4 +1,18 @@ -""" subsets of a given domain (sphere ...) +"""Subsets of a given domain: + +* :class:`~hysop.domain.subsets.Sphere`, +* :class:`~hysop.domain.subsets.HemiSphere`, +* :class:`~hysop.domain.subsets.Cylinder`, +* :class:`~hysop.domain.subsets.HemiCylinder`, +* :class:`~hysop.domain.subsets.SubBox`, +* :class:`~hysop.domain.subsets.Subset` (abstract base class). + +See also +-------- + +* :class:`~hysop.domain.porous.Porous` for porous (multi-layers) subsets. +* :ref:`subsets` in HySoP user guide. + """ from hysop.domain.domain import Domain import numpy as np @@ -13,24 +27,18 @@ class Subset(object): """ A subset is a geometry defined inside a domain. Given a topology on the parent domain, the subset must - provide 'slices' or 'tabs' to allow the computation of a discrete - field on the subset, with: + provide some lists of indices to allow the computation of a discrete + field on the subset, with something like - data[subset.slices] = ... + data[subset.ind[topo] = ... or data[subset.tab] = ... - slices : a list of python slices defining the grid points inside the subset - ind : a tuple of arrays of indices - There are two types of subsets: - 'regular' ones, those on which a regular mesh can be defined - others, like spheres, cylinders ... - For regular subsets, one must use 'slices' to compute field inside - the subset whereas 'tab' is required for others. - - Regular subsets have an attribute 'ind' which is a dictionnary of tuples + Subsets have an attribute 'ind' which is a dictionnary of tuples representing the points inside the subset, (keys = topology) such that: ind[topo] = (i_x, i_y, i_z), with i_x[i], i_y[i], i_z[i] for each index i being the indices in the @@ -61,10 +69,10 @@ class Subset(object): ----- func argument list must start with space coordinates, - e.g. for a 3D domain something like: + e.g. for a 3D domain something like:: - >>> def myfunc(x, y, z, ...): - ... + def myfunc(x, y, z, ...): + ... """ assert isinstance(parent, Domain) @@ -98,6 +106,22 @@ class Subset(object): """ return self._is_inside(*args) <= 0 + def _set_chi(self, chi): + """Reset indicator function + + Mostly for internal setup (during porous obst. init), so do not + use it or use it with care ... + """ + self._is_inside = chi + + def get_chi(self): + """Get indicator function + + Mostly for internal setup (during porous obst. init), so do not + use it or use it with care ... + """ + return self._is_inside + def discretize(self, topo): """ Create the list of indices for points inside the subset @@ -144,7 +168,7 @@ class Subset(object): Parameters ---------- - slist : a list of :class:`~hysop.subset.subsets.Subset` + slist : a list of :class:`~hysop.domain.subsets.Subset` topo : :class:`~hysop.mpi.topology.Cartesian` Returns @@ -162,7 +186,7 @@ class Subset(object): Parameters ---------- - slist : a list of :class:`~hysop.subset.subsets.Subset` + slist : a list of :class:`~hysop.domain.subsets.Subset` topo : :class:`~hysop.mpi.topology.Cartesian` Returns @@ -193,7 +217,7 @@ class Subset(object): Parameters ---------- - s1, s2 : lists of :class:`~hysop.subset.subsets.Subset` + s1, s2 : lists of :class:`~hysop.domain.subsets.Subset` topo : :class:`~hysop.mpi.topology.Cartesian` Returns @@ -211,8 +235,8 @@ class Subset(object): Parameters ---------- - slist : list of Subset - topo : `~hysop.mpi.topology.Cartesian` + slist : list of :class:`~hysop.domain.subsets.Subset` + topo : :class:`~hysop.mpi.topology.Cartesian` Returns -------- @@ -228,8 +252,8 @@ class Subset(object): Parameters ---------- - slist : list of Subset - topo : `~hysop.mpi.topology.Cartesian` + slist : list of :class:`~hysop.domain.subsets.Subset` + topo : :class:`~hysop.mpi.topology.Cartesian` Returns -------- @@ -260,8 +284,8 @@ class Subset(object): Parameters ---------- - s1, s2 : Subset - topo : `~hysop.mpi.topology.Cartesian` + s1, s2 : :class:`~hysop.domain.subsets.Subset` + topo : :class:`~hysop.mpi.topology.Cartesian` Returns -------- @@ -275,8 +299,8 @@ class Subset(object): Parameters ---------- - s1, s2 : Subset - topo : `~hysop.mpi.topology.Cartesian` + s1, s2 : :class:`~hysop.domain.subsets.Subset` + topo : :class:`~hysop.mpi.topology.Cartesian` Returns -------- @@ -298,8 +322,8 @@ class Subset(object): Parameters ---------- - s1, s2 : list of Subset - topo : `~hysop.mpi.topology.Cartesian` + s1, s2 : list of :class:`~hysop.domain.subsets.Subset` + topo : :class:`~hysop.mpi.topology.Cartesian` Returns -------- @@ -314,9 +338,9 @@ class Subset(object): Parameters ---------- - field : `~hysop.fields.continuous.Field` + field : :class:`~hysop.fields.continuous.Field` a field to be integrated on the box - topo : `~hysop.mpi.topology.Cartesian` + topo : :class:`~hysop.mpi.topology.Cartesian` set mesh/topology used for integration root : int, optional rank of the leading mpi process (to collect data) @@ -343,9 +367,9 @@ class Subset(object): Parameters ---------- - field : `~hysop.fields.continuous.Field` + field : :class:`~hysop.fields.continuous.Field` a field to be integrated on the box - topo : `~hysop.mpi.topology.Cartesian` + topo : :class:`~hysop.mpi.topology.Cartesian` set mesh/topology used for integration component : int, optional number of the field component to be integrated @@ -369,9 +393,9 @@ class Subset(object): Parameters ---------- - field : `~hysop.fields.continuous.Field` + field : :class:`~hysop.fields.continuous.Field` a field to be integrated on the box - topo : `~hysop.mpi.topology.Cartesian` + topo : :class:`~hysop.mpi.topology.Cartesian` set mesh/topology used for integration component : int, optional number of the field component to be integrated @@ -395,7 +419,7 @@ class Subset(object): Parameters ---------- - field : `~hysop.fields.discrete.DiscreteField` + field : :class:`~hysop.fields.discrete.DiscreteField` a field to be integrated on the box root : int, optional rank of the leading mpi process (to collect data) @@ -422,7 +446,7 @@ class Subset(object): Parameters ---------- - field : `~hysop.fields.discrete.DiscreteField` + field : :class:`~hysop.fields.discrete.DiscreteField` a field to be integrated on the box component : int, optional number of the field component to be integrated @@ -446,7 +470,7 @@ class Subset(object): Parameters ---------- - field : `~hysop.fields.discrete.DiscreteField` + field : :class:`~hysop.fields.discrete.DiscreteField` a field to be integrated on the box component : int, optional number of the field component to be integrated @@ -474,6 +498,8 @@ class Sphere(Subset): """ Parameters ---------- + origin : :class:`~hysop.domain.subsets.Subset` + origin : list or array position of the center radius : double, optional @@ -534,6 +560,13 @@ class HemiSphere(Sphere): self.LeftBox = left_box def chi(self, *args): + """Indicator function of the subset + + Returns + ------- + array of bool + + """ return np.logical_and(self._is_inside(*args) <= 0, self.LeftBox(args[self.cutdir]) <= 0) @@ -583,6 +616,13 @@ class Cylinder(Subset): length=length, origin=origin) def chi(self, *args): + """Indicator function of the subset + + Returns + ------- + array of bool + + """ return np.logical_and(self._is_inside(*args) <= 0, args[self.axis] == args[self.axis]) @@ -620,6 +660,13 @@ class HemiCylinder(Cylinder): self.LeftBox = left_box def chi(self, *args): + """Indicator function of the subset + + Returns + ------- + array of bool + + """ return (np.logical_and( np.logical_and(self._is_inside(*args) <= 0, self.LeftBox(args[self.cutdir]) <= 0), @@ -682,7 +729,7 @@ class SubBox(Subset): """ Compute a local submesh on the subset, for a given topology - :param topo: `~hysop.mpi.topology.Cartesian` + :param topo: :class:`~hysop.mpi.topology.Cartesian` """ assert isinstance(topo, Cartesian) # Find the indices of starting/ending points in the global_end @@ -703,9 +750,9 @@ class SubBox(Subset): Parameters ---------- - field : `~hysop.field.continuous.Field` + field : :class:`~hysop.field.continuous.Field` a field to be integrated on the box - topo : `~hysop.mpi.topology.Cartesian` + topo : :class:`~hysop.mpi.topology.Cartesian` set mesh/topology used for integration component : int, optional number of the field component to be integrated @@ -732,7 +779,7 @@ class SubBox(Subset): Parameters ---------- func: python function of space coordinates - topo: `~hysop.mpi.topology.Cartesian` + topo: :class:`~hysop.mpi.topology.Cartesian` set mesh/topology used for integration nbc : int number of components of the return value from func @@ -759,7 +806,7 @@ class SubBox(Subset): Parameters ---------- func: python function of space coordinates - topo: `~hysop.mpi.topology.Cartesian` + topo: :class:`~hysop.mpi.topology.Cartesian` set mesh/topology used for integration Returns @@ -783,7 +830,7 @@ class SubBox(Subset): Parameters ---------- - field : `~hysop.field.discrete.DiscreteField` + field : :class:`~hysop.field.discrete.DiscreteField` a field to be integrated on the box component : int, optional number of the field component to be integrated diff --git a/HySoP/hysop/domain/tests/test_box.py b/HySoP/hysop/domain/tests/test_box.py index 2fd63b584a4d251bf4fb25011181e08725a85c27..ca231e8ceec97c9634942eda24e8d32ad2fed1e3 100644 --- a/HySoP/hysop/domain/tests/test_box.py +++ b/HySoP/hysop/domain/tests/test_box.py @@ -20,11 +20,11 @@ def test_create_box1(): def test_create_box2(): - L = npw.asrealarray([1., 2.5]) + length = npw.asrealarray([1., 2.5]) ori = npw.asrealarray([1, 2.1]) - dom = Box(length=L, origin=ori) + dom = Box(length=length, origin=ori) assert dom.dimension == 2 - assert allclose(dom.length, L) + assert allclose(dom.length, length) assert allclose(dom.origin, ori) assert [b == PERIODIC for b in dom.boundaries] cond = [dom.tasks_on_proc(i) == DEFAULT_TASK_ID for i in range(main_size)] @@ -33,11 +33,11 @@ def test_create_box2(): def test_create_box3(): - L = [1, 2, 4.] - dom = Box(length=L) + length = [1, 2, 4.] + dom = Box(length=length) assert dom.dimension == 3 - assert allclose(dom.length, npw.asrealarray(L)) - assert allclose(dom.origin, zeros_like(L)) + assert allclose(dom.length, npw.asrealarray(length)) + assert allclose(dom.origin, zeros_like(length)) assert [b == PERIODIC for b in dom.boundaries] cond = [dom.tasks_on_proc(i) == DEFAULT_TASK_ID for i in range(main_size)] cond = npw.asboolarray(cond) @@ -45,20 +45,20 @@ def test_create_box3(): def test_create_box4(): - L = [1, 2, 4.] + length = [1, 2, 4.] tasks = [CPU] * main_size if main_size > 1: tasks[-1] = GPU - dom = Box(length=L, proc_tasks=tasks) + dom = Box(length=length, proc_tasks=tasks) last = main_size - 1 if main_size > 1: if main_rank != last: - assert dom.currentTask() == CPU + assert dom.current_task() == CPU else: - assert dom.currentTask() == GPU + assert dom.current_task() == GPU else: - assert dom.currentTask() == CPU + assert dom.current_task() == CPU # Test topology creation ... @@ -96,17 +96,17 @@ def test_topo_standard(): def test_topo_multi_tasks(): dom = Box(proc_tasks=proc_tasks) - if dom.isOnTask(CPU): + if dom.is_on_task(CPU): topo = dom.create_topology(discretization=r3D) - elif dom.isOnTask(GPU): + elif dom.is_on_task(GPU): topo = dom.create_topology(discretization=r3DGh, dim=2) assert len(dom.topologies) == 1 assert isinstance(topo, Cartesian) assert topo is dom.topologies.values()[0] - if dom.isOnTask(CPU): - assert not topo.hasGhosts() - elif dom.isOnTask(GPU): - assert topo.hasGhosts() + if dom.is_on_task(CPU): + assert not topo.has_ghosts() + elif dom.is_on_task(GPU): + assert topo.has_ghosts() def test_topo_plane(): @@ -125,21 +125,21 @@ def test_topo_from_mesh(): # e.g. for fftw dom = Box(proc_tasks=proc_tasks) from hysop.f2py import fftw2py - if dom.isOnTask(CPU): + if dom.is_on_task(CPU): localres, global_start = fftw2py.init_fftw_solver( r3D.resolution, dom.length, comm=comm_s.py2f()) print localres, global_start topo = dom.create_plane_topology_from_mesh(localres=localres, global_start=global_start, discretization=r3D) - elif dom.isOnTask(GPU): + elif dom.is_on_task(GPU): topo = dom.create_topology(discretization=r3DGh, dim=2) - if dom.isOnTask(CPU): + if dom.is_on_task(CPU): assert (topo.mesh.resolution == localres).all() assert (topo.mesh.start() == global_start).all() assert topo.dimension == 1 assert (topo.shape == [1, 1, comm_s.Get_size()]).all() - elif dom.isOnTask(GPU): + elif dom.is_on_task(GPU): assert topo.size == 1 diff --git a/HySoP/hysop/domain/tests/test_control_box.py b/HySoP/hysop/domain/tests/test_control_box.py index c4a4ada9ee7fcf716093ad84baa1e96c8ed3de75..19eac3b1d8b2b46418eba1947c06a46bd3364b03 100644 --- a/HySoP/hysop/domain/tests/test_control_box.py +++ b/HySoP/hysop/domain/tests/test_control_box.py @@ -1,5 +1,5 @@ -from hysop.domain.subsets.control_box import ControlBox -from hysop.domain.subsets.boxes import SubBox +from hysop.domain.control_box import ControlBox +from hysop.domain.subsets import SubBox from hysop.tools.parameters import Discretization from hysop import Field, Box import numpy as np @@ -64,7 +64,7 @@ def check_control_box(discr, xr, lr): return topo, cb -def test_cb_3D(): +def test_cb_3d(): topo, cb = check_control_box(discr3D, xdef, ldef) velo = Field(domain=topo.domain, is_vector=True, formula=v3d, name='velo') velo.discretize(topo) @@ -84,7 +84,7 @@ def test_cb_3D(): assert np.abs(isurf - sref) < 1e-6 -def test_cb_3D_2(): +def test_cb_3d_2(): topo, cb = check_control_box(discr3D, xdef, ldef) velo = Field(domain=topo.domain, is_vector=True, formula=v3d, name='velo') velo.discretize(topo) @@ -102,7 +102,7 @@ def test_cb_3D_2(): assert np.abs(isurf - sref) < 1e-6 -def test_cb_3D_3(): +def test_cb_3d_3(): topo, cb = check_control_box(discr3D, xdef, ldef) velo = Field(domain=topo.domain, is_vector=True, formula=v3d, name='velo') velo.discretize(topo) @@ -120,7 +120,7 @@ def test_cb_3D_3(): assert (np.abs(isurf - sref) < 1e-6).all() -def test_cb_2D(): +def test_cb_2d(): topo, cb = check_control_box(discr2D, xdef[:2], ldef[:2]) velo = Field(domain=topo.domain, is_vector=True, formula=v2d, name='velo') velo.discretize(topo) @@ -140,7 +140,7 @@ def test_cb_2D(): assert np.abs(isurf - sref) < 1e-6 -def test_cb_2D_2(): +def test_cb_2d_2(): topo, cb = check_control_box(discr2D, xdef[:2], ldef[:2]) velo = Field(domain=topo.domain, is_vector=True, formula=v2d, name='velo') velo.discretize(topo) @@ -158,7 +158,7 @@ def test_cb_2D_2(): assert np.abs(isurf - sref) < 1e-6 -def test_cb_2D_3(): +def test_cb_2d_3(): topo, cb = check_control_box(discr2D, xdef[:2], ldef[:2]) velo = Field(domain=topo.domain, is_vector=True, formula=v2d, name='velo') velo.discretize(topo) @@ -176,7 +176,7 @@ def test_cb_2D_3(): assert (np.abs(isurf - sref) < 1e-6).all() -def test_cb_3D_fulldomain(): +def test_cb_3d_fulldomain(): """ A cb defined on the whole domain. Integrals on upper surfaces must fail, @@ -206,10 +206,10 @@ def test_cb_3D_fulldomain(): # This may be useful to run mpi tests if __name__ == "__main__": - test_cb_3D() - test_cb_3D_2() - test_cb_3D_3() - test_cb_2D() - test_cb_2D_2() - test_cb_2D_3() - test_cb_3D_fulldomain() + test_cb_3d() + test_cb_3d_2() + test_cb_3d_3() + test_cb_2d() + test_cb_2d_2() + test_cb_2d_3() + test_cb_3d_fulldomain() diff --git a/HySoP/hysop/domain/tests/test_mesh.py b/HySoP/hysop/domain/tests/test_mesh.py index 993c6df42db5067019120075fc0012b11654a547..7e3e40d36b4a0dcc762bd9a5b3689dfa5b425819 100644 --- a/HySoP/hysop/domain/tests/test_mesh.py +++ b/HySoP/hysop/domain/tests/test_mesh.py @@ -60,22 +60,22 @@ def create_mesh(discr): assert mesh.local_indices(point) is False -def test_mesh3D(): +def test_mesh3d(): discr = Discretization([Nx + 1, Ny + 1, Nz + 1]) create_mesh(discr) -def test_mesh3D_ghost(): +def test_mesh3d_ghost(): discr = Discretization([Nx + 1, Ny + 1, Nz + 1], [2, 2, 2]) create_mesh(discr) -def test_mesh2D(): +def test_mesh2d(): discr = Discretization([Nx + 1, Nz + 1]) create_mesh(discr) -def test_mesh2D_ghost(): +def test_mesh2d_ghost(): discr = Discretization([Nx + 1, Nz + 1], [2, 2]) create_mesh(discr) @@ -133,9 +133,9 @@ def test_convert_global(): assert res == sl if __name__ == '__main__': - test_mesh3D() - test_mesh3D_ghost() - test_mesh2D() - test_mesh2D_ghost() + test_mesh3d() + test_mesh3d_ghost() + test_mesh2d() + test_mesh2d_ghost() test_convert_local() test_convert_global() diff --git a/HySoP/hysop/domain/tests/test_porous.py b/HySoP/hysop/domain/tests/test_porous.py index 682c56a7ca7c896bd9f994148fc189a85f3a5eb8..b813c1d7b5a8140b50692eac9faa6e018da5bc37 100644 --- a/HySoP/hysop/domain/tests/test_porous.py +++ b/HySoP/hysop/domain/tests/test_porous.py @@ -2,11 +2,11 @@ from hysop import Field, Box, Discretization, IOParams from hysop.mpi.topology import Cartesian -from hysop.domain.subsets.sphere import HemiSphere, Sphere -from hysop.domain.subsets.cylinder import Cylinder, HemiCylinder +from hysop.domain.subsets import HemiSphere, Sphere +from hysop.domain.subsets import Cylinder, HemiCylinder import hysop.tools.numpywrappers as npw import numpy as np -from hysop.domain.subsets.porous import BiPole, QuadriPole,\ +from hysop.domain.porous import BiPole, QuadriPole,\ Ring, RingPole, Porous from hysop.operator.hdf_io import HDF_Reader diff --git a/HySoP/hysop/domain/tests/test_regular_subset.py b/HySoP/hysop/domain/tests/test_regular_subset.py index cfb14a9485321f34d146bc0eafab2c9e3949b52b..da1b44652d5184dca3bb496bc68251a094d431b5 100644 --- a/HySoP/hysop/domain/tests/test_regular_subset.py +++ b/HySoP/hysop/domain/tests/test_regular_subset.py @@ -1,4 +1,4 @@ -from hysop.domain.subsets.boxes import SubBox +from hysop.domain.subsets import SubBox from hysop.tools.parameters import Discretization from hysop import Field, Box import hysop.tools.numpywrappers as npw @@ -182,7 +182,7 @@ def test_integ_fulldom(): assert (np.abs(i1 - vref) < 1e-6).all() -def test_integ_fulldom_2D(): +def test_integ_fulldom_2d(): topo, cub = check_subset(discr2D, xdom[:2], ldom[:2]) velo = Field(domain=topo.domain, is_vector=True, formula=v2d, name='velo') vd = velo.discretize(topo) @@ -228,4 +228,4 @@ if __name__ == "__main__": test_2d_subbox() test_2d_line() test_integ_fulldom() - test_integ_fulldom_2D() + test_integ_fulldom_2d() diff --git a/HySoP/hysop/domain/tests/test_submesh.py b/HySoP/hysop/domain/tests/test_submesh.py index f599b6fea722fb29d778c8f52b561e8c4d81c1e8..bec87e49d42d5fc1dd570f74a87c4abe64f326de 100644 --- a/HySoP/hysop/domain/tests/test_submesh.py +++ b/HySoP/hysop/domain/tests/test_submesh.py @@ -4,7 +4,7 @@ Testing regular grids. """ from hysop.domain.box import Box from hysop.tools.parameters import Discretization -from hysop.domain.subsets.submesh import SubMesh +from hysop.domain.submesh import SubMesh from hysop.tools.misc import utils import numpy as np @@ -75,7 +75,7 @@ def check_coords(m1, m2): assert np.allclose(xend, req_end) -def test_submesh_3D(): +def test_submesh_3d(): """ 3D Periodic mesh """ @@ -83,7 +83,7 @@ def test_submesh_3D(): check_submesh(discr) -def test_submesh_2D(): +def test_submesh_2d(): """ 2D Periodic mesh """ @@ -91,5 +91,5 @@ def test_submesh_2D(): check_submesh(discr) if __name__ == '__main__': - test_submesh_3D() - test_submesh_2D() + test_submesh_3d() + test_submesh_2d() diff --git a/HySoP/hysop/domain/tests/test_subset.py b/HySoP/hysop/domain/tests/test_subset.py index 73cc589ceefaa54b5ddbb5ded91c0806d277af62..0b11f5288725e28b070e7f4049f5615a4462738e 100644 --- a/HySoP/hysop/domain/tests/test_subset.py +++ b/HySoP/hysop/domain/tests/test_subset.py @@ -1,7 +1,6 @@ -from hysop.domain.subsets.sphere import Sphere, HemiSphere -from hysop.domain.subsets.subset import Subset -from hysop.domain.subsets.boxes import SubBox -from hysop.domain.subsets.cylinder import Cylinder, HemiCylinder +from hysop.domain.subsets import Sphere, HemiSphere +from hysop.domain.subsets import Subset, SubBox +from hysop.domain.subsets import Cylinder, HemiCylinder from hysop.operator.hdf_io import HDF_Reader from hysop.tools.parameters import Discretization, IOParams from hysop import Field, Box @@ -55,8 +54,8 @@ def check_subset(discr, fileref, class_name): scal = Field(domain=topo.domain, is_vector=False) sd = scal.discretize(topo) sd[0][subs.ind[topo][0]] = 2. - iCompute = topo.mesh.iCompute - return np.allclose(sd[0][iCompute], vd[0][iCompute]) + icompute = topo.mesh.iCompute + return np.allclose(sd[0][icompute], vd[0][icompute]) def test_sphere_3d(): @@ -117,8 +116,8 @@ def init_obst_list(discr, fileref): sd = Field(domain=topo.domain, is_vector=False).discretize(topo) indices = Subset.union(obs_list, topo) sd[0][indices] = 2. - iCompute = topo.mesh.iCompute - return np.allclose(sd[0][iCompute], vd[0][iCompute]) + icompute = topo.mesh.iCompute + return np.allclose(sd[0][icompute], vd[0][icompute]) def test_union_3d(): @@ -142,8 +141,8 @@ def init_subtract(discr, fileref): sd = Field(domain=topo.domain, is_vector=False).discretize(topo) indices = Subset.subtract(box, s1, topo) sd[0][indices] = 2. - iCompute = topo.mesh.iCompute - return np.allclose(sd[0][iCompute], vd[0][iCompute]) + icompute = topo.mesh.iCompute + return np.allclose(sd[0][icompute], vd[0][icompute]) def test_subtract_3d(): diff --git a/HySoP/hysop/mpi/__init__.py b/HySoP/hysop/mpi/__init__.py index f990cd8c790a9effd03543088d92b99edf09e8a5..94aa37bd9ca80eb741e837d584331b2efd9a8a29 100644 --- a/HySoP/hysop/mpi/__init__.py +++ b/HySoP/hysop/mpi/__init__.py @@ -1,33 +1,36 @@ -""" -@package hysop.mpi -hysop interface to the mpi implementation. +"""Hysop interface to the mpi implementation. It contains : -- mpi basic variables (main communicator, rank, size ...) -- topology.Cartesian : mpi process distribution + local mesh -- mesh.SubMesh : local mesh + +* mpi basic variables (main communicator, rank, size ...) +* :class:`hysop.mpi.topology.Cartesian` : mpi process distribution + local mesh This package is used to hide the underlying mpi interface in order to make any change of this interface, if required, easiest. + At the time we use mpi4py : http://mpi4py.scipy.org """ -## Everything concerning the chosen mpi implementation is hidden in main_var +# Everything concerning the chosen mpi implementation is hidden in main_var # Why? --> to avoid that things like mpi4py. ... spread everywhere in the # soft so to ease a change of this implementation (if needed). from hysop.mpi import main_var -## A list of mpi variables that can be "seen" by user -## MPI underlying implementation + MPI = main_var.MPI +"""MPI underlying implementation +""" -## Main communicator (copy of comm_world) main_comm = main_var.main_comm +"""Main communicator (copy of comm_world) +""" -## Rank of the current process in the main communicator main_rank = main_var.main_rank +"""Rank of the current process in the main communicator +""" -## Size of the main communicator main_size = main_var.main_size +"""Size of the main communicator +""" diff --git a/HySoP/hysop/mpi/bridge.py b/HySoP/hysop/mpi/bridge.py index 33957e1fe76f71c64570498e695ef5ae4e39bba8..b9765d94dbaa751aac8fd52a05b51889bdaa85b8 100644 --- a/HySoP/hysop/mpi/bridge.py +++ b/HySoP/hysop/mpi/bridge.py @@ -3,7 +3,7 @@ Tools to compute the intersection between two HySoP topologies. """ -from hysop.mpi.topology import Cartesian, topotools +from hysop.mpi.topology import Cartesian, TopoTools from hysop.tools.misc import utils @@ -51,7 +51,7 @@ class Bridge(object): msg = 'Bridge error, both source/target topologies' msg += ' must have the same parent communicator.' - assert topotools.compare_comm(self._source.parent(), + assert TopoTools.compare_comm(self._source.parent(), self._target.parent()), msg # The assert above ensure that source and target hold the same # group of process in the same communication context. @@ -63,10 +63,10 @@ class Bridge(object): # are on both source and target mesh. # Get global indices of the mesh on source for all mpi processes. - indices_source = topotools.gatherGlobalIndices(self._source) + indices_source = TopoTools.gather_global_indices(self._source) # Get global indices of the mesh on target for all mpi processes. - indices_target = topotools.gatherGlobalIndices(self._target) + indices_target = TopoTools.gather_global_indices(self._target) # From now on, we have indices_source[rk] = global indices (slice) # of grid points of the source on process number rk in parent. # And the same thing for indices_target. @@ -128,8 +128,8 @@ class Bridge(object): """ if self._recv_types is None: data_shape = self._target.mesh.resolution - self._recv_types = topotools.createSubArray(self._recv_indices, - data_shape) + self._recv_types = TopoTools.create_subarray(self._recv_indices, + data_shape) return self._recv_types def sendTypes(self): @@ -141,6 +141,6 @@ class Bridge(object): """ if self._send_types is None: data_shape = self._source.mesh.resolution - self._send_types = topotools.createSubArray(self._send_indices, - data_shape) + self._send_types = TopoTools.create_subarray(self._send_indices, + data_shape) return self._send_types diff --git a/HySoP/hysop/mpi/bridge_inter.py b/HySoP/hysop/mpi/bridge_inter.py index 6516bd7abcf6071264448b544295b13007b1cc40..b800d6988b0d79f5ee0c89692d020146df3d2fb0 100644 --- a/HySoP/hysop/mpi/bridge_inter.py +++ b/HySoP/hysop/mpi/bridge_inter.py @@ -3,7 +3,7 @@ Tools to compute the intersection between two HySoP topologies. """ -from hysop.mpi.topology import Cartesian, topotools +from hysop.mpi.topology import Cartesian, TopoTools from hysop.tools.misc import utils from hysop.mpi import MPI import hysop.tools.numpywrappers as npw @@ -40,7 +40,7 @@ class BridgeInter(object): assert isinstance(parent, MPI.Intracomm) self._topology = current # current task id - current_task = self._topology.domain.currentTask() + current_task = self._topology.domain.current_task() # True if current process is in the 'from' group' task_is_source = current_task == self.source_id @@ -87,7 +87,7 @@ class BridgeInter(object): def _swap_indices(self): # First, we need to collect the global indices, as arrays # since we need to broadcast them later. - current_indices = topotools.gatherGlobalIndices(self._topology, + current_indices = TopoTools.gather_global_indices(self._topology, toslice=False) # To allocate remote_indices array, we need the size of # the remote communicator. @@ -96,7 +96,7 @@ class BridgeInter(object): remote_indices = npw.dim_zeros((dimension * 2, remote_size)) # Then they are broadcasted to the remote communicator rank = self._topology.rank - current_task = self._topology.domain.currentTask() + current_task = self._topology.domain.current_task() if current_task is self.source_id: # Local 0 broadcast current_indices to remote comm if rank == 0: @@ -130,6 +130,6 @@ class BridgeInter(object): """ if self._transfer_types is None: data_shape = self._topology.mesh.resolution - self._transfer_types = topotools.createSubArray( + self._transfer_types = TopoTools.create_subarray( self._tranfer_indices, data_shape) return self._transfer_types diff --git a/HySoP/hysop/mpi/bridge_overlap.py b/HySoP/hysop/mpi/bridge_overlap.py index 64585e68b31fe4e87a31e96952fe6cbe6c9772b6..4dfa0b904ed6c83bdd59d0f250ed00c4b41c22dc 100644 --- a/HySoP/hysop/mpi/bridge_overlap.py +++ b/HySoP/hysop/mpi/bridge_overlap.py @@ -4,7 +4,7 @@ Tools to compute the intersection between two HySoP topologies defined on the same comm but for a different number of processes. """ -from hysop.mpi.topology import Cartesian, topotools +from hysop.mpi.topology import Cartesian, TopoTools from hysop.tools.misc import utils from hysop.mpi.bridge import Bridge @@ -48,21 +48,21 @@ class BridgeOverlap(Bridge): msg = 'BridgeOverlap error: mpi group from ' msg += 'source and topo must overlap. If not ' msg += 'BridgeInter will probably suits better.' - assert topotools.intersection_size(self._source.comm, + assert TopoTools.intersection_size(self._source.comm, self._target.comm) > 0, msg assert self._source.domain == self._target.domain if self._source is not None: assert isinstance(self._source, Cartesian) s_size = self._source.size - assert topotools.intersection_size(self._source.comm, + assert TopoTools.intersection_size(self._source.comm, self.comm) == s_size self.domain = self._source.domain if self._target is not None: assert isinstance(self._target, Cartesian) t_size = self._target.size - assert topotools.intersection_size(self._target.comm, + assert TopoTools.intersection_size(self._target.comm, self.comm) == t_size self.domain = self._target.domain @@ -71,12 +71,12 @@ class BridgeOverlap(Bridge): def _build_send_recv_dict(self): # Compute local intersections : i.e. find which grid points # are on both source and target mesh. - indices_source = topotools.gatherGlobalIndicesOverlap(self._source, - self.comm, - self.domain) - indices_target = topotools.gatherGlobalIndicesOverlap(self._target, - self.comm, - self.domain) + indices_source = TopoTools.gather_global_indices_overlap(self._source, + self.comm, + self.domain) + indices_target = TopoTools.gather_global_indices_overlap(self._target, + self.comm, + self.domain) # From now on, we have indices_source[rk] = global indices (slice) # of grid points of the source on process number rk in parent. diff --git a/HySoP/hysop/mpi/tests/test_bridge.py b/HySoP/hysop/mpi/tests/test_bridge.py index fa02f3b69ca84d28f565c14b1abbb79821a49d23..0621466c23680c0181c82c2c6a445d8aebbb20b0 100644 --- a/HySoP/hysop/mpi/tests/test_bridge.py +++ b/HySoP/hysop/mpi/tests/test_bridge.py @@ -88,9 +88,9 @@ def test_bridgeInter2D(): # The real tests are done in test_redistribute.py # Bridge from topo2 on GPU to topo1 on CPU: - if dom.isOnTask(GPU): + if dom.is_on_task(GPU): bridge2 = BridgeInter(topo2, main_comm, source_id=GPU, target_id=CPU) - elif dom.isOnTask(CPU): + elif dom.is_on_task(CPU): bridge2 = BridgeInter(topo1, main_comm, source_id=GPU, target_id=CPU) assert bridge2 is not None @@ -114,9 +114,9 @@ def test_bridgeInter3D(): # so we just create a bridge. # The real tests are done in test_redistribute.py # Bridge from topo2 on GPU to topo1 on CPU: - if dom.isOnTask(GPU): + if dom.is_on_task(GPU): bridge2 = BridgeInter(topo2, main_comm, source_id=GPU, target_id=CPU) - elif dom.isOnTask(CPU): + elif dom.is_on_task(CPU): bridge2 = BridgeInter(topo1, main_comm, source_id=GPU, target_id=CPU) assert bridge2 is not None diff --git a/HySoP/hysop/mpi/tests/test_topology.py b/HySoP/hysop/mpi/tests/test_topology.py index 926eb3af659c3fe581a6827be56b1d303674660b..23e6fcb0095075e99cbee9ae29a5123900e21afb 100644 --- a/HySoP/hysop/mpi/tests/test_topology.py +++ b/HySoP/hysop/mpi/tests/test_topology.py @@ -62,9 +62,9 @@ def test_create_default_topology2_2d(): topo = dom.create_topology(r2D) assert topo.domain == dom assert topo.size == dom2D.comm_task.Get_size() - if dom.isOnTask(CPU): + if dom.is_on_task(CPU): assert topo.task_id() == CPU - if dom.isOnTask(GPU): + if dom.is_on_task(GPU): assert topo.task_id() == GPU @@ -151,9 +151,9 @@ def test_create_default_topology2(): topo = dom.create_topology(r3D) assert topo.domain == dom assert topo.size == dom3D.comm_task.Get_size() - if dom.isOnTask(CPU): + if dom.is_on_task(CPU): assert topo.task_id() == CPU - if dom.isOnTask(GPU): + if dom.is_on_task(GPU): assert topo.task_id() == GPU diff --git a/HySoP/hysop/mpi/topology.py b/HySoP/hysop/mpi/topology.py index b11214c134f513157ab7a504bf77840df3dbaed3..41dd5d78ac41e906a9c94b3cae8cbb968f339d8a 100644 --- a/HySoP/hysop/mpi/topology.py +++ b/HySoP/hysop/mpi/topology.py @@ -1,8 +1,9 @@ -""" -@file topology.py -Tools and definitions for HySoP topologies +"""Tools and definitions for HySoP topologies (MPI Processes layout + space discretization) +* :class:`~hysop.mpi.topology.Cartesian` +* :class:`~hysop.mpi.topology.TopoTools` + """ from hysop.constants import debug, ORDER, PERIODIC @@ -51,34 +52,38 @@ class Cartesian(object): def __init__(self, domain, discretization, dim=None, mpi_params=None, isperiodic=None, cutdir=None, shape=None, mesh=None): """ - Required parameters : domain, discretization - Others are optional. You must choose one and only one param - among dim, cutdir and shape. + + Parameters + ----------- + domain : :class:`~hysop.domain.domain.Domain` + the geometry on which the topology is defined. + discretization : :class:`~hysop.tools.parameters.Discretization` + description of the global space discretization + (resolution and ghosts). + dim : int, optional + mpi topology dimension. + mpi_params : :class:`~hysop.tools.parameters.MPIParams`, optional + mpi setup (comm, task ...). + If None, comm = main_comm, task = DEFAULT_TASK_ID. + isperiodic : list or array of bool, optional + mpi grid periodicity + cutdir : list or array of bool + set which directions must (may) be distributed, + cutdir[dir] = True to distribute data along dir. + shape : list or array of int + mpi grid layout + mesh : :class:`~hysop.domain.mesh.Mesh`, optional + a predifined mesh (it includes local and global grids.) + + Notes: + ------ + Almost all parameters above are optional. + Only one must be chosen among dim, cutdir and shape. + See :ref:`topologies` in the Hysop User Guide for details. See hysop.mpi.topology.Cartesian.plane_precomputed details to build a plane topology from a given local discretization (e.g. from fftw or scales precomputation). - @param domain : the geometry; it must be a box. - @param discretization : a hysop.tools.parameters.Discretization - with: - - resolution = Number of points in the domain - in each direction. We assume that first point corresponds - to origin, and last point to boundary point, - whatever the boundary type is. - That is x[0] = domain.origin and - x[Discretization.resolution-1] = domain.Lengths_x. - - ghosts = number of points in the ghost layer - @param dim : dimension of the topology - @param mpi_params : a hysop.tools.parameters.MPIParams, with: - - comm : MPI communicator used to create this topology - (default = main_comm) - - task_id : id of the task that owns this topology. - @param isperiodic : periodicity of the topology (in each direction) - @param cutdir : array of bool, set cutdir[dir] = True if you want - to distribute data through direction dir. - @param shape : topology resolution - (i.e process layout in each direction). - @param mesh : a predefined hysop.mpi.mesh.SubMesh """ # ===== 1 - Required parameters : domain and mpi (comm, task) ==== # An id for the topology @@ -89,10 +94,10 @@ class Cartesian(object): # - MPI topo params : if mpi_params is None: mpi_params = MPIParams(comm=domain.comm_task, - task_id=domain.currentTask()) + task_id=domain.current_task()) - ## mpi parameters : Communicator used to build the topology - ## and task_id + # mpi parameters : Communicator used to build the topology + # and task_id self._mpis = mpi_params # Each topo must be associated to one and only one topology assert self._mpis.task_id is not None @@ -105,50 +110,50 @@ class Cartesian(object): # (c) - from dimension of the topology ==> let MPI # choose the 'best' layout. - ## Layout of the grid of mpi processes. + # Layout of the grid of mpi processes. self.shape = None - ## Dim of the cartesian grid for MPI processes. + # Dim of the cartesian grid for MPI processes. self.dimension = None # MPI grid periodicity self._isperiodic = None # directions where data are distributed self.cutdir = None - ## mpi communicator (cartesian topo) + # mpi communicator (cartesian topo) self.comm = None self._build_mpi_topo(shape, cutdir, dim, isperiodic) # ===== 3 - Get features of the mpi processes grid ===== - ## Size of the topology (i.e. total number of mpi processes) + # Size of the topology (i.e. total number of mpi processes) self.size = self.comm.Get_size() - ## Rank of the current process in the topology + # Rank of the current process in the topology self.rank = self.comm.Get_rank() - ## Coordinates of the current process + # Coordinates of the current process reduced_coords = npw.asdimarray(self.comm.Get_coords(self.rank)) - ## Coordinates of the current process - ## What is different between proc_coords and reduced_coords? - ## --> proc_coords has values even for directions that - ## are not distributed. If cutdir = [False, False, True] - ## then reduced_coords = [ nx ] and proc_coords = [0, 0, nx] + # Coordinates of the current process + # What is different between proc_coords and reduced_coords? + # --> proc_coords has values even for directions that + # are not distributed. If cutdir = [False, False, True] + # then reduced_coords = [ nx ] and proc_coords = [0, 0, nx] self.proc_coords = npw.dim_zeros(self.domain.dimension) self.proc_coords[self.cutdir] = reduced_coords - ## Neighbours : self.neighbours[0,i] (resp. [1,i]) - ## previous (resp. next) neighbour in direction i - ## (warning : direction in the grid of process). + # Neighbours : self.neighbours[0,i] (resp. [1,i]) + # previous (resp. next) neighbour in direction i + # (warning : direction in the grid of process). self.neighbours = npw.dim_zeros((2, self.dimension)) for direction in range(self.dimension): self.neighbours[:, direction] = self.comm.Shift(direction, 1) # ===== 4 - Computation of the local meshes ===== - ## Local mesh on the current mpi process. + # Local mesh on the current mpi process. # mesh from external function (e.g. fftw, scales ...) self.mesh = mesh # If mesh is None, we must compute local resolution and other # parameters, using discretization. if mesh is None: - self._computeMesh(discretization) + self._compute_mesh(discretization) # ===== 5 - Final setup ==== @@ -168,11 +173,11 @@ class Cartesian(object): self.mesh = topo.mesh self.comm = topo.comm -# self.is_registered = False - - def _build_mpi_topo(self, shape, cutdir, dim, isperiodic): - """ - Build mpi topology + def _build_mpi_topo(self, shape=None, cutdir=None, + dim=None, isperiodic=None): + """ default builder + See :ref:`topologies` for details. + """ # number of process in parent comm origin_size = self._mpis.comm.Get_size() @@ -197,7 +202,7 @@ class Cartesian(object): self.dimension = self.cutdir[self.cutdir].size shape = npw.asdimarray(MPI.Compute_dims(origin_size, self.dimension)) - self.optimizeshape(shape) + self._optimizeshape(shape) self.shape = npw.dim_ones(self.domain.dimension) self.shape[self.cutdir] = shape @@ -227,10 +232,10 @@ class Cartesian(object): # For C-like (row major) arrays, first dir is the # first to be distributed, and so on. self.shape[:self.dimension] = shape - self.optimizeshape(self.shape) + self._optimizeshape(self.shape) self.cutdir = self.shape != 1 - ## MPI processes grid periodicity. Default is true. + # MPI processes grid periodicity. Default is true. if isperiodic is None: self._isperiodic = npw.ones((self.domain.dimension), dtype=npw.bool) @@ -257,9 +262,8 @@ class Cartesian(object): reorder=True) @staticmethod - def optimizeshape(shape): - """ - Reorder 'shape' according to the chosen + def _optimizeshape(shape): + """Reorder 'shape' according to the chosen data layout to optimize data distribution. """ shape.sort() @@ -267,34 +271,36 @@ class Cartesian(object): shape[:] = shape[::-1] def parent(self): - """ - returns the communicator used to build this topology + """returns the communicator used to build this topology """ return self._mpis.comm def ghosts(self): - """ - Get ghost layer width. + """returns ghost layer width. """ return self.mesh.discretization.ghosts def task_id(self): - """ - @return id of the task that owns this topology + """returns id of the task that owns this topology """ return self._mpis.task_id @classmethod def plane_precomputed(cls, localres, global_start, cdir=None, **kwds): - """ - Define a 'plane' (1D) topology for a given mesh resolution. + """Defines a 'plane' (1D) topology for a given mesh resolution. + This function is to be used when topo/discretization features come from an external routine (e.g. scales or fftw) - @param localres : local mesh resolution - @param global_start : global indices of the lowest point - of the local mesh - @param cdir : direction of cutting (i.e. normal to mpi planes) - default = last if fortran order, first if C order. + + Parameters + ---------- + localres : list or array of int + local mesh resolution + global_start : list or array of int + indices in the global mesh of the lowest point of the local mesh. + cdir : int, optional + direction where data must be distributed in priority. + Default = last if fortran order, first if C order. """ msg = 'parameter is not required for plane_precomputed' msg += ' topology construction.' @@ -320,7 +326,7 @@ class Cartesian(object): return cls(mesh=mesh, cutdir=cutdir, **kwds) - def _computeMesh(self, discretization): + def _compute_mesh(self, discretization): assert isinstance(discretization, Discretization) assert discretization.resolution.size == self.domain.dimension assert self.domain.dimension == discretization.resolution.size, \ @@ -334,28 +340,28 @@ class Cartesian(object): is_periodic = len(np.where(self.domain.boundaries == PERIODIC)[0])\ == self.domain.dimension if is_periodic: - resolCalc = discretization.resolution - 1 + computational_grid_resolution = discretization.resolution - 1 else: raise AttributeError('Unknwon boundary conditions.') - pts_noghost[:] = resolCalc // self.shape + pts_noghost[:] = computational_grid_resolution // self.shape # If any, remaining points are # added on the mesh of the last process. - remainingPts = npw.dim_zeros(self.domain.dimension) - remainingPts[:] = resolCalc % self.shape + remaining_points = npw.dim_zeros(self.domain.dimension) + remaining_points[:] = computational_grid_resolution % self.shape # Total number of points (size of arrays to be allocated) nbpoints = pts_noghost.copy() for i in range(self.domain.dimension): if self.proc_coords[i] == self.shape[i] - 1: - nbpoints[i] += remainingPts[i] + nbpoints[i] += remaining_points[i] - local_resolution = resolCalc.copy() + local_resolution = computational_grid_resolution.copy() local_resolution[:] = nbpoints[:] local_resolution[:] += 2 * discretization.ghosts[:] - ## Global indices for the local mesh points + # Global indices for the local mesh points global_start = npw.dim_zeros((self.domain.dimension)) global_start[:] = self.proc_coords[:] * pts_noghost[:] @@ -363,12 +369,9 @@ class Cartesian(object): local_resolution, global_start) def __eq__(self, other): - """ - Operator for comparison. Based on: - @li mesh - @li shape - @li domain - @param other : Topology to compare with. + """Comparison of two topologies. + + Two topos are equal if they have the same mesh, shape and domain. """ if self.__class__ != other.__class__: return False @@ -377,11 +380,10 @@ class Cartesian(object): self.domain == other.domain def __ne__(self, other): - """ - Not equal operator. + """Not equal operator. + Seems to be required in addition to __eq__ to avoid 'corner-case' behaviors. - @param other : Topology to compare with. """ result = self.__eq__(other) if result is NotImplemented: @@ -389,7 +391,6 @@ class Cartesian(object): return not result def __str__(self): - """ Topology info display """ s = '======== Topology id : ' + str(self.__id) + ' ========\n' s += ' - on task : ' + str(self.task_id()) + '\n' s += ' - shape :' + str(self.shape) + '\n' @@ -402,7 +403,7 @@ class Cartesian(object): s += '\n=================================\n' return s - def hasGhosts(self): + def has_ghosts(self): """ True if ghost layer length is not zero. """ @@ -415,20 +416,21 @@ class Cartesian(object): """ return self.__id - def isConsistentWith(self, topo): - same_parent = self.parent() == topo.parent() + def is_consistent_with(self, target): + """True if target and current object are equal and + have the same parent. Equal means same mesh, same shape and + same domain. + """ + same_parent = self.parent() == target.parent() # Note FP. Is it really required to have the # same parent? Inclusion of all proc may be enough? - return npw.equal(self.shape, topo.shape).all() and same_parent + return npw.equal(self.shape, target.shape).all() and same_parent - def canCommunicateWith(self, target): - """ - return True if current topo is complient with topo. - (See below for what 'complient' implies) - @param target : the targeted topology + def can_communicate_with(self, target): + """True if current topo is complient with target. - Complient if : - - all processes in current are in topo + Complient means : + - all processes in current are in target - both topologies belong to the same mpi task """ if self == target: @@ -438,44 +440,51 @@ class Cartesian(object): msg += ' InterBridge.' assert self.task_id() == target.task_id(), msg - ## Parent communicator - ## Todo : define some proper conditions for compatibility - ## between topo_from, topo_to and parent: - ## - same size - ## - same domain - ## - common processus ... - ## At the time we check that both topo have - ## the same comm_origin. - return self.isConsistentWith(target) + # Parent communicator + # Todo : define some proper conditions for compatibility + # between topo_from, topo_to and parent: + # - same size + # - same domain + # - common processus ... + # At the time we check that both topo have + # the same comm_origin. + return self.is_consistent_with(target) @staticmethod def reset_counter(): Cartesian.__ids = count(0) -class topotools(object): +class TopoTools(object): @staticmethod - def gatherGlobalIndices(topo, toslice=True, root=None, comm=None): - """ - Collect global indices of local meshes on each process of topo - @param topo : a hysop.mpi.topology.Cartesian - @param toslice : true (default) if you want a dict of slice - as return value, false if you need a numpy array. - @param root : rank (in topo.parent()) of the gathering process. - If root is None (default) indices are gathered - on all processes of topo. - @param comm : communicator used for global communications. - Default = topo.parent(). - @return : either : - - a dictionnary which maps rank number with + def gather_global_indices(topo, toslice=True, root=None, comm=None): + """Collect global indices of local meshes on each process of topo + + Parameters + ---------- + topo : :class:`~ hysop.mpi.topology.Cartesian` + topology on which indices are collected. + toslice : boolean, optional + true to return the result a dict of slices, else + return a numpy array. See notes below. + root : int, optional + rank of the root mpi process. If None, reduce operation + is done on all processes. + comm : mpi communicator, optional + Communicator used to reduce indices. Default = topo.parent + + Returns + ------- + either : + * a dictionnary which maps rank number with a list of slices such that res[rank][i] = a slice defining the indices of the points of the local mesh, in direction i, in global notation. - - a numpy array, each column corresponds to a rank number, + * or a numpy array where each column corresponds to a rank number, with column = [start_x, end_x, start_y, end_y ...] + Ranks number are the processes numbers in comm. - Ranks number are the processes numbers in topo.parent(). """ if comm is None: comm = topo.parent() @@ -502,11 +511,11 @@ class topotools(object): return iglob @staticmethod - def gatherGlobalIndicesOverlap(topo=None, comm=None, dom=None, - toslice=True, root=None): - """ - This functions does the same thing as gatherGlobalIndices but + def gather_global_indices_overlap(topo=None, comm=None, dom=None, + toslice=True, root=None): + """This functions does the same thing as gather_global_indices but may also work when topo is None. + The function is usefull if you need to collect global indices on a topo define only on a subset of comm, when for the procs not in this subset, topo will be @@ -516,9 +525,36 @@ class topotools(object): between the two groups of processes of the topologies. In that case, a call to - gatherGlobalIndices(topo, comm, dom) + gather_global_indices(topo, comm, dom) will work on all processes belonging to comm, topo being None or not. The values corresponding to ranks not in topo will be empty slices. + + Parameters + ---------- + topo : :class:`~ hysop.mpi.topology.Cartesian`, optional + topology on which indices are collected. + toslice : boolean, optional + true to return the result a dict of slices, else + return a numpy array. See notes below. + root : int, optional + rank of the root mpi process. If None, reduce operation + is done on all processes. + comm : mpi communicator, optional + Communicator used to reduce indices. Default = topo.parent + dom : :class:`~hysop.domain.domain.Domain` + current domain. + + Returns + ------- + either : + * a dictionnary which maps rank number with + a list of slices such that res[rank][i] = a slice + defining the indices of the points of the local mesh, + in direction i, in global notation. + * or a numpy array where each column corresponds to a rank number, + with column = [start_x, end_x, start_y, end_y ...] + Ranks number are the processes numbers in comm. + """ if topo is None: assert comm is not None and dom is not None @@ -538,12 +574,12 @@ class topotools(object): return iglob else: - return topotools.gatherGlobalIndices(topo, toslice, root, comm) + return TopoTools.gather_global_indices(topo, toslice, root, comm) @staticmethod def is_parent(child, parent): """ - Return true if all mpi processes of child belongs to parent + Return true if all mpi processes of child belong to parent """ # Get the list of processes assert child is not None @@ -556,6 +592,8 @@ class topotools(object): @staticmethod def intersection_size(comm_1, comm_2): + """Number of processess common to comm_1 and comm_2 + """ if comm_1 == MPI.COMM_NULL or comm_2 == MPI.COMM_NULL: return None group_1 = comm_1.Get_group() @@ -565,12 +603,11 @@ class topotools(object): @staticmethod def compare_comm(comm_1, comm_2): - """ - Compare two mpi communicators. - Return true if the two communicators are handles for the same + """Compare two mpi communicators. + + Returns true if the two communicators are handles for the same group of proc and for the same communication context. - @param comm_1 : a mpi communicator - @param comm_2 : a mpi communicator + Warning : if comm_1 or comm_2 is invalid, the function will fail. """ @@ -582,12 +619,11 @@ class topotools(object): @staticmethod def compare_groups(comm_1, comm_2): - """ - Compare two mpi communicators. - Return true if each comm handles the + """Compare the groups of two mpi communicators. + + Returns true if each comm handles the same group of mpi processes. - @param comm_1 : a mpi communicator - @param comm_2 : a mpi communicator + Warning : if comm_1 or comm_2 is invalid, the function will fail. """ @@ -599,11 +635,13 @@ class topotools(object): @staticmethod def convert_ranks(source, target): - """ - find the values of ranks in target from ranks in source. - @param source : a mpi communicator - @param target : a mpi communicator - @return a list 'ranks' such that ranks[i] = rank in target + """Find the values of ranks in target from ranks in source. + + Parameters + ---------- + source, target : mpi communicators + + Returns a list 'ranks' such that ranks[i] = rank in target of process of rank i in source. """ assert source != MPI.COMM_NULL and target != MPI.COMM_NULL @@ -615,15 +653,18 @@ class topotools(object): return {r_source[i]: res[i] for i in xrange(size_source)} @staticmethod - def createSubArray(sl_dict, data_shape): - """ - Create a MPI subarray mask to be used in send/recv operations + def create_subarray(sl_dict, data_shape): + """Create a MPI subarray mask to be used in send/recv operations between some topologies. - @param[in] sl_dict : dictionnary which contains mesh indices - of the subarray for each rank, - such that sl_dict[rk] = (slice(...), slice(...), ...) - @param[in] data_shape : shape (numpy-like) of the original array - @return : dictionnary of MPI derived types. + + Parameters + ---------- + sl_dict : dictionnary + indices of the subarray for each rank, + such that sl_dict[rk] = (slice(...), slice(...), ...) + data_shape : shape (numpy-like) of the original array + + :Returns : dictionnary of MPI derived types. Keys = ranks in parent communicator. """ from hysop.constants import HYSOP_MPI_REAL, ORDERMPI diff --git a/HySoP/hysop/operator/absorption_BC.py b/HySoP/hysop/operator/absorption_BC.py index e6ed682ac9c095ae2516b67329c291279675dd9e..303ac78ae6344c1f3b70a3a05d62c58e525f5c59 100755 --- a/HySoP/hysop/operator/absorption_BC.py +++ b/HySoP/hysop/operator/absorption_BC.py @@ -1,17 +1,14 @@ # -*- coding: utf-8 -*- -""" -@file operator/absorption_BC.py - -Operator to kill the vorticity at the outlet boundary -(i.e. removal of the periodic BC in the flow direction -by vorticity absorption in order to set the far field +"""Operator to kill the vorticity at the outlet boundary +(i.e. removal of the periodic BC in the flow direction +by vorticity absorption in order to set the far field velocity to u_inf at the inlet) """ from hysop.constants import debug from hysop.operator.discrete.absorption_BC import AbsorptionBC_D from hysop.operator.computational import Computational -from hysop.domain.subsets.control_box import ControlBox +from hysop.domain.control_box import ControlBox from hysop.operator.continuous import opsetup @@ -25,7 +22,7 @@ class AbsorptionBC(Computational): """ @debug - def __init__(self, velocity, vorticity, req_flowrate, + def __init__(self, velocity, vorticity, req_flowrate, x_coords_absorp, **kwds): """ @param[in] velocity field diff --git a/HySoP/hysop/operator/adapt_timestep.py b/HySoP/hysop/operator/adapt_timestep.py index 9f21d3ff7a005d52262026d676d53b3f3417d1cc..0079aa1eb6c29f4f82846e05cfcef484cf790c23 100755 --- a/HySoP/hysop/operator/adapt_timestep.py +++ b/HySoP/hysop/operator/adapt_timestep.py @@ -75,7 +75,7 @@ class AdaptTimeStep(Computational): Create intercommunicators, if required (i.e. if there are several tasks defined in the domain). """ - task_is_source = self._mpis.task_id == self.domain.currentTask() + task_is_source = self._mpis.task_id == self.domain.current_task() tasks_list = self.domain.tasks_list() others = (v for v in tasks_list if v != self._mpis.task_id) if task_is_source: @@ -127,7 +127,7 @@ class AdaptTimeStep(Computational): self._is_uptodate = True def wait(self): - task_is_source = self._mpis.task_id == self.domain.currentTask() + task_is_source = self._mpis.task_id == self.domain.current_task() rank = self._mpis.rank dt = self.simulation.timeStep for rk in self._intercomms: diff --git a/HySoP/hysop/operator/continuous.py b/HySoP/hysop/operator/continuous.py index a45d6cf377faa3f6fbe90139a213e9a98eeec8f3..a470261f34cb9ec53dd60b72ae4e45c9f985135c 100644 --- a/HySoP/hysop/operator/continuous.py +++ b/HySoP/hysop/operator/continuous.py @@ -9,8 +9,7 @@ import hysop.tools.io_utils as io class Operator(object): - """ - Abstract interface to continuous operators. + """Abstract interface to continuous operators. """ __metaclass__ = ABCMeta @@ -38,21 +37,23 @@ class Operator(object): 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.mpi.topology.Cartesian` - or :class:`hysop.tools.parameters.Discretization`. + The values of the dict can be either + :class:`hysop.mpi.topology.Cartesian` + or :class:`hysop.tools.parameters.Discretization`:: - op = Analytic(variables = [velo, vorti], ... + op = Analytic(variables = [velo, vorti], ...) - or - op = Analytic(variables = {velo: topo, vorti: discr3D}, ... + or:: - TODO : write a full explanation for field discretization. + op = Analytic(variables = {velo: topo, vorti: discr3D}, ...) Attributes ---------- variables : dict or list - fields with their discretisation - domain : the physical domain on which this operator applies + :class:`~hysop.fields.continuous.Continuous` + with their discretisation + domain : :class:`~hysop.domain.domain.Domain`, + the geometry on which this operator applies """ # 1 ---- Variables setup ---- @@ -125,7 +126,7 @@ class Operator(object): if isinstance(self.variables, list): self.domain = self.variables[0].domain elif isinstance(self.variables, dict): - self.domain = self.variables.keys()[0].domain + self.domain = self.variables.keys()[0].domain # Check if all variables have the same domain for v in self.variables: @@ -134,7 +135,7 @@ class Operator(object): # Set/check mpi context if self._mpis is None: self._mpis = MPIParams(comm=self.domain.comm_task, - task_id=self.domain.currentTask()) + task_id=self.domain.current_task()) # Set profiler self.profiler = Profiler(self, self.domain.comm_task) @@ -143,9 +144,9 @@ class Operator(object): raise RuntimeError("This operator is not defined for the current task") def wait_for(self, op): - """ + """MPI synchronisation - :param op: Continuous + :param op: :class:`~hysop.operator.continuous.Continuous` Add an operator into 'wait' list of the present object. It means that before any apply of this operator, all (mpi) operations in op must be fulfilled, which implies @@ -155,7 +156,8 @@ class Operator(object): self._wait_list.append(op) def wait_list(self): - """ + """Get MPI running comm. list + Returns ------- python list of all operators that must be fulfilled @@ -173,10 +175,11 @@ class Operator(object): pass def test_requests(self): - """ + """Checks for unfinished mpi communications. + Returns ------- - bool : MPI send/recv test for synchronisation: when this function is + bool : MPI send/recv test for synchronisation, when this function is called, the programm checks if this operator handles some uncomplete mpi requests (if so return true, else false). This is a non-blocking call. @@ -190,7 +193,7 @@ class Operator(object): ready to apply. In derived classes, called through @opsetup decorator. """ - if not self.domain.currentTask() == self._mpis.task_id: + if not self.domain.current_task() == self._mpis.task_id: self.ontask = False self._error_() @@ -225,9 +228,8 @@ class Operator(object): """ def is_up(self): - """ - Returns True if ready to be applied (--> setup function has - been called succesfully) + """Returns True if ready to be applied + (--> setup function has been called succesfully) """ return self._is_uptodate diff --git a/HySoP/hysop/operator/discrete/discrete.py b/HySoP/hysop/operator/discrete/discrete.py index e24b24701eebb8d599532e1fdcc76e997053e807..baff4c8f8d71adf8672c2b866a33d4c72c7c7680 100644 --- a/HySoP/hysop/operator/discrete/discrete.py +++ b/HySoP/hysop/operator/discrete/discrete.py @@ -95,9 +95,9 @@ class DiscreteOperator(object): self._writer = None # Check topologies consistency if self.variables is not None: - topoRef = self.variables[0].topology + toporef = self.variables[0].topology for v in self.variables: - assert v.topology.isConsistentWith(topoRef) + assert v.topology.is_consistent_with(toporef) def get_work_properties(self): """ diff --git a/HySoP/hysop/operator/discrete/drag_and_lift.py b/HySoP/hysop/operator/discrete/drag_and_lift.py index 2eea997d9a7676f6a53610a6dd42ed1eec51a872..a85de1030d3fbf3c4208af159b2ab7857780c4ac 100644 --- a/HySoP/hysop/operator/discrete/drag_and_lift.py +++ b/HySoP/hysop/operator/discrete/drag_and_lift.py @@ -7,7 +7,8 @@ import hysop.tools.numpywrappers as npw from abc import ABCMeta, abstractmethod from hysop.numerics.utils import Utils from hysop.constants import XDIR, YDIR, ZDIR -import numpy as np +from hysop.domain.control_box import ControlBox +from hysop.domain.subsets import Subset class Forces(DiscreteOperator): @@ -23,7 +24,7 @@ class Forces(DiscreteOperator): """ Parameters ---------- - obstacles : list of :class:`~hysop.domain.subsets.subset.Subset` + obstacles : list of :class:`~hysop.domain.subsets.Subset` list of bodies inside the flow normalization : double, optional a normalization coefficient applied to the force, default = 1. @@ -85,7 +86,7 @@ class Forces(DiscreteOperator): """ Parameters ----------- - obstacles : a list of :class:`hysop.domain.subsets.subset.Subset` + obstacles : a list of :class:`hysop.domain.subsets.Subset` Returns ------- @@ -186,12 +187,6 @@ class MomentumForces(Forces): self._buff = None self._init_buffers() - # A local discrete operator, to penalize temporary var. - ## from hysop.operator.discrete.penalization import Penalization - ## self._penalize = Penalization(variables=self._buff, - ## obstacles=self._obstacles, - ## coeff=self._coeff) - def _init_buffers(self): """ Allocate memory for local buffers (used to compute @@ -216,10 +211,7 @@ class MomentumForces(Forces): # mpi communicator #self._subcomm = obstacles[0].subcomm[self._topology] # Return the list of indices of points inside the obstacle - if obst.is_porous: - return obst.ind[self._topology] - else: - return [obst.ind[self._topology]] + return obst.ind[self._topology] def _momentum(self, dt): # -- Integration over the obstacle -- @@ -233,21 +225,9 @@ class MomentumForces(Forces): for i in xrange(len(self._indices)): self._buff[...] = 0.0 icurrent = self._indices[i] -# print 'shape', np.shape(icurrent), 'r =', self._topology.rank, 'i =', i - current = self._buff[icurrent] coeff = self._coeff[i] * dt / (1. + self._coeff[i] * dt) - current[...] = self.velocity.data[d][icurrent] * coeff - self.force[d] += npw.real_sum(current) -# print 'd=', d, 'force =', self.force[d] - -# i = 0 -# for ind in self._indices: -# self._buff[...] = 0.0 -# self._buff[ind] = self.velocity.data[d][ind] / (1.0 + self._coeff[i] * dt) -# self._buff[ind] *= -1.0 -# self._buff[ind] += self.velocity.data[d][ind] -# i += 1 -# self.force[d] += npw.real_sum(self._buff[ind]) + self._buff[icurrent] = self.velocity.data[d][icurrent] * coeff + self.force[d] += npw.real_sum(self._buff[icurrent]) self.force *= self._dvol self.force /= dt @@ -338,7 +318,7 @@ class NocaForces(Forces): Parameters ---------- - obstacles: list of :class:`~hysop.domain.subsets.subset.Subset` + obstacles: list of :class:`~hysop.domain.subsets.Subset` obstacles in the flow Returns @@ -346,12 +326,10 @@ class NocaForces(Forces): list of indices of points inside those obstacles """ - from hysop.domain.subsets.control_box import ControlBox assert isinstance(self._voc, ControlBox) self._on_proc = self._voc.on_proc[self._topology] msg = 'obstacles arg must be a list.' assert isinstance(obstacles, list), msg - from hysop.domain.subsets.subset import Subset # no obstacle in the box, just for test purpose. if obstacles is None or len(obstacles) == 0: return self._voc.ind[self._topology] diff --git a/HySoP/hysop/operator/discrete/penalization.py b/HySoP/hysop/operator/discrete/penalization.py index 983faf515316aa379025051f2e237fa016400424..b8113bb13928dea8a1506d74a48fcba598b721a4 100644 --- a/HySoP/hysop/operator/discrete/penalization.py +++ b/HySoP/hysop/operator/discrete/penalization.py @@ -1,18 +1,18 @@ # -*- coding: utf-8 -*- -""" -@file operator/discrete/penalization.py -Discrete operator for penalization problem. +"""Discrete operators for penalization problem. +.. currentmodule:: hysop.operator.discrete.penalization +* :class:`Penalization` : standard penalisation +* :class:`PenalizeVorticity` : vorticity formulation + """ from hysop.constants import debug from hysop.operator.discrete.discrete import DiscreteOperator from hysop.tools.profiler import profile -from hysop.domain.subsets.subset import Subset +from hysop.domain.subsets import Subset class Penalization(DiscreteOperator): - """ - Discretized penalisation operator. - See details in hysop.operator.penalization + """Discretized penalisation operator. """ @debug @@ -25,19 +25,6 @@ class Penalization(DiscreteOperator): coeff : double, optional penalization factor applied to all geometries. - Set : - - obstacles = {obs1: coeff1, obs2: coeff2, ...} and coeff=None - to apply a different coefficient on each subset. - - obstacles = [obs1, obs2, ...], coeff=coeff1 to apply the - same penalization on all subsets. - obs1, ob2 ... must be some `~hysop.domain.subsets.Subset` - and coeff1 must be either a real scalar or a function of the - coordinates like - def coeff(*args): - return 3 * args[0] - - with args[0,1,...] = x,y,... - Warning : coeff as a function is not yet implemented!! """ super(Penalization, self).__init__(**kwds) @@ -123,3 +110,72 @@ class Penalization(DiscreteOperator): "Simulation parameter is required." dt = simulation.timeStep self._apply(dt) + + +class PenalizeVorticity(Penalization): + """ + Discretized penalisation operator. + See details in hysop.operator.penalization + """ + + @debug + def __init__(self, vorticity, velocity, curl, **kwds): + """ + Parameters + ---------- + velocity, vorticity: :class:`~hysop.fields.continuous.Field` + curl : :class:`~hysop..operator.differential` + internal operator to compute the curl of the penalised velocity + **kwds : extra parameters for parent class. + + Notes + ----- + velocity is not modified by this operator. + vorticity is an in-out parameter. + input and ouput variables of the curl are some local buffers. + """ + assert 'variables' not in kwds, 'variables parameter is useless.' + super(PenalizeVorticity, self).__init__(variables=[vorticity, + velocity], + **kwds) + self.velocity = velocity + self.vorticity = vorticity + # warning : a buffer is added for invar variable in curl + topo = self.velocity.topology + msg = 'Multiresolution not implemented for penalization.' + assert self.vorticity.topology == topo, msg + self._curl = curl + + def _apply_single_coeff(self, dt): + # Vorticity penalization + # warning : the buff0 array ensures "invar" to be 0 + # outside the obstacle for the curl evaluation + invar = self._curl.invar + nbc = invar.nb_components + for d in xrange(nbc): + invar.data[d][...] = 0.0 + coeff = -dt * self._coeff / (1.0 + dt * self._coeff) + for d in xrange(nbc): + invar.data[d][self._cond] = \ + self.velocity[d][self._cond] * coeff + self._curl.apply() + for d in xrange(self.vorticity.nb_components): + self.vorticity[d][...] += self._curl.outvar[d][...] + + def _apply_multi_coeff(self, dt): + invar = self._curl.invar + nbc = invar.nb_components + + for d in xrange(nbc): + invar.data[d][...] = 0.0 + + for i in xrange(len(self._cond)): + coeff = -dt * self._coeff[i] / (1.0 + dt * self._coeff[i]) + cond = self._cond[i] + for d in xrange(nbc): + invar.data[d][cond] = self.velocity[d][cond] * coeff + + self._curl.apply() + + for d in xrange(self.vorticity.nb_components): + self.vorticity[d][...] += self._curl.outvar[d][...] diff --git a/HySoP/hysop/operator/discrete/penalize_vorticity.py b/HySoP/hysop/operator/discrete/penalize_vorticity.py index a33cead92f98479a966852f3da39ad7a95d2f405..279bf74b399ec6dc2d9a0aaccfb6e2847dfed021 100644 --- a/HySoP/hysop/operator/discrete/penalize_vorticity.py +++ b/HySoP/hysop/operator/discrete/penalize_vorticity.py @@ -1,75 +1,7 @@ # -*- coding: utf-8 -*- -""" -@file operator/discrete/penalization.py -Discrete operator for penalization problem. +"""Discrete operators for penalization problem. """ from hysop.constants import debug from hysop.operator.discrete.penalization import Penalization -class PenalizeVorticity(Penalization): - """ - Discretized penalisation operator. - See details in hysop.operator.penalization - """ - - @debug - def __init__(self, vorticity, velocity, curl, **kwds): - """ - @param[in,out] vorticity : vorticity field - @param[in] curl : curl operator used to compute - somebuffer = curl(penalized vorticity) - """ - assert 'variables' not in kwds, 'variables parameter is useless.' - super(PenalizeVorticity, self).__init__(variables=[vorticity, - velocity], - **kwds) - self.velocity = velocity - self.vorticity = vorticity - # \warning : a buffer is added for invar variable in curl - topo = self.velocity.topology - msg = 'Multiresolution not implemented for penalization.' - assert self.vorticity.topology == topo, msg - self._curl = curl - - def _apply_single_coeff(self, dt): - # Vorticity penalization - # \warning : the buff0 array ensures "invar" to be 0 - # outside the obstacle for the curl evaluation - invar = self._curl.invar - nbc = invar.nb_components - for d in xrange(nbc): - invar.data[d][...] = 0.0 - coeff = -dt * self._coeff / (1.0 + dt * self._coeff) - for d in xrange(nbc): - invar.data[d][self._cond] = \ - self.velocity[d][self._cond] * coeff - self._curl.apply() - for d in xrange(self.vorticity.nb_components): - self.vorticity[d][...] += self._curl.outvar[d][...] - # Velocity penalization (nor used for the moment since - # it implies a non divergence free velocity field) -# coeff = 1. / (1. + dt * self._coeff) -# self._penalize(self.velocity, coeff, self._cond) - - def _apply_multi_coeff(self, dt): - invar = self._curl.invar - nbc = invar.nb_components - - for d in xrange(nbc): - invar.data[d][...] = 0.0 - - for i in xrange(len(self._cond)): - coeff = -dt * self._coeff[i] / (1.0 + dt * self._coeff[i]) - cond = self._cond[i] - for d in xrange(nbc): - invar.data[d][cond] = self.velocity[d][cond] * coeff - - self._curl.apply() - - for d in xrange(self.vorticity.nb_components): - self.vorticity[d][...] += self._curl.outvar[d][...] - - def _penalize(self, field, coef, cond): - for d in xrange(field.nb_components): - field[d][cond] *= coef diff --git a/HySoP/hysop/operator/drag_and_lift.py b/HySoP/hysop/operator/drag_and_lift.py index a9100f7f59b8a20e0a1f1f660085d6555cefabf5..e8ecb741db2cb59b6017d01b05850c3230c8f5bc 100644 --- a/HySoP/hysop/operator/drag_and_lift.py +++ b/HySoP/hysop/operator/drag_and_lift.py @@ -7,6 +7,7 @@ from hysop.operator.computational import Computational from hysop.operator.continuous import opsetup from abc import ABCMeta, abstractmethod +from hysop.domain.control_box import ControlBox class Forces(Computational): @@ -201,7 +202,6 @@ class NocaForces(Forces): if volume_of_control is None: lr = self.domain.length * 0.9 xr = self.domain.origin + 0.04 * self.domain.length - from hysop.domain.subsets.control_box import ControlBox volume_of_control = ControlBox(parent=self.domain, origin=xr, length=lr) self._voc = volume_of_control diff --git a/HySoP/hysop/operator/penalization.py b/HySoP/hysop/operator/penalization.py index 9d164a41253c8756ccde375c56e5b5e1d1432467..81b451ef8960972617c235ec49c7fa85c8f0b55d 100644 --- a/HySoP/hysop/operator/penalization.py +++ b/HySoP/hysop/operator/penalization.py @@ -1,13 +1,27 @@ # -*- coding: utf-8 -*- -""" -@file operator/penalization.py +"""Operators for penalization problem. + +.. currentmodule:: hysop.operator.penalization + +* :class:`Penalization` : standard penalisation +* :class:`PenalizeVorticity` : vorticity formulation + +See details in :ref:`penalisation` section of HySoP user guide. -Penalisation of a given field """ + from hysop.operator.computational import Computational -from hysop.operator.discrete.penalization import Penalization as DiscrPenal +from hysop.operator.discrete.penalization import Penalization as DPenalV +from hysop.operator.discrete.penalization import PenalizeVorticity as DPenalW from hysop.constants import debug from hysop.operator.continuous import opsetup +from hysop.domain.subsets import Subset +import hysop.default_methods as default +from hysop.methods_keys import SpaceDiscretisation +from hysop.numerics.finite_differences import FD_C_4,\ + FD_C_2 +from hysop.operator.differential import Curl +from hysop.fields.continuous import Field class Penalization(Computational): @@ -25,30 +39,45 @@ class Penalization(Computational): @debug def __init__(self, obstacles, coeff=None, **kwds): """ - @param[in] obstacles : dictionnary or list of - subsets on which penalization must be applied - @param[in] coeff : penalization factor or function. + Parameters + ---------- + obstacles : dict or list of :class:`~hysop.domain.subsets.Subset` + sets of geometries on which penalization must be applied + coeff : double, optional + penalization factor applied to all geometries. + **kwds : extra parameters for parent class + + Notes + ----- + Set:: + + obstacles = {obs1: coeff1, obs2: coeff2, ...} + coeff = None - Set : - - obstacles = {obs1: coeff1, obs2: coeff2, ...} and coeff=None to apply a different coefficient on each subset. - - obstacles = [obs1, obs2, ...], coeff=coeff1 to apply the - same penalization on all subsets. - obs1, ob2 ... must be some hysop.domain.subsets.Subset - and coeff1 must be either a scalar or a function of the - coordinates like - def coeff(*args): - return 3 * args[0] + Set:: + + obstacles = [obs1, obs2, ...] + coeff = some_value + + to apply the same penalization on all subsets. + obs1, ob2 ... must be some :class:`~hysop.domain.subsets.Subset` + and some_value must be either a real scalar or a function of the + coordinates like:: + + def coeff(*args): + return 3 * args[0] with args[0,1,...] = x,y,... + Warning : coeff as a function is not yet implemented!! """ super(Penalization, self).__init__(**kwds) - ## The list of subset on which penalization must be applied + # The list of subset on which penalization must be applied self.obstacles = obstacles - ## Penalization functions or coef + # Penalization functions or coef self.coeff = coeff self.input = self.output = self.variables @@ -57,7 +86,6 @@ class Penalization(Computational): # all variables must have the same resolution assert self._single_topo, 'multi-resolution case not allowed.' topo = self.variables.values()[0] - from hysop.domain.subsets.subset import Subset for obs in self.obstacles: assert isinstance(obs, Subset) obs.discretize(topo) @@ -65,8 +93,87 @@ class Penalization(Computational): @debug @opsetup def setup(self, rwork=None, iwork=None): - self.discrete_op = DiscrPenal( + self.discrete_op = DPenalV( variables=self.discreteFields.values(), obstacles=self.obstacles, coeff=self.coeff, rwork=rwork, iwork=iwork) self._is_uptodate = True + + +class PenalizeVorticity(Penalization): + """ + Solve + \f{eqnarray*} + \frac{\partial w}{\partial t} &=& \lambda\chi_s\nabla\times(v_D - v) + \f} + using penalization. + """ + + @debug + def __init__(self, velocity, vorticity, **kwds): + """ + Parameters + ---------- + velocity, vorticity: :class:`~hysop.fields.continuous.Field` + **kwds : extra parameters for parent class. + + Notes + ----- + velocity is not modified by this operator. + vorticity is an in-out parameter. + """ + assert 'variables' not in kwds, 'variables parameter is useless.' + super(PenalizeVorticity, self).__init__( + variables=[velocity, vorticity], **kwds) + # velocity field + self.velocity = velocity + # vorticity field + self.vorticity = vorticity + # A method is required to set how the curl will be computed. + if self.method is None: + self.method = default.DIFFERENTIAL + # operator to compute buffer = curl(penalised velocity) + self._curl = None + + def discretize(self): + + if self.method[SpaceDiscretisation] is FD_C_4: + # Finite differences method + # Minimal number of ghost points + nb_ghosts = 2 + elif self.method[SpaceDiscretisation] is FD_C_2: + nb_ghosts = 1 + else: + raise ValueError("Unknown method for space discretization of the\ + differential operator in penalization.") + super(PenalizeVorticity, self)._standard_discretize(nb_ghosts) + # all variables must have the same resolution + assert self._single_topo, 'multi-resolution case not allowed.' + topo = self.variables[self.velocity] + for obs in self.obstacles: + assert isinstance(obs, Subset) + obs.discretize(topo) + invar = Field(domain=self.velocity.domain, + name='curl_in', is_vector=True) + dimension = self.domain.dimension + outvar = Field(domain=self.velocity.domain, + name='curl_out', + is_vector=dimension == 3) + self._curl = Curl(invar=invar, outvar=outvar, + discretization=topo, method=self.method) + self._curl.discretize() + + def get_work_properties(self): + return self._curl.get_work_properties() + + @debug + @opsetup + def setup(self, rwork=None, iwork=None): + self._curl.setup(rwork, iwork) + self.discrete_op = DPenalW( + vorticity=self.discreteFields[self.vorticity], + velocity=self.discreteFields[self.velocity], + curl=self._curl.discrete_op, + obstacles=self.obstacles, + coeff=self.coeff, rwork=rwork, iwork=iwork) + self._is_uptodate = True diff --git a/HySoP/hysop/operator/penalize_vorticity.py b/HySoP/hysop/operator/penalize_vorticity.py index 9d1ab278af5d67543173b01968a16b073ba879c2..adaa33a24b39ea70487911c837643cbdd9d09645 100644 --- a/HySoP/hysop/operator/penalize_vorticity.py +++ b/HySoP/hysop/operator/penalize_vorticity.py @@ -1,90 +1,3 @@ # -*- coding: utf-8 -*- +"""Compute the vorticity field from velocity using penalization. """ -@file operator/penalization_and_curl.py - -Compute the vorticity field from velocity using penalization. -""" -from hysop.operator.penalization import Penalization -from hysop.operator.discrete.penalize_vorticity\ - import PenalizeVorticity as PW -from hysop.constants import debug -from hysop.operator.continuous import opsetup -import hysop.default_methods as default -from hysop.methods_keys import SpaceDiscretisation -from hysop.numerics.finite_differences import FD_C_4,\ - FD_C_2 -from hysop.operator.differential import Curl -from hysop.fields.continuous import Field - - -class PenalizeVorticity(Penalization): - """ - Solve - \f{eqnarray*} - \frac{\partial w}{\partial t} &=& \lambda\chi_s\nabla\times(v_D - v) - \f} - using penalization. - """ - - @debug - def __init__(self, velocity, vorticity, **kwds): - """ - @param[in,out] velocity : velocity field - @param[in,out] vorticity : vorticity field - """ - assert 'variables' not in kwds, 'variables parameter is useless.' - super(PenalizeVorticity, self).__init__( - variables=[velocity, vorticity], **kwds) - # velocity field - self.velocity = velocity - # vorticity field - self.vorticity = vorticity - # A method is required to set how the curl will be computed. - if self.method is None: - self.method = default.DIFFERENTIAL - # operator to compute buffer = curl(penalised velocity) - self._curl = None - - def discretize(self): - - if self.method[SpaceDiscretisation] is FD_C_4: - # Finite differences method - # Minimal number of ghost points - nb_ghosts = 2 - elif self.method[SpaceDiscretisation] is FD_C_2: - nb_ghosts = 1 - else: - raise ValueError("Unknown method for space discretization of the\ - differential operator in penalization.") - super(PenalizeVorticity, self)._standard_discretize(nb_ghosts) - # all variables must have the same resolution - assert self._single_topo, 'multi-resolution case not allowed.' - topo = self.variables[self.velocity] - from hysop.domain.subsets.subset import Subset - for obs in self.obstacles: - assert isinstance(obs, Subset) - obs.discretize(topo) - invar = Field(domain=self.velocity.domain, - name='curl_in', is_vector=True) - dimension = self.domain.dimension - outvar = Field(domain=self.velocity.domain, - name='curl_out', - is_vector=dimension == 3) - self._curl = Curl(invar=invar, outvar=outvar, - discretization=topo, method=self.method) - self._curl.discretize() - - def get_work_properties(self): - return self._curl.get_work_properties() - - @debug - @opsetup - def setup(self, rwork=None, iwork=None): - self._curl.setup(rwork, iwork) - self.discrete_op = PW( - vorticity=self.discreteFields[self.vorticity], - velocity=self.discreteFields[self.velocity], - curl=self._curl.discrete_op, - obstacles=self.obstacles, - coeff=self.coeff, rwork=rwork, iwork=iwork) - self._is_uptodate = True diff --git a/HySoP/hysop/operator/poisson.py b/HySoP/hysop/operator/poisson.py index 73d29b5351b5080641d2a63d42d161db72b59b80..5151d2380bb00321dd18aac451c85f0322a21b56 100644 --- a/HySoP/hysop/operator/poisson.py +++ b/HySoP/hysop/operator/poisson.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -""" -@file poisson.py -Poisson problem. - +"""Poisson problem. """ from hysop.operator.computational import Computational from hysop.operator.discrete.poisson_fft import PoissonFFT @@ -16,7 +13,6 @@ import hysop.default_methods as default class Poisson(Computational): """ - Poisson problem, \f{eqnarray*} v = Op(\omega) \f} with : diff --git a/HySoP/hysop/operator/redistribute.py b/HySoP/hysop/operator/redistribute.py index c66846c7b230019657a0a2ef2115cd255ebecec2..c37db1da90577dc2fc684637dba68a281f12101e 100644 --- a/HySoP/hysop/operator/redistribute.py +++ b/HySoP/hysop/operator/redistribute.py @@ -177,7 +177,7 @@ class Redistribute(Operator): else: msg = "the source/target is neither an operator or a topology." raise AttributeError(msg) - assert result.task_id() == self.domain.currentTask() + assert result.task_id() == self.domain.current_task() return result def printComputeTime(self): diff --git a/HySoP/hysop/operator/redistribute_inter.py b/HySoP/hysop/operator/redistribute_inter.py index 96fc24521c4edae94d76eacb3ee3b2ea89e16445..f72bdb7c7d76fba564cafcbf151528cbd830f0b2 100644 --- a/HySoP/hysop/operator/redistribute_inter.py +++ b/HySoP/hysop/operator/redistribute_inter.py @@ -53,7 +53,7 @@ class RedistributeInter(Redistribute): self._set_domain_and_tasks() # Domain is set, we can check if we are on source or target - current_task = self.domain.currentTask() + current_task = self.domain.current_task() self._is_source = current_task == self._source_id self._is_target = current_task == self._target_id assert self._is_target or self._is_source diff --git a/HySoP/hysop/operator/tests/ref_files/penal_vort_2d_multi_sphere_00000.h5 b/HySoP/hysop/operator/tests/ref_files/penal_vort_2d_multi_sphere_00000.h5 index 6878cadc01a867c59511df698ffc5a9e5eebaec7..1c91c8933ca496944febab6633f8a4678c5c1e49 100644 Binary files a/HySoP/hysop/operator/tests/ref_files/penal_vort_2d_multi_sphere_00000.h5 and b/HySoP/hysop/operator/tests/ref_files/penal_vort_2d_multi_sphere_00000.h5 differ diff --git a/HySoP/hysop/operator/tests/ref_files/penal_vort_3d_multi_sphere_00000.h5 b/HySoP/hysop/operator/tests/ref_files/penal_vort_3d_multi_sphere_00000.h5 index 6e261313f258d3197c0ab79c5db6ba3215670f19..56b96e09bfa8827e24d40fa5f0df2aa3d5199657 100644 Binary files a/HySoP/hysop/operator/tests/ref_files/penal_vort_3d_multi_sphere_00000.h5 and b/HySoP/hysop/operator/tests/ref_files/penal_vort_3d_multi_sphere_00000.h5 differ diff --git a/HySoP/hysop/operator/tests/test_adaptive_time_step.py b/HySoP/hysop/operator/tests/test_adaptive_time_step.py index d31377a96e2d2abcf4021404c8dc8f02006751e0..6792f4b3a49fa79a2010890c5b90b4f106306896 100644 --- a/HySoP/hysop/operator/tests/test_adaptive_time_step.py +++ b/HySoP/hysop/operator/tests/test_adaptive_time_step.py @@ -119,18 +119,18 @@ def test_adapt_4(): discretization=d3d, lcfl=0.125, cfl=0.5, mpi_params=cpu_task) simu.initialize() - if dom.isOnTask(CPU): + if dom.is_on_task(CPU): op.discretize() op.setup() vorti.initialize() while not simu.isOver: - if dom.isOnTask(CPU): + if dom.is_on_task(CPU): op.apply() op.wait() simu.advance() refval = 0 - if dom.isOnTask(CPU): + if dom.is_on_task(CPU): refval = simu.timeStep refval = main_comm.bcast(refval, root=0) assert refval == simu.timeStep diff --git a/HySoP/hysop/operator/tests/test_drag_and_lift.py b/HySoP/hysop/operator/tests/test_drag_and_lift.py index 50fcb6e69d08122ebef15c2c2985c12680e966e0..27282aa9ff18006a7a8b89f022cc349f8e48b6d7 100644 --- a/HySoP/hysop/operator/tests/test_drag_and_lift.py +++ b/HySoP/hysop/operator/tests/test_drag_and_lift.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from hysop.domain.subsets.sphere import Sphere -from hysop.operator.penalize_vorticity import PenalizeVorticity +from hysop.domain.subsets import Sphere +from hysop.operator.penalization import PenalizeVorticity from hysop.problem.simulation import Simulation from hysop.tools.parameters import Discretization, IOParams from hysop.mpi.topology import Cartesian @@ -162,6 +162,6 @@ def test_noca3(): # This may be useful to run mpi tests if __name__ == "__main__": test_momentum_forces_3d() - test_noca1() - test_noca2() + #test_noca1() + #test_noca2() #test_noca3() diff --git a/HySoP/hysop/operator/tests/test_energy_enstrophy.py b/HySoP/hysop/operator/tests/test_energy_enstrophy.py index 17125d6b0050ea9cda3ea52a608b8844a60231de..124466d2694b8c8980e758352d7d991872552859 100644 --- a/HySoP/hysop/operator/tests/test_energy_enstrophy.py +++ b/HySoP/hysop/operator/tests/test_energy_enstrophy.py @@ -3,11 +3,22 @@ import hysop as pp from hysop.operator.energy_enstrophy import EnergyEnstrophy from hysop.problem.simulation import Simulation from hysop.tools.parameters import Discretization -from hysop import VariableParameter, Field +from hysop import Field import numpy as np -import hysop.tools.numpywrappers as npw -import os -from scipy.integrate import nquad +try: + from scipy.integrate import nquad + +except: + def nquad(func, coords_range): + coords = [] + nbsteps = 1000 + for x in coords_range: + coords.append(np.linspace(x[0], x[1], nbsteps)) + coords = tuple(coords) + hstep = coords[0][1] - coords[0][0] + ll = [coords_range[i][1] - coords_range[i][0] for i in xrange(1, 3)] + return [np.sum(func(*coords)[:-1]) * hstep * np.prod(ll)] + sin = np.sin cos = np.cos @@ -58,19 +69,19 @@ def test_energy_enstrophy(): op.apply(simu) intrange = [] box = topo.domain - invvol = 1./ np.prod(box.length) + invvol = 1. / np.prod(box.length) for i in xrange(dim): origin = box.origin[i] end = origin + box.length[i] intrange.append([origin, end]) intrange = 2 * intrange - Eref = nquad(energy_ref, intrange[:dim])[0] - Eref += nquad(energy_ref, intrange[1:dim + 1])[0] - Eref += nquad(energy_ref, intrange[2:dim + 2])[0] - Eref *= invvol + eref = nquad(energy_ref, intrange[:dim])[0] + eref += nquad(energy_ref, intrange[1:dim + 1])[0] + eref += nquad(energy_ref, intrange[2:dim + 2])[0] + eref *= invvol tol = (topo.mesh.space_step).max() ** 2 - assert (op.energy() - Eref * 0.5) < tol - assert (op.enstrophy() - Eref) < tol + assert (op.energy() - eref * 0.5) < tol + assert (op.enstrophy() - eref) < tol if __name__ == "__main__": test_energy_enstrophy() diff --git a/HySoP/hysop/operator/tests/test_hdf5_io.py b/HySoP/hysop/operator/tests/test_hdf5_io.py index 458f15f07e0ac299637f68fe1d7c49f10ba78341..c1c0f5ab7bd4ecc68e4bdbd30da19e570288ccbd 100644 --- a/HySoP/hysop/operator/tests/test_hdf5_io.py +++ b/HySoP/hysop/operator/tests/test_hdf5_io.py @@ -13,6 +13,7 @@ from hysop.tools.parameters import Discretization, IOParams from hysop.operator.hdf_io import HDF_Writer, HDF_Reader from hysop.tools.io_utils import IO from hysop.mpi import main_rank, main_size +from hysop.domain.subsets import SubBox Lx = 2. nb = 65 @@ -286,7 +287,7 @@ def test_write_read_subset_1(): velo = Field(domain=dom, formula=vec3D, name='velo', is_vector=True) # A subset of the current domain - from hysop.domain.subsets.boxes import SubBox + from hysop.domain.subsets import SubBox mybox = SubBox(origin=[-0.5, 2.3, 4.1], length=[Lx / 2, Lx / 3, Lx], parent=dom) # Write a vector field, using default for output location @@ -339,7 +340,6 @@ def test_write_read_subset_2(): velo = Field(domain=dom, formula=vec3D, name='velo', is_vector=True) # A subset of the current domain - from hysop.domain.subsets.boxes import SubBox # a plane ... mybox = SubBox(origin=[-0.5, 2.3, 4.1], length=[Lx / 2, Lx / 3, 0.0], parent=dom) diff --git a/HySoP/hysop/operator/tests/test_operators.py b/HySoP/hysop/operator/tests/test_operators.py index 2a86d6d45b28f7e03d71093803f603f47e43adf8..1b184f29bd99d3faa2e2df79e48e613ebdbdb16e 100644 --- a/HySoP/hysop/operator/tests/test_operators.py +++ b/HySoP/hysop/operator/tests/test_operators.py @@ -25,18 +25,18 @@ def test_tasks(): test proper tasks assignment """ dom, topo = create_multitask_context(dim=3, discr=r_ng) - assert topo.task_id() == dom.currentTask() + assert topo.task_id() == dom.current_task() velo = pp.Field(domain=dom, name='velocity', is_vector=True, formula=v3d) op = Analytic(variables={velo: r_ng}) op.discretize() op.setup() - assert op.task_id() == dom.currentTask() + assert op.task_id() == dom.current_task() if main_size == 8: - if dom.isOnTask(CPU): + if dom.is_on_task(CPU): assert op.variables[velo].size == 5 - elif dom.isOnTask(GPU): + elif dom.is_on_task(GPU): assert op.variables[velo].size == 2 - elif dom.isOnTask(OTHER): + elif dom.is_on_task(OTHER): assert op.variables[velo].size == 1 diff --git a/HySoP/hysop/operator/tests/test_penalization.py b/HySoP/hysop/operator/tests/test_penalization.py index 50e5915585f1ea65902153f7f0409721b3c403c5..b2ae2276220efacbd684fce33c4674942098f992 100644 --- a/HySoP/hysop/operator/tests/test_penalization.py +++ b/HySoP/hysop/operator/tests/test_penalization.py @@ -1,8 +1,7 @@ # -*- coding: utf-8 -*- from hysop.domain.subsets import HemiSphere, Sphere, Cylinder from hysop.domain.porous import Porous -from hysop.operator.penalization import Penalization -from hysop.operator.penalize_vorticity import PenalizeVorticity +from hysop.operator.penalization import Penalization, PenalizeVorticity from hysop.problem.simulation import Simulation from hysop.tools.parameters import Discretization, IOParams from hysop.mpi.topology import Cartesian @@ -11,6 +10,7 @@ import numpy as np import os from hysop import Field, Box from hysop.operator.hdf_io import HDF_Reader +from hysop.domain.subsets import SubBox def v2d(res, x, y, t): @@ -51,7 +51,6 @@ ldom = npw.asrealarray([math.pi * 2., ] * 3) xdef = npw.asrealarray(xdom + 0.2) xpos = npw.asrealarray(ldom * 0.5) xpos[-1] += 0.1 -from hysop.mpi import main_size working_dir = os.getcwd() + '/' @@ -128,7 +127,6 @@ def test_penal_2d_multi(): radius=rd + 0.3) ll = topo.domain.length.copy() ll[1] = 0. - from hysop.domain.subsets.boxes import SubBox downplane = SubBox(parent=topo.domain, origin=topo.domain.origin, length=ll) penal = Penalization(variables=[scal, velo], discretization=topo, @@ -170,7 +168,6 @@ def test_penal_3d_multi(): radius=rd + 0.3) ll = topo.domain.length.copy() ll[1] = 0. - from hysop.domain.subsets.boxes import SubBox downplane = SubBox(parent=topo.domain, origin=topo.domain.origin, length=ll) penal = Penalization(variables=[scal, velo], discretization=topo, @@ -192,7 +189,6 @@ def test_penal_3d_porous(): source=Sphere, layers=[0.5, 0.7, 0.3]) ll = topo.domain.length.copy() ll[1] = 0. - from hysop.domain.subsets.boxes import SubBox downplane = SubBox(parent=topo.domain, origin=topo.domain.origin, length=ll) penal = Penalization(variables=[scal, velo], discretization=topo, @@ -214,7 +210,6 @@ def test_penal_3d_porous_cyl(): source=Cylinder, layers=[0.5, 0.7, 0.3]) ll = topo.domain.length.copy() ll[1] = 0. - from hysop.domain.subsets.boxes import SubBox downplane = SubBox(parent=topo.domain, origin=topo.domain.origin, length=ll) penal = Penalization(variables=[scal, velo], discretization=topo, @@ -236,7 +231,6 @@ def test_penal_2d_porous(): source=Sphere, layers=[0.5, 0.7, 0.3]) ll = topo.domain.length.copy() ll[1] = 0. - from hysop.domain.subsets.boxes import SubBox downplane = SubBox(parent=topo.domain, origin=topo.domain.origin, length=ll) penal = Penalization(variables=[scal, velo], discretization=topo, diff --git a/HySoP/hysop/operator/tests/test_poisson.py b/HySoP/hysop/operator/tests/test_poisson.py index 7676e87dbfb32d91466dae6b7a4b098ff98ea1e2..30bc1106e39a49b4014d08e90d5a07b7b041f4ee 100755 --- a/HySoP/hysop/operator/tests/test_poisson.py +++ b/HySoP/hysop/operator/tests/test_poisson.py @@ -6,12 +6,13 @@ from hysop.operator.analytic import Analytic from hysop.operator.reprojection import Reprojection from hysop.problem.simulation import Simulation from hysop.tools.parameters import Discretization -from hysop.methods_keys import Scales, TimeIntegrator, Interpolation,\ - Remesh, Support, Splitting, dtCrit, SpaceDiscretisation, \ +from hysop.methods_keys import SpaceDiscretisation, \ GhostUpdate, Formulation import numpy as np import hysop.tools.numpywrappers as npw import math +from hysop.domain.subsets import SubBox + pi = math.pi sin = np.sin cos = np.cos @@ -217,7 +218,6 @@ def test_Poisson3D_correction(): refOp.apply(simu) refD = ref.discretize(topo) vd = velocity.discretize(topo) - from hysop.domain.subsets.boxes import SubBox surf = SubBox(parent=dom, origin=dom.origin, length=[0., LL[1], LL[2]]) surf.discretize(topo) diff --git a/HySoP/hysop/operator/tests/test_redistribute.py b/HySoP/hysop/operator/tests/test_redistribute.py index d5a15c8490dda0378a585d71a1c953e35efb0559..e72ce82681a10cf3564fd68c160b83ed96c8a908 100644 --- a/HySoP/hysop/operator/tests/test_redistribute.py +++ b/HySoP/hysop/operator/tests/test_redistribute.py @@ -14,6 +14,7 @@ from hysop.mpi.main_var import main_comm, main_size from hysop.mpi.tests.utils import create_inter_topos, CPU, GPU, OTHER,\ create_multitask_context from hysop.fields.tests.func_for_tests import v3d, v2d, v3dbis +from hysop.operator.poisson import Poisson dim3 = 3 @@ -413,7 +414,7 @@ def test_distribute_inter(): velocity = fields['velocity'] # Inititialize velocity on CPU task vd = velocity.discretize(topo=topo1) - if dom_tasks.isOnTask(CPU): + if dom_tasks.is_on_task(CPU): velocity.initialize(time=simu.time, topo=topo1) # A field to compute a reference solution, initialized with an analytic @@ -425,10 +426,10 @@ def test_distribute_inter(): op.apply(simu) wnorm = reference.norm(topo1) vnorm = velocity.norm(topo1) - if dom_tasks.isOnTask(CPU): + if dom_tasks.is_on_task(CPU): assert (vnorm > 0).all() assert np.allclose(vnorm, wnorm) - elif dom_tasks.isOnTask(GPU): + elif dom_tasks.is_on_task(GPU): assert (wnorm > 0).all() assert np.allclose(vnorm, 0) @@ -440,10 +441,10 @@ def test_distribute_inter(): red.apply(simu) red.wait() wd = reference.discretize(topo1) - if dom.isOnTask(CPU): + if dom.is_on_task(CPU): assert (vnorm > 0).all() assert np.allclose(vnorm, wnorm) - elif dom.isOnTask(GPU): + elif dom.is_on_task(GPU): assert (wnorm > 0).all() assert np.allclose(vnorm, wnorm) for d in xrange(dom.dimension): @@ -461,19 +462,19 @@ def test_distribute_inter_2(): if main_size > 2: proc_tasks[-1] = GPU proc_tasks[0] = GPU - domTasks = pp.Box(proc_tasks=proc_tasks) - fields, simu = init_3d(domTasks) + domtasks = pp.Box(proc_tasks=proc_tasks) + fields, simu = init_3d(domtasks) velocity = fields['velocity'] # Inititialize velocity on GPU task - if domTasks.isOnTask(GPU): - topo_GPU = domTasks.create_topology(r_ng) + if domtasks.is_on_task(GPU): + topo_GPU = domtasks.create_topology(r_ng) vd = velocity.discretize(topo=topo_GPU) velocity.initialize(time=simu.time, topo=topo_GPU) vnorm = velocity.norm(topo_GPU) assert (vnorm > 0).all() topo_CPU = None - elif domTasks.isOnTask(CPU): + elif domtasks.is_on_task(CPU): # A field to compute a reference solution, initialized with an analytic # operator, on both tasks. reference = fields['vorticity'] @@ -491,7 +492,7 @@ def test_distribute_inter_2(): red.setup() red.apply(simu) red.wait() - if domTasks.isOnTask(CPU): + if domtasks.is_on_task(CPU): vd = velocity.discretize(topo=topo_CPU) wd = reference.discretize(topo=topo_CPU) vnorm = velocity.norm(topo_CPU) @@ -512,7 +513,7 @@ def test_distribute_inter_3(): fields, simu = init_3d(dom_tasks) velocity = fields['velocity'] # Inititialize velocity on GPU task - if dom_tasks.isOnTask(GPU): + if dom_tasks.is_on_task(GPU): vd = velocity.discretize(topo=topo1) velocity.initialize(time=simu.time, topo=topo1) @@ -524,7 +525,7 @@ def test_distribute_inter_3(): op.setup() op.apply(simu) wnorm = reference.norm(topo2) - if dom_tasks.isOnTask(GPU): + if dom_tasks.is_on_task(GPU): vnorm = velocity.norm(topo1) assert (vnorm > 0).all() assert np.allclose(vnorm, wnorm) @@ -535,7 +536,7 @@ def test_distribute_inter_3(): red.setup() red.apply(simu) red.wait() - if dom_tasks.isOnTask(CPU): + if dom_tasks.is_on_task(CPU): wd = reference.discretize(topo=topo2) vd = velocity.discretize(topo=topo2) vnorm = velocity.norm(topo2) @@ -551,11 +552,11 @@ def test_distribute_inter_4(): """ if main_size < 4: return - dom_tasks, topo= create_multitask_context(3, r_ng) + dom_tasks, topo = create_multitask_context(3, r_ng) fields, simu = init_3d(dom_tasks) velocity = fields['velocity'] # Inititialize velocity on GPU task - if dom_tasks.isOnTask(GPU): + if dom_tasks.is_on_task(GPU): vd = velocity.discretize(topo=topo) velocity.initialize(time=simu.time, topo=topo) @@ -568,7 +569,7 @@ def test_distribute_inter_4(): op.apply(simu) # Redistribute from topo on CPU to topo on GPU, ignoring OTHER - if not dom_tasks.isOnTask(OTHER): + if not dom_tasks.is_on_task(OTHER): red = RedistributeInter(source=topo, target=topo, parent=main_comm, variables=[velocity], source_id=GPU, target_id=CPU) @@ -576,14 +577,14 @@ def test_distribute_inter_4(): red.apply(simu) red.wait() - if dom_tasks.isOnTask(CPU): + if dom_tasks.is_on_task(CPU): wd = reference.discretize(topo=topo) vd = velocity.discretize(topo=topo) ind = topo.mesh.iCompute for d in xrange(dom.dimension): assert np.allclose(wd.data[d][ind], vd.data[d][ind]) - if dom_tasks.isOnTask(OTHER): + if dom_tasks.is_on_task(OTHER): assert topo not in velocity.discreteFields @@ -591,31 +592,30 @@ def test_distribute_inter_5(): """ 2 tasks, redistribute op to op """ - from hysop.operator.poisson import Poisson if main_size < 4: return proc_tasks = [CPU, ] * main_size proc_tasks[-1] = GPU proc_tasks[0] = GPU - domTasks = pp.Box(proc_tasks=proc_tasks) + domtasks = pp.Box(proc_tasks=proc_tasks) - fields, simu = init_3d(domTasks) + fields, simu = init_3d(domtasks) velocity = fields['velocity'] reference = fields['vorticity'] - if domTasks.isOnTask(CPU): + if domtasks.is_on_task(CPU): # initialize velocity on CPU op = Analytic(variables={velocity: r_ng}) op.discretize() op.setup() op.apply(simu) - elif domTasks.isOnTask(GPU): + elif domtasks.is_on_task(GPU): # initialize reference on CPU op_init = Analytic(variables={reference: r_ng}) op_init.discretize() op_init.setup() op_init.apply(simu) # An empty operator for velocity - op = Poisson(velocity=velocity, vorticity=reference, + op = Poisson(output_field=velocity, input_field=reference, discretization=r_ng) op.discretize() op.setup() @@ -628,11 +628,11 @@ def test_distribute_inter_5(): red.apply(simu) red.wait() - if domTasks.isOnTask(GPU): + if domtasks.is_on_task(GPU): toporef = op.variables[reference] vd = velocity.discretize(toporef) wd = velocity.discretize(toporef) - for d in xrange(dom.dimension): + for d in xrange(domtasks.dimension): assert np.allclose(wd.data[d], vd.data[d]) @@ -640,34 +640,33 @@ def test_distribute_inter_2d(): """ 2 tasks, redistribute op to op, 2D domain """ - from hysop.operator.poisson import Poisson if main_size < 4: return proc_tasks = [CPU, ] * main_size proc_tasks[-1] = GPU proc_tasks[0] = GPU - domTasks = pp.Box(dimension=2, proc_tasks=proc_tasks) - velocity = pp.Field(domain=domTasks, name='velocity', + domtasks = pp.Box(dimension=2, proc_tasks=proc_tasks) + velocity = pp.Field(domain=domtasks, name='velocity', is_vector=True, formula=v2d) - vort = pp.Field(domain=domTasks, name='vort') + vort = pp.Field(domain=domtasks, name='vort') simu = Simulation(nbIter=3) - reference = pp.Field(domain=domTasks, name='ref', + reference = pp.Field(domain=domtasks, name='ref', is_vector=True, formula=v2d) r_2d = Discretization([33, ] * 2) - if domTasks.isOnTask(CPU): + if domtasks.is_on_task(CPU): # initialize velocity on CPU op = Analytic(variables={velocity: r_2d}) op.discretize() op.setup() op.apply(simu) - elif domTasks.isOnTask(GPU): + elif domtasks.is_on_task(GPU): # initialize reference on CPU op_init = Analytic(variables={reference: r_2d}) op_init.discretize() op_init.setup() op_init.apply(simu) # An empty operator for velocity - op = Poisson(velocity=velocity, vorticity=vort, + op = Poisson(output_field=velocity, input_field=vort, discretization=r_2d) op.discretize() op.setup() @@ -680,7 +679,7 @@ def test_distribute_inter_2d(): red.apply(simu) red.wait() - if domTasks.isOnTask(GPU): + if domtasks.is_on_task(GPU): toporef = op.variables[velocity] vd = velocity.discretize(toporef) wd = velocity.discretize(toporef) diff --git a/HySoP/hysop/operator/velocity_correction.py b/HySoP/hysop/operator/velocity_correction.py index aa1eb885db8bef0c6b1ef35384f920d166827a29..088eabd876bf26f84df9e8304f84fba237feb5a2 100755 --- a/HySoP/hysop/operator/velocity_correction.py +++ b/HySoP/hysop/operator/velocity_correction.py @@ -8,7 +8,7 @@ Operator to shift velocity to fit with a required input flowrate. from hysop.constants import debug from hysop.operator.discrete.velocity_correction import VelocityCorrection_D from hysop.operator.computational import Computational -from hysop.domain.subsets.control_box import ControlBox +from hysop.domain.control_box import ControlBox from hysop.operator.continuous import opsetup diff --git a/HySoP/hysop/tools/io_utils.py b/HySoP/hysop/tools/io_utils.py index e838752cb6eaf8bea3663c7c68dcc308da11d9c3..f6c77076c4e66184e33834f7000c1ffc6828ae4d 100644 --- a/HySoP/hysop/tools/io_utils.py +++ b/HySoP/hysop/tools/io_utils.py @@ -286,7 +286,7 @@ class XMF(object): Parameters ----------- - topo : hysop.mpi.topology.Cartesian + topo : :class:`hysop.mpi.topology.Cartesian` used as reference to define local and global meshes in xdmf file. dataset_names : list all datasets names @@ -296,7 +296,7 @@ class XMF(object): current time filename : string name of the hdf file which contains datas for the current process. - subset : hysop.domain.subsets.subset.Subset, optional + subset : :class:`hysop.domain.subsets.Subset`, optional to define a grid only on this subset. If None, grid on the whole domain (from topo) diff --git a/HySoP/hysop/tools/parameters.py b/HySoP/hysop/tools/parameters.py index 2cc89f445ccbaf120496ecf9b1ca3023cf1bb6d2..860c639c721c61a3267f799d5a42b7f7b4795fc1 100644 --- a/HySoP/hysop/tools/parameters.py +++ b/HySoP/hysop/tools/parameters.py @@ -10,7 +10,7 @@ from hysop.constants import DEFAULT_TASK_ID class MPIParams(namedtuple('MPIParams', ['comm', 'task_id', - 'rank', 'on_task'])): + 'rank', 'on_task'])): """ Struct to save mpi parameters : - comm : parent mpi communicator (default = main_comm) @@ -27,10 +27,10 @@ class MPIParams(namedtuple('MPIParams', ['comm', 'task_id', --------- op = SomeOperator(..., task_id=1) - if op.isOnTask(): + if op.is_on_task(): ... - 'isOnTask' will return MPIParams.on_task value for op + 'is_on_task' will return MPIParams.on_task value for op and tell if the current operator belongs to the current process mpi task. """ @@ -41,7 +41,7 @@ class MPIParams(namedtuple('MPIParams', ['comm', 'task_id', else: rank = MPI.UNDEFINED return super(MPIParams, cls).__new__(cls, comm, task_id, - rank, on_task) + rank, on_task) import hysop.tools.numpywrappers as npw diff --git a/HySoP/hysop/tools/remeshing_formula_parsing.py b/HySoP/hysop/tools/remeshing_formula_parsing.py index a3616178bb0397ecbfe90a55fabaf722ed75a6f7..cb158d9334b5716fe940b00efb8e2f9adef14e21 100644 --- a/HySoP/hysop/tools/remeshing_formula_parsing.py +++ b/HySoP/hysop/tools/remeshing_formula_parsing.py @@ -7,108 +7,115 @@ hysop.numerics.remeshing module or in OpenCL code in hysop/gpu/cl_src/remeshing/weights* """ import re -import sympy as sp +try: + import sympy as sp -# Weights names -weights_names = ['alpha', - 'beta', - 'gamma', - 'delta', - 'eta', - 'zeta', - 'theta', - 'iota', - 'kappa', - 'mu'] + # Weights names + weights_names = ['alpha', + 'beta', + 'gamma', + 'delta', + 'eta', + 'zeta', + 'theta', + 'iota', + 'kappa', + 'mu'] - -def parse(f, fac=1, vec=False, toOpenCL=True, CLBuiltins=False, keep=False): - """ - Parsing function. - @param f : functions to parse as string - @param fac : numeric factor for all formulas - @param vec : is OpenCL output is generated to use vector builtin types - @param toOpenCL : is OpenCL output - @param CLBuiltins : is OpenCL output uses fma builtin function - @param keep : low parsing - """ - assert not (vec and not toOpenCL), "Vector works only in OpenCL parsing" - assert not (CLBuiltins and not toOpenCL),\ - "CLBuiltins only in OpenCL parsing" - t = "float__N__" if vec else "float" - cteEnd = ".0" if toOpenCL else "." - res = "" - # Split each line - fl = f.split('\n') - # sympy formulas - y = sp.symbols('y') - print (y) - sw = [None] * f.count(';') - i = 0 - for wl in fl: - if len(wl) > 2: - # replace pow - power = re.search('pow\(y, ([0-9]+)\)', wl) - if power is not None: - np = "y" + "*y" * (int(power.group(1)) - 1) - wl = wl.replace(power.group(0), np) - sw[i] = '(' - sw[i] += str(sp.horner(eval(wl.split(';')[0].split('=')[1]) * fac)) - sw[i] += ')/' + str(fac) - i += 1 - for i, s in enumerate(sw): - if not keep: - if toOpenCL: - res += "inline " + t + " " - res += weights_names[i] + "(" + t + " y){\n" - res += ' return ' + def parse(f, fac=1, vec=False, toOpenCL=True, + CLBuiltins=False, keep=False): + """ + Parsing function. + @param f : functions to parse as string + @param fac : numeric factor for all formulas + @param vec : is OpenCL output is generated to use vector builtin types + @param toOpenCL : is OpenCL output + @param CLBuiltins : is OpenCL output uses fma builtin function + @param keep : low parsing + """ + msg = 'Vector works only in OpenCL parsing' + assert not (vec and not toOpenCL), msg + assert not (CLBuiltins and not toOpenCL),\ + "CLBuiltins only in OpenCL parsing" + t = "float__N__" if vec else "float" + cteEnd = ".0" if toOpenCL else "." + res = "" + # Split each line + fl = f.split('\n') + # sympy formulas + y = sp.symbols('y') + print (y) + sw = [None] * f.count(';') + i = 0 + for wl in fl: + if len(wl) > 2: + # replace pow + power = re.search('pow\(y, ([0-9]+)\)', wl) + if power is not None: + np = "y" + "*y" * (int(power.group(1)) - 1) + wl = wl.replace(power.group(0), np) + sw[i] = '(' + sw[i] += str(sp.horner(eval(wl.split(';')[0].split('=')[1]) * fac)) + sw[i] += ')/' + str(fac) + i += 1 + for i, s in enumerate(sw): + if not keep: + if toOpenCL: + res += "inline " + t + " " + res += weights_names[i] + "(" + t + " y){\n" + res += ' return ' + else: + res += 'lambda y, s: s * ' + res += '(' + # replace y**n + power = re.findall('y\*\*[0-9]+', s) + if power is not None: + for pw in power: + n = int(pw.split('**')[1]) + npower = 'y' + "*y" * (n - 1) + s = s.replace(pw, npower) + s = s.replace(' ', '') + if CLBuiltins: + s = createFMA(s) + # From integers to floats + s = re.sub(r"(?P<id>[0-9]+)", r"\g<id>" + cteEnd, s) + s = s.replace('*', ' * ') + s = s.replace('/', ' / ') + s = s.replace('+', ' + ') + s = s.replace('-', ' - ') + s = s.replace('( - ', '(-') + s = s.replace(' ', ' ') + s = s.replace(", - ", ", -") + res += s + ')' + if toOpenCL: + res += ";}" + if i < len(sw) - 1: + res += "\n" else: - res += 'lambda y, s: s * ' - res += '(' - # replace y**n - power = re.findall('y\*\*[0-9]+', s) - if power is not None: - for pw in power: - n = int(pw.split('**')[1]) - npower = 'y' + "*y" * (n - 1) - s = s.replace(pw, npower) - s = s.replace(' ', '') - if CLBuiltins: - s = createFMA(s) - # From integers to floats - s = re.sub(r"(?P<id>[0-9]+)", r"\g<id>" + cteEnd, s) - s = s.replace('*', ' * ') - s = s.replace('/', ' / ') - s = s.replace('+', ' + ') - s = s.replace('-', ' - ') - s = s.replace('( - ', '(-') - s = s.replace(' ', ' ') - s = s.replace(", - ", ", -") - res += s + ')' - if toOpenCL: - res += ";}" - if i < len(sw) - 1: - res += "\n" - else: - res += "w[{0}] = ".format(i) - # replace y**n - power = re.findall('y\*\*[0-9]+', s) - if power is not None: - for pw in power: - n = int(pw.split('**')[1]) - npower = 'y' + "*y" * (n - 1) - s = s.replace(pw, npower) - # From integers to floats - s = re.sub(r"(?P<id>[0-9]+)", r"\g<id>.", s) - s = s.replace('*', ' * ') - s = s.replace('/', ' / ') - s = s.replace('+', ' + ') - s = s.replace('-', ' - ') - s = s.replace('( - ', '(-') - s = s.replace(' ', ' ') - s = s.replace(", - ", ", -") - res += s + "\n" - return res + res += "w[{0}] = ".format(i) + # replace y**n + power = re.findall('y\*\*[0-9]+', s) + if power is not None: + for pw in power: + n = int(pw.split('**')[1]) + npower = 'y' + "*y" * (n - 1) + s = s.replace(pw, npower) + # From integers to floats + s = re.sub(r"(?P<id>[0-9]+)", r"\g<id>.", s) + s = s.replace('*', ' * ') + s = s.replace('/', ' / ') + s = s.replace('+', ' + ') + s = s.replace('-', ' - ') + s = s.replace('( - ', '(-') + s = s.replace(' ', ' ') + s = s.replace(", - ", ", -") + res += s + "\n" + return res + +except: + msge = 'Sympy not available - remeshing formula parsing will not work.' + msge += 'If you need parsing, try "pip install sympy" and reinstall hysop.' + print msge def createFMA(s): diff --git a/HySoP/hysop/tools/tests/test_formula_parsing.py b/HySoP/hysop/tools/tests/test_formula_parsing.py index acd9fdad565c3f7fdd084e414f1e3251c598cabb..1f093ec1e9c68deeadb23fdb6e62f63b7eb8817f 100644 --- a/HySoP/hysop/tools/tests/test_formula_parsing.py +++ b/HySoP/hysop/tools/tests/test_formula_parsing.py @@ -1,5 +1,3 @@ -from hysop.tools.remeshing_formula_parsing import parse - m4p = """ w[0] = (-1 + (2 - y) * y) * y / 2; w[1] = 1 + (-5 + 3 * y) * y * y / 2; @@ -81,15 +79,19 @@ l63 += """* y) * y) * y / 720; w[7] = (-145 + (367 + (-311 + 89 * y) * y) * y) * pow(y, 4) / 720; """ +try: + from hysop.tools.remeshing_formula_parsing import parse -def test_parsing_toPython(): - assert m4p_python == parse(m4p, 2, toOpenCL=False) + def test_parsing_toPython(): + assert m4p_python == parse(m4p, 2, toOpenCL=False) + def test_parsing_toOpenCL(): + assert m4p_cl_novec == parse(m4p, 2, vec=False, toOpenCL=True) + assert m4p_cl == parse(m4p, 2, vec=True, toOpenCL=True) + assert m4p_cl_builtin == \ + parse(m4p, 2, vec=True, toOpenCL=True, CLBuiltins=True) + res = parse(l63, 720, vec=True, toOpenCL=True, CLBuiltins=True) + assert l6_cl_builtin == res -def test_parsing_toOpenCL(): - assert m4p_cl_novec == parse(m4p, 2, vec=False, toOpenCL=True) - assert m4p_cl == parse(m4p, 2, vec=True, toOpenCL=True) - assert m4p_cl_builtin == \ - parse(m4p, 2, vec=True, toOpenCL=True, CLBuiltins=True) - assert l6_cl_builtin == parse(l63, 720, - vec=True, toOpenCL=True, CLBuiltins=True) +except: + pass