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

Space time profile #182

Open
wants to merge 55 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
63de332
Add files via upload
spencerjolly Sep 13, 2023
92f3b71
Update spacetime_profile.py
spencerjolly Sep 13, 2023
9f8511f
Update spacetime_profile.py
spencerjolly Sep 13, 2023
54cba90
Update spacetime_profile.py
spencerjolly Sep 13, 2023
5e26c58
correct time variable name
spencerjolly Sep 13, 2023
2f98d43
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 13, 2023
8ae8c09
fix typos and remove mention of pft
spencerjolly Sep 18, 2023
e321db7
style changes
spencerjolly Sep 18, 2023
7553b76
remove spurious indent
spencerjolly Sep 20, 2023
deeee9b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 20, 2023
d7ce091
Update lasy/profiles/spacetime_profile.py
spencerjolly Dec 1, 2023
deaaee8
change sc to b in spacetime
spencerjolly Dec 1, 2023
6e0d701
change sc to b in spacetime
spencerjolly Dec 1, 2023
a3cae81
Update gaussian_profile.py
spencerjolly Dec 14, 2023
cf69c74
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 14, 2023
9602196
Update gaussian_profile.py
spencerjolly Dec 14, 2023
a37a7aa
Update test_gaussian_propagator.py
spencerjolly Dec 14, 2023
3e17248
Update test_laser_profiles.py
spencerjolly Dec 14, 2023
ae1f801
Update test_laser_profiles.py
spencerjolly Dec 14, 2023
7ace218
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 14, 2023
0fb6242
Update test_gaussian_propagator.py
spencerjolly Dec 14, 2023
8da190a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 14, 2023
b9405ee
Update gaussian_profile.py
spencerjolly Dec 14, 2023
4654d15
Update test_gaussian_propagator.py
spencerjolly Dec 14, 2023
a7a2bfd
Update test_laser_utils.py
spencerjolly Dec 14, 2023
74dbf87
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 14, 2023
6085d77
Update test_t2z2t.py
spencerjolly Dec 14, 2023
ae50294
Update test_t2z2t.py
spencerjolly Dec 14, 2023
ef8765d
Update test_gaussian_propagator.py
spencerjolly Jan 12, 2024
a3b1a54
Update gaussian_profile.py
spencerjolly Jan 12, 2024
c830f08
Update laser_utils.py
spencerjolly Jan 12, 2024
d118d42
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 12, 2024
78524b3
Update laser_utils.py
spencerjolly Jan 15, 2024
fb2f9ae
Update test_laser_utils.py
spencerjolly Jan 15, 2024
483fcf3
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 15, 2024
864f22c
Update test_laser_utils.py
spencerjolly Jan 15, 2024
0670864
Update test_laser_utils.py
spencerjolly Jan 15, 2024
afc1cff
Update test_laser_utils.py
spencerjolly Jan 15, 2024
df03522
Update test_gaussian_propagator.py
spencerjolly Jan 15, 2024
08a73ae
Update test_gaussian_propagator.py
spencerjolly Jan 15, 2024
d849dde
Update test_gaussian_propagator.py
spencerjolly Jan 15, 2024
5a21323
Update test_gaussian_propagator.py
spencerjolly Jan 15, 2024
46eb96a
Update test_laser_utils.py
spencerjolly Jan 15, 2024
43250a6
Update test_laser_profiles.py
spencerjolly Jan 15, 2024
15cc158
Update test_laser_profiles.py
spencerjolly Jan 15, 2024
9663291
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 15, 2024
41b17de
Update test_laser_profiles.py
spencerjolly Jan 15, 2024
f525a8e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 15, 2024
d05ea42
Update test_laser_utils.py
spencerjolly Jan 15, 2024
b4d6b9b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 15, 2024
2f6b091
Update test_laser_utils.py
spencerjolly Jan 15, 2024
b675f8a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 15, 2024
02aa927
Update test_laser_utils.py
spencerjolly Jan 15, 2024
b6f32e6
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 15, 2024
8717ea8
Update test_laser_utils.py
spencerjolly Jan 15, 2024
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
94 changes: 79 additions & 15 deletions lasy/profiles/gaussian_profile.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
from . import CombinedLongitudinalTransverseProfile
from .longitudinal import GaussianLongitudinalProfile
from .transverse import GaussianTransverseProfile
import numpy as np

from .profile import Profile

class GaussianProfile(CombinedLongitudinalTransverseProfile):

class GaussianProfile(Profile):
r"""
Derived class for the analytic profile of a Gaussian laser pulse.

This includes space-time couplings: pulse-front tilt and spatial chirp

More precisely, the electric field corresponds to:

.. math::

E_u(\boldsymbol{x}_\perp,t) = Re\left[ E_0\,
\exp\left( -\frac{\boldsymbol{x}_\perp^2}{w_0^2}
- \frac{(t-t_{peak})^2}{\tau^2} -i\omega_0(t-t_{peak})
+ i\phi_{cep}\right) \times p_u \right]
E_u(\\boldsymbol{x}_\\perp,t) = Re\\left[ E_0\\,
\\exp\\left(-\\frac{\\boldsymbol{x}_\\perp^2}{w_0^2}
- \\frac{(t-t_{peak}-ax+2ibx/w_0^2)^2}{\\tau_{eff}^2}
- i\\omega_0(t-t_{peak}) + i\\phi_{cep}\\right) \\times p_u \\right]

where :math:`u` is either :math:`x` or :math:`y`, :math:`p_u` is
the polarization vector, :math:`Re` represent the real part, and
Expand Down Expand Up @@ -53,6 +55,18 @@ class GaussianProfile(CombinedLongitudinalTransverseProfile):
The time at which the laser envelope reaches its maximum amplitude,
i.e. :math:`t_{peak}` in the above formula.

a: float (in second/meter), optional
Pulse-front tilt, i.e. :math:`a` in the above formula, that results in the laser arrival
time varying as a function of `x`. A representative real value is a = tau / w0.

b: float (in meter.second), optional
Spatial chirp, i.e. :math:`b` in the above formula, that results in the laser frequency
varying as a function of `x`. A representative real value is b = w0 * tau.

GDD: float (in second.second), optional
Group-delay dispersion, i.e. :math:`gdd` in the formula for tau_eff, that results
in temporal chirp. A representative real value is gdd = tau * tau.

cep_phase : float (in radian), optional
The Carrier Envelope Phase (CEP), i.e. :math:`\phi_{cep}`
in the above formula (i.e. the phase of the laser
Expand Down Expand Up @@ -104,12 +118,62 @@ class GaussianProfile(CombinedLongitudinalTransverseProfile):
"""

def __init__(
self, wavelength, pol, laser_energy, w0, tau, t_peak, cep_phase=0, z_foc=0
self,
wavelength,
pol,
laser_energy,
w0,
tau,
t_peak,
a=0.0,
b=0.0,
gdd=0.0,
cep_phase=0.0,
z_foc=0.0,
):
super().__init__(
wavelength,
pol,
laser_energy,
GaussianLongitudinalProfile(wavelength, tau, t_peak, cep_phase),
GaussianTransverseProfile(w0, z_foc, wavelength),
super().__init__(wavelength, pol)
self.laser_energy = laser_energy
self.w0 = w0
self.tau = tau
self.t_peak = t_peak
self.a = a
self.b = b
self.gdd = gdd
self.cep_phase = cep_phase
self.z_foc = z_foc

def evaluate(self, x, y, t):
"""
Return the envelope field of the laser.

Parameters
----------
x, y, t: ndarrays of floats
Define points on which to evaluate the envelope
These arrays need to all have the same shape.

Returns
-------
envelope: ndarray of complex numbers
Contains the value of the envelope at the specified points
This array has the same shape as the arrays x, y, t
"""
transverse = np.exp(-(x**2 + y**2) / self.w0**2)

tau_eff = np.sqrt(
self.tau**2 + (2 * self.b / self.w0) ** 2 + 2 * 1j * self.gdd
)

spacetime = np.exp(
-(
(t - self.t_peak - self.a * x + (2 * 1j * self.b * x / self.w0**2))
** 2
)
/ tau_eff**2
)

oscillatory = np.exp(1.0j * (self.cep_phase - self.omega0 * (t - self.t_peak)))

envelope = transverse * spacetime * oscillatory

return envelope
103 changes: 103 additions & 0 deletions lasy/profiles/spacetime_profile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import numpy as np

from .profile import Profile


class SpaceTimeProfile(Profile):
Copy link
Member

Choose a reason for hiding this comment

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

Maybe this could actually be part of the GaussianProfile laser. See other comments.

Copy link
Author

Choose a reason for hiding this comment

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

I don't actually see how it could be applied with GaussianProfile. In that class it calls the temporal and spatial profiles separately, but we need access to the space and time dimensions at the same time.

Copy link
Member

@RemiLehe RemiLehe Dec 1, 2023

Choose a reason for hiding this comment

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

Yes, agreed. If we decide to have it applied with GaussianProfile, we would completely change the implementation of the GaussianProfile (which is definitely possible): instead of calling the temporal and spatial profile separately, we would have the Python code for the gaussian profile directly in the body of the GaussianProfile class. Does that work for you?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, ok with me. In fact, it would mostly entail pasting the code from this PR to the GaussianProfile class. However, I also wanted to add a pulse-front tilt parameter (in addition to the current "spatial chirp" parameter). I could do that in this PR so that it is all there, and then we could paste it.

Let me know how you want to continue.

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good. I think it is good to do all of the above as part of the current PR, i.e. adding spatial chirp, and moving all of this to the GaussianProfile class.

Copy link
Author

Choose a reason for hiding this comment

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

I've done these changes. There are some weird test and docs errors at the moment, but in any case I need to build a stand-alone test for the space-time cases. I will try to do it next week.

r"""
Class that can evaluate a pulse that has certain space-time couplings.

More precisely, the electric field corresponds to:

.. math::

E_u(\\boldsymbol{x}_\\perp,t) = Re\\left[ E_0\\,
\\exp\\left(-\\frac{\\boldsymbol{x}_\\perp^2}{w_0^2}
- \\frac{(t-t_{peak}+2ibx/w_0^2)^2}{\\tau_{eff}^2}
- i\\omega_0(t-t_{peak}) + i\\phi_{cep}\\right) \\times p_u \\right]

where :math:`u` is either :math:`x` or :math:`y`, :math:`p_u` is
the polarization vector, :math:`Re` represent the real part.
The other parameters in this formula are defined below.

Parameters
----------
wavelength: float (in meter)
The main laser wavelength :math:`\\lambda_0` of the laser, which
defines :math:`\\omega_0` in the above formula, according to
:math:`\\omega_0 = 2\\pi c/\\lambda_0`.

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.

laser_energy: float (in Joule)
The total energy of the laser pulse. The amplitude of the laser
field (:math:`E_0` in the above formula) is automatically
calculated so that the pulse has the prescribed energy.

tau: float (in second)
The duration of the laser pulse, i.e. :math:`\\tau` in the above
formula. Note that :math:`\\tau = \\tau_{FWHM}/\\sqrt{2\\log(2)}`,
where :math:`\\tau_{FWHM}` is the Full-Width-Half-Maximum duration
of the intensity distribution of the pulse.

w0: float (in meter)
The waist of the laser pulse, i.e. :math:`w_0` in the above formula.

b: float (in meter.second)
Spatial chirp, i.e. :math:`b` in the above formula, that results in the laser frequency
varying as a function of `x`. A representative real value is b = w0 * tau.


t_peak: float (in second)
The time at which the laser envelope reaches its maximum amplitude,
i.e. :math:`t_{peak}` in the above formula.

cep_phase: float (in radian), optional
The Carrier Enveloppe Phase (CEP), i.e. :math:`\\phi_{cep}`
in the above formula (i.e. the phase of the laser
oscillation, at the time where the laser envelope is maximum)
"""

def __init__(self, wavelength, pol, laser_energy, w0, tau, b, t_peak, cep_phase=0):
super().__init__(wavelength, pol)
self.laser_energy = laser_energy
self.w0 = w0
self.tau = tau
self.b = b
self.t_peak = t_peak
self.cep_phase = cep_phase

def evaluate(self, x, y, t):
"""
Return the envelope field of the laser.

Parameters
----------
x, y, t: ndarrays of floats
Define points on which to evaluate the envelope
These arrays need to all have the same shape.

Returns
-------
envelope: ndarray of complex numbers
Contains the value of the envelope at the specified points
This array has the same shape as the arrays x, y, t
"""
transverse = np.exp(-(x**2 + y**2) / self.w0**2)

tau_eff = np.sqrt(self.tau**2 + (2 * self.b / self.w0) ** 2)

spacetime = np.exp(
-((t - self.t_peak + (2 * 1j * self.b * x / self.w0**2)) ** 2)
/ tau_eff**2
)

oscillatory = np.exp(1.0j * (self.cep_phase - self.omega0 * (t - self.t_peak)))

envelope = transverse * spacetime * oscillatory

return envelope
26 changes: 26 additions & 0 deletions lasy/utils/laser_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,32 @@ def get_frequency(
return omega, central_omega


def get_t_peak(grid, dim):
"""Get central time of the intensity of the envelope, measured as an average.

Parameters
----------
grid : Grid
The grid with the envelope to analyze.
dim : str
Dimensionality of the grid.

Returns
-------
float
average position of the envelope intensity in seconds.
"""
# Calculate weights of each grid cell (amplitude of the field).
dV = get_grid_cell_volume(grid, dim)
if dim == "xyt":
weights = np.abs(grid.field) ** 2 * dV
else: # dim == "rt":
weights = np.abs(grid.field) ** 2 * dV[np.newaxis, :, np.newaxis]
# project weights to longitudinal axes
weights = np.sum(weights, axis=(0, 1))
return np.average(grid.axes[-1], weights=weights)


def get_duration(grid, dim):
"""Get duration of the intensity of the envelope, measured as RMS.

Expand Down
2 changes: 1 addition & 1 deletion tests/test_gaussian_propagator.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def check_gaussian_propagation(
laser, propagation_distance=100e-6, propagation_step=10e-6
):
# Do the propagation and check evolution of waist with theory
w0 = laser.profile.trans_profile.w0
w0 = laser.profile.w0
L_R = np.pi * w0**2 / laser.profile.lambda0

propagated_distance = 0.0
Expand Down
66 changes: 65 additions & 1 deletion tests/test_laser_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,45 @@ def gaussian():
t_peak = 0.0e-15 # s
tau = 30.0e-15 # s
w0 = 5.0e-6 # m
profile = GaussianProfile(wavelength, pol, laser_energy, w0, tau, t_peak)
profile = GaussianProfile(
wavelength, pol, laser_energy, w0, tau, t_peak, a=0.0, b=0.0, gdd=0.0
)

return profile


@pytest.fixture(scope="function")
def spatial_chirp():
# Cases with Gaussian laser having non-zero spatial chirp (b)
wavelength = 0.8e-6
pol = (1, 0)
laser_energy = 1.0 # J
t_peak = 0.0e-15 # s
tau = 30.0e-15 # s
w0 = 5.0e-6 # m
a = 0.0
b = w0 * tau / 2 # m.s
profile = GaussianProfile(
wavelength, pol, laser_energy, w0, tau, t_peak, a, b, gdd=0.0
)

return profile


@pytest.fixture(scope="function")
def angular_dispersion():
# Cases with Gaussian laser having non-zero angular dispersion (a)
wavelength = 0.8e-6
pol = (1, 0)
laser_energy = 1.0 # J
t_peak = 0.0e-15 # s
tau = 30.0e-15 # s
w0 = 5.0e-6 # m
a = tau / w0 # s/m
b = 0.0
profile = GaussianProfile(
wavelength, pol, laser_energy, w0, tau, t_peak, a, b, gdd=0.0
)

return profile

Expand Down Expand Up @@ -158,6 +196,32 @@ def test_profile_gaussian_cylindrical(gaussian):
laser.write_to_file("gaussianlaserRZ")


def test_profile_gaussian_spatial_chirp(spatial_chirp):
# - 3D Cartesian case
dim = "xyt"
lo = (-10e-6, -10e-6, -60e-15)
hi = (+10e-6, +10e-6, +60e-15)
npoints = (100, 100, 100)

laser = Laser(dim, lo, hi, npoints, spatial_chirp)
laser.write_to_file("gaussianlaserSC")
laser.propagate(1e-6)
laser.write_to_file("gaussianlaserSC")


def test_profile_gaussian_angular_dispersion(angular_dispersion):
# - 3D Cartesian case
dim = "xyt"
lo = (-10e-6, -10e-6, -60e-15)
hi = (+10e-6, +10e-6, +60e-15)
npoints = (100, 100, 100)

laser = Laser(dim, lo, hi, npoints, angular_dispersion)
laser.write_to_file("gaussianlaserAD")
laser.propagate(1e-6)
laser.write_to_file("gaussianlaserAD")


def test_from_array_profile():
# Create a 3D numpy array, use it to create a LASY profile,
# and check that the resulting profile has the correct width
Expand Down
Loading
Loading