diff --git a/src/scgenerator/evaluator.py b/src/scgenerator/evaluator.py index 6568064..52985de 100644 --- a/src/scgenerator/evaluator.py +++ b/src/scgenerator/evaluator.py @@ -3,7 +3,7 @@ from __future__ import annotations import itertools import traceback from collections import ChainMap, defaultdict -from typing import Any, Callable, MutableMapping, NamedTuple, Optional, Type, Union +from typing import Any, Callable, MutableMapping, NamedTuple, Optional, Union import numpy as np diff --git a/src/scgenerator/parameter.py b/src/scgenerator/parameter.py index 5ec8b6c..9a9bf5f 100644 --- a/src/scgenerator/parameter.py +++ b/src/scgenerator/parameter.py @@ -369,6 +369,7 @@ class Parameters: noise_correlation: float = Parameter(in_range_incl(-10, 10), default=0) width: float = Parameter(in_range_excl(0, 1e-9), display_info=(1e15, "fs")) t0: float = Parameter(in_range_excl(0, 1e-9), display_info=(1e15, "fs")) + delay: float = Parameter(type_checker(float, int), display_info=(1e15, "fs")) # Behaviors to include quantum_noise: bool = Parameter(boolean, default=False) diff --git a/src/scgenerator/physics/pulse.py b/src/scgenerator/physics/pulse.py index 9356a33..2e94b93 100644 --- a/src/scgenerator/physics/pulse.py +++ b/src/scgenerator/physics/pulse.py @@ -107,6 +107,7 @@ def initial_full_field( peak_power: float, w0: float, n0: float, + delay: float | None = None, ) -> np.ndarray: """ initial field in full field simulations @@ -125,6 +126,9 @@ def initial_full_field( center frequency n0 : float refractive index at center frequency + delay : float | None, optional, + if given, delays the pulse by that amount. This means that in the time domain, the pulse + won't be in the center of the window anymore. Returns ------- @@ -132,13 +136,15 @@ def initial_full_field( initial field """ return ( - initial_field_envelope(t, shape, t0, peak_power) + initial_field_envelope(t, shape, t0, peak_power, delay) * np.cos(w0 * t) * units.W_to_Vm(n0, effective_area) ) -def initial_field_envelope(t: np.ndarray, shape: str, t0: float, peak_power: float) -> np.ndarray: +def initial_field_envelope( + t: np.ndarray, shape: str, t0: float, peak_power: float, delay: float | None = None +) -> np.ndarray: """ returns the initial field @@ -153,6 +159,9 @@ def initial_field_envelope(t: np.ndarray, shape: str, t0: float, peak_power: flo `scgenerator.physics.pulse.fwhm_to_T0_fac[shape]` peak_power : float peak power + delay : float | None, optional, + if given, delays the pulse by that amount. This means that in the time domain, the pulse + won't be in the center of the window anymore. Returns ------- @@ -164,6 +173,9 @@ def initial_field_envelope(t: np.ndarray, shape: str, t0: float, peak_power: flo ValueError raised when shape is not recognized """ + if delay is not None: + t = t + delay + if shape == "gaussian": return gaussian_pulse(t, t0, peak_power) elif shape == "sech": @@ -382,11 +394,41 @@ def adjust_custom_field( input_time: np.ndarray, input_field: np.ndarray, t: np.ndarray, - intensity_noise: float, - noise_correlation: float, - energy: float = None, + delay: float | None = None, + intensity_noise: float | None = None, + noise_correlation: float | None = None, + energy: float | None = None, peak_power: float = None, ) -> np.ndarray: + """ + When loading a custom input field, use this function to conform the custom data to the simulation + parameters. + + Parameters + ---------- + input_time : np.ndarray, shape (m,) + time axis of the custom data + input_field : np.ndarray, shape (m,) + complex field values + t : np.ndarray, shape (nt,) + time axis of the simulation + delay : float | None, optional + if given, delays the pulse by that amount. This means that in the time domain, the pulse + won't be in the center of the window anymore. + intensity_noise : float | None, optional + if given, pick a value `delta` on a normal distribution such that, when considering an + ensemble of similar input fields, the relative `intensity_noise` is the specified amount. + by default None + noise_correlation : float | None, optional + if given, correlation factor between intensity noise and pulse width variations + by default None + energy : float | None, optional + if given, readjusts the amplitude of the field such that the total energy is the specified + value. Takes precedence over peak_power, by default None + peak_power : float | None, optional + if given, readjusts the amplitude of the field such that the peak power is the specified + value, by default None + """ field_0 = interp_custom_field(input_time, input_field, t) if energy is not None: curr_energy = np.trapz(math.abs2(field_0), t) @@ -405,8 +447,11 @@ def adjust_custom_field( def interp_custom_field( - input_time: np.ndarray, input_field: np.ndarray, t: np.ndarray + input_time: np.ndarray, input_field: np.ndarray, t: np.ndarray, delay: float | None = None ) -> np.ndarray: + if delay is not None: + t = t + delay + field_interp = interp1d(input_time, input_field, bounds_error=False, fill_value=(0, 0)) field_0 = field_interp(t) return field_0