solprop.pulses#
Initial pulses on either grid: shape, exact scaling and the user’s spectral phase.
Every builder here is a frozen dataclass with one method, build(grid), which returns
the initial spectrum in the canonical (n_modes, n_pol, n_omega) layout of
docs/dev/conventions.md §6 with both leading axes equal to one. The spectrum is in
the internal FFT convention, ready to be handed to the translation layer.
Signs. Spectral phases, Taylor coefficients and delays are given in the optics
convention (docs/dev/conventions.md §2): a positive group-delay dispersion
chirps the pulse positively, and a positive t0 makes it arrive later. They are
converted to the internal convention exactly once, when the phase factor is formed, with
solprop.fourier.to_internal(); a measured absolute spectrum additionally needs
the half-window origin shift and goes through
solprop.fourier.spectrum_to_internal(). This module is the first of the three
boundary places where the optics sign convention meets the internal FFT convention
(see docs/dev/decisions.md), and nothing else in it conjugates anything.
Scaling. A pulse is specified by its energy or by its peak_power, never
both. Both are realised exactly on the grid, to the round-off of one rescaling, and
both are applied after the spectral phase, so the peak power of a chirped pulse is the
peak power the caller asked for.
The manual page is docs/guide/setup/pulses.md.
Classes
|
A continuous-wave field, monochromatic or with a |
|
A pulse whose power envelope is an arbitrary callable of time. |
|
A pulse from a measured complex spectrum, interpolated onto the grid. |
|
A (super-)Gaussian pulse of given intensity FWHM. |
|
An incident beam, normalised over its whole plane, sampled at given points. |
|
Launch an astigmatic Gaussian beam into the hybrid planar grid. |
|
The one method every input pulse provides. |
|
Several pulses launched together: the input is the sum of their fields. |
|
A |
|
Common parameters and build path of a pulse defined by its power envelope. |
|
Wrap a temporal pulse in an energy-normalised transverse beam. |
- class solprop.pulses.CWPulse(*, wavelength, average_power, bandwidth=0.0, seed=None)[source]#
Bases:
DisplayedA continuous-wave field, monochromatic or with a
sech**2power spectrum.The spectrum is written straight onto the grid’s own bins, so the field is periodic in the time window by construction and needs no apodisation; the average power is then realised in one pass over the flat part of the temporal window, rather than by alternating between a fixed spectral magnitude and a windowed field until the two agree. The single pass trades a slightly perturbed spectral shape for an exact power and a bounded cost.
- Attributes:
- wavelengthfloat
Centre wavelength, m. Must fall inside the grid’s active band.
- average_powerfloat
Average power, W, over the samples where the temporal collar is exactly one.
- bandwidthfloat
Full width at half maximum of the power spectrum, in wavelength, m. Zero, the default, gives a monochromatic field on the single nearest bin. It is mapped to frequency at first order,
Δω = 2πcΔλ/λ²at the centre wavelength: thesech**2spectrum has that frequency FWHM, the frequency equivalent of the requested width, and its wavelength FWHM isbandwidthonly in the narrowband limit.- seedint or None
Seed of the uniform random spectral phase used when
bandwidthis positive, so that a realisation is reproducible. Unused for a monochromatic field.
- Raises:
- PulseError
If the wavelength, power or bandwidth is invalid.
Examples
>>> import numpy as np >>> from solprop.grid import EnvelopeGrid >>> from solprop.pulses import CWPulse >>> grid = EnvelopeGrid.from_limits(1.0, 1030e-9, (900e-9, 1.2e-6), 20e-12) >>> spectrum = CWPulse(wavelength=1030e-9, average_power=5.0).build(grid) >>> spectrum.shape[:2] (1, 1)
- build(grid)[source]#
Return the continuous-wave spectrum on the given grid.
- Parameters:
- gridsolprop.grid.TimeGrid
Grid to build on.
- Returns:
- numpy.ndarray
Stored spectrum, shape
(1, 1, n_omega), in the square root of watts.
- Raises:
- PulseError
If the centre wavelength falls outside the grid’s active band.
Examples
>>> import numpy as np >>> from solprop import fourier >>> from solprop.grid import EnvelopeGrid >>> from solprop.pulses import CWPulse >>> grid = EnvelopeGrid.from_limits(1.0, 1030e-9, (900e-9, 1.2e-6), 20e-12) >>> spectrum = CWPulse(wavelength=1030e-9, average_power=5.0).build(grid) >>> samples = fourier.inverse(spectrum[0, 0], kind=grid.kind) >>> flat = grid.t_window == 1.0 >>> bool(np.isclose(np.mean(np.abs(samples[flat]) ** 2), 5.0, rtol=1e-12)) True
- class solprop.pulses.CustomPulse(*, wavelength, energy=None, peak_power=None, phase=(), t0=0.0, shape_fn)[source]#
Bases:
ShapedPulseA pulse whose power envelope is an arbitrary callable of time.
- Attributes:
- wavelengthfloat
Centre wavelength of the pulse, m, and the reference of the Taylor phase.
- shape_fncallable
I(t)returning the power envelope for an array of times in seconds. It must be finite and non-negative on the grid; its normalisation is irrelevant.- energyfloat or None
Pulse energy, J. Exactly one of
energyandpeak_poweris given.- peak_powerfloat or None
Peak power, W, realised after the spectral phase.
- phasearray_like
Optics-convention Taylor coefficients about
wavelength: radians at order 0 ands**nat ordernthereafter.- t0float
Delay, s, positive for later arrival.
Examples
>>> import numpy as np >>> from solprop.pulses import CustomPulse >>> triangle = CustomPulse( ... wavelength=800e-9, ... shape_fn=lambda t: np.clip(1.0 - np.abs(t) / 20e-15, 0.0, None), ... energy=1e-6, ... ) >>> float(triangle.intensity(np.array([10e-15]))[0]) 0.5
- intensity(t)[source]#
Return the caller’s power envelope evaluated on the time samples.
- Parameters:
- tnumpy.ndarray
Time samples, s, shape
(n_t,).
- Returns:
- numpy.ndarray
The value of
shape_fn(t), shape(n_t,).
Examples
>>> import numpy as np >>> from solprop.pulses import CustomPulse >>> flat = CustomPulse( ... wavelength=800e-9, shape_fn=np.ones_like, energy=1e-6 ... ) >>> flat.intensity(np.zeros(3)).tolist() [1.0, 1.0, 1.0]
- class solprop.pulses.DataPulse(*, omega, spectrum, energy, phase=(), t0=0.0, wavelength=None)[source]#
Bases:
DisplayedA pulse from a measured complex spectrum, interpolated onto the grid.
The stored form is the spectrum because that is what a measurement gives: a spectral power density and a retrieved phase. The phase is absolute and referenced to
t = 0in the optics convention, so it fixes where in the window the pulse sits; it therefore goes throughsolprop.fourier.spectrum_to_internal(), which carries the half-window origin shift as well as the conjugation.Magnitude and unwrapped phase are interpolated separately with a cubic B-spline, never the complex spectrum itself, whose real and imaginary parts oscillate. Outside the data’s own frequency range the spectrum is zero, and the grid’s spectral collar is applied so that nothing is left sitting on the window edge.
- Attributes:
- omeganumpy.ndarray
Angular frequency axis of the data, rad/s, shape
(n_data,), strictly increasing and positive. It need not be uniform.- spectrumnumpy.ndarray
Measured complex spectrum on
omega, shape(n_data,), optics convention, arbitrary units.- energyfloat
Pulse energy to scale to, J.
- phasearray_like
Further optics-convention Taylor coefficients, applied on top of the phase already in the data, about
wavelength; radians at order 0 ands**nthereafter.- t0float
Further delay, s, on top of the arrival time the data’s own phase implies.
- wavelengthfloat or None
Reference wavelength of the Taylor phase, m. Defaults to the spectral centre of mass of the data.
- Raises:
- PulseError
If the two arrays disagree in length, are too short to interpolate, are not finite, or
omegais not strictly increasing and positive.
Examples
>>> import numpy as np >>> from solprop.pulses import DataPulse >>> omega = np.linspace(1.5e15, 3.5e15, 256) >>> intensity = np.exp(-(((omega - 2.35e15) / 1e14) ** 2)) >>> pulse = DataPulse.from_spectral_density( ... omega, intensity, np.zeros_like(omega), energy=1e-6 ... ) >>> float(round(pulse.reference_wavelength * 1e9)) 802.0
- build(grid)[source]#
Return the initial spectrum of the tabulated pulse on the given grid.
- Parameters:
- gridsolprop.grid.TimeGrid
Grid to build on.
- Returns:
- numpy.ndarray
Stored spectrum, shape
(1, 1, n_omega), in the square root of watts.
- Raises:
- PulseError
If the delay leaves the window, or the data does not reach the grid’s active band at all.
Examples
>>> import numpy as np >>> from solprop.grid import FieldGrid >>> from solprop.pulses import DataPulse >>> omega = np.linspace(4e14, 1.1e16, 4096) # covers the grid's active band >>> intensity = np.exp(-(((omega - 2.35e15) / 1e14) ** 2)) >>> pulse = DataPulse.from_spectral_density( ... omega, intensity, np.zeros_like(omega), energy=1e-6 ... ) >>> grid = FieldGrid.from_limits(1.0, 800e-9, (200e-9, 2e-6), 400e-15) >>> pulse.build(grid).shape[:2] (1, 1)
- classmethod from_spectral_density(omega, intensity, spectral_phase, *, energy, phase=(), t0=0.0, wavelength=None)[source]#
Build a pulse from a spectral power density and an unwrapped spectral phase.
This is the form a spectrometer and a phase retrieval give. Negative densities, which fits and baseline subtraction can produce, are clipped to zero.
- Parameters:
- omeganumpy.ndarray
Angular frequency axis, rad/s, shape
(n_data,), strictly increasing.- intensitynumpy.ndarray
Spectral power density on
omega, arbitrary units, shape(n_data,).- spectral_phasenumpy.ndarray
Unwrapped optics-convention spectral phase on
omega, rad, shape(n_data,).- energyfloat
Pulse energy to scale to, J.
- phasearray_like, optional
Further Taylor coefficients about
wavelength,s**nat ordern.- t0float, optional
Further delay, s.
- wavelengthfloat or None, optional
Reference wavelength of the Taylor phase, m.
- Returns:
- DataPulse
The pulse, with
spectrum = sqrt(intensity) exp(i spectral_phase).
- Raises:
- PulseError
If the three arrays disagree in length, or the data fails validation.
Examples
>>> import numpy as np >>> from solprop.pulses import DataPulse >>> omega = np.linspace(2.0e15, 2.7e15, 64) >>> pulse = DataPulse.from_spectral_density( ... omega, np.ones_like(omega), np.zeros_like(omega), energy=1e-6 ... ) >>> pulse.spectrum.shape (64,)
- classmethod from_time(t, field, *, energy, phase=(), t0=0.0, wavelength=None)[source]#
Build a pulse from measured time-domain samples on a uniform axis.
The optics-convention spectrum referenced to
t = 0issum_n E_n exp(+i omega t_n), which is evaluated as an inverse DFT times a linear phase for the axis offset, so anyt[0]is handled exactly and no assumption is made about where the pulse sits in the record. Only the non-negative frequencies are kept, and the zero-frequency bin is dropped: a spectral phase is meaningless there.A real array is the electric field. A complex array is taken as the analytic signal in the optics convention, so that a positive frequency means a positive frequency, as everywhere else at the interface.
- Parameters:
- tnumpy.ndarray
Time samples, s, shape
(n_data,), uniformly spaced and increasing.- fieldnumpy.ndarray
Measured field on
t, arbitrary units, shape(n_data,).- energyfloat
Pulse energy to scale to, J.
- phasearray_like, optional
Further Taylor coefficients about
wavelength,s**nat ordern.- t0float, optional
Further delay, s.
- wavelengthfloat or None, optional
Reference wavelength of the Taylor phase, m.
- Returns:
- DataPulse
The pulse, holding the spectrum of the record.
- Raises:
- PulseError
If the arrays disagree in length, the axis is not uniform and increasing, or the record is too short.
Examples
>>> import numpy as np >>> from solprop.pulses import DataPulse >>> t = np.linspace(-100e-15, 100e-15, 1024) >>> field = np.exp(-((t / 20e-15) ** 2)) * np.cos(2.35e15 * t) >>> pulse = DataPulse.from_time(t, field, energy=1e-6) >>> float(round(pulse.reference_wavelength * 1e9)) 802.0
- omega: Float[ndarray, 'n_data']#
- property reference_wavelength: float[source]#
Return the wavelength the Taylor phase is expanded about, m.
- Returns:
- float
wavelengthif it was given, otherwise the wavelength of the spectral centre of mass of the data.
- spectrum: Complex[ndarray, 'n_data_spectrum']#
- class solprop.pulses.GaussianPulse(*, wavelength, energy=None, peak_power=None, phase=(), t0=0.0, fwhm, m=1)[source]#
Bases:
ShapedPulseA (super-)Gaussian pulse of given intensity FWHM.
The envelope is
exp(-(t/sigma)**(2m) / 2)withsigma = fwhm / (2 (2 ln 2)**(1/2m)), which realises the requested FWHM for every orderm.- Attributes:
- wavelengthfloat
Centre wavelength of the pulse, m, and the reference of the Taylor phase.
- fwhmfloat
Full width at half maximum of the power envelope, s.
- mint
Super-Gaussian order, at least 1. The exponent of the envelope is
2m, som = 1is an ordinary Gaussian and largemapproaches a flat top.- energyfloat or None
Pulse energy, J. Exactly one of
energyandpeak_poweris given.- peak_powerfloat or None
Peak power, W, realised after the spectral phase.
- phasearray_like
Optics-convention Taylor coefficients about
wavelength: radians at order 0 ands**nat ordernthereafter.- t0float
Delay, s, positive for later arrival.
- Raises:
- PulseError
If the duration or the order is invalid, or the scaling request is ambiguous.
Examples
>>> import numpy as np >>> from solprop.pulses import GaussianPulse >>> pulse = GaussianPulse(wavelength=800e-9, fwhm=30e-15, energy=1e-6) >>> float(pulse.intensity(np.array([0.0, 15e-15]))[1]) 0.5
- intensity(t)[source]#
Return the (super-)Gaussian power envelope, normalised to a peak of one.
- Parameters:
- tnumpy.ndarray
Time samples, s, shape
(n_t,).
- Returns:
- numpy.ndarray
Envelope values in
[0, 1], shape(n_t,).
Examples
>>> import numpy as np >>> from solprop.pulses import GaussianPulse >>> pulse = GaussianPulse(wavelength=800e-9, fwhm=2.0, energy=1.0, m=3) >>> float(round(pulse.intensity(np.array([1.0]))[0], 12)) 0.5
- class solprop.pulses.IncidentField(field, energy)[source]#
Bases:
objectAn incident beam, normalised over its whole plane, sampled at given points.
The result of
SpatialPulse.incident().- Attributes:
- fieldnumpy.ndarray
The beam’s x and y components at the points, complex128, shape
(n_nodes, 2, n_omega), in the internal convention: the electric field in V/m per square root of watt times a stored spectrum in the square root of watts, the units in which a guide’s node profiles project to channel amplitudes.- energynumpy.ndarray
The energy each bin carries through the whole plane, float64, shape
(n_omega,), J; its sum is the beam’s incident energy.
- energy: Float[ndarray, 'n_omega']#
- field: Complex[ndarray, 'n_nodes n_pol n_omega']#
- class solprop.pulses.PlanarPulse(temporal, waist, order=1, propz=0.0, jones=(1 + 0j,), *, waist_x, offset_x=0.0)[source]#
Bases:
SpatialPulseLaunch an astigmatic Gaussian beam into the hybrid planar grid.
The beam is
exp(-((x - offset_x)/waist_x)**2) exp(-(|y|/waist)**(2 order))in field, carrying the temporal pulse, polarised along the slab modes’ own component (yfor TE,xfor TM). In x it is projected onto the slab modes (solprop.modes.slab.gaussian_projection(), with signs); in y it is sampled on the grid and transformed. The temporal pulse’s energy or peak power is the incident beam’s, over the whole beam in x and the sampled y axis: what falls on the plates is lost at the entrance, so the modes carry the coupled share of it (98.8 % intop = 1atwaist_x = 0.735 a, Arnold et al. 2010), not a beam renormalised over the gap. The geometry integrates over y, so the energy is in J and the peak power in W.- Attributes:
- temporalPulse
Temporal pulse shape; its energy (J) or peak power (W) is the incident beam’s.
- waistfloat
The 1/e field radius in y (the 1/e^2 intensity radius), m, positive.
- orderint
Super-Gaussian order in y, at least 1.
- propzfloat
Must be 0: the launch is at the entrance plane; a free-space pre-propagation before a guide is refused.
- jonestuple of complex
One component, a phase; the component itself is the modes’. Two components are refused, the planar state being scalar.
- waist_xfloat
The 1/e field radius in x, m, positive (keyword).
- offset_xfloat
The beam centre’s displacement from the mid-plane in x, m (keyword); an offset couples to even orders.
- Raises:
- PulseError
If a waist, the order, offset_x, propz or jones is invalid.
Examples
>>> import numpy as np >>> from solprop.grid import EnvelopeGrid >>> from solprop.modes import PlanarGrid, SlabMode >>> from solprop.pulses import GaussianPulse, PlanarPulse >>> from solprop.spatial import SpatialMedium >>> grid = EnvelopeGrid.from_limits(0.1, 800e-9, (650e-9, 1000e-9), 400e-15) >>> space = PlanarGrid([SlabMode(63.5e-6, order=p) for p in (1, 3)], 1e-3, 32) >>> pulse = PlanarPulse( ... GaussianPulse(wavelength=800e-9, fwhm=100e-15, energy=1e-3), ... 250e-6, waist_x=0.735 * 63.5e-6, ... ) >>> pulse.build(grid, space, medium=SpatialMedium()).shape (64, 1, 256)
- build(grid, space, *, medium, z=0.0, side=None, frame=None)[source]#
Build the launched
(p, k_y)spectrum on a planar grid.The requested energy or peak power is the incident beam’s physical axial flux in the fill medium, over the whole beam in x and the sampled y axis (SEP 34): the x integral of the continuous Gaussian’s angular spectrum is taken by Gauss-Hermite quadrature of its admittance. The slab modes then carry the coupled share of that beam.
- Parameters:
- gridsolprop.grid.TimeGrid
Field or envelope temporal grid.
- spacesolprop.modes.slab.PlanarGrid
The slab modes in x and the y axis; any other grid is refused.
- mediumsolprop.spatial.SpatialMedium
The fill medium at the entrance.
- zfloat, optional
Position of the entrance plane in m.
- side{‘before’, ‘after’} or None, optional
Side of a declared index jump at
z; required there.- framesolprop.linear.Frame or None, optional
Unused: the launch is at the entrance, with no pre-propagation.
- Returns:
- numpy.ndarray
Modal amplitudes in sqrt(W/m), raw-DFT transformed in y, times the temporal spectrum, shape
(n_p*n_y, 1, n_omega), the mode index fastest, in the internal convention.
- Raises:
- PulseError
If the grid is not a planar one, the temporal pulse is not scalar, or the beam has no propagating content on the y axis.
- class solprop.pulses.Pulse(*args, **kwargs)[source]#
Bases:
ProtocolThe one method every input pulse provides.
A pulse is a specification, not an array: it is built against a particular grid, and the same specification may be built against a field grid and an envelope grid to give the same physical pulse in the two representations.
Examples
>>> from solprop.grid import EnvelopeGrid >>> from solprop.pulses import GaussianPulse, Pulse >>> pulse: Pulse = GaussianPulse(wavelength=800e-9, fwhm=30e-15, energy=1e-6) >>> grid = EnvelopeGrid.from_limits(1.0, 800e-9, (400e-9, 2e-6), 2e-12) >>> pulse.build(grid).shape[:2] (1, 1)
- build(grid)[source]#
Return the initial spectrum of this pulse on the given grid.
- Parameters:
- gridsolprop.grid.TimeGrid
Grid to build on; its
kindselects the field or envelope layout.
- Returns:
- numpy.ndarray
Stored spectrum, shape
(n_modes, n_pol, n_omega), complex, in the square root of watts and in the internal FFT convention.
- class solprop.pulses.PulseSum(pulses)[source]#
Bases:
DisplayedSeveral pulses launched together: the input is the sum of their fields.
Two-colour and multi-colour inputs – a pump and a seed for four-wave mixing, a strong pump and a weak probe for cross-phase modulation, a pulse and its delayed replica – are the sum of independently specified pulses. Each member keeps its own centre wavelength, duration, energy or peak power, spectral phase and delay, and is built and scaled on the grid exactly as it would be alone; the spectra are then added. The energies of well-separated members therefore add, and members that overlap in time and frequency interfere, as the fields do.
- Attributes:
- pulsestuple of Pulse
The members, at least one; any object with a
build(grid)method whose spectra share a shape, typicallyGaussianPulse,SechPulse,CustomPulseorDataPulse.
Examples
>>> import numpy as np >>> from solprop import fourier >>> from solprop.grid import FieldGrid >>> from solprop.pulses import GaussianPulse, PulseSum >>> grid = FieldGrid.from_limits(1.0, 800e-9, (150e-9, 3e-6), 1e-12) >>> pump = GaussianPulse(wavelength=267e-9, fwhm=35e-15, energy=95e-6) >>> seed = GaussianPulse(wavelength=800e-9, fwhm=35e-15, energy=45e-6) >>> spectrum = PulseSum((pump, seed)).build(grid) >>> energy = fourier.energy_omega(spectrum[0, 0], grid.dt, kind=grid.kind) >>> bool(np.isclose(energy, 140e-6, rtol=1e-6)) True
- build(grid)[source]#
Return the sum of the members’ spectra on the given grid.
- Parameters:
- gridsolprop.grid.TimeGrid
Grid to build on; every member is built on it independently.
- Returns:
- numpy.ndarray
Stored spectrum, the members’ common shape, in the square root of watts.
- Raises:
- PulseError
If the members build to spectra of different shapes.
- class solprop.pulses.SechPulse(*, wavelength, energy=None, peak_power=None, phase=(), t0=0.0, fwhm=None, width=None)[source]#
Bases:
ShapedPulseA
sech**2pulse, specified by its FWHM or by its natural width.- Attributes:
- wavelengthfloat
Centre wavelength of the pulse, m, and the reference of the Taylor phase.
- fwhmfloat or None
Full width at half maximum of the power envelope, s. Exactly one of
fwhmandwidthis given.- widthfloat or None
Natural width of
sech**2(t/width), s, which isfwhm / (2 ln(1 + sqrt(2))).- energyfloat or None
Pulse energy, J. Exactly one of
energyandpeak_poweris given.- peak_powerfloat or None
Peak power, W, realised after the spectral phase.
- phasearray_like
Optics-convention Taylor coefficients about
wavelength: radians at order 0 ands**nat ordernthereafter.- t0float
Delay, s, positive for later arrival.
- Raises:
- PulseError
If both or neither duration is given, or the scaling request is ambiguous.
Examples
>>> import numpy as np >>> from solprop.pulses import SechPulse >>> pulse = SechPulse(wavelength=1030e-9, fwhm=100e-15, energy=10e-6) >>> float(round(pulse.intensity(np.array([50e-15]))[0], 12)) 0.5
- intensity(t)[source]#
Return the
sech**2power envelope, normalised to a peak of one.- Parameters:
- tnumpy.ndarray
Time samples, s, shape
(n_t,).
- Returns:
- numpy.ndarray
Envelope values in
(0, 1], shape(n_t,).
Examples
>>> import numpy as np >>> from solprop.pulses import SechPulse >>> pulse = SechPulse(wavelength=1030e-9, width=1.0, energy=1.0) >>> float(pulse.intensity(np.array([0.0]))[0]) 1.0
- class solprop.pulses.ShapedPulse(*, wavelength, energy=None, peak_power=None, phase=(), t0=0.0)[source]#
Bases:
DisplayedCommon parameters and build path of a pulse defined by its power envelope.
GaussianPulse,SechPulseandCustomPulsederive from this class: it holds the parameters they share and the whole ofbuild()(the carrier, the spectral phase, the exact scaling and the canonical layout), so that the three differ only in one line of arithmetic. Subclass it and supplyintensity()to add a shape of your own; it is abstract here.- Attributes:
- wavelengthfloat
Centre wavelength of the pulse, m. Also the frequency the Taylor spectral phase is expanded about, which need not be the grid’s reference wavelength.
- energyfloat or None
Pulse energy, J. Exactly one of
energyandpeak_poweris given.- peak_powerfloat or None
Peak power, W, realised after the spectral phase is applied.
- phasearray_like
Optics-convention spectral phase as Taylor coefficients about
wavelength:[CEP (rad), group delay (s), GDD (s**2), TOD (s**3), ...]. Empty by default.- t0float
Delay, s, positive for later arrival. Must lie strictly inside the window.
- build(grid)[source]#
Return the initial spectrum of this pulse on the given grid.
On a field grid the samples are
sqrt(I(t)) cos(omega_p t), whose analytic signal is the pulse; on an envelope grid they aresqrt(I(t)) exp(i (omega_p - omega0) t), which is that analytic signal with the grid’s carrier removed (conventions.md §4). The spectral phase is applied to the stored spectrum and the scaling comes last.- Parameters:
- gridsolprop.grid.TimeGrid
Grid to build on.
- Returns:
- numpy.ndarray
Stored spectrum, shape
(1, 1, n_omega), in the square root of watts.
- Raises:
- PulseError
If the delay leaves the window, the envelope is not finite and non-negative, or the pulse carries nothing to scale on this grid.
Examples
>>> import numpy as np >>> from solprop import fourier >>> from solprop.grid import EnvelopeGrid >>> from solprop.pulses import GaussianPulse >>> grid = EnvelopeGrid.from_limits(1.0, 800e-9, (400e-9, 2e-6), 2e-12) >>> spectrum = GaussianPulse( ... wavelength=800e-9, fwhm=30e-15, energy=1e-6 ... ).build(grid) >>> energy = fourier.energy_omega(spectrum[0, 0], grid.dt, kind=grid.kind) >>> bool(np.isclose(energy, 1e-6, rtol=1e-12)) True
- intensity(t)[source]#
Return the power envelope of the pulse, in arbitrary units.
The normalisation is irrelevant: the spectrum is rescaled to the requested energy or peak power afterwards.
- Parameters:
- tnumpy.ndarray
Time samples, s, shape
(n_t,).
- Returns:
- numpy.ndarray
Non-negative power envelope, shape
(n_t,).
- Raises:
- NotImplementedError
Always; each shaped pulse supplies its own envelope.
- class solprop.pulses.SpatialPulse(temporal, waist, order=1, propz=0.0, jones=(1 + 0j,))[source]#
Bases:
DisplayedWrap a temporal pulse in an energy-normalised transverse beam.
- Attributes:
- temporalPulse
Existing temporal pulse shape. Its energy or peak power sets the whole beam’s physical energy or peak axial power through the launch plane, in the medium there (SEP 34). On a
solprop.grid.LineGridthe beam is uniform in y and these are per metre of y: the energy is in J/m and the peak power in W/m (solprop.modes.slab.gaussian_to_per_unit_width()converts a beam of finite height).- waistfloat
Positive finite 1/e^2 intensity radius in m; on a line, the 1/e^2 intensity half-width in x.
- orderint
Super-Gaussian order, at least 1. The field profile is
exp(-(r/waist)**(2*order)), withr = |x|on a line.- propzfloat
Signed launch pre-propagation distance in m, finite. Applied by the exact longitudinal operator with the launch plane’s material frozen. Zero launches at the waist; negative values launch before it. Normalisation follows the pre-propagation, so the launch plane carries the requested quantity.
- jonestuple of complex
One scalar component or two optics-convention Cartesian components (x, y). The vector is normalised to unit squared magnitude. Default is scalar.
- Raises:
- PulseError
If the waist, order, distance or Jones vector is invalid.
- build(grid, space, *, medium, z=0.0, side=None, frame=None)[source]#
Build the beam’s transverse and temporal spectrum on a spatial grid.
The requested energy or peak power is the physical axial Poynting flux through the launch plane, in the medium there (SEP 34): what an energy meter in that medium reads. Each member of a
PulseSumcarries its own requested quantity. The inactive band and zero frequency are hard-masked as everywhere; a beam with more than rounding-level content in nonpropagating columns is refused, since no energy can be assigned to it.- Parameters:
- gridsolprop.grid.TimeGrid
Field or envelope temporal grid.
- spacesolprop.grid.RadialGrid or solprop.grid.FreeGrid
Transverse samples and their spectral integration measure. A
solprop.modes.slab.PlanarGridis refused: its launch is aPlanarPulse.- mediumsolprop.spatial.SpatialMedium
The medium at the launch plane; there is no vacuum default.
- zfloat, optional
Position of the launch plane in m, at which the medium is evaluated.
- side{‘before’, ‘after’} or None, optional
Side of a declared index jump at
z; required there.- framesolprop.linear.Frame or None, optional
Launch frame, normally the run’s frame. None uses the grid’s co-moving frame without envelope THG.
- Returns:
- numpy.ndarray
Hankel/temporal spectrum in V m or Cartesian DFT/temporal spectrum in V/m, shape
(n_modes, n_pol, n_omega), in the internal convention.
- Raises:
- PulseError
If the grid is a planar one, the temporal pulse is not scalar, the beam has no propagating content or more than rounding-level nonpropagating content, or launch pre-propagation would overflow the field.
- incident(grid, xy, *, index)[source]#
Sample the beam, normalised over its whole incident plane, at given points.
The incident-field boundary of a guide launch (SEP 31, phase 1): the beam is given its requested energy or peak power over the whole transverse plane and only then sampled where a receiving guide wants it, so light that misses the guide still counts as incident and is lost, not restored. The measure is the paraxial local one, \(M_0 = Y_0\,dA/2\) with \(Y_0 = n_{\rm ref}(\omega)\varepsilon_0 c\): the axial Poynting flux of a paraxial beam in a medium of phase index \(n_{\rm ref}\), which is what an energy meter in that medium reads (SEP 34). The plane integral of the super-Gaussian profile \(u = \exp(-(r/w)^{2N})\) is exact, \(\int|u|^2 dA = \pi w^2 2^{-1/N}\Gamma(1 + 1/N)\), so no transverse window or quadrature enters the incident energy.
A one-component Jones vector is polarised along x; two components are the x and y components. The beam is centred on the origin of xy.
- Parameters:
- gridsolprop.grid.TimeGrid
Temporal grid.
- xynumpy.ndarray
Transverse sample positions in m, shape
(n_nodes, 2).- indexnumpy.ndarray
Real phase index of the launch medium at each of the grid’s frequencies, shape
(n_omega,), positive on every bin that carries flux; a zero bin carries none and is masked.
- Returns:
- IncidentField
The normalised field at the points and the energy each bin carries through the whole plane.
- Raises:
- PulseError
If the beam is a
PlanarPulseor is launched away from its waist, the index is not finite and non-negative on the grid, the temporal pulse is not scalar, or the beam carries no forward flux.
Examples
>>> import numpy as np >>> from solprop.grid import FieldGrid >>> from solprop.pulses import GaussianPulse, SpatialPulse >>> grid = FieldGrid.from_limits(1e-3, 800e-9, (700e-9, 900e-9), 200e-15) >>> beam = SpatialPulse( ... GaussianPulse(wavelength=800e-9, fwhm=30e-15, energy=1e-6), 50e-6 ... ) >>> index = np.where(grid.omega > 0, 1.45, 0.0) >>> sampled = beam.incident(grid, np.zeros((1, 2)), index=index) >>> f"{np.sum(sampled.energy):.6e} J" # the whole plane, wherever it is sampled '1.000000e-06 J'