diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b5fb6a5d3ec5f9a24153141bf07f3bd4d6a779d..e2750a84254497bceee6a8ae1a184e0e47123e61 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -469,7 +469,7 @@ add_custom_target(pyclean COMMAND rm -f ${PYCFILES} # ============= Tests ============= if(WITH_TESTS) if(NOT ENABLE_LONG_TESTS) - set(TEST_TIMEOUT 10 CACHE STRING "Default value for test runtime limit (in seconds)") + set(TEST_TIMEOUT 15 CACHE STRING "Default value for test runtime limit (in seconds)") else() set(TEST_TIMEOUT 500 CACHE STRING "Default value for test runtime limit (in seconds)") endif() diff --git a/docs/sphinx/contacts.rst b/docs/sphinx/contacts.rst new file mode 100644 index 0000000000000000000000000000000000000000..f8646da86d08ebdf50a0f31bbc9fac93f4c59db0 --- /dev/null +++ b/docs/sphinx/contacts.rst @@ -0,0 +1,14 @@ +.. _contacts: + + +======== +Contacts +======== + +.. topic:: HySoP development team + + `Laboratoire Jean Kuntzmann`_, Grenoble. + + **E-Mail** : hysop-members@lists.forge.imag.fr + +.. _Laboratoire Jean Kuntzmann: http://ljk.imag.fr diff --git a/hysop/default_methods.py b/hysop/default_methods.py index 425cafd5f9e8e7f7886dead7cf75c825aa6c5fd8..d1e20b5c0545f6ce67d535b35f356d1e54bf4a03 100644 --- a/hysop/default_methods.py +++ b/hysop/default_methods.py @@ -4,39 +4,54 @@ from hysop.methods_keys import TimeIntegrator, Interpolation, GhostUpdate,\ Remesh, Support, Splitting, MultiScale, Formulation, SpaceDiscretisation, \ dtCrit, Precision from hysop.constants import HYSOP_REAL -from hysop.numerics.integrators.runge_kutta2 import RK2 -from hysop.numerics.integrators.runge_kutta3 import RK3 -from hysop.numerics.finite_differences import FDC4, FDC2 +from hysop.numerics.odesolvers import RK2, RK3 from hysop.numerics.interpolation import Linear from hysop.numerics.remeshing import L2_1, Linear as Rmsh_Linear +from hysop.numerics.finite_differences import FDC4, FDC2 #from hysop.operator.discrete.stretching import Conservative ADVECTION = {TimeIntegrator: RK2, Interpolation: Linear, Remesh: L2_1, Support: '', Splitting: 'o2', MultiScale: L2_1, Precision: HYSOP_REAL} - -from hysop.numerics.finite_differences import FDC4, FDC2 +"""Advection operators default setup +""" DIFFERENTIAL = {SpaceDiscretisation: FDC4, GhostUpdate: True} +"""Differential operators (finite differencies based) default setup +""" ADAPT_TIME_STEP = {TimeIntegrator: RK3, SpaceDiscretisation: FDC4, dtCrit: 'vort'} +"""Adaptative time step default setup +""" BAROCLINIC = {SpaceDiscretisation: FDC4} +"""Baroclinic operators default setup +""" MULTIPHASEBAROCLINIC = {SpaceDiscretisation: FDC4} MULTIPHASEGRADP = {SpaceDiscretisation: FDC4} DIFFUSION = {SpaceDiscretisation: 'fftw', GhostUpdate: True} +"""Differential operators (finite differencies based) default setup +""" POISSON = {SpaceDiscretisation: 'fftw', GhostUpdate: True, Formulation: 'velocity'} +"""Poisson operators default setup +""" STRETCHING = {TimeIntegrator: RK3, Formulation: "Conservative", SpaceDiscretisation: FDC4} +"""Stretching operators default setup +""" FORCES = {SpaceDiscretisation: FDC2} +"""Drag/lift computation operators default setup +""" -MULTIRESOLUTION_FILER = {Remesh: Rmsh_Linear, } +MULTIRESOLUTION_FILTER = {Remesh: Rmsh_Linear, } +"""filters operators default setup +""" diff --git a/hysop/domain/box.py b/hysop/domain/box.py index cbf6317fc5d6b267bb700bc9db88947335aaac6f..7a45d2e8142165a21bc9071ac7869d4f89efddb1 100644 --- a/hysop/domain/box.py +++ b/hysop/domain/box.py @@ -40,16 +40,16 @@ class Box(Domain): super(Box, self).__init__(**kwds) - ## Box length. + # Box length. if length is None: length = [1.0] * self.dimension if origin is None: origin = [0.] * self.dimension self.length = npw.const_realarray(length) - ## Box origin + # Box origin self.origin = npw.const_realarray(origin) - ## Maximum Box position. max = origin + length + # Maximum Box position. max = origin + length self.end = self.origin + self.length # set periodic boundary conditions if self.boundaries is None: diff --git a/hysop/domain/tests/test_box.py b/hysop/domain/tests/test_box.py index d9ab03cd0075118da370971ed1b1b04e54a78d9f..24a12f79a52c1a6222ea2fd973dce23fb8b16332 100755 --- a/hysop/domain/tests/test_box.py +++ b/hysop/domain/tests/test_box.py @@ -124,20 +124,16 @@ def test_topo_plane(): def test_topo_from_mesh(): # e.g. for fftw dom = Box(proc_tasks=proc_tasks) - from hysop.f2hysop import fftw2py - if dom.is_on_task(CPU): + from hysop import __FFTW_ENABLED__ + if dom.is_on_task(CPU) and __FFTW_ENABLED__: + from hysop.f2hysop import fftw2py 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.is_on_task(GPU): - topo = dom.create_topology(discretization=r3DGh, dim=2) - 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.is_on_task(GPU): - assert topo.size == 1 diff --git a/hysop/domain/tests/test_subset.py b/hysop/domain/tests/test_subset.py index 8501fc3b41600e3b9a4efb28eae4338d08ae069e..8f3de686d3983a3ceb67fbbdd0ac7ad16a78b60b 100755 --- a/hysop/domain/tests/test_subset.py +++ b/hysop/domain/tests/test_subset.py @@ -183,7 +183,8 @@ def init_subtract_lists(discr, fileref): sd = Field(domain=topo.domain, is_vector=False, name='s2').discretize(topo) indices = Subset.subtract_list_of_sets(box, obs, topo) sd[0][indices] = 2. - return np.allclose(sd[0][topo.mesh.compute_index], vd[0][topo.mesh.compute_index]) + return np.allclose(sd[0][topo.mesh.compute_index], + vd[0][topo.mesh.compute_index]) def test_subtract_list_3d(): diff --git a/hysop/numerics/odesolvers.py b/hysop/numerics/odesolvers.py index 00d55837f9f5d90e2f4d5ad9e1550469ca289e57..1ef90454caee2dccefc7ae143851999d217505ac 100755 --- a/hysop/numerics/odesolvers.py +++ b/hysop/numerics/odesolvers.py @@ -101,7 +101,7 @@ class ODESolver(object): self.in_indices[i].start for i in xrange(len(self.in_indices))]) else: - subsize = wk_prop['rwork'] + subsize = topo.mesh.resolution return WorkSpaceTools.check_work_array(len(wk_prop), subsize, rwork) diff --git a/hysop/numerics/tests/test_odesolvers.py b/hysop/numerics/tests/test_odesolvers.py index f1122cda86fecf782515dcc4d78752eaacbd3f39..b75b58418116b2d85df91718fbd0260984050f48 100755 --- a/hysop/numerics/tests/test_odesolvers.py +++ b/hysop/numerics/tests/test_odesolvers.py @@ -44,8 +44,12 @@ def rhs3d(t, y, work): return work -# -- 1D cases -- def integrate(integ, nb_steps): + """Execute odesolver 'integ' nb_steps times. + + * integ must be an instance of an odesolver (i.e. Euler, RK2 ...). + * the system must be 1D. + """ t = start time_points = np.linspace(start, end, nb_steps) dtt = time_points[1] - time_points[0] @@ -67,8 +71,12 @@ def integrate(integ, nb_steps): return dtt, err -# -- 3D cases -- def integrate_3d(integ, nb_steps): + """Execute odesolver 'integ' nb_steps times. + + * integ must be an instance of an odesolver (i.e. Euler, RK2 ...). + * 3D system + """ t = start time_points = np.linspace(start, end, nb_steps) dtt = time_points[1] - time_points[0] @@ -114,7 +122,7 @@ def run_integ(integrator, order): def run_integ_3d(integrator, order): - nb_steps = 100 + nb_steps = 50 wk_prop = integrator.get_work_properties(3, topo3d)['rwork'] work = [] for shape in wk_prop: @@ -126,7 +134,7 @@ def run_integ_3d(integrator, order): def run_integ_3d_no_work(integrator, order): - nb_steps = 100 + nb_steps = 50 dt, err = integrate_3d(integrator(3, topo3d, f=rhs3d), nb_steps) for e in err: assert e < dt ** order @@ -170,7 +178,7 @@ def test_rk4_3d(): def test_user_defined_common_workspace(): integrators = {Euler: 1, RK2: 2, RK3: 3, RK4: 4} - nb_steps = 100 + nb_steps = 50 wk_prop = {} for integ in integrators: wk_prop[integ] = integ.get_work_properties(3, topo3d)['rwork'] diff --git a/hysop/operator/absorption_BC.py b/hysop/operator/absorption_BC.py index 303ac78ae6344c1f3b70a3a05d62c58e525f5c59..4d27323be8ef208f5144740406e2781e97919ff1 100755 --- a/hysop/operator/absorption_BC.py +++ b/hysop/operator/absorption_BC.py @@ -69,5 +69,5 @@ class AbsorptionBC(Computational): cb=self.cb, rwork=rwork, iwork=iwork) # Output setup self._set_io('absorption_BC', (1, 2 + self.domain.dimension)) - self.discrete_op.setWriter(self._writer) + self.discrete_op.set_writer(self._writer) self._is_uptodate = True diff --git a/hysop/operator/adapt_timestep.py b/hysop/operator/adapt_timestep.py index b298ea7200e214064203ce4cd26b060907e17924..1e07503556f74572e89e0332df56600ea4c39e72 100755 --- a/hysop/operator/adapt_timestep.py +++ b/hysop/operator/adapt_timestep.py @@ -123,7 +123,7 @@ class AdaptTimeStep(Computational): rwork=rwork, iwork=iwork) # Output setup self._set_io('dt_adapt', (1, 7)) - self.discrete_op.setWriter(self._writer) + self.discrete_op.set_writer(self._writer) self._is_uptodate = True def wait(self): diff --git a/hysop/operator/custom.py b/hysop/operator/custom.py index d7d8d91243f6cea3beeb39f01fc544c534c0fd40..5af161e895e8daab3d2fd387bbd7a37ce7a821d2 100644 --- a/hysop/operator/custom.py +++ b/hysop/operator/custom.py @@ -41,5 +41,5 @@ class CustomMonitor(Computational): variables=self.discreteFields.values()) # Output setup self._set_io(self.function.__name__, (1, 1 + self.res_shape)) - self.discrete_op.setWriter(self._writer) + self.discrete_op.set_writer(self._writer) self._is_uptodate = True diff --git a/hysop/operator/discrete/discrete.py b/hysop/operator/discrete/discrete.py index 2069227a8f868aa77fe26757b35379b338d82a0c..94e5418a36211662fa301e44a68349ff8d862da7 100755 --- a/hysop/operator/discrete/discrete.py +++ b/hysop/operator/discrete/discrete.py @@ -99,29 +99,7 @@ class DiscreteOperator(object): for v in self.variables: assert v.topology.is_consistent_with(toporef) - def get_work_properties(self): - """ - Get properties of internal work arrays. - Returns - ------- - dictionnary - keys = 'rwork' and 'iwork', values = list of shapes of internal - arrays required by this operator (real arrays for rwork, integer - arrays for iwork). - - Example - ------- - >>> works_prop = op.get_work_properties() - >>> print works_prop - {'rwork': [(12, 12), (45, 12, 33)], 'iwork': None} - - means that the operator requires two real arrays of - shape (12,12) and (45, 12, 33) and no integer arrays - - """ - return {'rwork': None, 'iwork': None} - - def _set_work_arrays(self, rwork, iwork): + def _set_work_arrays(self, rwork=None, iwork=None): """ To set the internal work arrays used by this operator. Parameters @@ -134,7 +112,7 @@ class DiscreteOperator(object): """ pass - def setWriter(self, writer): + def set_writer(self, writer): """ Assign a writer to the current operator """ @@ -159,8 +137,8 @@ class DiscreteOperator(object): def __str__(self): """Common printings for discrete operators.""" - shortName = str(self.__class__).rpartition('.')[-1][0:-2] - s = shortName + " discrete operator. \n" + short_name = str(self.__class__).rpartition('.')[-1][0:-2] + s = short_name + " discrete operator. \n" if self.input is not None: s += "Input fields : \n" for f in self.input: diff --git a/hysop/operator/discrete/energy_enstrophy.py b/hysop/operator/discrete/energy_enstrophy.py index 974cf16653d0d9cb964118068af4830ea13c8ee7..06c77227017485e7f467f425ccfb56f415a9fe98 100644 --- a/hysop/operator/discrete/energy_enstrophy.py +++ b/hysop/operator/discrete/energy_enstrophy.py @@ -1,38 +1,41 @@ # -*- coding: utf-8 -*- -""" -@file energy_enstrophy.py -Compute Energy and Enstrophy +"""Discrete operator to compute Energy and Enstrophy """ from hysop.constants import debug from hysop.tools.profiler import profile import hysop.tools.numpywrappers as npw from hysop.operator.discrete.discrete import DiscreteOperator +from hysop.tools.misc import WorkSpaceTools class EnergyEnstrophy(DiscreteOperator): - """ - Discretization of the energy/enstrophy computation process. + """Discretization of the energy/enstrophy computation process. """ def __init__(self, velocity, vorticity, is_normalized=True, **kwds): """ - Constructor. - @param velocity : discretization of the velocity field - @param vorticity : discretization of the vorticity field - @param coeffs : dict of coefficients + Parameters + ---------- + velocity : :class:`~hysop.operator.field.discrete.DiscreteField` + velocity discrete fields + vorticity : :class:`~hysop.operator.field.discrete.DiscreteField` + vorticity discrete fields + isNormalized : boolean + true if enstrophy and energy values have to be normalized + by the domain lengths. """ - ## velocity field + # velocity field self.velocity = velocity - ## vorticity field + # vorticity field self.vorticity = vorticity assert 'variables' not in kwds, 'variables parameter is useless.' super(EnergyEnstrophy, self).__init__(variables=[velocity, vorticity], **kwds) - ## Coeffs for integration + # Coeffs for integration self.coeff = {} - ## Global energy + # Global energy self.energy = 0.0 - ## Global enstrophy + # Global enstrophy self.enstrophy = 0.0 topo_w = self.vorticity.topology topo_v = self.velocity.topology @@ -52,37 +55,11 @@ class EnergyEnstrophy(DiscreteOperator): v_ind = self.velocity.topology.mesh.compute_index w_ind = self.vorticity.topology.mesh.compute_index - shape_v = self.velocity.data[0][v_ind].shape - shape_w = self.velocity.data[0][w_ind].shape - # setup for rwork, iwork is useless. - if rwork is None: - # --- Local allocation --- - if shape_v == shape_w: - self._rwork = [npw.zeros(shape_v)] - else: - self._rwork = [npw.zeros(shape_v), npw.zeros(shape_w)] - else: - assert isinstance(rwork, list), 'rwork must be a list.' - # --- External rwork --- - self._rwork = rwork - if shape_v == shape_w: - assert len(self._rwork) == 1 - assert self._rwork[0].shape == shape_v - else: - assert len(self._rwork) == 2 - assert self._rwork[0].shape == shape_v - assert self._rwork[1].shape == shape_w - - def get_work_properties(self): - - v_ind = self.velocity.topology.mesh.compute_index - w_ind = self.vorticity.topology.mesh.compute_index - shape_v = self.velocity.data[0][v_ind].shape - shape_w = self.velocity.data[0][w_ind].shape - if shape_v == shape_w: - return {'rwork': [shape_v], 'iwork': None} - else: - return {'rwork': [shape_v, shape_w], 'iwork': None} + size_v = self.velocity.data[0][v_ind].size + size_w = self.vorticity.data[0][w_ind].size + size_work = max(size_w, size_v) + lwork = 1 + self._rwork = WorkSpaceTools.check_work_array(lwork, size_work, rwork) @debug @profile @@ -94,23 +71,24 @@ class EnergyEnstrophy(DiscreteOperator): vd = self.velocity # get the list of computation points (no ghosts) nbc = vd.nb_components - v_ind = self.velocity.topology.mesh.compute_index + ic = self.velocity.topology.mesh.compute_index # Integrate (locally) velocity ** 2 local_energy = 0. + size_v = vd[0][ic].size for i in xrange(nbc): - self._rwork[0][...] = vd[i][v_ind] ** 2 - local_energy += npw.real_sum(self._rwork[0]) + self._rwork[0][:size_v] = (vd[i][ic] ** 2).flat + local_energy += npw.real_sum(self._rwork[0][:size_v]) # --- Enstrophy computation --- - vortd = self.vorticity - nbc = vortd.nb_components - w_ind = self.vorticity.topology.mesh.compute_index + wd = self.vorticity + nbc = wd.nb_components + ic = self.vorticity.topology.mesh.compute_index + size_w = wd[0][ic].size # Integrate (locally) vorticity ** 2 - work = self._rwork[-1] local_enstrophy = 0. for i in xrange(nbc): - work[...] = vortd[i][w_ind] ** 2 - local_enstrophy += npw.real_sum(work) + self._rwork[0][:size_w] = (wd[i][ic] ** 2).flat + local_enstrophy += npw.real_sum(self._rwork[0][:size_w]) # --- Reduce energy and enstrophy values overs all proc --- # two ways : numpy or classical. Todo : check perf and comm diff --git a/hysop/operator/discrete/stretching.py b/hysop/operator/discrete/stretching.py index 4f1527605ff478edb26a6ee5192bafc220d5c9af..8e3d2e16d71cdd3464bcdb90293a241eb4c8391b 100755 --- a/hysop/operator/discrete/stretching.py +++ b/hysop/operator/discrete/stretching.py @@ -5,33 +5,26 @@ Formulations: * :class:`~hysop.operator.discrete.stretching.Conservative` * :class:`~hysop.operator.discrete.stretching.GradUW` -* :class:`~hysop.operator.discrete.stretching.Symmetric` """ from hysop.constants import debug, WITH_GUESS from hysop.methods_keys import TimeIntegrator, SpaceDiscretisation from hysop.operator.discrete.discrete import DiscreteOperator -from hysop.numerics.integrators.euler import Euler -from hysop.numerics.integrators.runge_kutta2 import RK2 -from hysop.numerics.integrators.runge_kutta3 import RK3 -from hysop.numerics.integrators.runge_kutta4 import RK4 import hysop.numerics.differential_operations as diff_op import hysop.tools.numpywrappers as npw from hysop.numerics.update_ghosts import UpdateGhosts from hysop.tools.profiler import profile +from hysop.tools.misc import WorkSpaceTools from abc import ABCMeta, abstractmethod import math +import numpy as np ceil = math.ceil class Stretching(DiscreteOperator): - """ - Abstract interface to stretching discrete operators. - Three formulations are available: - - conservative, see operator.discrete.stretching.Conservative - - 'Grad(UxW), see operator.discrete.stretching.GradUW - - Symmetric, see operator.discrete.stretching.Symmetric + """Abstract interface to stretching discrete operators. + Check user guide to see the available formulations. """ __metaclass__ = ABCMeta @@ -61,6 +54,8 @@ class Stretching(DiscreteOperator): self._ti_work = None # Work vector used by numerical diff operator. self._str_work = None + # all works vectors are set in base class init, + # thanks to _set_work_arrays function. super(Stretching, self).__init__(variables=[self.velocity, self.vorticity], **kwds) @@ -75,94 +70,74 @@ class Stretching(DiscreteOperator): # prepare ghost points synchro for velocity and vorticity self._synchronize = UpdateGhosts(self.velocity.topology, - self.velocity.nb_components - + self.vorticity.nb_components) + self.velocity.nb_components + + self.vorticity.nb_components) - # A function to compute the gradient of a vector field + # A function to compute the gradient of the velocity. # Work vector is provided in input. self.strFunc = \ self.formulation(topo=self.velocity.topology, work=self._str_work, method=self.method[SpaceDiscretisation]) - # Time integrator self.timeIntegrator = \ self.method[TimeIntegrator](self.nb_components, - self._ti_work, self.velocity.topology, + rwork=self._ti_work, f=rhs, optim=WITH_GUESS) + # In self._integrate ti_work is used as in/out parameters in the rhs, + # of the time-integrator so it needs a reshape. + # Warning : this is done without any memory realloc. + for i in xrange(len(self._ti_work)): + self._ti_work[i] = self.timeIntegrator.rwork[i] def _set_work_arrays(self, rwork=None, iwork=None): - shape_v = self.velocity.data[0][...].shape ti = self.method[TimeIntegrator] - # work list length for time-integrator - work_length_ti = ti.getWorkLengths(3) - rwork_length = work_length_ti + self.formulation.get_work_length() - # setup for rwork, iwork is useless. - if rwork is None: - # --- Local allocation --- - self._rwork = [] - for _ in xrange(rwork_length): - self._rwork.append(npw.zeros(shape_v)) - else: - assert isinstance(rwork, list), 'rwork must be a list.' - # --- External rwork --- - self._rwork = rwork - assert len(self._rwork) == rwork_length - for wk in rwork: - assert wk.shape == shape_v - self._ti_work = self._rwork[:work_length_ti] - self._str_work = self._rwork[work_length_ti:] + topo = self.velocity.topology + nbc = self.velocity.nb_components + # properties of working arrays required for time-integrator + wk_prop = ti.get_work_properties(nbc, topo)['rwork'] + lenti = len(wk_prop) + # properties of working arrays required for differential operations, + wk_prop += self.formulation.get_work_properties(topo)['rwork'] + subshape = np.prod(topo.mesh.resolution) + self._rwork = WorkSpaceTools.check_work_array(len(wk_prop), + subshape, rwork) + self._ti_work = self._rwork[:lenti] + self._str_work = self._rwork[lenti:] @profile def update_ghosts(self): + """Ghost points synchronization """ - Update ghost points values - """ - self._synchronize(self.velocity.data + self.vorticity.data) - - def _apply(self, t, dt): - # Synchronize ghost points self._synchronize(self.velocity.data + self.vorticity.data) - # Compute stretching term (rhs) and update vorticity - self._compute(dt, t) def apply(self, simulation=None): + """Solve stretching equation and update vorticity """ - """ assert simulation is not None, \ "Missing simulation value for computation." - # current time - t = simulation.time + # time step dt = simulation.time_step - self._apply(t, dt) - -# def apply(self, simulation=None): -# """ -# """ -# assert simulation is not None, \ -# "Missing simulation value for computation." -# -# # time step -# dt = simulation.time_step -# # current time -# t = simulation.time -# -# # Synchronize ghost points of velocity -# self._synchronize(self.velocity.data + self.vorticity.data) -# self._compute(dt, t) + # current time + t = simulation.time + + # Synchronize ghost points of velocity + self._synchronize(self.velocity.data + self.vorticity.data) + self._compute(dt, t) @abstractmethod def _compute(self, dt, t): - """ + """Abstract interface to local stretching resolution """ @profile def _integrate(self, dt, t): - # - Call time integrator - + """Apply time integrator + """ # Init workspace with a first evaluation of the # rhs of the integrator self._ti_work[:self.nb_components] = \ @@ -182,8 +157,9 @@ class Conservative(Stretching): # Right-hand side for time integration def rhs(t, y, result): - result = self.strFunc(y, self.velocity.data, result) - return result + """rhs used in time integrator + """ + return self.strFunc(y, self.velocity.data, result) super(Conservative, self).__init__(formulation=diff_op.DivWV, rhs=rhs, **kwds) @@ -199,10 +175,10 @@ class GradUW(Stretching): """ def __init__(self, **kwds): - # a vector to save diagnostics computed from GradVxW (max div ...) - self.diagnostics = npw.ones(2) def rhs(t, y, result): + """rhs used in time integrator + """ result, self.diagnostics =\ self.strFunc(self.velocity.data, y, result, self.diagnostics) return result @@ -211,22 +187,15 @@ class GradUW(Stretching): rhs=rhs, **kwds) # stability constant - self.cststretch = 0. # Depends on time integration method - timeint = self.method[TimeIntegrator] - classtype = timeint.mro()[0] - if classtype is Euler: - self.cststretch = 2.0 - elif classtype is RK2: - self.cststretch = 2.0 - elif classtype is RK3: - self.cststretch = 2.5127 - elif classtype is RK4: - self.cststretch = 2.7853 + self.cststretch = self.method[TimeIntegrator].stability_coeff() + # a vector to save diagnostics computed from GradVxW (max div ...) + self.diagnostics = npw.zeros(2) + self.diagnostics[1] = self.cststretch @debug @profile - def _compute(self, t, dt): + def _compute(self, dt, t): # Compute the number of required subcycles ndt, subdt = self._check_stability(dt) assert sum(subdt) == dt @@ -255,12 +224,11 @@ class GradUW(Stretching): return nb_cycles, subdt -class StretchingLinearized(Stretching): - """By default: Conservative formulation +class StretchingLinearized(Conservative): + """Conservative formulation of the linearized stretching. """ - + def __init__(self, vorticity_BF, usual_op, **kwds): - # vorticity of the base flow (steady solution) self.vorticity_BF = vorticity_BF # prepare ghost points synchro for vorticity_BF diff --git a/hysop/operator/drag_and_lift.py b/hysop/operator/drag_and_lift.py index 537a767396e889a5e58affc5a36c9f50ca993819..0ded0b6fd0dd3694e6dd284b4bc774e670528533 100755 --- a/hysop/operator/drag_and_lift.py +++ b/hysop/operator/drag_and_lift.py @@ -136,7 +136,7 @@ class MomentumForces(Forces): # output setup self._set_io('drag_and_lift', (1, 4)) - self.discrete_op.setWriter(self._writer) + self.discrete_op.set_writer(self._writer) self._is_uptodate = True def get_work_properties(self): @@ -266,5 +266,5 @@ class NocaForces(Forces): # output setup self._set_io('drag_and_lift', (1, 4)) - self.discrete_op.setWriter(self._writer) + self.discrete_op.set_writer(self._writer) self._is_uptodate = True diff --git a/hysop/operator/energy_enstrophy.py b/hysop/operator/energy_enstrophy.py index a59c1edeb1816023ceb6113400be8ffc0eb0a3e3..5251e6502a9003d28bc8890fb88af8e41521e179 100644 --- a/hysop/operator/energy_enstrophy.py +++ b/hysop/operator/energy_enstrophy.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- -""" -@file energy_enstrophy.py -Compute Energy and Enstrophy +"""Compute Energy and Enstrophy from velocity and vorticity + + +See :ref:`energy_enstrophy`. """ from hysop.operator.discrete.energy_enstrophy import EnergyEnstrophy as DEE from hysop.operator.computational import Computational @@ -10,78 +11,71 @@ from hysop.operator.continuous import opsetup class EnergyEnstrophy(Computational): """ - Computes enstrophy and the kinetic energy - \f{eqnarray*} - enstrophy = \frac{1}{\Omega}\int_\Omega \omega^2 d\Omega - \f} with \f$\Omega\f$ the volume or surface of the physical domain - \f$\omega\f$ the vorticity and - \f{eqnarray*} - energy = \frac{1}{2\Omega}\int_\Omega v^2 d\Omega - \f} + Computes enstrophy and kinetic energy """ def __init__(self, velocity, vorticity, is_normalized=True, **kwds): """ - Constructor. - @param velocity field - @param vorticity field - @param isNormalized : boolean indicating whether the enstrophy - and energy values have to be normalized by the domain lengths. - Default file name = 'energy_enstrophy.dat' - See hysop.tools.io_utils.Writer for details + Parameters + ---------- + velocity, vorticity : :class:`~hysop.operator.field.continuous.Field` + velocity and vorticity fields + isNormalized : boolean + true if enstrophy and energy values have to be normalized + by the domain lengths. + + Notes + ----- + By default, values at each time step are appended + to energy_enstrophy file. See :ref:`operators_io` + for more details. """ assert 'variables' not in kwds, 'variables parameter is useless.' super(EnergyEnstrophy, self).__init__(variables=[velocity, vorticity], **kwds) - ## velocity field + # velocity field self.velocity = velocity - ## vorticity field + # vorticity field self.vorticity = vorticity - ## are the energy end enstrophy values normalized by domain lengths ? + # are the energy end enstrophy values normalized by domain lengths ? self.is_normalized = is_normalized - ## self._buffer_1 = 0. - ## self._buffer_2 = 0. + # self._buffer_1 = 0. + # self._buffer_2 = 0. self.input = [velocity, vorticity] self.output = [] def get_work_properties(self): - if not self._is_discretized: - msg = 'The operator must be discretized ' - msg += 'before any call to this function.' - raise RuntimeError(msg) + super(EnergyEnstrophy, self).get_work_properties() vd = self.discreteFields[self.velocity] wd = self.discreteFields[self.vorticity] v_ind = vd.topology.mesh.compute_index w_ind = wd.topology.mesh.compute_index - shape_v = vd[0][v_ind].shape - shape_w = wd[0][w_ind].shape - if shape_v == shape_w: - return {'rwork': [shape_v], 'iwork': None} - else: - return {'rwork': [shape_v, shape_w], 'iwork': None} + size_v = vd[0][v_ind].size + size_w = wd[0][w_ind].size + size_work = max(size_v, size_w) + # work array is a flat array, shared between energy and enstrophy + return {'rwork': [size_work], 'iwork': None} @opsetup def setup(self, rwork=None, iwork=None): if not self._is_uptodate: self.discrete_op = DEE(self.discreteFields[self.velocity], - self.discreteFields[self.vorticity], - self.is_normalized, - rwork=rwork) + self.discreteFields[self.vorticity], + self.is_normalized, + rwork=rwork) # Output setup self._set_io('energy_enstrophy', (1, 3)) - self.discrete_op.setWriter(self._writer) + self.discrete_op.set_writer(self._writer) self._is_uptodate = True def energy(self): - """ - Return last computed value of the energy + """Return last computed value of the energy """ return self.discrete_op.energy def enstrophy(self): - """ - Return last computed value of the enstrophy + """Return last computed value of the enstrophy """ return self.discrete_op.enstrophy diff --git a/hysop/operator/monitoringPoints.py b/hysop/operator/monitoringPoints.py index d67adc99fc418be061980f985aca2f1561d02f7c..4626d22899dea2b613d3f8f5fa59dd52af3eab7a 100644 --- a/hysop/operator/monitoringPoints.py +++ b/hysop/operator/monitoringPoints.py @@ -63,6 +63,6 @@ class MonitoringPoints(Computational): self.monitPt_coords, rwork=rwork) # Output setup self._set_io('monit', (1, 3)) - self.discrete_op.setWriter(self._writer) + self.discrete_op.set_writer(self._writer) self._is_uptodate = True diff --git a/hysop/operator/poisson.py b/hysop/operator/poisson.py index 5151d2380bb00321dd18aac451c85f0322a21b56..324ed1168d074be0c8756d0d46475c910af00daa 100644 --- a/hysop/operator/poisson.py +++ b/hysop/operator/poisson.py @@ -9,6 +9,7 @@ from hysop.operator.reprojection import Reprojection from hysop.methods_keys import SpaceDiscretisation, Formulation from hysop.operator.continuous import opsetup import hysop.default_methods as default +from hysop import __FFTW_ENABLED__ class Poisson(Computational): @@ -83,7 +84,7 @@ class Poisson(Computational): def discretize(self): # Poisson solver based on fftw - if self.method[SpaceDiscretisation] is 'fftw': + if self.method[SpaceDiscretisation] is 'fftw' and __FFTW_ENABLED__: super(Poisson, self)._fftw_discretize() if self.withCorrection: toporef = self.discreteFields[self.output_field].topology @@ -102,7 +103,7 @@ class Poisson(Computational): **self._config) self.projection.discretize() else: - raise AttributeError("Method not yet implemented.") + raise AttributeError("Method not implemented.") @debug @opsetup @@ -131,3 +132,6 @@ class Poisson(Computational): formulation=self.formulation) self._is_uptodate = True + + def get_work_properties(self): + return {'rwork': None, 'iwork': None} diff --git a/hysop/operator/profiles.py b/hysop/operator/profiles.py index ac7dfbdfe8486f5a6b0695f48dade759752a87b6..3f74bbb2949f5415c8120e1b016876df0470421d 100644 --- a/hysop/operator/profiles.py +++ b/hysop/operator/profiles.py @@ -70,6 +70,6 @@ class Profiles(Computational): rwork=rwork) # Output setup self._set_io('profile', (1, 9)) - self.discrete_op.setWriter(self._writer) + self.discrete_op.set_writer(self._writer) self._is_uptodate = True diff --git a/hysop/operator/reprojection.py b/hysop/operator/reprojection.py index ded88362032a68c7d6bb576cb926316b65d7ee7f..d252314a26d80063fbff29a7aa76e18e9e1546b2 100644 --- a/hysop/operator/reprojection.py +++ b/hysop/operator/reprojection.py @@ -43,7 +43,7 @@ class Reprojection(Computational): self.frequency, rwork=rwork, method=self.method) self._set_io('reprojection', (1, 4)) - self.discrete_op.setWriter(self._writer) + self.discrete_op.set_writer(self._writer) self._is_uptodate = True def do_projection(self, ite): diff --git a/hysop/operator/residual.py b/hysop/operator/residual.py index 077f52632b343d85a6f096231bcf7aac7332115e..3eeee3530c037548dd0f3b6fe7eb73cf29ac2eb5 100644 --- a/hysop/operator/residual.py +++ b/hysop/operator/residual.py @@ -48,6 +48,6 @@ class Residual(Computational): self.discrete_op.initialize_vortPrev() # Output setup self._set_io('residual', (1, 3)) - self.discrete_op.setWriter(self._writer) + self.discrete_op.set_writer(self._writer) self._is_uptodate = True diff --git a/hysop/operator/stretching.py b/hysop/operator/stretching.py index 4b183ac67e907c3f5086649baffab1ecdf774b10..7c58412dc859bdc732da4c2f4f6c4a97c948c22e 100755 --- a/hysop/operator/stretching.py +++ b/hysop/operator/stretching.py @@ -70,58 +70,26 @@ class Stretching(Computational): self.output = [self.vorticity] def get_work_properties(self): - """ - Get properties of internal work arrays. Must be call after discretize - but before setup. - - Returns - ------- - dictionnary - keys = 'rwork' and 'iwork', values = list of shapes of internal - arrays required by this operator (real arrays for rwork, integer - arrays for iwork). - - Example - ------- - - >> works_prop = op.get_work_properties() - >> print works_prop - {'rwork': [(12, 12), (45, 12, 33)], 'iwork': None} - - means that the operator requires two real arrays of - shape (12,12) and (45, 12, 33) and no integer arrays - - """ - if not self._is_discretized: - msg = 'The operator must be discretized ' - msg += 'before any call to this function.' - raise RuntimeError(msg) + super(Stretching, self).get_work_properties() # Get fields local shape. vd = self.discreteFields[self.velocity] - shape_v = vd[0][...].shape # Collect info from numerical methods # --> time-integrator required work space ti = self.method[TimeIntegrator] - rwork_length = ti.getWorkLengths(3) + topo = vd.topology + nbc = self.velocity.nb_components + res = ti.get_work_properties(nbc, topo) # ---> differential operator work space if self.formulation is GradUW: - rwork_length += diff_op.GradVxW.get_work_length() + dop = diff_op.GradVxW elif self.formulation is Conservative: - rwork_length += diff_op.DivWV.get_work_length() - res = {'rwork': [], 'iwork': None} - # Set rwork shapes - for _ in xrange(rwork_length): - res['rwork'].append(shape_v) + dop = diff_op.DivWV + res['rwork'] += dop.get_work_properties(topo)['rwork'] return res @debug def discretize(self): - if self.method[SpaceDiscretisation] is FDC4: - nbghosts = 2 - else: - raise ValueError("Unknown method for space discretization of the\ - stretching operator.") - + nbghosts = self.method[SpaceDiscretisation].ghosts_layer_size super(Stretching, self)._standard_discretize(nbghosts) @debug @@ -142,7 +110,6 @@ class StretchingLinearized(Stretching): (\om_b \cdot \nabla)u \f} """ - @debug def __init__(self, velocity_BF, vorticity_BF, **kwds): """ @@ -151,13 +118,12 @@ class StretchingLinearized(Stretching): velocity_BF, vorticity_BF : base flow fields : class:`~hysop.fields.continuous.Field` **kwds : extra parameters for base class - + Notes ----- * The default formulation is the 'Conservative' one. * The default solving method is finite differences, 4th order, in space and Runge-Kutta 3 in time. - """ super(StretchingLinearized, self).__init__(**kwds) self.variables[velocity_BF] = None @@ -167,7 +133,6 @@ class StretchingLinearized(Stretching): self.velocity_BF = velocity_BF # Base flow vorticity variable (vector) self.vorticity_BF = vorticity_BF - # Usual stretching operator to compute the # first term of the linearized rhs: (w'.grad)ub self.usual_stretch = Stretching(self.velocity_BF, self.vorticity, @@ -175,18 +140,13 @@ class StretchingLinearized(Stretching): self.input.append(self.velocity_BF) self.input.append(self.vorticity_BF) - + @debug def discretize(self): - if self.method[SpaceDiscretisation] is FDC4: - nbghosts = 2 - else: - raise ValueError("Unknown method for space discretization of the\ - stretching operator.") + nbghosts = self.method[SpaceDiscretisation].ghosts_layer_size self.usual_stretch.discretize() super(StretchingLinearized, self)._standard_discretize(nbghosts) - @debug @opsetup def setup(self, rwork=None, iwork=None): @@ -198,11 +158,9 @@ class StretchingLinearized(Stretching): method_lin = self.method.copy() method_lin[TimeIntegrator] = Euler self.discrete_op =\ - StretchLinD(velocity=self.discreteFields[self.velocity], - vorticity=self.discreteFields[self.vorticity], - vorticity_BF=self.discreteFields[self.vorticity_BF], - usual_op=self.usual_stretch.discrete_op, - method=method_lin, rwork=rwork, iwork=iwork) + SLD(velocity=self.discreteFields[self.velocity], + vorticity=self.discreteFields[self.vorticity], + vorticity_BF=self.discreteFields[self.vorticity_BF], + usual_op=self.usual_stretch.discrete_op, + method=method_lin, rwork=rwork, iwork=iwork) self._is_uptodate = True - - diff --git a/hysop/operator/velocity_correction.py b/hysop/operator/velocity_correction.py index 088eabd876bf26f84df9e8304f84fba237feb5a2..7cdd7e1c4676984390f22acbecd21f94576b0419 100755 --- a/hysop/operator/velocity_correction.py +++ b/hysop/operator/velocity_correction.py @@ -67,7 +67,7 @@ class VelocityCorrection(Computational): iwork=iwork) # Output setup self._set_io('velocity_correction', (1, 2 + self.domain.dimension)) - self.discrete_op.setWriter(self._writer) + self.discrete_op.set_writer(self._writer) self._is_uptodate = True def computeCorrection(self): diff --git a/hysop/operators.py b/hysop/operators.py new file mode 100644 index 0000000000000000000000000000000000000000..9dfb7a2ebdacc4e8c8f664cf85540036ed9c2753 --- /dev/null +++ b/hysop/operators.py @@ -0,0 +1,29 @@ +"""Shortcuts for operators import + +Allows things like: + +from hysop.operators import Advection +""" +import hysop.operator.stretching +import hysop.operator.differential +import hysop.operator.analytic +import hysop.operator.penalization +import hysop.operator.drag_and_lift +import hysop.operator.hdf_io +import hysop.operator.poisson +import hysop.operator.advection +import hysop.operator.energy_enstrophy +Stretching = hysop.operator.stretching.Stretching +Curl = hysop.operator.differential.Curl +Grad = hysop.operator.differential.Grad +DivAdvection = hysop.operator.differential.DivAdvection +Analytic = hysop.operator.analytic.Analytic +Penalization = hysop.operator.penalization.Penalization +PenalizeVorticity = hysop.operator.penalization.PenalizeVorticity +MomentumForces = hysop.operator.drag_and_lift.MomentumForces +NocaForces = hysop.operator.drag_and_lift.NocaForces +HDF_Reader = hysop.operator.hdf_io.HDF_Reader +HDF_Writer = hysop.operator.hdf_io.HDF_Writer +Poisson = hysop.operator.poisson.Poisson +Advection = hysop.operator.advection.Advection +EnergyEnstrophy = hysop.operator.energy_enstrophy.EnergyEnstrophy diff --git a/hysop/tools/misc.py b/hysop/tools/misc.py index a065daba83f264055e67b56f316fbec716051a86..ce8c646dfba0cc91422175afe97eb75efdba1660 100644 --- a/hysop/tools/misc.py +++ b/hysop/tools/misc.py @@ -120,6 +120,8 @@ class WorkSpaceTools(object): assert len(np.where( np.asarray(wk.shape) > 1)[0]) == 1, msg2 result.append(wk.ravel()[:subsize[i]].reshape(subshape[i])) + for i in xrange(len(result)): + assert npw.arrays_share_data(result[i], work[i]) except AttributeError: # Work array has been replaced by an OpenCL Buffer @@ -231,7 +233,9 @@ class WorkSpaceTools(object): shapes = [(0,), ] * lwork for prop in properties: lp = len(prop) - shapes[:lp] = np.maximum(shapes[:lp], prop) + for i in xrange(lp): + shapes[i] = tuple(np.maximum(shapes[i], np.prod(prop[i]))) + print shapes work = [npw.zeros(shape) for shape in shapes] return work diff --git a/hysop/tools/tests/test_work_arrays.py b/hysop/tools/tests/test_work_arrays.py index dfb07f0e8331fee5286891b9a3ca82312f76ace1..e6f79b488f125435c6fc44051c7cf36959f47809 100644 --- a/hysop/tools/tests/test_work_arrays.py +++ b/hysop/tools/tests/test_work_arrays.py @@ -8,6 +8,7 @@ from hysop import Field, Box from hysop.operators import Stretching, EnergyEnstrophy, Curl from hysop.tools.misc import WorkSpaceTools import hysop.tools.numpywrappers as npw +from hysop.constants import HYSOP_REAL, HYSOP_INTEGER cos = np.cos sin = np.sin @@ -53,6 +54,70 @@ def init(): return op, simu +def test_build_work_1(): + """Build work array. Input = nb of required arrays + and a common shape for all of them. + """ + + # First case : create a work list + inshape = (12,) + work = WorkSpaceTools.check_work_array(3, inshape) + assert len(work) == 3 + for wk in work: + assert wk.shape == inshape + assert wk.dtype == HYSOP_REAL + # Second case : 'reuse' memory from a predefined work array + inshape2 = (3, 4) + work2 = WorkSpaceTools.check_work_array(2, inshape2, work) + assert len(work2) == 2 + for i in xrange(len(work2)): + assert work2[i].shape == inshape2 + # ensure that work[i] and work2[i] use the same memory + assert npw.arrays_share_data(work2[i], work[i]) + + +def test_build_work_2(): + """Build work array. Input = nb of required arrays + and list of shapes for those arrays. + """ + inshapes = [(12,), (27,), (30,)] + work = WorkSpaceTools.check_work_array(3, inshapes) + assert len(work) == 3 + for i in xrange(len(work)): + assert work[i].shape == inshapes[i] + assert work[i].dtype == HYSOP_REAL + + # Second case : 'reuse' memory from a predefined work array + inshapes2 = [(3, 2, 2), (3, 8)] + work2 = WorkSpaceTools.check_work_array(2, inshapes2, work) + assert len(work2) == 2 + for i in xrange(len(work2)): + assert work2[i].shape == inshapes2[i] + # ensure that work[i] and work2[i] use the same memory + assert npw.arrays_share_data(work2[i], work[i]) + + +def test_build_work_3(): + """Same as above but for integers + """ + inshapes = [(12,), (27,), (30,)] + work = WorkSpaceTools.check_work_array(3, inshapes, + data_type=HYSOP_INTEGER) + assert len(work) == 3 + for i in xrange(len(work)): + assert work[i].shape == inshapes[i] + assert work[i].dtype == HYSOP_INTEGER + + # Second case : 'reuse' memory from a predefined work array + inshapes2 = [(3, 2, 2), (3, 8)] + work2 = WorkSpaceTools.check_work_array(2, inshapes2, work) + assert len(work2) == 2 + for i in xrange(len(work2)): + assert work2[i].shape == inshapes2[i] + # ensure that work[i] and work2[i] use the same memory + assert npw.arrays_share_data(work2[i], work[i]) + + def test_default_work(): """Default case : each op has its own work (or no work at all) """ @@ -122,9 +187,3 @@ def test_common_work(): ll = min(lref, lw) for i in xrange(ll): assert npw.arrays_share_data(wkref[i], w[i]) - - -if __name__ == "__main__": - test_default_work() - test_no_common_work() - test_common_work()