Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Profile from openPMD file #151

Merged
merged 18 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/source/api/profiles/from_openpmd_profile.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Profile read from an openPMD file
=================================

.. autoclass:: lasy.profiles.from_openpmd_profile.FromOpenPMDProfile
:members:
1 change: 1 addition & 0 deletions docs/source/api/profiles/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ Typically a laser will be constructed through a combination of two classes ``Lon
gaussian
combined_profile
from_array_profile
from_openpmd_profile
longitudinal/index
transverse/index
2 changes: 2 additions & 0 deletions lasy/profiles/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from .combined_profile import CombinedLongitudinalTransverseProfile
from .gaussian_profile import GaussianProfile
from .from_array_profile import FromArrayProfile
from .from_openpmd_profile import FromOpenPMDProfile

__all__ = [
"CombinedLongitudinalTransverseProfile",
"GaussianProfile",
"FromArrayProfile",
"FromOpenPMDProfile",
]
98 changes: 98 additions & 0 deletions lasy/profiles/from_openpmd_profile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import numpy as np
from scipy.signal import hilbert
from scipy.constants import c
import openpmd_api as io
from openpmd_viewer import OpenPMDTimeSeries
from .from_array_profile import FromArrayProfile


class FromOpenPMDProfile(FromArrayProfile):
r"""
Profile defined from an openPMD file.

Parameters
----------
path : string
Path to the openPMD file containing the laser field or envelope.
Passed directly OpenPMDTimeSeries.

iteration : int
Iteration at which the argument is read.
Passed directly OpenPMDTimeSeries.

pol : list of 2 complex numbers (dimensionless)
Polarization vector. It corresponds to :math:`p_u` in the above
formula ; :math:`p_x` is the first element of the list and
:math:`p_y` is the second element of the list. Using complex
numbers enables elliptical polarizations.

field : string
Name of the field containing the laser pulse
Passed directly OpenPMDTimeSeries.

coord : string
Name of the field containing the laser pulse
Passed directly OpenPMDTimeSeries.

envelope : boolean
Whether the file represents a laser envelope.
If not, the envelope is obtained from the electric field
using a Hilbert transform

prefix : string
Prefix of the openPMD file from which the envelope is read.
Only used when envelope=True.
The provided iteration is read from <path>/<prefix>_%T.h5.
"""

def __init__(
self, path, iteration, pol, field, coord=None, envelope=False, prefix=None
):
ts = OpenPMDTimeSeries(path)
F, m = ts.get_field(iteration=iteration, field=field, coord=coord, theta=None)
assert m.axes in [
{0: "x", 1: "y", 2: "z"},
{0: "z", 1: "y", 2: "x"},
{0: "x", 1: "y", 2: "t"},
{0: "t", 1: "y", 2: "x"},
]

if m.axes in [{0: "z", 1: "y", 2: "x"}, {0: "t", 1: "y", 2: "x"}]:
F = F.swapaxes(0, 2)

if "z" in m.axes.values():
t = (m.z - m.z[0]) / c
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here you implicitly set t[0]=0, while we usually center beam in time so t_peak=0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also to be discussed in #155.

else:
t = m.t
axes = {"x": m.x, "y": m.y, "t": t}

# If array does not contain the envelope but the electric field,
# extract the envelope with a Hilbert transform
if envelope == False:
# Assumes z is last dimension!
h = hilbert(F)

if "z" in m.axes.values():
# Flip to get complex envelope in t assuming z = -c*t
h = np.flip(h, axis=2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should probably use converters

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A typical workflow:

  • Run a FBPIC simulation, get z output
  • Read in LASY, and do this dummy conversion t=-z/c (needed as lasy requires t representation)
  • Write envelope to file, t representation
  • Read in from another code, and if needed do the inverse conversion (z = - c*t). Alternatively, this and the step above could be simplified if LASY could output z representation directly, see More features to the z-t converters #155.

Points 2 and 4 are exactly symmetric, which is what we want, and that's easily done with this dummy convertor, so I think it should be like this. But the clean way would be to make this dummy convertor an option of the import_from_z convertor, that's for another PR (after #155 is addressed).


# Get central wavelength from array
phase = np.unwrap(np.angle(h))
omg0_h = np.average(np.gradient(-phase, t, axis=-1), weights=np.abs(h))
wavelength = 2 * np.pi * c / omg0_h
array = h * np.exp(1j * omg0_h * t)
else:
s = io.Series(path + "/" + prefix + "_%T.h5", io.Access.read_only)
it = s.iterations[iteration]
omg0 = it.meshes["laserEnvelope"].get_attribute("angularFrequency")
wavelength = 2 * np.pi * c / omg0
array = F

axes_order = ["x", "y", "t"]
super().__init__(
wavelength=wavelength,
pol=pol,
array=array,
axes=axes,
axes_order=axes_order,
)
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
axiprop
numpy
openpmd-api
openpmd-viewer
scipy
Loading