Source code for pyDive.mappings
"""
Copyright 2014 Heiko Burau
This file is part of pyDive.
pyDive is free software: you can redistribute it and/or modify
it under the terms of of either the GNU General Public License or
the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
pyDive is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License and the GNU Lesser General Public License
for more details.
You should have received a copy of the GNU General Public License
and the GNU Lesser General Public License along with pyDive.
If not, see <http://www.gnu.org/licenses/>.
"""
__doc__=\
"""If `numba <http://numba.pydata.org/>`_ is installed the particle shape functions will
be compiled which gives an appreciable speedup.
"""
import numpy as np
try:
from numba import vectorize
except ImportError:
vectorize = None
[docs]class NGP:
"""Nearest-Grid-Point
"""
support = 1.0
@staticmethod
def __call__(x):
if abs(x) < 0.5:
return 1.0
return 0.0
[docs]class CIC:
"""Cloud-in-Cell
"""
support = 2.0
@staticmethod
def __call__(x):
if abs(x) < 1.0:
return 1.0 - abs(x)
return 0.0
def __apply_MapOp(mesh, particles_pos, shape_function, mapOp):
half_supp = 0.5 * shape_function.support
num_coeffs_per_axis = int(np.ceil(shape_function.support))
dim = len(np.shape(mesh))
# ndarray which holds the non-zero particle assignment values of one particle
coeffs = np.empty([num_coeffs_per_axis] * dim)
if vectorize:
# vectorize shape function to a compiled ufunc
v_shape_function = vectorize(['f4(f4)', 'f8(f8)'])(shape_function.__call__)
else:
# numpy fallback
v_shape_function = np.vectorize(shape_function.__call__)
for idx, pos in enumerate(particles_pos):
# lowest mesh indices that contributes to the mapping
begin = np.ceil(pos - np.ones(dim) * half_supp).astype(int)
# rearrange mapping area if it overlaps the border
begin = np.maximum(begin, np.zeros(dim, dtype=int))
begin = np.minimum(begin, np.shape(mesh) - np.ones(dim) * half_supp)
# compute coefficients (particle assignment values)
for coeff_idx in np.ndindex(np.shape(coeffs)):
rel_vec = begin + coeff_idx - pos
coeffs[coeff_idx] = np.prod(v_shape_function(rel_vec))
# do the actual mapping
window = [slice(begin[i], begin[i] + num_coeffs_per_axis) for i in range(dim)]
mapOp(mesh[window], coeffs, idx)
[docs]def mesh2particles(mesh, particles_pos, shape_function=CIC):
"""
Map mesh values to particles according to a particle shape function.
:param array-like mesh: n-dimensional array.
Dimension of *mesh* has to be greater or equal to the number of particle position components.
:param particles_pos:
'd'-dim tuples for 'N' particle positions. The positions can be float32 or float64 and
must be within the shape of *mesh*.
:type particles_pos: (N, d)
:param shape_function:
Callable object returning the particle assignment value for a given param 'x'.
Has to provide a 'support' float attribute which defines the width of the non-zero area.
Defaults to cloud-in-cell.
:type shape_function: callable, optional
:return: Mapped mesh values for each particle.
:type return: ndarray(N, dtype='f')
Notes:
- The particle shape function is not evaluated outside the mesh.
"""
# resulting ndarray
particles = np.empty(len(particles_pos), dtype='f')
def mapOp(sub_mesh, coeffs, particle_idx):
particles[particle_idx] = np.add.reduce(sub_mesh * coeffs, axis=None)
__apply_MapOp(mesh, particles_pos, shape_function, mapOp)
return particles
[docs]def particles2mesh(mesh, particles, particles_pos, shape_function=CIC):
"""
Map particle values to mesh according to a particle shape function.
Particle values are added to the mesh.
:param array-like mesh: n-dimensional array.
Dimension of *mesh* has to be greater or equal to the number of particle position components.
:param particles: particle data.
len(*particles*) has to be the same as len(*particles_pos*)
:type particles: array_like (1 dim)
:param particles_pos:
'd'-dim tuples for 'N' particle positions. The positions can be float32 or float64 and
must be within the shape of *mesh*.
:type particles_pos: (N, d)
:param shape_function:
Callable object returning the particle assignment value for a given param 'x'.
Has to provide a 'support' float attribute which defines the width of the non-zero area.
Defaults to cloud-in-cell.
:type shape_function: callable, optional
:return: *mesh*
Notes:
- The particle shape function is not evaluated outside the mesh.
"""
def mapOp(sub_mesh, coeffs, particle_idx):
sub_mesh += particles[particle_idx] * coeffs
__apply_MapOp(mesh, particles_pos, shape_function, mapOp)
return mesh