Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
RemiLehe committed Jun 11, 2019
2 parents 4028a07 + eed000a commit c11e1d8
Show file tree
Hide file tree
Showing 13 changed files with 259 additions and 225 deletions.
4 changes: 1 addition & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,12 @@ install:
- pip install matplotlib
- python setup.py install
- pip install wget
- pip install pyflakes pycodestyle jupyter
- pip install pyflakes jupyter pytest

before_script :
# static code analysis
# pyflakes: mainly warnings, unused code, etc.
- python -m pyflakes opmd_viewer
# pep8: style only, enforce PEP 8
- python -m pycodestyle --ignore=E201,E202,E122,E127,E128,E131,W605 opmd_viewer

script:
- "python setup.py test"
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Change Log / Release Log for openPMD-viewer

## 0.9.0

This release adds two features:
- Improved calculation of the laser envelope, using the Hilbert transform.
- Reconstruction of full 3D field from a quasi-3D dataset, when passing `theta=None`.

## 0.8.2

This is a bug-fix release. It allows the slider to work properly in JupyterLab,
Expand Down
9 changes: 3 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

In order to contribute, please fork the [main repository](https://github.com/openPMD/openPMD-viewer):

- Click 'Fork' on the page of the main repository, in order to create a personal copy of this repository on your Github account.
- Click 'Fork' on the page of the main repository, in order to create a personal copy of this repository on your Github account.

- Clone this copy to your local machine:
```
Expand Down Expand Up @@ -46,13 +46,10 @@ git pull [email protected]:openPMD/openPMD-viewer.git dev
```

- Test and check your code:
- Use [pyflakes](https://pypi.python.org/pypi/pyflakes) and
[pycodestyle](https://pypi.python.org/pypi/pycodestyle) to detect any potential bug, and to
ensure that your code complies with some standard style conventions.
- Use [pyflakes](https://pypi.python.org/pypi/pyflakes) to detect any potential bug.
```
cd openPMD-viewer/
pyflakes opmd_viewer
pycodestyle --ignore=E201,E202,E122,E127,E128,E131,W605 opmd_viewer
```
- Make sure that the tests pass (please install `wget` and `jupyter` before running the tests: `pip install wget jupyter`)
```
Expand Down Expand Up @@ -101,7 +98,7 @@ def get_data( dset, i_slice=None, pos_slice=None ) :
i_slice: int, optional
The index of the slice to be taken
pos_slice: int, optional
The position at which to slice the array
When None, no slice is performed
Expand Down
3 changes: 1 addition & 2 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ packages, some of the steps below will be automatized.
Make sure that your local environment is ready for a full release on
PyPI and conda. In particular:

- you should install the packages
[`pypandoc`](https://pypi.python.org/pypi/pypandoc/),
- you should install the package
[`twine`](https://pypi.python.org/pypi/twine).
- you should have a registered account on [PyPI](https://pypi.python.org/pypi) and [test PyPI](https://testpypi.python.org/pypi), and your `$HOME` should contain a file `.pypirc` which contains the following text:

Expand Down
2 changes: 1 addition & 1 deletion conda_recipe/meta.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% set version = "0.8.2" %}
{% set version = "0.9.0" %}

package:
name: openpmd_viewer
Expand Down
2 changes: 1 addition & 1 deletion opmd_viewer/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.8.2"
__version__ = "0.9.0"
78 changes: 6 additions & 72 deletions opmd_viewer/addons/pic/lpa_diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import scipy.constants as const
from scipy.optimize import curve_fit
from opmd_viewer.openpmd_timeseries.plotter import check_matplotlib
from scipy.signal import hilbert
try:
import matplotlib.pyplot as plt
except ImportError:
Expand Down Expand Up @@ -437,8 +438,7 @@ def get_current( self, t=None, iteration=None, species=None, select=None,
return(current, info)

def get_laser_envelope( self, t=None, iteration=None, pol=None, m='all',
freq_filter=40, index='center', theta=0,
slicing_dir='y', plot=False, **kw ):
index='center', theta=0, slicing_dir='y', plot=False, **kw ):
"""
Calculate a laser field by filtering out high frequencies. Can either
return the envelope slice-wise or a full 2D envelope.
Expand All @@ -462,11 +462,6 @@ def get_laser_envelope( self, t=None, iteration=None, pol=None, m='all',
Either 'all' (for the sum of all the modes)
or an integer (for the selection of a particular mode)
freq_filter : float, optional
Range of frequencies in percent which to filter: Frequencies higher
than freq_filter/100 times the dominant frequencies will be
filtered out
index : int or str, optional
Transversal index of the slice from which to calculate the envelope
Default is 'center', using the center slice.
Expand Down Expand Up @@ -504,16 +499,16 @@ def get_laser_envelope( self, t=None, iteration=None, pol=None, m='all',
info = field[1]
if index == 'all':
# Filter the full 2D array
envelope = self._fft_filter(field[0], freq_filter)
e_complx = hilbert(field[0], axis=1)
elif index == 'center':
# Filter the central slice (1D array)
field_slice = field[0][int( field[0].shape[0] / 2), :]
envelope = self._fft_filter(field_slice, freq_filter)
e_complx = hilbert(field_slice)
else:
# Filter the requested slice (2D array)
field_slice = field[0][index, :]
envelope = self._fft_filter(field_slice, freq_filter)

e_complx = hilbert(field_slice)
envelope = np.abs(e_complx)
# Restrict the metainformation to 1d if needed
if index != 'all':
info.restrict_to_1Daxis( info.axes[1] )
Expand All @@ -539,67 +534,6 @@ def get_laser_envelope( self, t=None, iteration=None, pol=None, m='all',
# Return the result
return( envelope, info )

def _fft_filter(self, field, freq_filter):
"""
Filters out high frequencies in input data. Frequencies higher than
freq_filter / 100 times the dominant frequency will be filtered.
Parameters
----------
field : 1D array or 2D array
Array with input data in time/space domain
When a 2D array is provided, filtering is performed along
the last dimension.
freq_filter : float
Frequency range in percent around the dominant frequency which will
not be filtered out
Returns
-------
A 1D array or 2D array with filtered input data in time/space domain
"""
# Number of sample points along the filtered direction
N = field.shape[-1]
fft_freqs = np.fft.fftfreq(N)
# Fourier transform of the field
fft_field = np.fft.fft(field, axis=-1)
# Find central frequency
# (the code below works for both 1D and 2D arrays, and finds
# the global maximum across all dimensions in the case of the 2D array)
central_freq_i = np.unravel_index( np.argmax( np.abs(fft_field) ),
dims=fft_field.shape )[-1]
if central_freq_i > int( N / 2 ):
# Wrap index around, if it turns out to be in the
# negative-frequency part of the fft range
central_freq_i = N - central_freq_i
central_freq = fft_freqs[central_freq_i]
# Filter frequencies higher than central_freq * freq_filter/100
filter_bound = central_freq * freq_filter / 100.
# Find index from where to filter
filter_i = np.argmin(np.abs(filter_bound - fft_freqs))
filter_freq_range_i = central_freq_i - filter_i
# Write filtered FFT array
filtered_fft = np.zeros_like( field, dtype=np.complex )
# - Indices in the original fft array
i_fft_min = central_freq_i - filter_freq_range_i
i_fft_max = central_freq_i + filter_freq_range_i
# - Indices in the new filtered array
i_filter_min = int(N / 2) - filter_freq_range_i
i_filter_max = int(N / 2) + filter_freq_range_i
if field.ndim == 2:
filtered_fft[ :, i_filter_min:i_filter_max] = \
2 * fft_field[ :, i_fft_min:i_fft_max ]
elif field.ndim == 1:
filtered_fft[ i_filter_min:i_filter_max] = \
2 * fft_field[ i_fft_min:i_fft_max ]
# Calculate inverse FFT of filtered FFT array (along the last axis)
envelope = np.abs( np.fft.ifft(
np.fft.fftshift( filtered_fft, axes=-1 ), axis=-1 ) )

# Return the result
return( envelope )

def get_main_frequency( self, t=None, iteration=None, pol=None, m='all',
method='max'):
"""
Expand Down
33 changes: 33 additions & 0 deletions opmd_viewer/openpmd_timeseries/data_reader/field_metainfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,36 @@ def restrict_to_1Daxis(self, axis):
# Suppress imshow_extent and replace the dictionary
delattr(self, 'imshow_extent')
self.axes = {0: axis}

def _convert_cylindrical_to_3Dcartesian(self):
"""
Convert FieldMetaInformation from cylindrical to 3D Cartesian
"""

try:
assert self.axes[0] == 'r'
assert self.axes[1] == 'z'
except (KeyError, AssertionError):
raise ValueError('_convert_cylindrical_to_3Dcartesian'
' can only be applied to a timeseries in thetaMode geometry')

# Create x and y arrays
self.x = self.r.copy()
self.y = self.r.copy()
del self.r

# Create dx and dy
self.dx = self.dr
self.dy = self.dr
del self.dr

# Create xmin, xmax, ymin, ymax
self.xmin = self.rmin
self.ymin = self.rmin
del self.rmin
self.xmax = self.rmax
self.ymax = self.rmax
del self.rmax

# Change axes
self.axes = {0:'x', 1:'y', 2:'z'}
105 changes: 67 additions & 38 deletions opmd_viewer/openpmd_timeseries/data_reader/field_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import numpy as np
from .utilities import get_shape, get_data, get_bpath, join_infile_path
from .field_metainfo import FieldMetaInformation

from ..utilities import construct_3d_from_circ

def read_field_1d( filename, field_path, axis_labels ):
"""
Expand Down Expand Up @@ -103,7 +103,7 @@ def read_field_2d( filename, field_path, axis_labels ):
def read_field_circ( filename, field_path, m=0, theta=0. ):
"""
Extract a given field from an HDF5 file in the openPMD format,
when the geometry is 2d cartesian.
when the geometry is thetaMode
Parameters
----------
Expand All @@ -117,13 +117,17 @@ def read_field_circ( filename, field_path, m=0, theta=0. ):
m : int or string, optional
The azimuthal mode to be extracted
theta : float, optional
theta : float or None
Angle of the plane of observation with respect to the x axis
If `theta` is not None, then this function returns a 2D array
corresponding to the plane of observation given by `theta` ;
otherwise it returns a full 3D Cartesian array
Returns
-------
A tuple with
F : a 2darray containing the required field
F : a 3darray or 2darray containing the required field,
depending on whether `theta` is None or not
info : a FieldMetaInformation object
(contains information about the grid; see the corresponding docstring)
"""
Expand All @@ -138,40 +142,64 @@ def read_field_circ( filename, field_path, m=0, theta=0. ):
group.attrs['gridSpacing'], group.attrs['gridGlobalOffset'],
group.attrs['gridUnitSI'], dset.attrs['position'], thetaMode=True )

# Extract the modes and recombine them properly
F_total = np.zeros( (2 * Nr, Nz ) )
if m == 'all':
# Sum of all the modes
# - Prepare the multiplier arrays
mult_above_axis = [1]
mult_below_axis = [1]
for mode in range(1, int(Nm / 2) + 1):
cos = np.cos( mode * theta )
sin = np.sin( mode * theta )
mult_above_axis += [cos, sin]
mult_below_axis += [ (-1) ** mode * cos, (-1) ** mode * sin ]
mult_above_axis = np.array( mult_above_axis )
mult_below_axis = np.array( mult_below_axis )
# - Sum the modes
F = get_data( dset ) # (Extracts all modes)
F_total[Nr:, :] = np.tensordot( mult_above_axis,
F, axes=(0, 0) )[:, :]
F_total[:Nr, :] = np.tensordot( mult_below_axis,
F, axes=(0, 0) )[::-1, :]
elif m == 0:
# Extract mode 0
F = get_data( dset, 0, 0 )
F_total[Nr:, :] = F[:, :]
F_total[:Nr, :] = F[::-1, :]
# Convert to a 3D Cartesian array if theta is None
if theta is None:

# Get cylindrical info
rmax = info.rmax
inv_dr = 1./info.dr
Fcirc = get_data( dset ) # (Extracts all modes)
nr = Fcirc.shape[1]
if m == 'all':
modes = [ mode for mode in range(0, int(Nm / 2) + 1) ]
else:
modes = [ m ]
modes = np.array( modes, dtype='int' )
nmodes = len(modes)

# Convert cylindrical data to Cartesian data
info._convert_cylindrical_to_3Dcartesian()
nx, ny, nz = len(info.x), len(info.y), len(info.z)
F_total = np.zeros( (nx, ny, nz) )
construct_3d_from_circ( F_total, Fcirc, info.x, info.y, modes,
nx, ny, nz, nr, nmodes, inv_dr, rmax )

else:
# Extract higher mode
cos = np.cos( m * theta )
sin = np.sin( m * theta )
F_cos = get_data( dset, 2 * m - 1, 0 )
F_sin = get_data( dset, 2 * m, 0 )
F = cos * F_cos + sin * F_sin
F_total[Nr:, :] = F[:, :]
F_total[:Nr, :] = (-1) ** m * F[::-1, :]

# Extract the modes and recombine them properly
F_total = np.zeros( (2 * Nr, Nz ) )
if m == 'all':
# Sum of all the modes
# - Prepare the multiplier arrays
mult_above_axis = [1]
mult_below_axis = [1]
for mode in range(1, int(Nm / 2) + 1):
cos = np.cos( mode * theta )
sin = np.sin( mode * theta )
mult_above_axis += [cos, sin]
mult_below_axis += [ (-1) ** mode * cos, (-1) ** mode * sin ]
mult_above_axis = np.array( mult_above_axis )
mult_below_axis = np.array( mult_below_axis )
# - Sum the modes
F = get_data( dset ) # (Extracts all modes)
F_total[Nr:, :] = np.tensordot( mult_above_axis,
F, axes=(0, 0) )[:, :]
F_total[:Nr, :] = np.tensordot( mult_below_axis,
F, axes=(0, 0) )[::-1, :]
elif m == 0:
# Extract mode 0
F = get_data( dset, 0, 0 )
F_total[Nr:, :] = F[:, :]
F_total[:Nr, :] = F[::-1, :]
else:
# Extract higher mode
cos = np.cos( m * theta )
sin = np.sin( m * theta )
F_cos = get_data( dset, 2 * m - 1, 0 )
F_sin = get_data( dset, 2 * m, 0 )
F = cos * F_cos + sin * F_sin
F_total[Nr:, :] = F[:, :]
F_total[:Nr, :] = (-1) ** m * F[::-1, :]

# Close the file
dfile.close()
Expand Down Expand Up @@ -213,7 +241,8 @@ def read_field_3d( filename, field_path, axis_labels,
Returns
-------
A tuple with
F : a 2darray containing the required field
F : a 3darray or 2darray containing the required field,
depending on whether `slicing` is None or not
info : a FieldMetaInformation object
(contains information about the grid; see the corresponding docstring)
"""
Expand Down
Loading

0 comments on commit c11e1d8

Please sign in to comment.