From da58b5f5940add17ec2fae8b680dc29a1886ae66 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Keck <Jean-Baptiste.Keck@imag.fr> Date: Thu, 21 Feb 2019 16:59:03 +0100 Subject: [PATCH] compute energy host kernels --- examples/taylor_green/taylor_green.py | 2 +- hysop/numerics/fft/fft.py | 4 +- hysop/numerics/fft/host_fft.py | 137 ++++++++++++++++++----- hysop/operator/base/spectral_operator.py | 14 ++- hysop/tools/numba_utils.py | 8 +- 5 files changed, 130 insertions(+), 35 deletions(-) diff --git a/examples/taylor_green/taylor_green.py b/examples/taylor_green/taylor_green.py index 24cb866ae..12196ca9b 100644 --- a/examples/taylor_green/taylor_green.py +++ b/examples/taylor_green/taylor_green.py @@ -42,7 +42,7 @@ def compute(args): from hysop.operators import DirectionalAdvection, DirectionalStretchingDiffusion, \ DirectionalDiffusion, DirectionalStretching, \ StaticDirectionalStretching, Diffusion, \ - PoissonCurl, AdaptiveTimeStep, \ + PoissonCurl, AdaptiveTimeStep, \ Enstrophy, MinMaxFieldStatistics, StrangSplitting, \ ParameterPlotter, Advection, MinMaxGradientStatistics diff --git a/hysop/numerics/fft/fft.py b/hysop/numerics/fft/fft.py index 1e78f21c1..0b924aaaa 100644 --- a/hysop/numerics/fft/fft.py +++ b/hysop/numerics/fft/fft.py @@ -713,12 +713,12 @@ class FFTI(object): """Plan to compute energy from src to energy.""" assert src.ndim == len(transforms) assert dst.ndim == 1 - N = src.shape + N = tuple(int(_) for _ in src.shape) K2 = () NS2 = () C2C = () for (Ni, Ti) in zip(N, transforms): - c2c = STU.is_C2C(Ti) + c2c = int(STU.is_C2C(Ti)) Ki = Ni//2 if c2c else Ni-1 K2 += (Ki**2,) NS2 += ((Ni+1)//2,) diff --git a/hysop/numerics/fft/host_fft.py b/hysop/numerics/fft/host_fft.py index 06aa1d620..321043944 100644 --- a/hysop/numerics/fft/host_fft.py +++ b/hysop/numerics/fft/host_fft.py @@ -6,11 +6,14 @@ OpenCl backend base interface for fast Fourier Transforms. :class:`~hysop.numerics.host_fft.HostFFTQueue` """ -import psutil +import psutil, functools import numpy as np +import numba as nb -from hysop import __FFTW_NUM_THREADS__, __FFTW_PLANNER_EFFORT__, __FFTW_PLANNER_TIMELIMIT__ +from hysop import __FFTW_NUM_THREADS__, __FFTW_PLANNER_EFFORT__, __FFTW_PLANNER_TIMELIMIT__, \ + __DEFAULT_NUMBA_TARGET__ from hysop.tools.types import first_not_None, check_instance +from hysop.tools.numba_utils import make_numba_signature from hysop.backend.host.host_array_backend import HostArrayBackend from hysop.backend.host.host_array import HostArray from hysop.numerics.fft.fft import FFTQueueI, FFTPlanI, FFTI @@ -115,30 +118,114 @@ class HostFFTI(FFTI): buf[slc] = 0 return exec_fill_zeros - def plan_compute_energy(self, tg, src, dst, transforms, method='round'): - """Plan to compute energy from src to energy.""" + def plan_compute_energy(self, tg, src, dst, transforms, + method='round', target=None): + """ + Plan to compute energy from src array to dst array using given transforms, + method (round or weighted) and numba target. + """ (N, NS2, C2C) = super(HostFFTI, self).plan_compute_energy(tg, src, dst, transforms) - def exec_compute_energy(src=src, dst=dst, C2C=C2C, NS2=NS2): - dst[...] = 0.0 - for idx in np.ndindex(*src.shape): - Ek = (abs(src[idx])**2) / 2.0 - K = np.asarray(idx, dtype=np.int64) - for (i, (Ki, Ni, ns2, c2c)) in enumerate(zip(K, N, NS2, C2C)): - if c2c and Ki>ns2: - K[i] = Ni - Ki - k = (K.dot(K))**0.5 - if method=='round': - i = int(round(k)) - dst[i] += Ek - elif method=='weighted': - i = int(k) - a = k-i - dst[i] += (1-a)*Ek - dst[min(i+1,dst.size-1)] += a*Ek - else: - msg='Unknown method {}'.format(method) - raise NotImplementedError(msg) - print dst + + target = first_not_None(target, __DEFAULT_NUMBA_TARGET__) + args = (src,)+N+NS2+C2C+(dst,) + signature, layout = make_numba_signature(*args) + + dim = src.ndim + if (dim == 1): + @nb.guvectorize([signature], layout, target=target, + nopython=True, cache=True) + def compute_energy_1d(src, Nx, NS2x, C2Cx, dst): + for _ in range(dst.size): + dst[_] = 0.0 + for ix in range(Nx): + kx = ((Nx-ix) if (C2Cx and ix>NS2x) else ix) + Xi = src[ix] + Ei = 0.5*(abs(Xi)**2) + dst[kx] += Ei + exec_compute_energy = functools.partial(compute_energy_1d, *args) + elif (dim == 2): + if (method=='round'): + @nb.guvectorize([signature], layout, target=target, + nopython=True, cache=True) + def compute_energy_2d_round(src, Ny, Nx, NS2y, NS2x, C2Cy, C2Cx, dst): + for _ in range(dst.size): + dst[_] = 0.0 + for iy in range(Ny): + ky = ((Ny-iy) if (C2Cy and iy>NS2y) else iy) + for ix in range(Nx): + kx = ((Nx-ix) if (C2Cx and ix>NS2x) else ix) + Xi = src[iy,ix] + Ei = 0.5*(abs(Xi)**2) + k = int(round((kx**2 + ky**2)**0.5)) + dst[k] += Ei + exec_compute_energy = functools.partial(compute_energy_2d_round, *args) + elif method=='weighted': + @nb.guvectorize([signature], layout, target=target, + nopython=True, cache=True) + def compute_energy_2d_weighted(src, Ny, Nx, NS2y, NS2x, C2Cy, C2Cx, dst): + for _ in range(dst.size): + dst[_] = 0.0 + for iy in range(Ny): + ky = ((Ny-iy) if (C2Cy and iy>NS2y) else iy) + for ix in range(Nx): + kx = ((Nx-ix) if (C2Cx and ix>NS2x) else ix) + Xi = src[iy,ix] + Ei = 0.5*(abs(Xi)**2) + k = (kx**2 + ky**2)**0.5 + i = int(k) + j = min(i+1, dst.size-1) + a = k-i + dst[i] += (1-a)*Ei + dst[j] += a *Ei + exec_compute_energy = functools.partial(compute_energy_2d_weighted, *args) + else: + msg='Unknown method {}.'.format(method) + raise ValueError(msg) + elif (dim == 3): + if (method=='round'): + @nb.guvectorize([signature], layout, target=target, + nopython=True, cache=True) + def compute_energy_3d_round(src, Nz, Ny, Nx, NS2z, NS2y, NS2x, C2Cz, C2Cy, C2Cx, dst): + for _ in range(dst.size): + dst[_] = 0.0 + for iz in range(Nz): + kz = ((Nz-iz) if (C2Cz and iz>NS2z) else iz) + for iy in range(Ny): + ky = ((Ny-iy) if (C2Cy and iy>NS2y) else iy) + for ix in range(Nx): + kx = ((Nx-ix) if (C2Cx and ix>NS2x) else ix) + Xi = src[iy,ix,iz] + Ei = 0.5*(abs(Xi)**2) + k = int(round((kx**2 + ky**2 + kz**2)**0.5)) + dst[k] += Ei + exec_compute_energy = functools.partial(compute_energy_3d_round, *args) + elif method=='weighted': + @nb.guvectorize([signature], layout, target=target, + nopython=True, cache=True) + def compute_energy_3d_weighted(src, Nz, Ny, Nx, NS2z, NS2y, NS2x, C2Cz, C2Cy, C2Cx, dst): + for _ in range(dst.size): + dst[_] = 0.0 + for iz in range(Nz): + kz = ((Nz-iz) if (C2Cz and iz>NS2z) else iz) + for iy in range(Ny): + ky = ((Ny-iy) if (C2Cy and iy>NS2y) else iy) + for ix in range(Nx): + kx = ((Nx-ix) if (C2Cx and ix>NS2x) else ix) + Xi = src[iy,ix,iz] + Ei = 0.5*(abs(Xi)**2) + k = (kx**2 + ky**2 + kz**2)**0.5 + i = int(k) + j = min(i+1, dst.size-1) + a = k-i + dst[i] += (1-a)*Ei + dst[j] += a *Ei + exec_compute_energy = functools.partial(compute_energy_3d_weighted, *args) + else: + msg='Unknown method {}.'.format(method) + raise ValueError(msg) + else: + msg='{}D compute_energy has not been implemented yet.' + raise NotImplementedError(msg.format(dim)) return exec_compute_energy diff --git a/hysop/operator/base/spectral_operator.py b/hysop/operator/base/spectral_operator.py index 12ffa0bea..1ede8be34 100644 --- a/hysop/operator/base/spectral_operator.py +++ b/hysop/operator/base/spectral_operator.py @@ -1884,6 +1884,8 @@ SPECTRAL TRANSFORM SETUP compute_energy_queue += FFTI.plan_compute_energy(tg=tg, src=spectral_buffer, dst=energy_buffer, transforms=self.permuted_transforms) + if self._do_plot_energy: + self._energy_plotter = EnergyPlot(ioparams=self._plot_energy_ioparams, energy_buffer=energy_buffer) else: compute_energy_queue = None @@ -1936,8 +1938,14 @@ SPECTRAL TRANSFORM SETUP should_plot = (frequency>0) and (ite % frequency == 0) should_plot |= simulation.is_time_of_interest if should_plot: - return self._plot_energy() + filename = self._plot_energy_ioparams.filename + filename = filename.replace('{fname}', self.field.name).replace('{it}', ite) + evt = self._energy_plotter.update() + self._energy_plotter.filename(filename) + return evt - def _plot_energy(self): - pass + +class EnergyPlot(object): + def __init__(self, energy_buffer): + check_instance(energy_buffer, npw.ndarray) diff --git a/hysop/tools/numba_utils.py b/hysop/tools/numba_utils.py index e8abd9cd4..069ed7caa 100644 --- a/hysop/tools/numba_utils.py +++ b/hysop/tools/numba_utils.py @@ -9,9 +9,9 @@ def make_numba_signature(*args, **kwds): msg='Unknown kwds {}.'.forma(kwds.keys()) raise RuntimeError(kwds) dtype_to_ntype = { - int: np.int32, - long: np.int64, - float: np.float64, + int: nb.int32, + long: nb.int64, + float: nb.float64, np.int8: nb.int8, np.int16: nb.int16, @@ -78,7 +78,7 @@ def make_numba_signature(*args, **kwds): na = nb.types.Array(dtype=dtype, ndim=a.ndim, layout='A') numba_layout += (format_shape(*a.shape),) elif isinstance(a, np.dtype): - na = dtype_to_ntype[a.type] + a = dtype_to_ntype[a.type] numba_layout += ('()',) elif isinstance(a, type): na = dtype_to_ntype[a] -- GitLab