From a4041f76e2a629af9e49981ae84d0180dac8eae3 Mon Sep 17 00:00:00 2001 From: "rob.shalloo" Date: Fri, 21 Feb 2025 00:10:41 +0100 Subject: [PATCH 01/31] update definitions and add docs --- .../transverse/flattened_gaussian_profile.py | 142 +++++++++++------- tests/test_laser_profiles.py | 44 ++++++ 2 files changed, 134 insertions(+), 52 deletions(-) diff --git a/lasy/profiles/transverse/flattened_gaussian_profile.py b/lasy/profiles/transverse/flattened_gaussian_profile.py index 6d1b6fff..51a08cb7 100644 --- a/lasy/profiles/transverse/flattened_gaussian_profile.py +++ b/lasy/profiles/transverse/flattened_gaussian_profile.py @@ -1,5 +1,6 @@ import numpy as np from scipy.special import binom +import math from .transverse_profile import TransverseProfile @@ -17,10 +18,10 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): flatness of the transverse profile **far from focus**, and increases the number of rings **in the focal plane**. - The implementation of this class is directly copied from that in `FBPIC` + The implementation of this class is based on that from `FBPIC` . - **In the focal plane** (:math:`z=z_f`), the profile translates to a + **In the focal plane** (:math:`z=z_f`), or in the far field, the profile translates to a laser with a transverse electric field: .. math:: @@ -36,7 +37,8 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): - For :math:`N\rightarrow\infty`, this is a Jinc profile: :math:`E\propto \frac{J_1(r/w0)}{r/w0}`. - The equivalent expression **far from focus** is + The equivalent expression for the collimated beam in the near field which produces this focus is + given by: .. math:: @@ -46,15 +48,26 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): \mathrm{with} \qquad w(z) = \frac{\lambda_0}{\pi w0}|z-z_{foc}| + - Note that a beam defined using the near field definition would be equivalent to a beam defined with + the corresponding parameters in the far field, but without the parabolic phase arising from being defined + far from the focus. + - For :math:`N=0`, this is a Gaussian profile: :math:`E\propto\exp\left(-\frac{r^2}{w_(z)^2}\right)`. - For :math:`N\rightarrow\infty`, this is a flat profile: :math:`E\propto \Theta(w(z)-r)`. Parameters ---------- - w0 : float (in meter) - The waist of the laser pulse, - i.e. :math:`w_{0}` in the above formula. + field_type : string + Options: 'nearfield', when the beam is defined far from focus and + has been collimated, or 'farfield', when the beam is in the vincinity + of or has been directly propagated from the focus. In this case there + can be a large defocus in the spatial phase. + w : float (in meter) + The waist of the laser pulse. If field_type == 'farfield' then this + variable corresponds to :math:`w_{0}` in the above far field formula. + if field_type == 'nearfield' then this variable corresponds to + :math:`w(z)` in the above near field formula. N: int Determines the "flatness" of the transverse profile, far from focus (see the above formula). @@ -62,7 +75,8 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): wavelength : float (in meter) The main laser wavelength :math:`\lambda_0` of the laser. z_foc : float (in meter), optional - Position of the focal plane. (The laser pulse is initialized at + Only required if defining the pulse in the far field. Gives the position + of the focal plane. (The laser pulse is initialized at ``z=0``.) Warnings @@ -78,23 +92,31 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): not make this approximation. """ - def __init__(self, w0, N, wavelength, z_foc=0): + def __init__(self, field_type, w, N, wavelength, z_foc=0): super().__init__() # Ensure that N is an integer self.N = int(round(N)) - # Calculate effective waist of the Laguerre-Gauss modes, at focus - self.w_foc = w0 * (self.N + 1) ** 0.5 - # Calculate Rayleigh Length - self.zr = np.pi * self.w_foc**2 / wavelength - # Evaluation distance w.r.t focal position - self.z_eval = z_foc - # Calculate the coefficients for the Laguerre-Gaussian modes - self.cn = np.empty(self.N + 1) - for n in range(self.N + 1): - m_values = np.arange(n, self.N + 1) - self.cn[n] = np.sum((1.0 / 2) ** m_values * binom(m_values, n)) / ( - self.N + 1 - ) + assert field_type in ['nearfield', 'farfield'] + self.field_type = field_type + + if field_type == 'farfield': + w0 = w + # Calculate effective waist of the Laguerre-Gauss modes, at focus + self.w_foc = w0 * (self.N + 1) ** 0.5 + # Calculate Rayleigh Length + self.zr = np.pi * self.w_foc**2 / wavelength + # Evaluation distance w.r.t focal position + self.z_eval = z_foc + # Calculate the coefficients for the Laguerre-Gaussian modes + self.cn = np.empty(self.N + 1) + for n in range(self.N + 1): + m_values = np.arange(n, self.N + 1) + self.cn[n] = np.sum((1.0 / 2) ** m_values * binom(m_values, n)) / ( + self.N + 1 + ) + else: + self.w = w + def _evaluate(self, x, y): """ @@ -112,34 +134,50 @@ def _evaluate(self, x, y): Contains the value of the envelope at the specified points This array has the same shape as the arrays x, y """ - # Term for wavefront curvature + Gouy phase - diffract_factor = 1.0 - 1j * self.z_eval / self.zr - w = self.w_foc * np.abs(diffract_factor) - psi = np.angle(diffract_factor) - # Argument for the Laguerre polynomials - scaled_radius_squared = 2 * (x**2 + y**2) / w**2 - - # Sum recursively over the Laguerre polynomials - laguerre_sum = np.zeros_like(x, dtype=np.complex128) - for n in range(0, self.N + 1): - # Recursive calculation of the Laguerre polynomial - # - `L` represents $L_n$ - # - `L1` represents $L_{n-1}$ - # - `L2` represents $L_{n-2}$ - if n == 0: - L = 1.0 - elif n == 1: - L1 = L - L = 1.0 - scaled_radius_squared - else: - L2 = L1 - L1 = L - L = (((2 * n - 1) - scaled_radius_squared) * L1 - (n - 1) * L2) / n - # Add to the sum, including the term for the additional Gouy phase - laguerre_sum += self.cn[n] * np.exp(-(2j * n) * psi) * L - - # Final envelope: multiply by n-independent propagation factors - exp_argument = -(x**2 + y**2) / (self.w_foc**2 * diffract_factor) - envelope = laguerre_sum * np.exp(exp_argument) / diffract_factor - - return envelope + + if self.field_type == 'farfield': + # Term for wavefront curvature + Gouy phase + diffract_factor = 1.0 - 1j * self.z_eval / self.zr + w = self.w_foc * np.abs(diffract_factor) + psi = np.angle(diffract_factor) + # Argument for the Laguerre polynomials + scaled_radius_squared = 2 * (x**2 + y**2) / w**2 + + # Sum recursively over the Laguerre polynomials + laguerre_sum = np.zeros_like(x, dtype=np.complex128) + for n in range(0, self.N + 1): + # Recursive calculation of the Laguerre polynomial + # - `L` represents $L_n$ + # - `L1` represents $L_{n-1}$ + # - `L2` represents $L_{n-2}$ + if n == 0: + L = 1.0 + elif n == 1: + L1 = L + L = 1.0 - scaled_radius_squared + else: + L2 = L1 + L1 = L + L = (((2 * n - 1) - scaled_radius_squared) * L1 - (n - 1) * L2) / n + # Add to the sum, including the term for the additional Gouy phase + laguerre_sum += self.cn[n] * np.exp(-(2j * n) * psi) * L + + # Final envelope: multiply by n-independent propagation factors + exp_argument = -(x**2 + y**2) / (self.w_foc**2 * diffract_factor) + envelope = laguerre_sum * np.exp(exp_argument) / diffract_factor + + return envelope + + else: + N = self.N + w = self.w + + sumseries = 0 + if N > 0: + for n in range(N): + sumseries += 1/math.factorial(n) * ((N+1)*(x**2+y**2)/w**2)**n + + envelope = np.exp( - (N+1)*(x**2+y**2) / w**2) * sumseries + + return envelope + diff --git a/tests/test_laser_profiles.py b/tests/test_laser_profiles.py index d328f472..22ef835e 100644 --- a/tests/test_laser_profiles.py +++ b/tests/test_laser_profiles.py @@ -2,6 +2,7 @@ import numpy as np import pytest +import copy from scipy.constants import c from lasy.laser import Laser @@ -23,7 +24,9 @@ SuperGaussianTransverseProfile, TransverseProfile, TransverseProfileFromData, + FlattenedGaussianTransverseProfile, ) +from lasy.profiles import CombinedLongitudinalTransverseProfile from lasy.utils.exp_data_utils import find_center_of_mass @@ -533,3 +536,44 @@ def test_scale_trans_error_if_not_scalar(): trans_profile_1 * trans_profile_1 with pytest.raises(AssertionError): trans_profile_1 * [1.0, 2.0] + +def test_flattened_gaussian_profile(): + w = 20e-3 + N = 25 + wl = 800e-9 + tau = 30e-15 + pol = (1,0) + energy = 1.0 + focal_length = 1.0 + + w0 = focal_length * wl/np.pi/w + + nf = FlattenedGaussianTransverseProfile(field_type='nearfield',w=w,N=N,wavelength=wl) + ff = FlattenedGaussianTransverseProfile(field_type='farfield',w=w0,N=N,wavelength=wl) + + long = GaussianLongitudinalProfile(wl,tau,0) + + nf_prof = CombinedLongitudinalTransverseProfile(wl,pol,energy,long,nf) + ff_prof = CombinedLongitudinalTransverseProfile(wl,pol,energy,long,ff) + + dim = 'rt' + lo = (0,-100e-15) + hi_ff = (1000e-6,100e-15) + hi_nf = (40e-3,100e-15) + npoints = (5000,200) + + las_nf = Laser(dim, lo, hi_nf, npoints, nf_prof) + las_ff = Laser(dim, lo, hi_ff, npoints, ff_prof) + + las_nf_cp = copy.deepcopy(las_nf) + + OAP = ParabolicMirror(f=focal_length) + las_nf_cp.apply_optics(OAP) + las_nf_cp.propagate(focal_length,grid=Grid(dim, lo, hi_ff, npoints,n_azimuthal_modes=1)) + + radlineout_nf = np.abs(las_nf_cp.grid.get_temporal_field()[0,:,int(npoints[1]/2)])**2 + radlineout_ff = np.abs(las_ff.grid.get_temporal_field()[0,:,int(npoints[1]/2)])**2 + + err = np.sum(np.abs(las_nf_cp.grid.get_temporal_field()[0,:,:]-las_ff.grid.get_temporal_field()[0,:,:])**2) + + assert(err<1) \ No newline at end of file From 98a7a2c0e7f67a2c48f3aae98ad4ea6bb3eea702 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 20 Feb 2025 23:12:50 +0000 Subject: [PATCH 02/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../transverse/flattened_gaussian_profile.py | 36 +++++----- tests/test_laser_profiles.py | 70 ++++++++++++------- 2 files changed, 64 insertions(+), 42 deletions(-) diff --git a/lasy/profiles/transverse/flattened_gaussian_profile.py b/lasy/profiles/transverse/flattened_gaussian_profile.py index 51a08cb7..ff1b4783 100644 --- a/lasy/profiles/transverse/flattened_gaussian_profile.py +++ b/lasy/profiles/transverse/flattened_gaussian_profile.py @@ -1,6 +1,7 @@ +import math + import numpy as np from scipy.special import binom -import math from .transverse_profile import TransverseProfile @@ -37,7 +38,7 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): - For :math:`N\rightarrow\infty`, this is a Jinc profile: :math:`E\propto \frac{J_1(r/w0)}{r/w0}`. - The equivalent expression for the collimated beam in the near field which produces this focus is + The equivalent expression for the collimated beam in the near field which produces this focus is given by: .. math:: @@ -50,7 +51,7 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): - Note that a beam defined using the near field definition would be equivalent to a beam defined with the corresponding parameters in the far field, but without the parabolic phase arising from being defined - far from the focus. + far from the focus. - For :math:`N=0`, this is a Gaussian profile: :math:`E\propto\exp\left(-\frac{r^2}{w_(z)^2}\right)`. @@ -64,9 +65,9 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): of or has been directly propagated from the focus. In this case there can be a large defocus in the spatial phase. w : float (in meter) - The waist of the laser pulse. If field_type == 'farfield' then this + The waist of the laser pulse. If field_type == 'farfield' then this variable corresponds to :math:`w_{0}` in the above far field formula. - if field_type == 'nearfield' then this variable corresponds to + if field_type == 'nearfield' then this variable corresponds to :math:`w(z)` in the above near field formula. N: int Determines the "flatness" of the transverse profile, far from @@ -75,7 +76,7 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): wavelength : float (in meter) The main laser wavelength :math:`\lambda_0` of the laser. z_foc : float (in meter), optional - Only required if defining the pulse in the far field. Gives the position + Only required if defining the pulse in the far field. Gives the position of the focal plane. (The laser pulse is initialized at ``z=0``.) @@ -96,10 +97,10 @@ def __init__(self, field_type, w, N, wavelength, z_foc=0): super().__init__() # Ensure that N is an integer self.N = int(round(N)) - assert field_type in ['nearfield', 'farfield'] + assert field_type in ["nearfield", "farfield"] self.field_type = field_type - if field_type == 'farfield': + if field_type == "farfield": w0 = w # Calculate effective waist of the Laguerre-Gauss modes, at focus self.w_foc = w0 * (self.N + 1) ** 0.5 @@ -117,7 +118,6 @@ def __init__(self, field_type, w, N, wavelength, z_foc=0): else: self.w = w - def _evaluate(self, x, y): """ Return the transverse envelope. @@ -134,8 +134,7 @@ def _evaluate(self, x, y): Contains the value of the envelope at the specified points This array has the same shape as the arrays x, y """ - - if self.field_type == 'farfield': + if self.field_type == "farfield": # Term for wavefront curvature + Gouy phase diffract_factor = 1.0 - 1j * self.z_eval / self.zr w = self.w_foc * np.abs(diffract_factor) @@ -167,17 +166,18 @@ def _evaluate(self, x, y): envelope = laguerre_sum * np.exp(exp_argument) / diffract_factor return envelope - + else: N = self.N w = self.w - + sumseries = 0 if N > 0: for n in range(N): - sumseries += 1/math.factorial(n) * ((N+1)*(x**2+y**2)/w**2)**n - - envelope = np.exp( - (N+1)*(x**2+y**2) / w**2) * sumseries - - return envelope + sumseries += ( + 1 / math.factorial(n) * ((N + 1) * (x**2 + y**2) / w**2) ** n + ) + envelope = np.exp(-(N + 1) * (x**2 + y**2) / w**2) * sumseries + + return envelope diff --git a/tests/test_laser_profiles.py b/tests/test_laser_profiles.py index 22ef835e..a4b950c8 100644 --- a/tests/test_laser_profiles.py +++ b/tests/test_laser_profiles.py @@ -1,12 +1,18 @@ # -*- coding: utf-8 -*- +import copy + import numpy as np import pytest -import copy from scipy.constants import c from lasy.laser import Laser -from lasy.profiles import FromArrayProfile, GaussianProfile, SpeckleProfile +from lasy.profiles import ( + CombinedLongitudinalTransverseProfile, + FromArrayProfile, + GaussianProfile, + SpeckleProfile, +) from lasy.profiles.longitudinal import ( CosineLongitudinalProfile, GaussianLongitudinalProfile, @@ -15,6 +21,7 @@ ) from lasy.profiles.profile import Profile, ScaledProfile, SummedProfile from lasy.profiles.transverse import ( + FlattenedGaussianTransverseProfile, GaussianTransverseProfile, HermiteGaussianTransverseProfile, JincTransverseProfile, @@ -24,9 +31,7 @@ SuperGaussianTransverseProfile, TransverseProfile, TransverseProfileFromData, - FlattenedGaussianTransverseProfile, ) -from lasy.profiles import CombinedLongitudinalTransverseProfile from lasy.utils.exp_data_utils import find_center_of_mass @@ -537,43 +542,60 @@ def test_scale_trans_error_if_not_scalar(): with pytest.raises(AssertionError): trans_profile_1 * [1.0, 2.0] + def test_flattened_gaussian_profile(): w = 20e-3 N = 25 wl = 800e-9 tau = 30e-15 - pol = (1,0) + pol = (1, 0) energy = 1.0 focal_length = 1.0 - w0 = focal_length * wl/np.pi/w + w0 = focal_length * wl / np.pi / w - nf = FlattenedGaussianTransverseProfile(field_type='nearfield',w=w,N=N,wavelength=wl) - ff = FlattenedGaussianTransverseProfile(field_type='farfield',w=w0,N=N,wavelength=wl) + nf = FlattenedGaussianTransverseProfile( + field_type="nearfield", w=w, N=N, wavelength=wl + ) + ff = FlattenedGaussianTransverseProfile( + field_type="farfield", w=w0, N=N, wavelength=wl + ) - long = GaussianLongitudinalProfile(wl,tau,0) + long = GaussianLongitudinalProfile(wl, tau, 0) - nf_prof = CombinedLongitudinalTransverseProfile(wl,pol,energy,long,nf) - ff_prof = CombinedLongitudinalTransverseProfile(wl,pol,energy,long,ff) + nf_prof = CombinedLongitudinalTransverseProfile(wl, pol, energy, long, nf) + ff_prof = CombinedLongitudinalTransverseProfile(wl, pol, energy, long, ff) - dim = 'rt' - lo = (0,-100e-15) - hi_ff = (1000e-6,100e-15) - hi_nf = (40e-3,100e-15) - npoints = (5000,200) + dim = "rt" + lo = (0, -100e-15) + hi_ff = (1000e-6, 100e-15) + hi_nf = (40e-3, 100e-15) + npoints = (5000, 200) - las_nf = Laser(dim, lo, hi_nf, npoints, nf_prof) - las_ff = Laser(dim, lo, hi_ff, npoints, ff_prof) + las_nf = Laser(dim, lo, hi_nf, npoints, nf_prof) + las_ff = Laser(dim, lo, hi_ff, npoints, ff_prof) las_nf_cp = copy.deepcopy(las_nf) OAP = ParabolicMirror(f=focal_length) las_nf_cp.apply_optics(OAP) - las_nf_cp.propagate(focal_length,grid=Grid(dim, lo, hi_ff, npoints,n_azimuthal_modes=1)) + las_nf_cp.propagate( + focal_length, grid=Grid(dim, lo, hi_ff, npoints, n_azimuthal_modes=1) + ) + + radlineout_nf = ( + np.abs(las_nf_cp.grid.get_temporal_field()[0, :, int(npoints[1] / 2)]) ** 2 + ) + radlineout_ff = ( + np.abs(las_ff.grid.get_temporal_field()[0, :, int(npoints[1] / 2)]) ** 2 + ) - radlineout_nf = np.abs(las_nf_cp.grid.get_temporal_field()[0,:,int(npoints[1]/2)])**2 - radlineout_ff = np.abs(las_ff.grid.get_temporal_field()[0,:,int(npoints[1]/2)])**2 + err = np.sum( + np.abs( + las_nf_cp.grid.get_temporal_field()[0, :, :] + - las_ff.grid.get_temporal_field()[0, :, :] + ) + ** 2 + ) - err = np.sum(np.abs(las_nf_cp.grid.get_temporal_field()[0,:,:]-las_ff.grid.get_temporal_field()[0,:,:])**2) - - assert(err<1) \ No newline at end of file + assert err < 1 From 2eb0ebb44c74d7158d3d3c7252850f37898cd932 Mon Sep 17 00:00:00 2001 From: "rob.shalloo" Date: Fri, 21 Feb 2025 21:52:44 +0100 Subject: [PATCH 03/31] test --- .../api/profiles/transverse/flattened_gaussian.rst | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 docs/source/api/profiles/transverse/flattened_gaussian.rst diff --git a/docs/source/api/profiles/transverse/flattened_gaussian.rst b/docs/source/api/profiles/transverse/flattened_gaussian.rst new file mode 100644 index 00000000..435f129a --- /dev/null +++ b/docs/source/api/profiles/transverse/flattened_gaussian.rst @@ -0,0 +1,9 @@ +Flattened Gaussian Transverse Profile +===================================== + +Implements a flattened transverse Gaussian profile. +This profile can either be defined close to a focus, in a far-field regime or in a collimated beam in the near field. +------------ + +.. autoclass:: lasy.profiles.transverse.FlattenedGaussianTransverseProfile + :members: From 5b6f4f1e3b06b6d26f5bf079da9673c8e61a7666 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 21 Feb 2025 20:53:41 +0000 Subject: [PATCH 04/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/source/api/profiles/transverse/flattened_gaussian.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/api/profiles/transverse/flattened_gaussian.rst b/docs/source/api/profiles/transverse/flattened_gaussian.rst index 435f129a..94ba6158 100644 --- a/docs/source/api/profiles/transverse/flattened_gaussian.rst +++ b/docs/source/api/profiles/transverse/flattened_gaussian.rst @@ -2,7 +2,7 @@ Flattened Gaussian Transverse Profile ===================================== Implements a flattened transverse Gaussian profile. -This profile can either be defined close to a focus, in a far-field regime or in a collimated beam in the near field. +This profile can either be defined close to a focus, in a far-field regime or in a collimated beam in the near field. ------------ .. autoclass:: lasy.profiles.transverse.FlattenedGaussianTransverseProfile From 6051a0480b99740c406a68a7c2d889ae7f9e997a Mon Sep 17 00:00:00 2001 From: "rob.shalloo" Date: Mon, 24 Feb 2025 21:04:17 +0100 Subject: [PATCH 05/31] fix import --- lasy/profiles/transverse/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lasy/profiles/transverse/__init__.py b/lasy/profiles/transverse/__init__.py index af9daa7a..65aee97c 100644 --- a/lasy/profiles/transverse/__init__.py +++ b/lasy/profiles/transverse/__init__.py @@ -2,6 +2,7 @@ from .hermite_gaussian_profile import HermiteGaussianTransverseProfile from .jinc_profile import JincTransverseProfile from .laguerre_gaussian_profile import LaguerreGaussianTransverseProfile +from .flattened_gaussian_profile import FlattenedGaussianTransverseProfile from .super_gaussian_profile import SuperGaussianTransverseProfile from .transverse_profile import ( ScaledTransverseProfile, @@ -20,4 +21,5 @@ "TransverseProfile", "SummedTransverseProfile", "ScaledTransverseProfile", + "FlattenedGaussianTransverseProfile", ] From 6a7c9691a6596f258b917f1c27caae240ef01687 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 20:04:35 +0000 Subject: [PATCH 06/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- lasy/profiles/transverse/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lasy/profiles/transverse/__init__.py b/lasy/profiles/transverse/__init__.py index 65aee97c..e0d80190 100644 --- a/lasy/profiles/transverse/__init__.py +++ b/lasy/profiles/transverse/__init__.py @@ -1,8 +1,8 @@ +from .flattened_gaussian_profile import FlattenedGaussianTransverseProfile from .gaussian_profile import GaussianTransverseProfile from .hermite_gaussian_profile import HermiteGaussianTransverseProfile from .jinc_profile import JincTransverseProfile from .laguerre_gaussian_profile import LaguerreGaussianTransverseProfile -from .flattened_gaussian_profile import FlattenedGaussianTransverseProfile from .super_gaussian_profile import SuperGaussianTransverseProfile from .transverse_profile import ( ScaledTransverseProfile, From 91f3a40e53df2973bdbb83368d5c34c6333f91fa Mon Sep 17 00:00:00 2001 From: "rob.shalloo" Date: Mon, 24 Feb 2025 21:08:05 +0100 Subject: [PATCH 07/31] fix some failures --- tests/test_laser_profiles.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/test_laser_profiles.py b/tests/test_laser_profiles.py index a4b950c8..e5b43783 100644 --- a/tests/test_laser_profiles.py +++ b/tests/test_laser_profiles.py @@ -33,7 +33,8 @@ TransverseProfileFromData, ) from lasy.utils.exp_data_utils import find_center_of_mass - +from lasy.optical_elements.parabolic_mirror import ParabolicMirror +from lasy.utils.grid import Grid class MockProfile(Profile): """A mock Profile class that always returns a constant value.""" @@ -583,13 +584,6 @@ def test_flattened_gaussian_profile(): focal_length, grid=Grid(dim, lo, hi_ff, npoints, n_azimuthal_modes=1) ) - radlineout_nf = ( - np.abs(las_nf_cp.grid.get_temporal_field()[0, :, int(npoints[1] / 2)]) ** 2 - ) - radlineout_ff = ( - np.abs(las_ff.grid.get_temporal_field()[0, :, int(npoints[1] / 2)]) ** 2 - ) - err = np.sum( np.abs( las_nf_cp.grid.get_temporal_field()[0, :, :] From 6c0c9683a8735edb5cecbb54318bb3eaf9ca6e44 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 20:08:44 +0000 Subject: [PATCH 08/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_laser_profiles.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_laser_profiles.py b/tests/test_laser_profiles.py index e5b43783..c5103337 100644 --- a/tests/test_laser_profiles.py +++ b/tests/test_laser_profiles.py @@ -7,6 +7,7 @@ from scipy.constants import c from lasy.laser import Laser +from lasy.optical_elements.parabolic_mirror import ParabolicMirror from lasy.profiles import ( CombinedLongitudinalTransverseProfile, FromArrayProfile, @@ -33,9 +34,9 @@ TransverseProfileFromData, ) from lasy.utils.exp_data_utils import find_center_of_mass -from lasy.optical_elements.parabolic_mirror import ParabolicMirror from lasy.utils.grid import Grid + class MockProfile(Profile): """A mock Profile class that always returns a constant value.""" From 6748491fb5cdd50b3afc6b90fe4450b7a6b2264a Mon Sep 17 00:00:00 2001 From: "rob.shalloo" Date: Mon, 24 Feb 2025 21:15:10 +0100 Subject: [PATCH 09/31] fix docs --- docs/source/api/profiles/transverse/index.rst | 1 + lasy/profiles/transverse/flattened_gaussian_profile.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/api/profiles/transverse/index.rst b/docs/source/api/profiles/transverse/index.rst index 45767445..a327b18b 100644 --- a/docs/source/api/profiles/transverse/index.rst +++ b/docs/source/api/profiles/transverse/index.rst @@ -14,3 +14,4 @@ Transverse Laser Profiles super_gaussian_profile jinc_profile transverse_profile_from_data + flattened_gaussian diff --git a/lasy/profiles/transverse/flattened_gaussian_profile.py b/lasy/profiles/transverse/flattened_gaussian_profile.py index ff1b4783..7471f9b7 100644 --- a/lasy/profiles/transverse/flattened_gaussian_profile.py +++ b/lasy/profiles/transverse/flattened_gaussian_profile.py @@ -46,7 +46,7 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): E(x,y,z=\infty) \propto \exp\left(-\frac{(N+1)r^2}{w(z)^2}\right) \sum_{n=0}^N \frac{1}{n!}\left(\frac{(N+1)\,r^2}{w(z)^2}\right)^n - + \mathrm{with} \qquad w(z) = \frac{\lambda_0}{\pi w0}|z-z_{foc}| - Note that a beam defined using the near field definition would be equivalent to a beam defined with From 28e78a2e44b1f2cf25ddbee82c30afddc434afe5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 20:15:24 +0000 Subject: [PATCH 10/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- lasy/profiles/transverse/flattened_gaussian_profile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lasy/profiles/transverse/flattened_gaussian_profile.py b/lasy/profiles/transverse/flattened_gaussian_profile.py index 7471f9b7..ff1b4783 100644 --- a/lasy/profiles/transverse/flattened_gaussian_profile.py +++ b/lasy/profiles/transverse/flattened_gaussian_profile.py @@ -46,7 +46,7 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): E(x,y,z=\infty) \propto \exp\left(-\frac{(N+1)r^2}{w(z)^2}\right) \sum_{n=0}^N \frac{1}{n!}\left(\frac{(N+1)\,r^2}{w(z)^2}\right)^n - + \mathrm{with} \qquad w(z) = \frac{\lambda_0}{\pi w0}|z-z_{foc}| - Note that a beam defined using the near field definition would be equivalent to a beam defined with From 9ab1ae737af5b70a23a1175a97836f3b60d135b8 Mon Sep 17 00:00:00 2001 From: "rob.shalloo" Date: Mon, 24 Feb 2025 21:23:51 +0100 Subject: [PATCH 11/31] fix docstring --- lasy/profiles/transverse/flattened_gaussian_profile.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lasy/profiles/transverse/flattened_gaussian_profile.py b/lasy/profiles/transverse/flattened_gaussian_profile.py index 7471f9b7..4737c533 100644 --- a/lasy/profiles/transverse/flattened_gaussian_profile.py +++ b/lasy/profiles/transverse/flattened_gaussian_profile.py @@ -30,9 +30,8 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): E(x,y,z=zf) \propto \exp\left(-\frac{r^2}{(N+1)w0^2}\right) \sum_{n=0}^N c'_n L^0_n\left(\frac{2\,r^2}{(N+1)w0^2}\right) - - \mathrm{with} Laguerre polynomials :math:`L^0_n` and - \qquad c'_n = \sum_{m=n}^{N}\frac{1}{2^m}\binom{m}{n} + + with Laguerre polynomials :math:`L^0_n` and :math:`\qquad c'_n = \sum_{m=n}^{N}\frac{1}{2^m}\binom{m}{n}` - For :math:`N=0`, this is a Gaussian profile: :math:`E\propto\exp\left(-\frac{r^2}{w0^2}\right)`. @@ -46,8 +45,8 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): E(x,y,z=\infty) \propto \exp\left(-\frac{(N+1)r^2}{w(z)^2}\right) \sum_{n=0}^N \frac{1}{n!}\left(\frac{(N+1)\,r^2}{w(z)^2}\right)^n - - \mathrm{with} \qquad w(z) = \frac{\lambda_0}{\pi w0}|z-z_{foc}| + + with :math:`\qquad w(z) = \frac{\lambda_0}{\pi w0}|z-z_{foc}|` - Note that a beam defined using the near field definition would be equivalent to a beam defined with the corresponding parameters in the far field, but without the parabolic phase arising from being defined From d825a10bc55ae3e1fce06da597d2987b8b21213e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 20:24:34 +0000 Subject: [PATCH 12/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- lasy/profiles/transverse/flattened_gaussian_profile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lasy/profiles/transverse/flattened_gaussian_profile.py b/lasy/profiles/transverse/flattened_gaussian_profile.py index 4737c533..dfb97dd7 100644 --- a/lasy/profiles/transverse/flattened_gaussian_profile.py +++ b/lasy/profiles/transverse/flattened_gaussian_profile.py @@ -30,7 +30,7 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): E(x,y,z=zf) \propto \exp\left(-\frac{r^2}{(N+1)w0^2}\right) \sum_{n=0}^N c'_n L^0_n\left(\frac{2\,r^2}{(N+1)w0^2}\right) - + with Laguerre polynomials :math:`L^0_n` and :math:`\qquad c'_n = \sum_{m=n}^{N}\frac{1}{2^m}\binom{m}{n}` - For :math:`N=0`, this is a Gaussian profile: :math:`E\propto\exp\left(-\frac{r^2}{w0^2}\right)`. @@ -45,7 +45,7 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): E(x,y,z=\infty) \propto \exp\left(-\frac{(N+1)r^2}{w(z)^2}\right) \sum_{n=0}^N \frac{1}{n!}\left(\frac{(N+1)\,r^2}{w(z)^2}\right)^n - + with :math:`\qquad w(z) = \frac{\lambda_0}{\pi w0}|z-z_{foc}|` - Note that a beam defined using the near field definition would be equivalent to a beam defined with From b1d17b0202d6d32dbb986734e8fd44e17ce8dc2f Mon Sep 17 00:00:00 2001 From: "rob.shalloo" Date: Mon, 24 Feb 2025 21:55:51 +0100 Subject: [PATCH 13/31] docstring --- .../transverse/flattened_gaussian_profile.py | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/lasy/profiles/transverse/flattened_gaussian_profile.py b/lasy/profiles/transverse/flattened_gaussian_profile.py index dfb97dd7..a16d7bd1 100644 --- a/lasy/profiles/transverse/flattened_gaussian_profile.py +++ b/lasy/profiles/transverse/flattened_gaussian_profile.py @@ -31,11 +31,16 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): \exp\left(-\frac{r^2}{(N+1)w0^2}\right) \sum_{n=0}^N c'_n L^0_n\left(\frac{2\,r^2}{(N+1)w0^2}\right) - with Laguerre polynomials :math:`L^0_n` and :math:`\qquad c'_n = \sum_{m=n}^{N}\frac{1}{2^m}\binom{m}{n}` + + with Laguerre polynomials :math:`L^0_n` and + + .. math:: + + \qquad c'_n=\sum_{m=n}^{N}\frac{1}{2^m}\binom{m}{n} - For :math:`N=0`, this is a Gaussian profile: :math:`E\propto\exp\left(-\frac{r^2}{w0^2}\right)`. - - For :math:`N\rightarrow\infty`, this is a Jinc profile: :math:`E\propto \frac{J_1(r/w0)}{r/w0}`. + - For :math:`N\rightarrow\infty`, this is a Jinc profile: :math:`E\propto\frac{J_1(r/w0)}{r/w0}`. The equivalent expression for the collimated beam in the near field which produces this focus is given by: @@ -49,31 +54,35 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): with :math:`\qquad w(z) = \frac{\lambda_0}{\pi w0}|z-z_{foc}|` - Note that a beam defined using the near field definition would be equivalent to a beam defined with - the corresponding parameters in the far field, but without the parabolic phase arising from being defined - far from the focus. + the corresponding parameters in the far field, but without the parabolic phase arising from being defined + far from the focus. - - For :math:`N=0`, this is a Gaussian profile: :math:`E\propto\exp\left(-\frac{r^2}{w_(z)^2}\right)`. + - For :math:`N=0`, this is a Gaussian profile: :math:`E\propto\exp\left(-\frac{r^2}{w(z)^2}\right)`. - - For :math:`N\rightarrow\infty`, this is a flat profile: :math:`E\propto \Theta(w(z)-r)`. + - For :math:`N\rightarrow\infty`, this is a flat profile: :math:`E\propto\Theta(w(z)-r)`. Parameters ---------- field_type : string Options: 'nearfield', when the beam is defined far from focus and - has been collimated, or 'farfield', when the beam is in the vincinity + has been collimated, or 'farfield', when the beam is in the vicinity of or has been directly propagated from the focus. In this case there can be a large defocus in the spatial phase. + w : float (in meter) The waist of the laser pulse. If field_type == 'farfield' then this variable corresponds to :math:`w_{0}` in the above far field formula. if field_type == 'nearfield' then this variable corresponds to :math:`w(z)` in the above near field formula. - N: int + + N : int Determines the "flatness" of the transverse profile, far from focus (see the above formula). Default: ``N=6`` ; somewhat close to an 8th order supergaussian. + wavelength : float (in meter) The main laser wavelength :math:`\lambda_0` of the laser. + z_foc : float (in meter), optional Only required if defining the pulse in the far field. Gives the position of the focal plane. (The laser pulse is initialized at From b0b79d8f1dccaa42345c4eda0b5660faf52b09c0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 20:56:06 +0000 Subject: [PATCH 14/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- lasy/profiles/transverse/flattened_gaussian_profile.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lasy/profiles/transverse/flattened_gaussian_profile.py b/lasy/profiles/transverse/flattened_gaussian_profile.py index a16d7bd1..94dc3bbc 100644 --- a/lasy/profiles/transverse/flattened_gaussian_profile.py +++ b/lasy/profiles/transverse/flattened_gaussian_profile.py @@ -31,9 +31,9 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): \exp\left(-\frac{r^2}{(N+1)w0^2}\right) \sum_{n=0}^N c'_n L^0_n\left(\frac{2\,r^2}{(N+1)w0^2}\right) - - with Laguerre polynomials :math:`L^0_n` and - + + with Laguerre polynomials :math:`L^0_n` and + .. math:: \qquad c'_n=\sum_{m=n}^{N}\frac{1}{2^m}\binom{m}{n} From 0c4abe36959ba3762e4144f64c9dcca7bef56fc1 Mon Sep 17 00:00:00 2001 From: "rob.shalloo" Date: Mon, 24 Feb 2025 21:58:40 +0100 Subject: [PATCH 15/31] . --- .../transverse/flattened_gaussian_profile.py | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/lasy/profiles/transverse/flattened_gaussian_profile.py b/lasy/profiles/transverse/flattened_gaussian_profile.py index a16d7bd1..195542f3 100644 --- a/lasy/profiles/transverse/flattened_gaussian_profile.py +++ b/lasy/profiles/transverse/flattened_gaussian_profile.py @@ -11,7 +11,7 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): Class for the analytic profile of a Flattened-Gaussian laser pulse. Define a complex transverse profile with a flattened Gaussian intensity - distribution **far from focus** that transform into a distribution + distribution **far from focus** that transforms into a distribution with rings **in the focal plane**. (See `Santarsiero et al., J. Modern Optics, 1997 `_) @@ -31,15 +31,13 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): \exp\left(-\frac{r^2}{(N+1)w0^2}\right) \sum_{n=0}^N c'_n L^0_n\left(\frac{2\,r^2}{(N+1)w0^2}\right) - - with Laguerre polynomials :math:`L^0_n` and - + with Laguerre polynomials :math:`L^0_n` and + .. math:: - \qquad c'_n=\sum_{m=n}^{N}\frac{1}{2^m}\binom{m}{n} + c'_n=\sum_{m=n}^{N}\frac{1}{2^m}\binom{m}{n} - For :math:`N=0`, this is a Gaussian profile: :math:`E\propto\exp\left(-\frac{r^2}{w0^2}\right)`. - - For :math:`N\rightarrow\infty`, this is a Jinc profile: :math:`E\propto\frac{J_1(r/w0)}{r/w0}`. The equivalent expression for the collimated beam in the near field which produces this focus is @@ -51,14 +49,18 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): \exp\left(-\frac{(N+1)r^2}{w(z)^2}\right) \sum_{n=0}^N \frac{1}{n!}\left(\frac{(N+1)\,r^2}{w(z)^2}\right)^n - with :math:`\qquad w(z) = \frac{\lambda_0}{\pi w0}|z-z_{foc}|` + with + + .. math:: - - Note that a beam defined using the near field definition would be equivalent to a beam defined with - the corresponding parameters in the far field, but without the parabolic phase arising from being defined - far from the focus. + w(z) = \frac{\lambda_0}{\pi w0}|z-z_{foc}| - - For :math:`N=0`, this is a Gaussian profile: :math:`E\propto\exp\left(-\frac{r^2}{w(z)^2}\right)`. + - Note that a beam defined using the near field definition would be + equivalent to a beam defined with the corresponding parameters in + the far field, but without the parabolic phase arising from being + defined far from the focus. + - For :math:`N=0`, this is a Gaussian profile: :math:`E\propto\exp\left(-\frac{r^2}{w(z)^2}\right)`. - For :math:`N\rightarrow\infty`, this is a flat profile: :math:`E\propto\Theta(w(z)-r)`. Parameters @@ -70,10 +72,10 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): can be a large defocus in the spatial phase. w : float (in meter) - The waist of the laser pulse. If field_type == 'farfield' then this - variable corresponds to :math:`w_{0}` in the above far field formula. - if field_type == 'nearfield' then this variable corresponds to - :math:`w(z)` in the above near field formula. + The waist of the laser pulse. If ``field_type == 'farfield'`` then this + variable corresponds to :math:`w_{0}` in the above far field formula. + If ``field_type == 'nearfield'`` then this variable corresponds to + :math:`w(z)` in the above near field formula. N : int Determines the "flatness" of the transverse profile, far from @@ -85,22 +87,23 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): z_foc : float (in meter), optional Only required if defining the pulse in the far field. Gives the position - of the focal plane. (The laser pulse is initialized at - ``z=0``.) + of the focal plane. (The laser pulse is initialized at ``z=0``.) Warnings -------- + In order to initialize the pulse out of focus, you can either: - - Use a non-zero ``z_foc`` - - Use ``z_foc=0`` (i.e. initialize the pulse at focus) and then call - ``laser.propagate(-z_foc)`` + - Use a non-zero ``z_foc``. + - Use ``z_foc=0`` (i.e., initialize the pulse at focus) and then call + ``laser.propagate(-z_foc)``. Both methods are in principle equivalent, but note that the first method uses the paraxial approximation, while the second method does not make this approximation. """ + def __init__(self, field_type, w, N, wavelength, z_foc=0): super().__init__() # Ensure that N is an integer From 48c42a7d57259721379251de6fbec2ab24fb3f8c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 21:02:26 +0000 Subject: [PATCH 16/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../transverse/flattened_gaussian_profile.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/lasy/profiles/transverse/flattened_gaussian_profile.py b/lasy/profiles/transverse/flattened_gaussian_profile.py index b56d0cbf..f7dc03b9 100644 --- a/lasy/profiles/transverse/flattened_gaussian_profile.py +++ b/lasy/profiles/transverse/flattened_gaussian_profile.py @@ -31,12 +31,12 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): \exp\left(-\frac{r^2}{(N+1)w0^2}\right) \sum_{n=0}^N c'_n L^0_n\left(\frac{2\,r^2}{(N+1)w0^2}\right) -<<<<<<< HEAD - with Laguerre polynomials :math:`L^0_n` and -======= + <<<<<<< HEAD + with Laguerre polynomials :math:`L^0_n` and + ======= with Laguerre polynomials :math:`L^0_n` and ->>>>>>> b0b79d8f1dccaa42345c4eda0b5660faf52b09c0 + >>>>>>> b0b79d8f1dccaa42345c4eda0b5660faf52b09c0 .. math:: @@ -54,15 +54,15 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): \exp\left(-\frac{(N+1)r^2}{w(z)^2}\right) \sum_{n=0}^N \frac{1}{n!}\left(\frac{(N+1)\,r^2}{w(z)^2}\right)^n - with + with .. math:: w(z) = \frac{\lambda_0}{\pi w0}|z-z_{foc}| - - Note that a beam defined using the near field definition would be - equivalent to a beam defined with the corresponding parameters in - the far field, but without the parabolic phase arising from being + - Note that a beam defined using the near field definition would be + equivalent to a beam defined with the corresponding parameters in + the far field, but without the parabolic phase arising from being defined far from the focus. - For :math:`N=0`, this is a Gaussian profile: :math:`E\propto\exp\left(-\frac{r^2}{w(z)^2}\right)`. @@ -96,7 +96,7 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): Warnings -------- - + In order to initialize the pulse out of focus, you can either: - Use a non-zero ``z_foc``. @@ -108,7 +108,6 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): not make this approximation. """ - def __init__(self, field_type, w, N, wavelength, z_foc=0): super().__init__() # Ensure that N is an integer From 706640075b2865fc14967ec040677d9b19d5c443 Mon Sep 17 00:00:00 2001 From: "rob.shalloo" Date: Mon, 24 Feb 2025 22:09:39 +0100 Subject: [PATCH 17/31] fix merge --- lasy/profiles/transverse/flattened_gaussian_profile.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lasy/profiles/transverse/flattened_gaussian_profile.py b/lasy/profiles/transverse/flattened_gaussian_profile.py index b56d0cbf..245d1fc7 100644 --- a/lasy/profiles/transverse/flattened_gaussian_profile.py +++ b/lasy/profiles/transverse/flattened_gaussian_profile.py @@ -31,18 +31,15 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): \exp\left(-\frac{r^2}{(N+1)w0^2}\right) \sum_{n=0}^N c'_n L^0_n\left(\frac{2\,r^2}{(N+1)w0^2}\right) -<<<<<<< HEAD - with Laguerre polynomials :math:`L^0_n` and -======= with Laguerre polynomials :math:`L^0_n` and ->>>>>>> b0b79d8f1dccaa42345c4eda0b5660faf52b09c0 .. math:: - c'_n=\sum_{m=n}^{N}\frac{1}{2^m}\binom{m}{n} + \qquad c'_n=\sum_{m=n}^{N}\frac{1}{2^m}\binom{m}{n} - For :math:`N=0`, this is a Gaussian profile: :math:`E\propto\exp\left(-\frac{r^2}{w0^2}\right)`. + - For :math:`N\rightarrow\infty`, this is a Jinc profile: :math:`E\propto\frac{J_1(r/w0)}{r/w0}`. The equivalent expression for the collimated beam in the near field which produces this focus is From 10bcc5025662010881becafa8e23272e0d7385e0 Mon Sep 17 00:00:00 2001 From: "rob.shalloo" Date: Mon, 24 Feb 2025 22:22:22 +0100 Subject: [PATCH 18/31] update docs --- docs/source/api/profiles/transverse/flattened_gaussian.rst | 1 + lasy/profiles/transverse/flattened_gaussian_profile.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/source/api/profiles/transverse/flattened_gaussian.rst b/docs/source/api/profiles/transverse/flattened_gaussian.rst index 94ba6158..ad37f0f6 100644 --- a/docs/source/api/profiles/transverse/flattened_gaussian.rst +++ b/docs/source/api/profiles/transverse/flattened_gaussian.rst @@ -3,6 +3,7 @@ Flattened Gaussian Transverse Profile Implements a flattened transverse Gaussian profile. This profile can either be defined close to a focus, in a far-field regime or in a collimated beam in the near field. + ------------ .. autoclass:: lasy.profiles.transverse.FlattenedGaussianTransverseProfile diff --git a/lasy/profiles/transverse/flattened_gaussian_profile.py b/lasy/profiles/transverse/flattened_gaussian_profile.py index edc6df8d..6ef7e647 100644 --- a/lasy/profiles/transverse/flattened_gaussian_profile.py +++ b/lasy/profiles/transverse/flattened_gaussian_profile.py @@ -94,7 +94,8 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): Warnings -------- - In order to initialize the pulse out of focus, you can either: + In order to initialize the pulse in the far field but out of focus, you + must select ``field_type == 'farfield'`` and then you can either: - Use a non-zero ``z_foc``. - Use ``z_foc=0`` (i.e., initialize the pulse at focus) and then call From 48e89ca7ff9fbc7e8164eb06fd7f3c4c5e3fe94f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 21:22:37 +0000 Subject: [PATCH 19/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- lasy/profiles/transverse/flattened_gaussian_profile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lasy/profiles/transverse/flattened_gaussian_profile.py b/lasy/profiles/transverse/flattened_gaussian_profile.py index 6ef7e647..a6d3664d 100644 --- a/lasy/profiles/transverse/flattened_gaussian_profile.py +++ b/lasy/profiles/transverse/flattened_gaussian_profile.py @@ -94,7 +94,7 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): Warnings -------- - In order to initialize the pulse in the far field but out of focus, you + In order to initialize the pulse in the far field but out of focus, you must select ``field_type == 'farfield'`` and then you can either: - Use a non-zero ``z_foc``. From 9bd79125b6852459e6c3639c480c98dfa7a892ec Mon Sep 17 00:00:00 2001 From: "rob.shalloo" Date: Mon, 24 Feb 2025 23:00:42 +0100 Subject: [PATCH 20/31] tidy --- lasy/profiles/transverse/flattened_gaussian_profile.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lasy/profiles/transverse/flattened_gaussian_profile.py b/lasy/profiles/transverse/flattened_gaussian_profile.py index 6ef7e647..6033a4bf 100644 --- a/lasy/profiles/transverse/flattened_gaussian_profile.py +++ b/lasy/profiles/transverse/flattened_gaussian_profile.py @@ -28,8 +28,8 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): .. math:: E(x,y,z=zf) \propto - \exp\left(-\frac{r^2}{(N+1)w0^2}\right) - \sum_{n=0}^N c'_n L^0_n\left(\frac{2\,r^2}{(N+1)w0^2}\right) + \exp\left(-\frac{r^2}{(N+1)w_0^2}\right) + \sum_{n=0}^N c'_n L^0_n\left(\frac{2\,r^2}{(N+1)w_0^2}\right) with Laguerre polynomials :math:`L^0_n` and @@ -38,9 +38,9 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): \qquad c'_n=\sum_{m=n}^{N}\frac{1}{2^m}\binom{m}{n} - - For :math:`N=0`, this is a Gaussian profile: :math:`E\propto\exp\left(-\frac{r^2}{w0^2}\right)`. + - For :math:`N=0`, this is a Gaussian profile: :math:`E\propto\exp\left(-\frac{r^2}{w_0^2}\right)`. - - For :math:`N\rightarrow\infty`, this is a Jinc profile: :math:`E\propto\frac{J_1(r/w0)}{r/w0}`. + - For :math:`N\rightarrow\infty`, this is a Jinc profile: :math:`E\propto\frac{J_1(r/w_0)}{r/w_0}`. The equivalent expression for the collimated beam in the near field which produces this focus is given by: @@ -55,7 +55,7 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): .. math:: - w(z) = \frac{\lambda_0}{\pi w0}|z-z_{foc}| + w(z) = \frac{\lambda_0}{\pi w_0}|z-z_{foc}| - Note that a beam defined using the near field definition would be equivalent to a beam defined with the corresponding parameters in From 9685e646aaeeecf5742796968ed5bfc0ac6d2ac8 Mon Sep 17 00:00:00 2001 From: "rob.shalloo" Date: Tue, 25 Feb 2025 09:20:01 +0100 Subject: [PATCH 21/31] bug fix --- .../transverse/flattened_gaussian_profile.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lasy/profiles/transverse/flattened_gaussian_profile.py b/lasy/profiles/transverse/flattened_gaussian_profile.py index 487a2fd0..a0856496 100644 --- a/lasy/profiles/transverse/flattened_gaussian_profile.py +++ b/lasy/profiles/transverse/flattened_gaussian_profile.py @@ -109,9 +109,10 @@ class FlattenedGaussianTransverseProfile(TransverseProfile): def __init__(self, field_type, w, N, wavelength, z_foc=0): super().__init__() # Ensure that N is an integer - self.N = int(round(N)) + assert isinstance(N, int) and N >= 0 assert field_type in ["nearfield", "farfield"] self.field_type = field_type + self.N = N if field_type == "farfield": w0 = w @@ -185,11 +186,10 @@ def _evaluate(self, x, y): w = self.w sumseries = 0 - if N > 0: - for n in range(N): - sumseries += ( - 1 / math.factorial(n) * ((N + 1) * (x**2 + y**2) / w**2) ** n - ) + for n in range(N+1): + sumseries += ( + 1 / math.factorial(n) * ((N + 1) * (x**2 + y**2) / w**2) ** n + ) envelope = np.exp(-(N + 1) * (x**2 + y**2) / w**2) * sumseries From c76f19573b409dd514854173de143ec5a8085491 Mon Sep 17 00:00:00 2001 From: "rob.shalloo" Date: Tue, 25 Feb 2025 09:20:35 +0100 Subject: [PATCH 22/31] update test --- tests/test_laser_profiles.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/tests/test_laser_profiles.py b/tests/test_laser_profiles.py index c5103337..42f26062 100644 --- a/tests/test_laser_profiles.py +++ b/tests/test_laser_profiles.py @@ -35,6 +35,7 @@ ) from lasy.utils.exp_data_utils import find_center_of_mass from lasy.utils.grid import Grid +from lasy.utils.laser_utils import compute_laser_energy, get_w0 class MockProfile(Profile): @@ -585,12 +586,21 @@ def test_flattened_gaussian_profile(): focal_length, grid=Grid(dim, lo, hi_ff, npoints, n_azimuthal_modes=1) ) - err = np.sum( - np.abs( - las_nf_cp.grid.get_temporal_field()[0, :, :] - - las_ff.grid.get_temporal_field()[0, :, :] - ) - ** 2 + radlineout_nf_cp = ( + np.abs(las_nf_cp.grid.get_temporal_field()[0, :, int(npoints[1] / 2)]) ** 2 + ) + radlineout_ff = ( + np.abs(las_ff.grid.get_temporal_field()[0, :, int(npoints[1] / 2)]) ** 2 ) - assert err < 1 + err = np.sum(np.abs(np.abs(radlineout_nf_cp)**2-np.abs(radlineout_ff)**2))/np.sum(np.abs(radlineout_ff)**2) + + assert err < 1e-2 + + energy_ff = compute_laser_energy(dim,las_ff.grid) + energy_nf_cp = compute_laser_energy(dim,las_nf_cp.grid) + assert(np.abs(energy_ff -energy_nf_cp)/energy_ff < 1e-5) + + w0_ff = get_w0(las_ff.grid,dim) + w0_nf_cp = get_w0(las_nf_cp.grid,dim) + assert(np.abs(w0_nf_cp - w0_ff)/w0_ff < 1e-2) From 3af014210ec7dda4b6a65095eb25c0d222e06238 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 25 Feb 2025 08:20:52 +0000 Subject: [PATCH 23/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../transverse/flattened_gaussian_profile.py | 2 +- tests/test_laser_profiles.py | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/lasy/profiles/transverse/flattened_gaussian_profile.py b/lasy/profiles/transverse/flattened_gaussian_profile.py index a0856496..11c16c62 100644 --- a/lasy/profiles/transverse/flattened_gaussian_profile.py +++ b/lasy/profiles/transverse/flattened_gaussian_profile.py @@ -186,7 +186,7 @@ def _evaluate(self, x, y): w = self.w sumseries = 0 - for n in range(N+1): + for n in range(N + 1): sumseries += ( 1 / math.factorial(n) * ((N + 1) * (x**2 + y**2) / w**2) ** n ) diff --git a/tests/test_laser_profiles.py b/tests/test_laser_profiles.py index 42f26062..8a6a3b7a 100644 --- a/tests/test_laser_profiles.py +++ b/tests/test_laser_profiles.py @@ -587,20 +587,22 @@ def test_flattened_gaussian_profile(): ) radlineout_nf_cp = ( - np.abs(las_nf_cp.grid.get_temporal_field()[0, :, int(npoints[1] / 2)]) ** 2 + np.abs(las_nf_cp.grid.get_temporal_field()[0, :, int(npoints[1] / 2)]) ** 2 ) radlineout_ff = ( np.abs(las_ff.grid.get_temporal_field()[0, :, int(npoints[1] / 2)]) ** 2 ) - err = np.sum(np.abs(np.abs(radlineout_nf_cp)**2-np.abs(radlineout_ff)**2))/np.sum(np.abs(radlineout_ff)**2) + err = np.sum( + np.abs(np.abs(radlineout_nf_cp) ** 2 - np.abs(radlineout_ff) ** 2) + ) / np.sum(np.abs(radlineout_ff) ** 2) assert err < 1e-2 - energy_ff = compute_laser_energy(dim,las_ff.grid) - energy_nf_cp = compute_laser_energy(dim,las_nf_cp.grid) - assert(np.abs(energy_ff -energy_nf_cp)/energy_ff < 1e-5) + energy_ff = compute_laser_energy(dim, las_ff.grid) + energy_nf_cp = compute_laser_energy(dim, las_nf_cp.grid) + assert np.abs(energy_ff - energy_nf_cp) / energy_ff < 1e-5 - w0_ff = get_w0(las_ff.grid,dim) - w0_nf_cp = get_w0(las_nf_cp.grid,dim) - assert(np.abs(w0_nf_cp - w0_ff)/w0_ff < 1e-2) + w0_ff = get_w0(las_ff.grid, dim) + w0_nf_cp = get_w0(las_nf_cp.grid, dim) + assert np.abs(w0_nf_cp - w0_ff) / w0_ff < 1e-2 From 8b3f8bded9cbc0192b652f22c0b5dc72b741563c Mon Sep 17 00:00:00 2001 From: "rob.shalloo" Date: Tue, 25 Feb 2025 09:28:42 +0100 Subject: [PATCH 24/31] relax over restrive asset --- tests/test_laser_profiles.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_laser_profiles.py b/tests/test_laser_profiles.py index 42f26062..6a01dc35 100644 --- a/tests/test_laser_profiles.py +++ b/tests/test_laser_profiles.py @@ -571,7 +571,7 @@ def test_flattened_gaussian_profile(): dim = "rt" lo = (0, -100e-15) - hi_ff = (1000e-6, 100e-15) + hi_ff = (500e-6, 100e-15) hi_nf = (40e-3, 100e-15) npoints = (5000, 200) @@ -599,7 +599,7 @@ def test_flattened_gaussian_profile(): energy_ff = compute_laser_energy(dim,las_ff.grid) energy_nf_cp = compute_laser_energy(dim,las_nf_cp.grid) - assert(np.abs(energy_ff -energy_nf_cp)/energy_ff < 1e-5) + assert(np.abs(energy_ff -energy_nf_cp)/energy_ff < 1e-4) w0_ff = get_w0(las_ff.grid,dim) w0_nf_cp = get_w0(las_nf_cp.grid,dim) From 49f0c3ca4572c74dece72e0edd5f6c5933641e63 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 25 Feb 2025 08:30:17 +0000 Subject: [PATCH 25/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_laser_profiles.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_laser_profiles.py b/tests/test_laser_profiles.py index b9cee9e9..04ca5282 100644 --- a/tests/test_laser_profiles.py +++ b/tests/test_laser_profiles.py @@ -599,9 +599,9 @@ def test_flattened_gaussian_profile(): assert err < 1e-2 - energy_ff = compute_laser_energy(dim,las_ff.grid) - energy_nf_cp = compute_laser_energy(dim,las_nf_cp.grid) - assert(np.abs(energy_ff -energy_nf_cp)/energy_ff < 1e-4) + energy_ff = compute_laser_energy(dim, las_ff.grid) + energy_nf_cp = compute_laser_energy(dim, las_nf_cp.grid) + assert np.abs(energy_ff - energy_nf_cp) / energy_ff < 1e-4 w0_ff = get_w0(las_ff.grid, dim) w0_nf_cp = get_w0(las_nf_cp.grid, dim) From cf31502f7ae932ee26330a0dc5bb82872246b1ae Mon Sep 17 00:00:00 2001 From: "rob.shalloo" Date: Wed, 26 Feb 2025 08:54:28 +0100 Subject: [PATCH 26/31] fix bug --- lasy/optical_elements/zernike_aberrations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lasy/optical_elements/zernike_aberrations.py b/lasy/optical_elements/zernike_aberrations.py index 66c028b4..700b3c45 100644 --- a/lasy/optical_elements/zernike_aberrations.py +++ b/lasy/optical_elements/zernike_aberrations.py @@ -62,7 +62,7 @@ def amplitude_multiplier(self, x, y, omega): rr = np.sqrt(x**2 + y**2) phase = np.zeros_like(rr) - _, _, nw = x.shape + nw = x.shape[-1] for j in list(self.zernike_amplitudes): phase += self.zernike_amplitudes[j] * np.tile( From 23c96bddf1ed1343797377b080698a1cc5510c44 Mon Sep 17 00:00:00 2001 From: "rob.shalloo" Date: Wed, 26 Feb 2025 13:38:30 +0100 Subject: [PATCH 27/31] fix indexing --- lasy/optical_elements/zernike_aberrations.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lasy/optical_elements/zernike_aberrations.py b/lasy/optical_elements/zernike_aberrations.py index 700b3c45..7f453259 100644 --- a/lasy/optical_elements/zernike_aberrations.py +++ b/lasy/optical_elements/zernike_aberrations.py @@ -64,10 +64,13 @@ def amplitude_multiplier(self, x, y, omega): nw = x.shape[-1] + for j in list(self.zernike_amplitudes): - phase += self.zernike_amplitudes[j] * np.tile( - zernike(x[:, :, 0], y[:, :, 0], self.pupil_coords, j)[:, :, np.newaxis], - (1, 1, nw), - ) + # Create the zernike phase and ensure it has the same number of dimensions as phase + zernike_phase = zernike(x[..., 0], y[..., 0], self.pupil_coords, j)[..., None] # Expand last axis + + # Increase the length of the frequency dimension such that the shape is suitable to be added + # to the phase array, then add it + phase += self.zernike_amplitudes[j] * np.broadcast_to(zernike_phase, phase.shape) return np.exp(1j * phase) From f1cc1d6c2dbb34b19128bbbbd09a4e9f2ca2631d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 26 Feb 2025 12:39:21 +0000 Subject: [PATCH 28/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- lasy/optical_elements/zernike_aberrations.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lasy/optical_elements/zernike_aberrations.py b/lasy/optical_elements/zernike_aberrations.py index 7f453259..0e1caad4 100644 --- a/lasy/optical_elements/zernike_aberrations.py +++ b/lasy/optical_elements/zernike_aberrations.py @@ -64,13 +64,16 @@ def amplitude_multiplier(self, x, y, omega): nw = x.shape[-1] - for j in list(self.zernike_amplitudes): # Create the zernike phase and ensure it has the same number of dimensions as phase - zernike_phase = zernike(x[..., 0], y[..., 0], self.pupil_coords, j)[..., None] # Expand last axis - + zernike_phase = zernike(x[..., 0], y[..., 0], self.pupil_coords, j)[ + ..., None + ] # Expand last axis + # Increase the length of the frequency dimension such that the shape is suitable to be added # to the phase array, then add it - phase += self.zernike_amplitudes[j] * np.broadcast_to(zernike_phase, phase.shape) + phase += self.zernike_amplitudes[j] * np.broadcast_to( + zernike_phase, phase.shape + ) return np.exp(1j * phase) From 806c9aa5a8d9755fc514a06a04f7a4ea9dc778c6 Mon Sep 17 00:00:00 2001 From: "rob.shalloo" Date: Wed, 26 Feb 2025 13:45:12 +0100 Subject: [PATCH 29/31] bug fix --- lasy/laser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lasy/laser.py b/lasy/laser.py index 36273daa..3152bb25 100644 --- a/lasy/laser.py +++ b/lasy/laser.py @@ -185,8 +185,8 @@ def apply_optics(self, optical_element): r, omega = np.meshgrid(self.grid.axes[0], self.omega_1d, indexing="ij") # The line below assumes that amplitude_multiplier # is cylindrically symmetric, hence we pass - # `r` as `x` and 0 as `y` - multiplier = optical_element.amplitude_multiplier(r, 0, omega) + # `r` as `x` and an array of 0s as `y` + multiplier = optical_element.amplitude_multiplier(r, np.zeros_like(r), omega) # The azimuthal modes are the components of the Fourier transform # along theta (FT_theta). Because the multiplier is assumed to be # cylindrically symmetric (i.e. theta-independent): From a21c632131be75c59acf154517a62f930a7885ce Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 26 Feb 2025 12:45:27 +0000 Subject: [PATCH 30/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- lasy/laser.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lasy/laser.py b/lasy/laser.py index 3152bb25..566f70d9 100644 --- a/lasy/laser.py +++ b/lasy/laser.py @@ -186,7 +186,9 @@ def apply_optics(self, optical_element): # The line below assumes that amplitude_multiplier # is cylindrically symmetric, hence we pass # `r` as `x` and an array of 0s as `y` - multiplier = optical_element.amplitude_multiplier(r, np.zeros_like(r), omega) + multiplier = optical_element.amplitude_multiplier( + r, np.zeros_like(r), omega + ) # The azimuthal modes are the components of the Fourier transform # along theta (FT_theta). Because the multiplier is assumed to be # cylindrically symmetric (i.e. theta-independent): From 9dabcded2e324704e512068492b818f8c20b6210 Mon Sep 17 00:00:00 2001 From: "rob.shalloo" Date: Wed, 26 Feb 2025 14:15:12 +0100 Subject: [PATCH 31/31] fix unused variable --- lasy/optical_elements/zernike_aberrations.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lasy/optical_elements/zernike_aberrations.py b/lasy/optical_elements/zernike_aberrations.py index 0e1caad4..b2764ec2 100644 --- a/lasy/optical_elements/zernike_aberrations.py +++ b/lasy/optical_elements/zernike_aberrations.py @@ -62,8 +62,6 @@ def amplitude_multiplier(self, x, y, omega): rr = np.sqrt(x**2 + y**2) phase = np.zeros_like(rr) - nw = x.shape[-1] - for j in list(self.zernike_amplitudes): # Create the zernike phase and ensure it has the same number of dimensions as phase zernike_phase = zernike(x[..., 0], y[..., 0], self.pupil_coords, j)[