Skip to content
Snippets Groups Projects
Commit 6e112254 authored by Stephan Kuschel's avatar Stephan Kuschel Committed by GitHub
Browse files

Merge pull request #89 from skuschel/ku/dev

v0.3.0
parents 03854b26 2527fc86
Branches
Tags
No related merge requests found
......@@ -26,7 +26,7 @@ before_install:
- chmod +x miniconda.sh
- ./miniconda.sh -b -p $HOME/miniconda
- export PATH=$HOME/miniconda/bin:$PATH
- conda update --yes conda
# - conda update --yes conda
- conda install --yes python=$TRAVIS_PYTHON_VERSION atlas numpy scipy matplotlib nose cython h5py numexpr
install:
......
# Changelog of postpic
## v0.3
2017-09-28
Many improvements in terms of speed and features. Unfortunately some changes are not backwards-compatible to v0.2.3, so you may have to adapt your code to the new interface. For details, see the corresponding section below.
**Highlights**
* kspace reconstruction and propagation of EM waves.
* `postpic.Field` properly handles operator overloading and slicing. Slicing can be index based (integers) or referring the actual physical extent on the axis of a Field object (using floats).
* Expression based interface to particle properties (see below)
**Incompatible adjustments to last version**
* New dependency: Postpic requires the `numexpr` package to be installed now.
* Expression based interface of for particles: If `ms` is a `postpic.MultiSpecies` object, then the call `ms.X()` has been deprecated. Use `ms('x')` instead. This new particle interface can handle expressions that the `numexpr` package understands. Also `ms('sqrt(x**2 + gamma - id)')` is valid. This interface is easier to use, has better functionality and is faster due to `numexpr`.
The list of known per particle scalars and their definitions is available at `postpic.particle_scalars`. In addition all constants of `scipy.constants.*` can be used.
In case you find particle scalar that you use regularly which is not in the list, please open an issue and let us know!
* The `postpic.Field` class now behaves more like an `numpy.ndarray` which means that almost all functions return a new field object instead of modifying the current. This change affects the following functions: `half_resolution`, `autoreduce`, `cutout`, `mean`.
**Other improvements and new features**
* `postpic.helper.kspace` can reconstruct the correct k-space from three EM fields provided to distinguish between forward and backward propagating waves (thanks to @Ablinne)
* `postpic.helper.kspace_propagate` will turn the phases in k-space to propagate the EM-wave.
* List of new functions in `postpic` from `postpic.helper` (thanks to @Ablinne): `kspace_epoch_like`, `kspace`, `kspace_propagate`.
* `Field.fft` function for fft optimized with pyfftw (thanks to @Ablinne).
* `Field.__getitem__` to slice a Field object. If integers are provided, it will interpret them as gridpoints. If float are provided they are interpreted as the physical region of the data and slice along the corresponding axis positions (thanks to @Ablinne).
* `Field` class has been massively impoved (thanks to @Ablinne): The operator overloading is now properly implemented and thanks to `__array__` method, it can be interpreted by numpy as an ndarray whenever neccessary.
* List of new functions of the `Field` class (thanks to @Ablinne): `meshgrid`, `conj`, `replace_data`, `pad`, `transform`, `squeeze`, `integrate`, `fft`, `shift_grid_by`, `__getitem__`, `__setitem__`.
* List of new properties of the `Field` class (thanks to @Ablinne): `matrix`, `real`, `imag`, `angle`.
* Many performance optimizations using pyfftw library (optional) or numexpr (now required by postpic) or by avoiding in memory data copying.
* Lots of fixes
## v0.2.3
2017-02-17
......
......@@ -27,7 +27,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera
from . import helper
from . import datahandling
from .datahandling import *
from .helper import PhysicalConstants
from .helper import *
from .particles import *
from . import datareader
from .datareader import chooseCode, readDump, readSim
......@@ -36,7 +36,7 @@ from . import _postpic_version
__all__ = ['helper']
__all__ += datahandling.__all__
__all__ += ['PhysicalConstants']
__all__ += helper.__all__
__all__ += particles.__all__
__all__ += ['datareader', 'plotting']
# high level functions
......
......@@ -22,7 +22,7 @@
from __future__ import absolute_import, division, print_function, unicode_literals
__version__ = 'v0.2.3'
__version__ = 'v0.3.0'
try:
FileNotFoundError # python3
......
......@@ -501,6 +501,8 @@ class Field(object):
pad_width_numpy = []
padded_axes = []
for i, axis_pad in enumerate(pad_width):
if not isinstance(axis_pad, collections.Iterable):
axis_pad = [axis_pad, axis_pad]
......@@ -521,6 +523,11 @@ class Field(object):
in axis_pad]
pad_width_numpy.append(axis_pad)
totalpad_axis = sum(axis_pad)
if totalpad_axis:
padded_axes.append(i)
extent = axis.extent
newextent = [extent[0] - axis_pad[0]*dx, extent[1] + axis_pad[1]*dx]
gridpoints = len(axis.grid_node) - 1 + axis_pad[0] + axis_pad[1]
......@@ -529,8 +536,9 @@ class Field(object):
ret._matrix = np.pad(self, pad_width_numpy, mode, **kwargs)
# This info is invalidated
ret.transformed_axes_origins = [None]*ret.dimensions
# This info is invalidated for all axes which have actually been padded
for i in padded_axes:
ret.transformed_axes_origins[i] = None
return ret
......@@ -558,7 +566,7 @@ class Field(object):
ret.setaxisobj(axis, ret.axes[axis].half_resolution())
# This info is invalidated
ret.transformed_axes_origins = [None]*ret.dimensions
ret.transformed_axes_origins[axis] = None
return ret
......@@ -656,13 +664,8 @@ class Field(object):
'''
ret = self # half_resolution will take care for the copy
for i in range(len(ret.axes)):
if len(ret.axes[i]) > maxlen:
while len(ret.axes[i]) > maxlen:
ret = ret.half_resolution(i)
ret = ret.autoreduce(maxlen=maxlen)
break
# This info is invalidated by reducing the grid
ret.transformed_axes_origins = [None]*ret.dimensions
return ret
......@@ -1012,14 +1015,20 @@ class Field(object):
# Operator overloading
def __getitem__(self, key):
old_shape = self.shape
key = self._normalize_slices(key)
field = copy.copy(self)
field._matrix = field.matrix[key]
for i, sl in enumerate(key):
field.setaxisobj(i, field.axes[i][sl])
new_shape = field.shape
# This info is invalidated
field.transformed_axes_origins = [None]*field.dimensions
for i, (o, n) in enumerate(zip(old_shape, new_shape)):
if o != n:
field.transformed_axes_origins[i] = None
return field
......
......@@ -40,6 +40,10 @@ except(ImportError):
particleshapes = [0]
__all__ = ['PhysicalConstants', 'kspace_epoch_like', 'kspace',
'kspace_propagate']
def isnotebook():
return 'ipykernel' in sys.modules
......@@ -114,6 +118,17 @@ def append_doc_of(obj):
return ret
def prepend_doc_of(obj):
'''
decorator to append the doc of `obj` to decorated object/class.
'''
def ret(a):
doc = '' if a.__doc__ is None else a.__doc__
a.__doc__ = obj.__doc__ + doc
return a
return ret
class float_with_name(float):
def __new__(self, value, name):
return float.__new__(self, value)
......@@ -593,7 +608,7 @@ def kspace(component, fields, extent=None, interpolation=None, omega_func=None):
def linear_phase(field, dx):
'''
Calculates the linear phase as used in Field._apply_linear_phase and
kspace_propagate_generator.
_kspace_propagate_generator.
'''
import numexpr as ne
transform_state = field._transform_state(dx.keys())
......@@ -629,7 +644,7 @@ def linear_phase(field, dx):
return exp_ikdx
def kspace_propagate_generator(kspace, dt, moving_window_vect=None,
def _kspace_propagate_generator(kspace, dt, moving_window_vect=None,
move_window=None,
remove_antipropagating_waves=None,
yield_zeroth_step=False):
......@@ -761,29 +776,16 @@ def kspace_propagate_generator(kspace, dt, moving_window_vect=None,
yield kspace
@prepend_doc_of(_kspace_propagate_generator)
def kspace_propagate(kspace, dt, nsteps=1, **kwargs):
'''
Evolve time on a field.
This function checks the transform_state of the field and transforms first from spatial
domain to frequency domain if necessary. In this case the inverse transform will also
be applied to the result before returning it. This works, however, only correctly with
fields that are the inverse transforms of a k-space reconstruction, i.e. with complex
fields.
dt: time in seconds
nsteps: number of steps to take
If nsteps == 1, this function will just return the result.
If nsteps > 1, this function will return a generator that will generate the results.
If you want a list, just put list(...) around the return value.
For additional arguments, see the documentation of kspace_propagate_generator, e.g.:
If yield_zeroth_step is True, then the kspace will also be yielded after removing the
antipropagating waves, but before the first actual step is done.
'''
gen = kspace_propagate_generator(kspace, dt, **kwargs)
gen = _kspace_propagate_generator(kspace, dt, **kwargs)
if nsteps == 1:
return next(gen)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment