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

Implementation of CW Temporal Profile and Plane Wave Transverse Profile #373

Open
wants to merge 44 commits into
base: development
Choose a base branch
from

Conversation

rob-shalloo
Copy link
Member

@rob-shalloo rob-shalloo commented Mar 12, 2025

This PR aims to address the problem raised #371. Ideally one would like to be able to perform fast ~1D or ~2D manipulations of the temporal/spectral profile or the spatial profile respectively. One could approach this by adding more functionality to the respective profiles, but this would bring with it significant overhead. After discussion with @MaxThevenet we thought a more efficient solution would be to keep the laser object as is (together with all the excellent functionality that entails) and instead generate profiles which mimic the effect of a plane wave (in the event that one wants to manipulate just the transverse profile) or a continuous wave laser (in the event that one is interested in manipulating just the transverse profile).

In this PR, you will find implementations of both a Plane Wave Transverse Profile and a Continuous Wave Longitudinal Profile, together with some modifications to the grid object to ensure that smooth operation.

Sample usage for Plane Wave

from lasy.laser import Laser
from lasy.profiles.longitudinal import GaussianLongitudinalProfile
from lasy.profiles.transverse import PlaneWaveProfile
from lasy.profiles import CombinedLongitudinalTransverseProfile


# Physical Parameters
tau = 30e-15
wavelength = 800e-9
t_peak = 0.0
pol = (1, 0)
peak_power = 1e12  # W

long_prof = GaussianLongitudinalProfile(wavelength, tau, t_peak)
tran_prof = PlaneWaveProfile()
profile = CombinedLongitudinalTransverseProfile(
        wavelength, pol, long_prof, tran_prof, peak_power=peak_power
    )

# Computational Grid
dim = "xyt"
lo = (None, None, -5 * tau)
hi = (None, None, 5 * tau)
npoints = (1, 1, 1000)

laser = Laser(dim, lo, hi, npoints, profile)

Here you can see the lo and hi variables have None in the x an y elements. I believe this would be more intuitive for a user than selecting a random set of low and high points. Grid has been adapted to automatically set the range when None is provided. Of course the user could set the lo and hi variables equal to the area of the beam to naturally calculate the correct peak field. However, as can be seen in this example, one can simple overwrite this if needed.

Sample usage for Continuous Wave

from lasy.laser import Laser
from lasy.profiles.transverse import SuperGaussianTransverseProfile
from lasy.profiles.longitudinal import ContinuousWaveProfile
from lasy.profiles import CombinedLongitudinalTransverseProfile


# Physical Parameters
peak_intensity = 1.0e10  # W/m^2
spot_size = 10e-3
wavelength = 800e-9
pol = (1, 0)

long_prof = ContinuousWaveProfile(wavelength)
tran_prof = GaussianTransverseProfile(spot_size)
profile = CombinedLongitudinalTransverseProfile(
        wavelength, pol, long_prof, tran_prof, peak_intensity=peak_intensity
    )

# Computational Grid
dim = "xyt"
lo = (-5 * spot_size, -5 * spot_size, None)
hi = (5 * spot_size, 5 * spot_size, None)
npoints = (1000, 1000, 1)

laser = Laser(dim, lo, hi, npoints, profile)

@rob-shalloo
Copy link
Member Author

Should add peak_power, peak_intensity to the LaserProfile as alternative options to energy. In the case that one of these special profiles are selected (cw or plane wave), then one option should be required.

We should change any meaningless number, eg dx in grid to be np.nan in the case of a plane wave. We should carefully think about how to handle lo and hi here.

@rob-shalloo
Copy link
Member Author

Had another think about the use of np.nan in place of upper and lower bounds of the grid. I suspect this will break quite a lot of things (eg the laser.show functionality). For this reason, I think its not too bad to have the laser for CW profile defined on a range of -1,1 seconds and for the Plane Wave pulses to be defined on a grid of -1,1 m for xyt and 0,1 m for rt. The dx in this case is still meaningful, in that it is correct for the grid.

@rob-shalloo rob-shalloo changed the title [WIP] Implementation of CW Temporal Profile and Plane Wave Transverse Profile Implementation of CW Temporal Profile and Plane Wave Transverse Profile Mar 12, 2025
Copy link
Contributor

@MaxThevenet MaxThevenet left a comment

Choose a reason for hiding this comment

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

Looks great, thanks a lot for this PR! See minor comments below. In addition, as a follow-up of our offline discussion yesterday, I would raise a few points:

  • Currently, the amplitude of the laser pulse is typically specified with the energy. For symmetry, could we consider the following?
    • full profile: energy (J);
    • CW wave: power (J/s), as the longitudinal dimension is infinite; -> already the case in this PR.
    • plane wave: fluence (J/m^2) as the transverse dimensions are infinite. -> currently intensity is used.
  • Likewise, for symmetry and consistency, if we have the 3 input parameters, it would be great to have the corresponding diagnostics, i.e., get_laser_energy (already there), get_peak_power (you added it), get_peak_fluence.
  • Should the time domain be 1-second long [-0.5, 0.5] in CW case, such that, numerically, energy [J] == power [J/s]? Likewise for transverse box size of [-0.5, 0.5] meter in plane wave case such that, numerically, energy[J] == fluency [J/m^2]?
  • I can envision that, before running the full expensive 3D beamline people check quick CW, then quick plane wave, then full thing. Do you think this is a reasonable workflow? If so, I recommend we make it simpler such that the user only needs to change either the longitudinal profile or the transverse profile. For this to work, we may need to overwrite some values (e.g. enforce 1 cell in transverse direction for plane wave, similar for lo and hi, etc.). What do you think?

"outputs": [],
"source": [
"tau0_meas = get_duration(laser.grid, laser.dim)\n",
"energy0_meas = compute_laser_energy(laser.dim, laser.grid)\n",
Copy link
Contributor

Choose a reason for hiding this comment

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

This may be slightly misleading, as the energy contained in a plane wave is infinite. Should we consider a compute_laser_fluence there?

Comment on lines 166 to 172
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think there's an empty cell at the end that can be removed.

Contains the value of the longitudinal envelope at the
specified points. This array has the same shape as the array t.
"""
return np.ones_like(t + 0 * 1j)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
return np.ones_like(t + 0 * 1j)
return np.ones_like(t + 0j)

Comment on lines 67 to 68
lo[-1] = -1.0
hi[-1] = 1.0
Copy link
Contributor

Choose a reason for hiding this comment

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

Having a box length of 1 in SI (here 1 second) could come in handy to connect energy and power (i.e., the value of the energy in the box in J = the power in J/s).

Suggested change
lo[-1] = -1.0
hi[-1] = 1.0
lo[-1] = -0.5
hi[-1] = 0.5

Same can be done below for plane wave in transverse position.

Comment on lines 4 to 6
This test file verifys the correct implementation of these special cases of the laser object and
additionally checks the implementation of the peak_intensity and peak_power normalizations
as well as some measurement functionality from laser utils
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
This test file verifys the correct implementation of these special cases of the laser object and
additionally checks the implementation of the peak_intensity and peak_power normalizations
as well as some measurement functionality from laser utils
Check the implementation of these profiles and peak_intensity and peak_power normalizations, as well as some measurement functionality from laser utils.

@rob-shalloo
Copy link
Member Author

All sounds good, these changes should be in place now

Copy link
Contributor

@MaxThevenet MaxThevenet left a comment

Choose a reason for hiding this comment

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

Thanks for this PR! See minor suggestions below. Just one more change: could you update the logic such that the user can also specify peak_power and peak_fluence in all cases to combined_profile and that the profile is normalized accordingly?

Comment on lines +99 to +101
"plt.ylim(\n",
" 0,\n",
")\n",
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"plt.ylim(\n",
" 0,\n",
")\n",
"plt.ylim(0, None)\n",

"cell_type": "markdown",
"metadata": {},
"source": [
"# Defining and Manipulating a Laser Pulse in Time Only\n",
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"# Defining and Manipulating a Laser Pulse in Time Only\n",
"# Laser Pulse in Time Only\n",

"cell_type": "markdown",
"metadata": {},
"source": [
"# Defining and Manipulating a Laser Pulse in Space Only\n",
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"# Defining and Manipulating a Laser Pulse in Space Only\n",
"# Laser Pulse in Space Only\n",

Comment on lines +140 to +141
"print(\"Final Pulse Duration = %.2f fs\" % (tau1_meas * 1e15))\n",
"print(\"\\n\")\n",
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"print(\"Final Pulse Duration = %.2f fs\" % (tau1_meas * 1e15))\n",
"print(\"\\n\")\n",
"print(\"Final Pulse Duration = %.2f fs\" % (tau1_meas * 1e15) + "\n")\n",

Comment on lines +145 to +146
"print(\"Expected Final Spot Size = %.2f mm\" % (w1_meas * 1e3))\n",
"print(\"\\n\")\n",
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"print(\"Expected Final Spot Size = %.2f mm\" % (w1_meas * 1e3))\n",
"print(\"\\n\")\n",
"print(\"Expected Final Spot Size = %.2f mm\" % (w1_meas * 1e3) + "\n")\n",

Comment on lines +205 to +212
field = grid.get_temporal_field()
power = get_laser_power(dim, grid)
input_peak_power = power.max()
if input_peak_power == 0.0:
print("Field is zero everywhere, normalization will be skipped")
else:
field *= np.sqrt(peak_power / input_peak_power)
grid.set_temporal_field(field)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
field = grid.get_temporal_field()
power = get_laser_power(dim, grid)
input_peak_power = power.max()
if input_peak_power == 0.0:
print("Field is zero everywhere, normalization will be skipped")
else:
field *= np.sqrt(peak_power / input_peak_power)
grid.set_temporal_field(field)
power = get_laser_power(dim, grid)
input_peak_power = power.max()
if input_peak_power == 0.0:
print("Field is zero everywhere, normalization will be skipped")
else:
field = grid.get_temporal_field()
grid.set_temporal_field(field * np.sqrt(peak_power / input_peak_power))

Comment on lines +4 to +6
This test file verifys the correct implementation of these special cases of the laser object and
additionally checks the implementation of the peak_fluence and peak_power normalizations
as well as some measurement functionality from laser utils
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
This test file verifys the correct implementation of these special cases of the laser object and
additionally checks the implementation of the peak_fluence and peak_power normalizations
as well as some measurement functionality from laser utils
This test file verifies the correct implementation of these cases of the laser object and
checks the implementation of the peak_fluence and peak_power normalizations
as well as some measurement functionality from laser utils

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.
Only used in case where laser is neither a plane wave or a continuous wave laser
Copy link
Contributor

Choose a reason for hiding this comment

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

This is still the general case so I would not make a specific case here.

Suggested change
Only used in case where laser is neither a plane wave or a continuous wave laser

The peak fluence 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 peak fluence.
Only used in case where laser is a continuous wave laser
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that is a good option in general and should be usable.

Suggested change
Only used in case where laser is a continuous wave laser
Required for a CW (monochromatic) profile.

The peak power 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 peak power.
Only used in case where laser is a plane wave laser
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Only used in case where laser is a plane wave laser
Required for a plane wave profile.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants