diff --git a/CMakeLists.txt b/CMakeLists.txt index ccbee338cd6143184fad5cb0d3fb1cf21310c92b..77936e8f3bc4c8c7beaaab9567e8ab1daae9ba78 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -546,7 +546,7 @@ if(WITH_TESTS) if(NOT USE_MPI) set(WITH_MPI_TESTS "OFF") endif() - include(HySoPTests) + include(hysop_tests) endif(WITH_TESTS) # ============= Documentation setup ============= diff --git a/cmake/HySoPTests.cmake b/cmake/hysop_tests.cmake similarity index 97% rename from cmake/HySoPTests.cmake rename to cmake/hysop_tests.cmake index bf9948b98835380afc4f2e231d62c8e5fe77aef1..09f38645bebed72e8ae2487eb7417d2c40a4e983 100755 --- a/cmake/HySoPTests.cmake +++ b/cmake/hysop_tests.cmake @@ -101,7 +101,7 @@ endforeach() # Doctest are run for every line which contains '>>>' set(py_doctest_files) foreach(testdir ${py_src_dirs}) - file(GLOB testfiles hysop/${testdir}/[a-zA-Z]*.py) + file(GLOB testfiles RELATIVE ${CMAKE_SOURCE_DIR} hysop/${testdir}/[a-zA-Z]*.py) foreach(testfile ${testfiles}) file(STRINGS ${testfile} test_doctest REGEX ">>>") if(NOT "${test_doctest}" STREQUAL "") @@ -110,6 +110,7 @@ foreach(testdir ${py_src_dirs}) endforeach() endforeach() + # === Create tests from py_test_files === foreach(testfile ${py_test_files}) get_filename_component(testName ${testfile} NAME_WE) @@ -121,6 +122,7 @@ endforeach() # Add files containing doctests foreach(testfile ${py_doctest_files}) get_filename_component(testName ${testfile} NAME_WE) - add_test("doctest_${testName}" py.test -v --doctest-modules ${testfile}) + set(exename ${testDir}/${testfile}) + add_test("doctest_${testName}" py.test -v --doctest-modules ${exename}) endforeach() diff --git a/docs/sphinx/users_guide/poisson.rst b/docs/sphinx/users_guide/poisson.rst index 853c660647b86011f81ce9e79748ed15ae079dfd..edba19f18f365ddb1892755faeab3e3e1e12a7f9 100644 --- a/docs/sphinx/users_guide/poisson.rst +++ b/docs/sphinx/users_guide/poisson.rst @@ -10,7 +10,7 @@ Poisson Operator The class :class:`Poisson` is used to model and solve the following problem : .. math:: - \nabla \psi = -\omega \\ + \Delta \psi = -\omega \\ v = \nabla \times \psi with diff --git a/hysop/domain/tests/test_box.py b/hysop/domain/tests/test_box.py index 24a12f79a52c1a6222ea2fd973dce23fb8b16332..9665ae861dc081b3c4f9f3291118129bd56a801e 100755 --- a/hysop/domain/tests/test_box.py +++ b/hysop/domain/tests/test_box.py @@ -122,6 +122,8 @@ def test_topo_plane(): def test_topo_from_mesh(): + """Build a topology from a previoulsy defined mesh. + """ # e.g. for fftw dom = Box(proc_tasks=proc_tasks) from hysop import __FFTW_ENABLED__ diff --git a/hysop/fields/tests/func_for_tests.py b/hysop/fields/tests/func_for_tests.py index 3e2eaf562a1fdc3560b039510e0a6274851d4a2d..5dc679504773b72ebb1588e2ababe349358acd45 100755 --- a/hysop/fields/tests/func_for_tests.py +++ b/hysop/fields/tests/func_for_tests.py @@ -121,3 +121,17 @@ def w_TG(res, x, y, z, t): res[1][...] = - sin(x) * cos(y) * sin(z) res[2][...] = 2. * sin(x) * sin(y) * cos(z) return res + + +def v_TG_2d(res, x, y, t): + """Taylor-Green-like 2d velocity""" + res[0][...] = sin(x) * cos(y) + res[1][...] = - cos(x) * sin(y) + return res + + +# Function to compute reference vorticity +def w_TG_2d(res, x, y, t): + """Taylor-Green-like vorticity, 2d domain""" + res[0][...] = - cos(x) * sin(y) + return res diff --git a/hysop/operator/computational.py b/hysop/operator/computational.py index d02936b5a6c03fbcdf1bd21a212209a256e39a05..b2bb201b9f6400059f48f5bb0ed598054d74ab78 100755 --- a/hysop/operator/computational.py +++ b/hysop/operator/computational.py @@ -6,7 +6,6 @@ from hysop.constants import debug from hysop.operator.continuous import Operator, opapply from hysop.mpi.topology import Cartesian from hysop.tools.parameters import Discretization -from hysop.tools.profiler import profile import hysop.tools.numpywrappers as npw from hysop import __FFTW_ENABLED__ if __FFTW_ENABLED__: diff --git a/hysop/operator/curlAndDiffusion.py b/hysop/operator/curlAndDiffusion.py deleted file mode 100644 index 8b86d2bff462b80e59862a6aa7746417db7d271b..0000000000000000000000000000000000000000 --- a/hysop/operator/curlAndDiffusion.py +++ /dev/null @@ -1,79 +0,0 @@ -# -*- coding: utf-8 -*- -""" -@file diffusion.py - -Operator for diffusion problem. - -""" -from hysop.operator.continuous import Operator -try: - from hysop.f2hysop import fftw2py -except ImportError: - from hysop.fakef2py import fftw2py -from hysop.operator.discrete.diffusion_fft import DiffusionFFT -from hysop.constants import debug -from hysop.operator.continuous import opsetup - - -class CurlDiffusion(Operator): - """ - Diffusion operator - \f{eqnarray*} - \omega = Op(\omega) - \f} with : - \f{eqnarray*} - \frac{\partial \omega}{\partial t} &=& \nu\Delta\omega - \f} - """ - - @debug - def __init__(self, velocity, vorticity, **kwds): - """ - Constructor. - Create a Diffusion operator using FFT. - - @param velocity ContinuousVectorField : velocity variable. - @param vorticity ContinuousVectorField : vorticity variable. - @param viscosity : viscosity of the considered medium. - """ - super(CurlDiffusion, self).__init__(variables=[velocity, vorticity], **kwds) - self.velocity = velocity - self.vorticity = vorticity - raise ValueError("This operator is obsolete and must be reviewed.\ - Do not use it.") - - @debug - @opsetup - def setup(self): - """ - Diffusion operator discretization method. - Create a discrete Diffusion operator from given specifications. - """ - if self._comm is None: - from hysop.mpi import main_comm as comm - else: - comm = self._comm - - localres, localoffset = fftw2py.init_fftw_solver( - self.resolutions[self.vorticity], - self.domain.length, comm=comm.py2f()) - - topodims = self.resolutions[self.vorticity] / localres - print ('topodims DIFFUSION', topodims) - # variables discretization - - for v in self.variables: - topo = self.domain.getOrCreateTopology(self.domain.dimension, - self.resolutions[v], - topodims, - comm=comm) - vd = v.discretize(topo) - self.discreteFields[v] = vd - - self.discrete_op =\ - DiffusionFFT(self.discreteFields[self.velocity], - self.discreteFields[self.vorticity], - self.method, **self.config) - - self.discrete_op.setup() - self._is_uptodate = True diff --git a/hysop/operator/diffusion.py b/hysop/operator/diffusion.py index bbdf1239fe7947767626e6c5e6291028d29eeba6..a7d81be9b21302658ce74375f30820690265871a 100644 --- a/hysop/operator/diffusion.py +++ b/hysop/operator/diffusion.py @@ -6,59 +6,61 @@ See :ref:`diffusion` in HySoP user guide. """ from hysop.operator.computational import Computational -from hysop.operator.discrete.diffusion_fft import DiffusionFFT +from hysop.operator.discrete.diffusion_fft import DiffusionFFT,\ + CurlAndDiffusionFFT from hysop.constants import debug from hysop.operator.continuous import opsetup -from hysop.methods_keys import SpaceDiscretisation -import hysop.default_methods as default -from hysop import __FFTW_ENABLED__ +from hysop.methods_keys import SpaceDiscretisation, GhostUpdate +from hysop import __FFTW_ENABLED__, __GPU_ENABLED__ +if __GPU_ENABLED__: + from hysop.gpu.gpu_diffusion import GPUDiffusion class Diffusion(Computational): - """ Diffusion of a field. - - - \f{eqnarray*} - \omega = Op(\omega) - \f} with : - \f{eqnarray*} - \frac{\partial \omega}{\partial t} &=& \nu\Delta\omega - \f} + """Diffusion of a field. """ + _authorized_methods = ['fftw', 'on_gpu'] + @debug - def __init__(self, viscosity, vorticity=None, **kwds): - """ - Constructor for the diffusion operator. - @param[in,out] vorticity : vorticity field. If None, it must be passed - through variables argument - @param[in] viscosity : viscosity of the considered medium. - """ - if vorticity is not None: - super(Diffusion, self).__init__(variables=[vorticity], **kwds) - else: - super(Diffusion, self).__init__(**kwds) + def __init__(self, viscosity, vorticity, **kwds): + """Diffusion operator. + See :ref:`diffusion` in HySoP user guide. - # The only available method at the time is fftw + Parameters + ---------- + viscosity : double + constant viscosity value + vorticity : :class:`~hysop.fields.continuous.Field` + vorticity field, in/out parameter. + kwds : base class parameters. + """ + assert 'variables' not in kwds, 'variables parameter is useless.' + super(Diffusion, self).__init__(variables=[vorticity], **kwds) + msg = 'Diffusion : unknown method for space discretization' if self.method is None: + import hysop.default_methods as default self.method = default.DIFFUSION - ## input/output field, solution of the problem - if vorticity is not None: - self.vorticity = vorticity - else: - self.vorticity = self.variables.keys()[0] - ## viscosity + assert self.method[SpaceDiscretisation] in self._authorized_methods,\ + msg + msg = 'Diffusion : on_gpu resolution is not available on your system.' + if self.method[SpaceDiscretisation] is 'on_gpu': + assert __GPU_ENABLED__, msg + # input/output field, solution of the problem + self.vorticity = vorticity + # viscosity self.viscosity = viscosity - - self.kwds = kwds - + # kwds required for gpu discrete operator + self.kwds = kwds.copy() + if 'discretization' in self.kwds: + self.kwds.pop('discretization') self.input = [self.vorticity] self.output = [self.vorticity] def discretize(self): if self.method[SpaceDiscretisation] is 'fftw' and __FFTW_ENABLED__: super(Diffusion, self)._fftw_discretize() - elif self.method[SpaceDiscretisation] is 'fd': + elif self.method[SpaceDiscretisation] is 'on_gpu': super(Diffusion, self)._standard_discretize() else: raise AttributeError("Method not yet implemented.") @@ -67,18 +69,75 @@ class Diffusion(Computational): @opsetup def setup(self, rwork=None, iwork=None): if self.method[SpaceDiscretisation] is 'fftw': - self.discrete_op = DiffusionFFT( - self.discreteFields[self.vorticity], self.viscosity, - method=self.method) - elif self.method[SpaceDiscretisation] is 'fd': - from hysop.gpu.gpu_diffusion import GPUDiffusion - kw = self.kwds.copy() - if 'discretization' in kw.keys(): - kw.pop('discretization') - self.discrete_op = GPUDiffusion( - self.discreteFields[self.vorticity], - viscosity=self.viscosity, - **kw) + dop = DiffusionFFT + elif self.method[SpaceDiscretisation] is 'on_gpu': + dop = GPUDiffusion + + self.discrete_op = dop( + viscosity=self.viscosity, + vorticity=self.discreteFields[self.vorticity], + method=self.method, **self.kwds) + self._is_uptodate = True + + def get_work_properties(self): + return {'rwork': None, 'iwork': None} + + +class CurlAndDiffusion(Computational): + """Solve curl of velocity and its diffusion in one shot + (in Fourier domain). + """ + + _authorized_methods = ['fftw'] + + @debug + def __init__(self, viscosity, velocity, vorticity, **kwds): + """Diffusion operator. + See :ref:`diffusion` in HySoP user guide. + + Parameters + ---------- + viscosity : double + constant viscosity value + velocity : :class:`~hysop.fields.continuous.Field + vorticity field, in/out parameter. + vorticity : :class:`~hysop.fields.continuous.Field + vorticity field, in/out parameter. + kwds : base class parameters. + + Notes: + * vorticity parameter is optional since the field + can be provided in the usual way for operators using + variables parameter:: + + op = Diffusion(variables={vorticity: topo}, ...) + + """ + self.viscosity = viscosity + self.velocity = velocity + self.vorticity = vorticity + msg = 'fftw required for CurlAndDiffusion. ' + msg += 'Try to recompile with WITH_FFTW=ON' + assert __FFTW_ENABLED__, msg + msg = 'CurlAndDiffusion : unknown method for space discretization' + self.method = {SpaceDiscretisation: 'fftw', GhostUpdate: True} + self.input = [self.velocity] + self.output = [self.vorticity] + assert 'variables' not in kwds, 'variables parameter is useless.' + super(CurlAndDiffusion, self).__init__( + variables=[velocity, vorticity], **kwds) + + def discretize(self): + super(CurlAndDiffusion, self)._fftw_discretize() + + @debug + @opsetup + def setup(self, rwork=None, iwork=None): + self.discrete_op = CurlAndDiffusionFFT( + viscosity=self.viscosity, + velocity=self.discreteFields[self.velocity], + vorticity=self.discreteFields[self.vorticity], + method=self.method) self._is_uptodate = True def get_work_properties(self): diff --git a/hysop/operator/discrete/curlAndDiffusion_fft.py b/hysop/operator/discrete/curlAndDiffusion_fft.py deleted file mode 100644 index d8828c7fd1e5c6769737938358f7c09c5a3352f4..0000000000000000000000000000000000000000 --- a/hysop/operator/discrete/curlAndDiffusion_fft.py +++ /dev/null @@ -1,117 +0,0 @@ -# -*- coding: utf-8 -*- -""" -@file diffusion_fft.py -Discrete Diffusion operator using FFTW (fortran) -""" -try: - from hysop.f2hysop import fftw2py -except ImportError: - from hysop.fakef2py import fftw2py -from hysop.operator.discrete.discrete import DiscreteOperator -from hysop.constants import debug -from hysop.tools.profiler import profile - - -class DiffusionFFT(DiscreteOperator): - """ - Discretized Poisson operator based on FFTW. - See details in hysop.operator.diffusion. - - """ - @debug - def __init__(self, vorticity, viscosity, method=None): - """ - Constructor. - @param[in,out] vorticity : discretisation of the field \f$ \omega \f$. - @param[in] viscosity : \f$\nu\f$, viscosity of the considered medium. - """ - - DiscreteOperator.__init__(self, [vorticity], method, - name="Diffusion FFT") - ## Velocity. - self.velocity = velocity - ## Vorticity. - self.vorticity = vorticity - ## Viscosity. - self.viscosity = viscosity - ## Boolean : pure vort diffusion or curl(u) + vort diffusion. - self.with_curl = with_curl - - @debug - @profile - def apply(self, simulation): - - if (self.vorticity.dimension == 2): - - if self.with_curl: - raise ValueError("Not yet implemented") - - else: - self.vorticity.data = \ - fftw2py.solve_diffusion_2d(self.viscosity * dt, - self.vorticity.data) - - elif (self.vorticity.dimension == 3): - - if self.with_curl: - self.vorticity.data[0], self.vorticity.data[1],\ - self.vorticity.data[2] = \ - fftw2py.solve_curl_diffusion_3d(self.viscosity * dt, - self.velocity.data[0], - self.velocity.data[1], - self.velocity.data[2], - self.vorticity.data[0], - self.vorticity.data[1], - self.vorticity.data[2]) - - else: - self.vorticity.data[0], self.vorticity.data[1],\ - self.vorticity.data[2] = \ - fftw2py.solve_diffusion_3d(self.viscosity * dt, - self.vorticity.data[0], - self.vorticity.data[1], - self.vorticity.data[2]) - - else: - raise ValueError("invalid problem dimension") -# ind0a = self.topology.mesh.local_start[0] -# ind0b = self.topology.mesh.local_end[0] + 1 -# ind1a = self.topology.mesh.local_start[1] -# ind1b = self.topology.mesh.local_end[1] + 1 -# ind2a = self.topology.mesh.local_start[2] -# ind2b = self.topology.mesh.local_end[2] + 1 - -# vorticityNoG = [npw.zeros((self.resolution - 2 * self.ghosts)) -# for d in xrange(self.dim)] -# velocityNoG = [nwp.zeros((self.resolution - 2 * self.ghosts)) -# for d in xrange(self.dim)] -# for i in xrange(self.dim): -# vorticityNoG[i][...] = self.vorticity[i][ind0a:ind0b, -# ind1a:ind1b, ind2a:ind2b] -# velocityNoG[i][...] = self.velocity[i][ind0a:ind0b, -# ind1a:ind1b, ind2a:ind2b] - -# # Curl + Vorticity diffusion -## vorticityNoG[0][...], vorticityNoG[1][...], vorticityNoG[2][...] = \ -## fftw2py.solve_curl_diffusion_3d(self.viscosity * dt, -## velocityNoG[0][...], -## velocityNoG[1][...], -## velocityNoG[2][...], -## vorticityNoG[0][...], -## vorticityNoG[1][...], -## vorticityNoG[2][...]) - -# # Pure Vorticity diffusion -# vorticityNoG[0][...], vorticityNoG[1][...], vorticityNoG[2][...] = \ -# fftw2py.solve_diffusion_3d(self.viscosity * dt, -# vorticityNoG[0][...], -# vorticityNoG[1][...], -# vorticityNoG[2][...]) - -# for i in xrange(self.dim): -# self.vorticity[i][ind0a:ind0b, ind1a:ind1b, ind2a:ind2b] = \ -# vorticityNoG[i][...] - - def __str__(self): - s = "Diffusion_d (DiscreteOperator). " + DiscreteOperator.__str__(self) - return s diff --git a/hysop/operator/discrete/diffusion_fft.py b/hysop/operator/discrete/diffusion_fft.py index f93ce20d36bd9e26af9ebc1ea8e82e14d32229cd..461a74c78e9c2f48924f01f7d6bb202df531b873 100644 --- a/hysop/operator/discrete/diffusion_fft.py +++ b/hysop/operator/discrete/diffusion_fft.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- """Discrete Diffusion operator using FFTW (fortran) + +See :ref:`diffusion` in HySoP user guide. + """ try: from hysop.f2hysop import fftw2py @@ -19,14 +22,15 @@ class DiffusionFFT(DiscreteOperator): """ @debug - def __init__(self, vorticity, viscosity, **kwds): + def __init__(self, viscosity, vorticity, **kwds): """Discrete diffusion operator, based on fftw solver. Parameters ---------- + viscosity : double + constant viscosity value vorticity : :class:`~hysop.fields.discrete.DiscreteField` vorticity field, in/out parameter - viscosity : double kwds : base class arguments """ # Discretisation of the solution field @@ -79,3 +83,52 @@ class DiffusionFFT(DiscreteOperator): # TODO : fix bug that occurs when several finalize # of fft operators are called. # fftw2py.clean_fftw_solver(self.vorticity.dimension) + + +class CurlAndDiffusionFFT(DiscreteOperator): + """Discretized Curl/Diffusion operator based on FFTW. + See details in hysop.operator.diffusion. + + """ + @debug + def __init__(self, viscosity, velocity, vorticity, **kwds): + """Solve diffusion problem in Fourier + domain (velocity curl + diffusion in one shot) + + Parameters + ---------- + viscosity : double + constant viscosity value + velocity : :class:`~hysop.fields.discrete.DiscreteField` + velocity field, input parameter + vorticity : :class:`~hysop.fields.discrete.DiscreteField` + velocity field, output parameter + kwds : base class arguments (vorticity, viscosity ...) + """ + self.velocity = velocity + self.vorticity = vorticity + self.viscosity = viscosity + assert 'variables' not in kwds, 'variables parameter is useless.' + super(CurlAndDiffusionFFT, self).__init__( + variables=[velocity, vorticity], **kwds) + self.input = [self.velocity] + self.output = [self.vorticity] + msge = 'CurlAndDiffusion: only implemented for 3d domain.' + assert self.velocity.dimension == 3, msge + + @debug + @profile + def apply(self, simulation=None): + dt = simulation.time_step + gh_velo = self.velocity.topology.ghosts() + gh_vorti = self.vorticity.topology.ghosts() + self.vorticity.data[0], self.vorticity.data[1],\ + self.vorticity.data[2] = \ + fftw2py.solve_curl_diffusion_3d(self.viscosity * dt, + self.velocity.data[0], + self.velocity.data[1], + self.velocity.data[2], + self.vorticity.data[0], + self.vorticity.data[1], + self.vorticity.data[2], + gh_velo, gh_vorti) diff --git a/hysop/operator/discrete/poisson_fft.py b/hysop/operator/discrete/poisson_fft.py index c26dfb106163d04e8fba4ea1770f0f5f48abaac7..5d3a0fb8f011aae5249ba01b73192cd8dccbe0d3 100644 --- a/hysop/operator/discrete/poisson_fft.py +++ b/hysop/operator/discrete/poisson_fft.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- """Discrete operator for Poisson problem (fftw based) + +.. currentmodule hysop.operator.discrete + """ import hysop.tools.numpywrappers as npw @@ -7,35 +10,39 @@ from hysop.operator.discrete.discrete import DiscreteOperator from hysop.operator.discrete.reprojection import Reprojection from hysop.constants import debug from hysop.tools.profiler import profile -from hysop import __FFTW_ENABLED__ - -if __FFTW_ENABLED__: +try: from hysop.f2hysop import fftw2py +except ImportError: + msg = 'fftw package not available for your hysop install.' + msg += 'Try to recompile with WITH_FFTW=ON' + raise ImportError(msg) class PoissonFFT(DiscreteOperator): - """ - Discretized Poisson operator based on FFTW. + """Discretized Poisson operator based on FFTW. See details in hysop.operator.poisson """ @debug def __init__(self, output_field, input_field, projection=None, - filterSize=None, correction=None, formulation=None, **kwds): - """ - Constructor. - @param[out] output_field : discretization of the solution field - @param[in] input_field : discretization of the RHS (mind the minus rhs!) - @param projection : if None, no projection. Else: - - either the value of the frequency of reprojection, never updated. - - or Reprojection discrete operator. In that case, a criterion - depending on the input_field will be computed at each time step, if - criterion > threshold, then frequency projection is active. - @param filterSize : - @param correction : operator used to shift output_field according - to a given input (fixed) flowrate. - See hysop.operator.velocity_correction. - Default = None. + filter_size=None, correction=None, formulation=None, **kwds): + """Poisson operator, incompressible flow. + + Parameters + ---------- + output_field : :class:`~hysop.fields.discrete.DiscreteField + solution field + input_field : :class:`~hysop.fields.discrete.DiscreteField + right-hand side + projection : double or tuple, optional + projection criterion, see notes below. + filter_size : + correction : :class:`~velocity_correction.VelocityCorrection_D + operator used to shift output_field according + to a given input (fixed) flowrate. + See hysop.operator.velocity_correction. + formulation : + kwds : base class parameters. """ # Base class initialisation assert 'variables' not in kwds, 'variables parameter is useless.' @@ -48,11 +55,13 @@ class PoissonFFT(DiscreteOperator): # Solenoidal projection of input_field ? self.projection = projection # Filter size array = domainLength/(CoarseRes-1) - self.filterSize = filterSize + self.filter_size = filter_size # If 2D problem, input_field must be a scalar - self.dim = self.output_field.domain.dimension - if self.dim == 2: + self._dim = self.output_field.domain.dimension + if self._dim == 2: assert self.input_field.nb_components == 1 + assert (self._dim >= 2),\ + "Wrong problem dimension: only 2D and 3D cases are implemented." self.correction = correction self.formulation = formulation self.input = [self.input_field] @@ -66,26 +75,24 @@ class PoissonFFT(DiscreteOperator): self._select_solve() def _select_solve(self): - """ TODO : add pressure solver selection f(output.nbComponents) + pb type 'pressure-poisson' """ - - # Multiresolution ? - multires = self.output_field.topology.mesh != self.input_field.topology.mesh - + # Multiresolution? + multires = \ + self.output_field.topology.mesh != self.input_field.topology.mesh # connexion to the required apply function - if self.dim == 2: - self._solve = self._solve2D - elif self.dim == 3: + if self._dim == 2: + self._solve = self._solve_2d + elif self._dim == 3: # If there is a projection, input_field is also an output if self.projection is not None: self.output.append(self.input_field) if multires: - self._solve = self._solve3D_proj_multires + self._solve = self._solve_3d_proj_multires else: - self._solve = self._solve3D_proj + self._solve = self._solve_3d_proj if isinstance(self.projection, Reprojection): self.do_projection = self.do_projection_with_op @@ -94,42 +101,34 @@ class PoissonFFT(DiscreteOperator): else: if multires: - self._solve = self._solve3D_multires + self._solve = self._solve_3d_multires elif self.formulation is not None: self._solve = self._solve_3d_scalar_fd else: - self._solve = self._solve3D - else: - raise AttributeError('Not implemented for 1D problems.') + self._solve = self._solve_3d - # Operator to shift output_field according to an input required flowrate + # swtich to the proper solving method (with or without correction) if self.correction is not None: self.solve = self._solve_and_correct else: self.solve = self._solve - - if not __FFTW_ENABLED__: - self.solve = self._solve_error - - def _solve_error(): - """Error message when fftw fortran interface is not available. - """ - msg = "Warning: fftw fortran interface is not available," - msg += "the fft solver does nothing." - raise NotImplementedError('msg') def do_projection_with_op(self, simu): + """Compute projection criterion and return + true if projection is needed. + """ self.projection.apply(simu) ite = simu.current_iteration return self.projection.do_projection(ite) def do_projection_no_op(self, simu): + """return true if projection is needed. + """ ite = simu.current_iteration return ite % self.projection == 0 - def _solve2D(self, simu=None): - """ - Solve 2D poisson problem + def _solve_2d(self, simu=None): + """ Solve 2D poisson problem, no projection, no correction. """ ghosts_v = self.output_field.topology.ghosts() ghosts_w = self.input_field.topology.ghosts() @@ -140,75 +139,75 @@ class PoissonFFT(DiscreteOperator): ghosts_w, ghosts_v) def _project(self): - """ - apply projection onto input_field + """apply projection onto input_field """ ghosts_w = self.input_field.topology.ghosts() self.input_field.data[0], self.input_field.data[1], \ self.input_field.data[2] = \ - fftw2py.projection_om_3d(self.input_field.data[0], - self.input_field.data[1], - self.input_field.data[2], ghosts_w) + fftw2py.projection_om_3d(self.input_field.data[0], + self.input_field.data[1], + self.input_field.data[2], ghosts_w) - def _solve3D_multires(self, simu=None): - """ - 3D, multiresolution + def _solve_3d_multires(self, simu=None): + """3D, multiresolution """ # Projects input_field values from fine to coarse grid # in frequencies space by nullifying the smallest modes vortFilter = npw.copy(self.input_field.data) vortFilter[0], vortFilter[1], vortFilter[2] = \ - fftw2py.multires_om_3d(self.filterSize[0], self.filterSize[1], - self.filterSize[2], self.input_field.data[0], - self.input_field.data[1], - self.input_field.data[2]) + fftw2py.multires_om_3d(self.filter_size[0], self.filter_size[1], + self.filter_size[2], + self.input_field.data[0], + self.input_field.data[1], + self.input_field.data[2]) # Solves Poisson equation using filter input_field ghosts_v = self.output_field.topology.ghosts() ghosts_w = self.input_field.topology.ghosts() - self.output_field.data[0], self.output_field.data[1], self.output_field.data[2] = \ + self.output_field.data[0], self.output_field.data[1],\ + self.output_field.data[2] = \ fftw2py.solve_poisson_3d(vortFilter[0], vortFilter[1], vortFilter[2], self.output_field.data[0], self.output_field.data[1], self.output_field.data[2], ghosts_w, ghosts_v) - def _solve3D_proj_multires(self, simu): - """ - 3D, multiresolution, with projection + def _solve_3d_proj_multires(self, simu): + """3D, multiresolution, with projection """ if self.do_projection(simu): self._project() - self._solve3D_multires() + self._solve_3d_multires() - def _solve3D_proj(self, simu): - """ - 3D, with projection + def _solve_3d_proj(self, simu): + """3D, with projection """ if self.do_projection(simu): self._project() - self._solve3D() + self._solve_3d() - def _solve3D(self,simu=None): - """ - Basic solve + def _solve_3d(self,simu=None): + """Basic solve """ # Solves Poisson equation using usual input_field ghosts_v = self.output_field.topology.ghosts() ghosts_w = self.input_field.topology.ghosts() - self.output_field.data[0], self.output_field.data[1], self.output_field.data[2] =\ + self.output_field.data[0], self.output_field.data[1],\ + self.output_field.data[2] =\ fftw2py.solve_poisson_3d(self.input_field.data[0], self.input_field.data[1], self.input_field.data[2], self.output_field.data[0], self.output_field.data[1], - self.output_field.data[2], ghosts_w, ghosts_v) + self.output_field.data[2], + ghosts_w, ghosts_v) def _solve_and_correct(self, simu): + """Solve Poisson problem and apply correction on velocity. + """ self._solve(simu.current_iteration) self.correction.apply(simu) - def _solve_3d_scalar_fd(self, simu=None): """solve poisson-pressure like problem input = 3D vector field @@ -222,7 +221,7 @@ class PoissonFFT(DiscreteOperator): ghosts = self.output_field.topology.ghosts() self.output_field.data[0] = fftw2py.pressure_3d( self.input_field.data[0], ghosts) - + def _solve_3d_scalar(self, simu=None): """solve poisson-pressure like problem input = 3D vector field diff --git a/hysop/operator/discrete/reprojection.py b/hysop/operator/discrete/reprojection.py index 86506809712df2b94b312f4d3e7aa85d334a86a7..0e680bb4bff94768f7736da23157d00e52f89189 100644 --- a/hysop/operator/discrete/reprojection.py +++ b/hysop/operator/discrete/reprojection.py @@ -1,10 +1,8 @@ # -*- coding: utf-8 -*- -""" -@file reprojection.py -Compute reprojection criterion and divergence maximum +"""Compute reprojection criterion and divergence maximum """ import numpy as np -from hysop.constants import debug, HYSOP_MPI_REAL +from hysop.constants import debug from hysop.methods_keys import SpaceDiscretisation from hysop.operator.discrete.discrete import DiscreteOperator from hysop.numerics.finite_differences import FDC4 @@ -16,37 +14,40 @@ from hysop.tools.profiler import profile class Reprojection(DiscreteOperator): - """ - Update the reprojection frequency, according to the current + """Update the reprojection frequency, according to the current value of the vorticity field. """ def __init__(self, vorticity, threshold, frequency, **kwds): """ - Constructor. - @param vorticity: discretization of the vorticity field - @param threshold : update frequency when criterion is greater than - this threshold - @param frequency : set frequency of execution of the reprojection + + Parameters + ---------- + vorticity : :class:`~hysop.fields.discrete.DiscreteField + vorticity field + threshold : double + update frequency when criterion is greater than this threshold + frequency : double + frequency of execution of the reprojection """ + # ensure that space discretisation method is set if 'method' in kwds and kwds['method'] is None: kwds['method'] = {SpaceDiscretisation: FDC4} - ## vorticity field + # vorticity field self.vorticity = vorticity assert 'variables' not in kwds, 'variables parameter is useless.' super(Reprojection, self).__init__(variables=[vorticity], **kwds) - ## Frequency for reprojection + # Frequency for reprojection self.frequency = frequency - ## The initial value will be used as default during + # The initial value will be used as default during # simulation self._default_frequency = frequency - # constant defining the reprojection criterion : + # constant threshold defining the reprojection criterion : # if the latter is greater than this constant, then a reprojection # is needed self.threshold = threshold - # local counter self._counter = 0 - ## Numerical methods for space discretization + # Numerical methods for space discretization assert SpaceDiscretisation in self.method self.method = self.method[SpaceDiscretisation] self.input = [vorticity] @@ -123,7 +124,6 @@ class Reprojection(DiscreteOperator): self._writer.write() def do_projection(self, ite): - """ - True if projection must be done + """True if projection must be done """ return ite % self.frequency == 0 diff --git a/hysop/operator/discrete/velocity_correction.py b/hysop/operator/discrete/velocity_correction.py index c09104af5f9a0f59a617f738bada36ef3e5f3a1a..8a187025207e3a97bc8c4a4db83f1202d5779b49 100755 --- a/hysop/operator/discrete/velocity_correction.py +++ b/hysop/operator/discrete/velocity_correction.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -""" -@file operator/discrete/velocity_correction.py +"""Operator used to shift velocity value + to fit with a required input flowrate. -Correction of the velocity field. -""" +Check details in :ref:`velocity_correction` in HySoP user guide. + +""" from hysop.constants import debug from hysop.operator.discrete.discrete import DiscreteOperator from hysop.fields.variable_parameter import VariableParameter @@ -14,44 +15,52 @@ from hysop.constants import XDIR, YDIR, ZDIR class VelocityCorrection_D(DiscreteOperator): - """ - The velocity field is corrected after solving the + """The velocity field is corrected after solving the Poisson equation. For more details about calculations, - see the "velocity_correction.pdf" explanations document - in Docs. + see :ref:`velocity_correction` in HySoP user guide. """ @debug def __init__(self, velocity, vorticity, req_flowrate, cb, **kwds): - """ - @param[in, out] velocity field to be corrected - @param[in] vorticity field used to compute correction - @param[in] req_flowrate : required value for the - flowrate (VariableParameter object) - @param[in] surf : surface (hysop.domain.obstacle.planes.SubPlane) - used to compute reference flow rates. Default = surface at x_origin, - normal to x-dir. + """Update velocity field (solution of Poisson equation) + in order to prescribe proper mean flow and ensure + the desired inlet flowrate. + + Parameters + ---------- + velocity : :class:`~hysop.fields.discrete.DiscreteField` + in/out velocity vector field. + vorticity : :class:`~hysop.fields.discrete.DiscreteField` + input vorticity vector field. + req_flowrate : a + :class:`~hysop.fields.variable_parameter.VariableParameter` + the desired inlet flowrate value + cb : :class:`~hysop.domain.control_box.ControlBox` + volume of control used to compute a reference for correction. + kwds : base class parameters """ assert 'variables' not in kwds, 'variables parameter is useless.' super(VelocityCorrection_D, self).__init__( variables=[velocity, vorticity], **kwds) - ## velocity discrete field + # velocity discrete field self.velocity = velocity - ## vorticity discrete field + # vorticity discrete field self.vorticity = vorticity - ## domain dimension - self.dim = self.velocity.domain.dimension # If 2D problem, vorticity must be a scalar - if self.dim == 2: + if self._dim == 2: assert self.vorticity.nb_components == 1 - assert (self.dim >= 2),\ + self._update_velocity_components = self._update_velocity_2d + elif self._dim == 3: + self._update_velocity_components = self._update_velocity_3d + + assert (self._dim >= 2),\ "Wrong problem dimension: only 2D and 3D cases are implemented." self.input = self.variables self.output = [self.velocity] - ## A reference topology + # A reference topology self.topo = self.velocity.topology - ## Volume of control + # Volume of control self.cb = cb self.cb.discretize(self.topo) # A reference surface, i.e. input surface for flow in x direction @@ -62,13 +71,13 @@ class VelocityCorrection_D(DiscreteOperator): cb_length = self.cb.real_length[self.topo] self._inv_ds = 1. / npw.prod(cb_length[sdirs]) self._inv_dvol = 1. / npw.prod(cb_length) - ## Expected value for the flow rate through self.surfRef + # Expected value for the flow rate through self.surfRef self.req_flowrate = req_flowrate assert isinstance(self.req_flowrate, VariableParameter),\ "the required flowrate must be a VariableParameter object." - ## The correction that must be applied on each - ## component of the velocity. - self.velocity_shift = npw.zeros(self.dim) + # The correction that must be applied on each + # component of the velocity. + self.velocity_shift = npw.zeros(self._dim) nbf = self.velocity.nb_components + self.vorticity.nb_components # temp buffer, used to save flow rates and mean # values of vorticity @@ -83,20 +92,19 @@ class VelocityCorrection_D(DiscreteOperator): # surface for the flow. self.x_coord = self.topo.mesh.coords[XDIR] - x0 - def computeCorrection(self): - """ - Compute the required correction for the current state + def compute_correction(self): + """Compute the required correction for the current state but do not apply it onto velocity. """ - ## Computation of the flowrates evaluated from - ## current (ie non corrected) velocity + # Computation of the flowrates evaluated from + # current (ie non corrected) velocity nbf = self.velocity.nb_components + self.vorticity.nb_components localrates = npw.zeros((nbf)) for i in xrange(self.velocity.nb_components): localrates[i] = self._in_surf.integrate_dfield_on_proc( self.velocity, component=i) start = self.velocity.nb_components - ## Integrate vorticity over the whole domain + # Integrate vorticity over the whole domain for i in xrange(self.vorticity.nb_components): localrates[start + i] = self.cb.integrate_dfield_on_proc( self.vorticity, component=i) @@ -110,7 +118,7 @@ class VelocityCorrection_D(DiscreteOperator): self.rates[:start] *= self._inv_ds self.rates[start:] *= self._inv_dvol - + # Set velocity_shift == [Vx_shift, vort_mean[Y], vort_mean[Z]] # or (in 2D) velocity_shift == [Vx_shift, vort_mean] # Velocity shift for main dir component @@ -119,6 +127,20 @@ class VelocityCorrection_D(DiscreteOperator): # Shifts in other directions depend on x coord # and will be computed during apply. + def _update_velocity_2d(self, vort_mean): + """update velocity value, 2d case + """ + self.velocity[YDIR][...] += self.req_flowrate_val[YDIR] +\ + vort_mean[XDIR] * self.x_coord - self.rates[YDIR] + + def _update_velocity_3d(self, vort_mean): + """update velocity value, 3d case + """ + self.velocity[YDIR][...] += self.req_flowrate_val[YDIR] + \ + vort_mean[ZDIR] * self.x_coord - self.rates[YDIR] + self.velocity[ZDIR][...] += self.req_flowrate_val[ZDIR] - \ + vort_mean[YDIR] * self.x_coord - self.rates[ZDIR] + @debug @profile def apply(self, simulation=None): @@ -129,10 +151,10 @@ class VelocityCorrection_D(DiscreteOperator): self.req_flowrate_val = self.req_flowrate.data * self._inv_ds # Computation of the required velocity shift # for the current state - self.computeCorrection() + self.compute_correction() compute_index = self.topo.mesh.compute_index - # Apply shift to velocity + # Apply shift to velocity (x component) self.velocity[XDIR][compute_index] += self.velocity_shift[XDIR] start = self.velocity.nb_components # reminder : self.rates =[vx_shift, flowrates[Y], flowrate[Z], @@ -146,14 +168,4 @@ class VelocityCorrection_D(DiscreteOperator): self._writer.buffer[0, 2:] = vort_mean[...] self._writer.write() - if self.dim == 2: - # Correction of the Y-velocity component - self.velocity[YDIR][...] += self.req_flowrate_val[YDIR] + \ - vort_mean[XDIR] * self.x_coord - self.rates[YDIR] - - elif self.dim == 3: - # Correction of the Y and Z-velocity components - self.velocity[YDIR][...] += self.req_flowrate_val[YDIR] + \ - vort_mean[ZDIR] * self.x_coord - self.rates[YDIR] - self.velocity[ZDIR][...] += self.req_flowrate_val[ZDIR] - \ - vort_mean[YDIR] * self.x_coord - self.rates[ZDIR] + self._update_velocity_components(vort_mean) diff --git a/hysop/operator/poisson.py b/hysop/operator/poisson.py index 324ed1168d074be0c8756d0d46475c910af00daa..da9328ae7ae043546db0f2bd609c65fad210d999 100644 --- a/hysop/operator/poisson.py +++ b/hysop/operator/poisson.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- -"""Poisson problem. +"""Operator to solve Poisson problem. + +See :ref:`poisson` in HySoP user guide. """ from hysop.operator.computational import Computational from hysop.operator.discrete.poisson_fft import PoissonFFT @@ -8,61 +10,68 @@ from hysop.operator.velocity_correction import VelocityCorrection 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): + """Solve Poisson problem (in: vorticity, out velocity) + for incompressible flow. """ - \f{eqnarray*} - v = Op(\omega) - \f} with : - \f{eqnarray*} - \Delta \phi &=& -\omega \\ - v &=& \nabla \times \phi - \f} - """ + + _authorized_methods = ['fftw'] @debug def __init__(self, output_field, input_field, flowrate=None, projection=None, **kwds): - """ - Constructor for the Poisson problem. - - @param[out] output_field : solution field - @param[in] input_field : rhs field - @param[in] flowrate : a flow rate value (through input_field surf, - normal to xdir) used to compute a correction of the solution field. - Default = 0 (no correction). See hysop.operator.output_field_correction. - @param projection : if None, no projection. Else: - - either the value of the frequency of reprojection, never update. - - or a tuple = (frequency, threshold). - In that case, a criterion - depending on the input_field will be computed at each time step, if - criterion > threshold, then frequency projection is active. - - Note about method: - - SpaceDiscretisation == fftw - - Formulation = 'velocity' or 'pressure' - velocity : laplacian(phi) = -w and v = nabla X psi, in = vorticity, out = velo - pressure : laplacian(p) = -nabla.(u.nabla u, in = velo, out = pressure + """Poisson operator, incompressible flow. + + Parameters + ---------- + output_field : :class:`~hysop.fields.continuous.Field + solution field + input_field : :class:`~hysop.fields.continuous.Field` + right-hand side + flowrate: :class:`~hysop.fields.variable_parameter.VariableParameter` + or double, optional + flow rate value through input surface (normal to xdir), + used to calibrate solution, default=0. + projection : double or tuple, optional + projection criterion, see notes below. + kwds : base class parameters. + + + Notes: + * projection might be: + * None: no projection + * the value of the frequency of reprojection (constant) + * a tuple (frequency, threshold). In that case, a criterion + depending on the input_field will be computed at each time step, + and if criterion > threshold., then frequency projection is active. + * About method parameter: + - SpaceDiscretisation == fftw + - Formulation = 'velocity' or 'pressure' + velocity : laplacian(phi) = -w and v = nabla X psi, in = vorticity, out = velo + pressure : laplacian(p) = -nabla.(u.nabla u, in = velo, out = pressure """ # Warning : for fftw all variables must have # the same resolution. assert 'variables' not in kwds, 'variables parameter is useless.' super(Poisson, self).__init__(variables=[output_field, input_field], **kwds) - ## solution of the problem + # solution of the problem self.output_field = output_field - ## -(right-hand side) + # -(right-hand side) self.input_field = input_field if self.method is None: + import hysop.default_methods as default self.method = default.POISSON + if self.method[SpaceDiscretisation] is 'fftw': + assert __FFTW_ENABLED__ + msg = 'Poisson : unknown method for space discretization' + assert self.method[SpaceDiscretisation] in self._authorized_methods,\ + msg - if self.method[SpaceDiscretisation] is not 'fftw': - raise AttributeError("Method not yet implemented.") - - # Deterlination of the Poisson equation formulation : + # Set Poisson equation formulation : # Velo Poisson eq or Pressure Poisson eq self.formulation = None if self.method[Formulation] is not 'velocity': @@ -70,6 +79,7 @@ class Poisson(Computational): self.input = [self.input_field] self.output = [self.output_field] + # Enable correction if required if flowrate is not None: self.withCorrection = True self._flowrate = flowrate @@ -84,8 +94,9 @@ class Poisson(Computational): def discretize(self): # Poisson solver based on fftw - if self.method[SpaceDiscretisation] is 'fftw' and __FFTW_ENABLED__: + if self.method[SpaceDiscretisation] is 'fftw': super(Poisson, self)._fftw_discretize() + # prepare correction and projection, if needed if self.withCorrection: toporef = self.discreteFields[self.output_field].topology if 'discretization' in self._config: @@ -102,8 +113,6 @@ class Poisson(Computational): threshold, freq, **self._config) self.projection.discretize() - else: - raise AttributeError("Method not implemented.") @debug @opsetup diff --git a/hysop/operator/tests/test_diffusion.py b/hysop/operator/tests/test_diffusion.py index 5f87c4b335a991956ca4449fc206ac0e55f73eb1..effb9ebe3dcc4d9786560246ad2c53d122fdee46 100755 --- a/hysop/operator/tests/test_diffusion.py +++ b/hysop/operator/tests/test_diffusion.py @@ -1,13 +1,16 @@ +"""Tests for diffusion and curl_diffusion operators. +""" # -*- coding: utf-8 -*- import hysop as pp -from hysop.operator.diffusion import Diffusion +from hysop.operator.diffusion import Diffusion, CurlAndDiffusion from hysop.problem.simulation import Simulation from hysop.tools.parameters import Discretization import numpy as np import hysop.tools.numpywrappers as npw import math from hysop import testsenv +from hysop.fields.tests.func_for_tests import v_TG, w_TG pi = math.pi sin = np.sin cos = np.cos @@ -19,13 +22,6 @@ d3D = Discretization([33, 33, 33]) d2D = Discretization([33, 33]) -def computeVort(res, x, y, z, t): - res[0][...] = sin(x * cc[0]) * sin(y * cc[1]) * cos(z * cc[2]) - res[1][...] = cos(x * cc[0]) * sin(y * cc[1]) * sin(z * cc[2]) - res[2][...] = cos(x * cc[0]) * cos(y * cc[1]) * sin(z * cc[2]) - return res - - def computeVort2D(res, x, y, t): # todo ... res[0][...] = 4 * pi ** 2 * (cos(x * cc[0]) * sin(y * cc[1])) * \ @@ -37,44 +33,62 @@ def computeVort2D(res, x, y, t): def test_diffusion_3d(): """Vector field diffusion, 3d domain""" dom = pp.Box(length=LL) - vorticity = pp.Field(domain=dom, formula=computeVort, + vorticity = pp.Field(domain=dom, formula=w_TG, name='Vorticity', is_vector=True) diff = Diffusion(viscosity=0.3, vorticity=vorticity, discretization=d3D) diff.discretize() diff.setup() topo = diff.discreteFields[vorticity].topology - simu = Simulation(nb_iter=10) + simu = Simulation(end=0.1, time_step=0.01) vorticity.initialize(topo=topo) diff.apply(simu) diff.finalize() @testsenv.fftw_failed -def test_diffusion_3d_2(): - """Vector field diffusion, 3d domain, """ - dom = pp.Box(length=LL) - vorticity = pp.Field(domain=dom, formula=computeVort, - name='Vorticity', is_vector=True) - diff = Diffusion(viscosity=0.3, variables={vorticity: d3D}) +def test_diffusion_2d(): + """Vector field diffusion 2d domain""" + dom = pp.Box(length=LL[:2]) + vorticity = pp.Field(domain=dom, formula=computeVort2D, name='Vorticity') + diff = Diffusion(viscosity=0.3, vorticity=vorticity, discretization=d2D) diff.discretize() diff.setup() topo = diff.discreteFields[vorticity].topology - simu = Simulation(nb_iter=10) + simu = Simulation(end=0.1, time_step=0.01) vorticity.initialize(topo=topo) diff.apply(simu) diff.finalize() @testsenv.fftw_failed -def test_diffusion_2d(): - """Vector field diffusion 2d domain""" - dom = pp.Box(length=LL[:2]) - vorticity = pp.Field(domain=dom, formula=computeVort2D, name='Vorticity') - diff = Diffusion(viscosity=0.3, vorticity=vorticity, discretization=d2D) +def test_curl_and_diffusion_3d(): + """Vector field diffusion, 3d domain""" + dom = pp.Box(length=LL) + vorticity_ref = pp.Field(domain=dom, name='Wref', formula=w_TG, + is_vector=True) + vorticity = pp.Field(domain=dom, name='Vorticity', is_vector=True) + velocity = pp.Field(domain=dom, name='Velocity', formula=v_TG, + is_vector=True) + + diff_0 = Diffusion(viscosity=0.3, vorticity=vorticity_ref, + discretization=d3D) + diff_0.discretize() + diff_0.setup() + topo = diff_0.discreteFields[vorticity_ref].topology + diff = CurlAndDiffusion(viscosity=0.3, velocity=velocity, + vorticity=vorticity, discretization=d3D) diff.discretize() diff.setup() - topo = diff.discreteFields[vorticity].topology - simu = Simulation(nb_iter=10) - vorticity.initialize(topo=topo) + + simu = Simulation(end=0.1, time_step=0.01) + vorticity_ref.initialize(topo=topo) + velocity.initialize(topo=topo) + simu.initialize() + wd = vorticity.discretize(topo).data + wd_ref = vorticity_ref.discretize(topo).data + diff_0.apply(simu) diff.apply(simu) - diff.finalize() + for i in xrange(3): + assert np.allclose(wd[i], wd_ref[i]) + + diff_0.finalize() diff --git a/hysop/operator/tests/test_velocity_correction.py b/hysop/operator/tests/test_velocity_correction.py index 7c883152045cf49aac2e72ee5bb5563b646294a7..e5196bada8e0435e2b084d726feb659905caca79 100755 --- a/hysop/operator/tests/test_velocity_correction.py +++ b/hysop/operator/tests/test_velocity_correction.py @@ -1,11 +1,12 @@ -# -*- coding: utf-8 -*- - +"""Test correction operator +""" import hysop as pp from hysop.operator.velocity_correction import VelocityCorrection from hysop.problem.simulation import Simulation import numpy as np import hysop.tools.numpywrappers as npw from hysop.tools.parameters import Discretization +from hysop.fields.tests.func_for_tests import v_TG, v_TG_2d, w_TG, w_TG_2d pi = np.pi cos = np.cos sin = np.sin @@ -14,47 +15,21 @@ uinf = 1.0 tol = 1e-12 -## Function to compute TG velocity -def computeVel(res, x, y, z, t): - res[0][...] = sin(x) * cos(y) * cos(z) - res[1][...] = - cos(x) * sin(y) * cos(z) - res[2][...] = 0. - return res - - -## Function to compute reference vorticity -def computeVort(res, x, y, z, t): - res[0][...] = - cos(x) * sin(y) * sin(z) - res[1][...] = - sin(x) * cos(y) * sin(z) - res[2][...] = 2. * sin(x) * sin(y) * cos(z) - return res - - -## Function to compute TG velocity -def computeVel2D(res, x, y, t): - res[0][...] = sin(x) * cos(y) - res[1][...] = - cos(x) * sin(y) - return res - - -## Function to compute reference vorticity -def computeVort2D(res, x, y, t): - res[0][...] = - cos(x) * sin(y) - return res - -## Global resolution +# Global resolution g = 0 d2D = Discretization([33, 33], [g, g]) d3D = Discretization([33, 33, 33], [g, g, g]) -def test_velocity_correction_3D(): +def test_velocity_correction_3d(): + """Apply velocity correction, 3d domain + """ # Domain box = pp.Box(length=[2.0 * pi, pi, pi]) # Vector Fields - velo = pp.Field(domain=box, formula=computeVel, + velo = pp.Field(domain=box, formula=v_TG, name='Velocity', is_vector=True) - vorti = pp.Field(domain=box, formula=computeVort, + vorti = pp.Field(domain=box, formula=w_TG, name='Vorticity', is_vector=True) # Usual Cartesian topology definition @@ -80,17 +55,19 @@ def test_velocity_correction_3D(): assert (np.abs(flowrate - ref_rate) < tol).all() -def test_velocity_correction_2D(): - ## Domain +def test_velocity_correction_2d(): + """Apply velocity correction, 2d domain + """ + # Domain box = pp.Box(length=[2.0 * pi, pi], origin=[0., 0.]) - ## Vector Fields - velo = pp.Field(domain=box, formula=computeVel2D, + # Vector Fields + velo = pp.Field(domain=box, formula=v_TG_2d, name='Velocity', is_vector=True) - vorti = pp.Field(domain=box, formula=computeVort2D, + vorti = pp.Field(domain=box, formula=w_TG_2d, name='Vorticity', is_vector=False) - ## Usual Cartesian topology definition + # Usual Cartesian topology definition topo = box.create_topology(discretization=d2D) ref_rate = npw.zeros(2) diff --git a/hysop/operator/velocity_correction.py b/hysop/operator/velocity_correction.py index 9c863f19a7b0d8055930755eab411810a97f0222..7d3a5dc9196422b0a7c4e4a933f24e0212f9c176 100755 --- a/hysop/operator/velocity_correction.py +++ b/hysop/operator/velocity_correction.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- -""" -@file operator/velocity_correction.py +"""Operator used to shift velocity value + to fit with a required input flowrate. + +Check details in :ref:`velocity_correction` in HySoP user guide. -Operator to shift velocity to fit with a required input flowrate. """ from hysop.constants import debug @@ -13,44 +14,43 @@ from hysop.operator.continuous import opsetup class VelocityCorrection(Computational): - """ - The velocity field is corrected after solving the + """The velocity field is corrected after solving the Poisson equation. For more details about calculations, - see the "velocity_correction.pdf" explanations document - in Docs. + see :ref:`velocity_correction` in HySoP user guide. """ @debug def __init__(self, velocity, vorticity, req_flowrate, **kwds): - """ - Corrects the values of the velocity field after - solving Poisson equation in order to prescribe proper - mean flow and ensure the desired inlet flowrate. + """Update velocity field (solution of Poisson equation) + in order to prescribe proper mean flow and ensure + the desired inlet flowrate. - @param[in, out] velocity field to be corrected - @param[in] vorticity field used to compute correction - @param resolutions : grid resolutions of velocity and vorticity - @param[in] req_flowrate : required value for the flowrate - @param topo : a predefined topology to discretize velocity/vorticity + Parameters + ---------- + velocity : :class:`~hysop.fields.continuous.Field` + in/out velocity continuous vector field. + vorticity : :class:`~hysop.fields.continuous.Field` + input vorticity vector field. + req_flowrate : a + :class:`~hysop.fields.variable_parameter.VariableParameter` + the desired inlet flowrate value + kwds : base class parameters """ assert 'variables' not in kwds, 'variables parameter is useless.' - super(VelocityCorrection, self).__init__(variables=[velocity, - vorticity], **kwds) - ## velocity variable (vector) + super(VelocityCorrection, self).__init__( + variables=[velocity, vorticity], **kwds) + # velocity field self.velocity = velocity - ## velocity variable + # vorticity field self.vorticity = vorticity - self.input = [self.velocity, self.vorticity] self.output = [self.velocity] - ## Expected value for the flow rate through input surface + # Expected value for the flow rate through input surface self.req_flowrate = req_flowrate dom = self.velocity.domain + # volume of control used to compute a reference for correction. self.cb = ControlBox(origin=dom.origin, length=dom.length, parent=dom) - ## Extra parameters that may be required for discrete operator - ## (at the time, only io_params) - self.config = kwds def discretize(self): super(VelocityCorrection, self)._standard_discretize() @@ -70,12 +70,11 @@ class VelocityCorrection(Computational): self.discrete_op.set_writer(self._writer) self._is_uptodate = True - def computeCorrection(self): - """ - Compute the required correction for the current state + def compute_correction(self): + """Compute the required correction for the current state but do not apply it onto velocity. """ - self.discrete_op.computeCorrection() + self.discrete_op.compute_correction() def get_work_properties(self): return {'rwork': None, 'iwork': None}