initial commit

This commit is contained in:
Benoît Sierro
2021-01-28 22:43:54 +01:00
commit bf8e2ec0b1
80 changed files with 61379 additions and 0 deletions

18
.gitignore vendored Normal file
View File

@@ -0,0 +1,18 @@
.DS_store
.idea
**/*.npy
plots*
Archive
*.mp4
*.png
*.pdf
__pycache__
*.egg-info
*sim_data*
tmp*
paths.json
scgenerator_log*
.scgenerator_tmp
.vscode

19
LICENSE Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2020 Benoît Sierro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1
MANIFEST.in Normal file
View File

@@ -0,0 +1 @@
include src/scgenerator/data/*

204
README.md Normal file
View File

@@ -0,0 +1,204 @@
It is recommended to import scgenerator in the following manner :
`import scgenerator as sc`
# How to run a set of simulations
create a config file
run `sc.parallel_simulations(config_file)` or `sc.simulate(config_file)`
# How to analyse a simulation
load data with the load_sim_data method
spectra = load_sim_data("varyTechNoise100kW_sim_data", nickname="cool_sim")
to plot
plot_results_2D(spectra[0], (600, 1450, nm)) # will take care of looking up the nickname
to retrieve a paramteter:
z = state.params["cool_sim"]["z"]
# Configuration
You can load parameters by simpling passing the path to a toml file to the appropriate simulation function. Each possible key of this dictionary is described below. Every value must be given in standard SI units (m, s, W, J, ...)
The configuration file can have a ```name``` parameter at the root and must otherwise contain the following sections with the specified parameters
note : internally, another structure with a flattened dictionary is used
## Fiber parameters
If you already know the Taylor coefficients corresponding to the expansion of the beta2 profile, you can specify them and skip to "Other fiber parameters":
beta: list-like
list of Taylor coefficients for the beta_2 function
else, you can choose a mathematical fiber model
model: str {"pcf", "marcatili", "marcatili_adjusted", "hasan"}
**PCF** : solid core silica photonic crystal fiber, as modeled in Saitoh, Kunimasa, and Masanori Koshiba. "Empirical relations for simple design of photonic crystal fibers." Optics express 13.1 (2005): 267-274.
**marcatili** : Marcatili model of a capillary fiber : Marcatili, Enrique AJ, and R. A. Schmeltzer. "Hollow metallic and dielectric waveguides for long distance optical transmission and lasers." Bell System Technical Journal 43.4 (1964): 1783-1809.
**marcatili_adjusted** : Marcatili model of a capillary fiber with adjusted effective radius in the longer wavelength : Köttig, F., et al. "Novel mid-infrared dispersive wave generation in gas-filled PCF by transient ionization-driven changes in dispersion." arXiv preprint arXiv:1701.04843 (2017).
**hasan** : Hasan model of hollow core anti-resonance fibers : Hasan, Md Imran, Nail Akhmediev, and Wonkeun Chang. "Empirical formulae for dispersion and effective mode area in hollow-core antiresonant fibers." Journal of Lightwave Technology 36.18 (2018): 4060-4065.
and specify the parameters it needs
pcf :
pitch: float
distance between air holes in m
pitch_ratio: float 0.2 < pitch_ratio < 0.8
ratio hole diameter/pich
marcatili, marcatili_adjusted, hasan :
core_radius: float
radius of the hollow core in m
marcatili, marcatili_adjusted :
he_mode: list, shape (2, ), optional
mode of propagation. default is (1, 1), which is the fundamental mode
marcatili_adjusted :
fit_parameters: list, shape (2, ), optional
parameters for the effective radius correction. Defaults are (s, h) = (0.08, 200e-9) as in the referenced paper.
hasan :
capillary_num : int
number of capillaries
capillary_outer_d : float, optional if g is specified
outer diameter of the capillaries
capillary_thickness : float
thickness of the capillary walls
capillary_spacing : float, optional if d is specified
spacing between the capillary
capillray_resonance_strengths : list, optional
list of resonance strengths. Default is []
capillary_nested : int, optional
how many nested capillaries. Default is 0
## Other fiber parameters :
gamma: float, optional unless beta is directly provided
nonlinear parameter in m^2 / W. Will overwrite any computed gamma parameter.
length: float, optional
length of the fiber in m. default : 1
## Gas parameters
this section is completely optional and ignored if the fiber model is "pcf"
gas_name: str
name of the gas. default : "vacuum"
pressure: float
pressure of the gas in the fiber. default : 1e5
temperature: float
temperature of the gas in the fiber. default : 300
plasma_density: float
constant plasma density (in m^-3). default : 0
## Pulse parameters:
### Mandatory
wavelength: float
pump wavelength in m
To specify the initial pulse shape, either use one of 2 in (power, energy) together with one of 2 in (width, t0), or use soliton_num together with one of 4 in (power, energy, width, t0)
power: float
peak power in W
energy: float
total pulse energy in J
width: float
full width half maximum of the pulse in s. Will be converted to appropriate t0 depending on pulse shape
t0: float
pulse width parameter
solition_num: float
soliton number
### optional
quantum_noise: bool
whether or not one-photon-per-mode quantum noise is activated. default : False
intensity_noise: float
relative intensity noise
shape: str {"gaussian", "sech"}
shape of the pulse. default : gaussian
## Simulation parameters
### 2 of 3
dt: float
resolution of the temporal grid in s
t_num: int
number of temporal grid points
time_window: float
total length of the temporal grid in s
### optional
behaviors: list of str {"spm", "raman", "ss"}
spm is self-phase modulation
raman is raman effect
ss is self-steepening
default : ["spm", "ss"]
raman_type: str {"measured", "stolen", "agrawal"}
type of Raman effect. Default is "agrawal".
ideal_gas: bool
if True, use the ideal gas law. Otherwise, use van der Waals equation. default : False
z_num : int
number of spatial grid points along the fiber. default : 128
frep: float
repetition rate in Hz. Only useful to convert units. default : 80e6
tolerated_error: float
relative tolerated step-to-step error. default : 1e-11
step_size: float
if given, sets a constant step size rather than adapting it.
parallel: int
how many parallel simulations to run. default : 1
repeat: int
how many simulations to run per parameter set. default : 1
lower_wavelength_interp_limit: float
dispersion coefficients are computed over a certain wavelength range. This parameter
sets the lowest end of this range. If the set value is lower than the lower end of the
wavelength window, it is raised up to that point. default : 0
upper_wavelength_interp_limit: float
dispersion coefficients are computed over a certain wavelength range. This parameter
sets the lowest end of this range. If the set value is higher than the higher end of the
wavelength window, it is lowered down to that point. default : 1900e-9

BIN
config.npz Normal file

Binary file not shown.

7
developement_help.md Normal file
View File

@@ -0,0 +1,7 @@
## add parameter
- add it to ```const.valid_param_types```
- add it to README.md
- add the necessary logic in the appropriate ```initialize.ensure_consistency``` subfunction
- optional : add a default value
- optional : add to valid varying
- optional : add/update dependency map

586
scgenerator.log Normal file
View File

@@ -0,0 +1,586 @@
scgenerator.initialize: computed initial N = 8.66
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 0)]
wavelength_8.35e-07_num_0: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_0: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_0: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_0: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:01:39 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 16:01:44 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 25s (30% in total). ETA : 2021-01-28 16:02:16 (2638 steps). z = 0.0060, h = 9.7856e-07
wavelength_8.35e-07_num_0: remaining : 0h 0min 21s (41% in total). ETA : 2021-01-28 16:02:15 (3444 steps). z = 0.0083, h = 1.7968e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 16s (50% in total). ETA : 2021-01-28 16:02:10 (3706 steps). z = 0.0102, h = 2.8346e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 11s (60% in total). ETA : 2021-01-28 16:02:07 (3915 steps). z = 0.0121, h = 8.8081e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 7s (71% in total). ETA : 2021-01-28 16:02:03 (4135 steps). z = 0.0143, h = 5.7601e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 4s (80% in total). ETA : 2021-01-28 16:02:01 (4307 steps). z = 0.0162, h = 6.6994e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:02:00 (4466 steps). z = 0.0181, h = 5.4078e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:01:59 (4634 steps). z = 0.0200, h = 7.4967e-06
wavelength_8.35e-07_num_0: propagation finished in 4634 steps (19.990754 seconds)
scgenerator.initialize: computed initial N = 8.66
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.3e-07), ('num', 0)]
wavelength_8.3e-07_num_0: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.3e-07_num_0: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.3e-07_num_0: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.3e-07_num_0: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:02:00 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 16:02:05 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.3e-07_num_0: step 997 rejected with h = 2.7537e-06, doing over
wavelength_8.3e-07_num_0: remaining : 0h 0min 23s (30% in total). ETA : 2021-01-28 16:02:33 (2162 steps). z = 0.0060, h = 3.8127e-08
wavelength_8.3e-07_num_0: remaining : 0h 0min 19s (41% in total). ETA : 2021-01-28 16:02:31 (2991 steps). z = 0.0083, h = 5.8562e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 14s (50% in total). ETA : 2021-01-28 16:02:28 (3242 steps). z = 0.0102, h = 6.5756e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 10s (60% in total). ETA : 2021-01-28 16:02:25 (3443 steps). z = 0.0121, h = 2.9227e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 6s (71% in total). ETA : 2021-01-28 16:02:22 (3664 steps). z = 0.0143, h = 1.1425e-05
wavelength_8.3e-07_num_0: remaining : 0h 0min 4s (80% in total). ETA : 2021-01-28 16:02:21 (3833 steps). z = 0.0162, h = 1.0515e-05
wavelength_8.3e-07_num_0: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:02:19 (4003 steps). z = 0.0181, h = 7.4644e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:02:18 (4181 steps). z = 0.0200, h = 1.21e-05
wavelength_8.3e-07_num_0: propagation finished in 4181 steps (18.946698 seconds)
scgenerator.physics.simulate: Merging data...
scgenerator.physics.simulate: Finished simulations from config full anomalous !
scgenerator.physics.simulate: 1 node in the Ray cluster : ['Obento.fritz.box']
scgenerator.initialize: computed initial N = 8.66
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 0)], job : df5a1a828c9685d3ffffffff0100000001000000
scgenerator.initialize: computed initial N = 8.66
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.3e-07), ('num', 0)], job : cb230a572350ff44ffffffff0100000001000000
wavelength_8.35e-07_num_0: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_0: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_0: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.3e-07_num_0: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.3e-07_num_0: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.3e-07_num_0: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_0: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:08:57 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:08:58 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 16:09:02 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 16:09:03 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.3e-07_num_0: step 997 rejected with h = 2.7537e-06, doing over
wavelength_8.3e-07_num_0: remaining : 0h 0min 23s (30% in total). ETA : 2021-01-28 16:09:31 (2162 steps). z = 0.0060, h = 3.8127e-08
wavelength_8.35e-07_num_0: remaining : 0h 0min 28s (30% in total). ETA : 2021-01-28 16:09:37 (2638 steps). z = 0.0060, h = 9.7856e-07
wavelength_8.3e-07_num_0: remaining : 0h 0min 20s (41% in total). ETA : 2021-01-28 16:09:31 (2991 steps). z = 0.0083, h = 5.8562e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 16s (50% in total). ETA : 2021-01-28 16:09:28 (3242 steps). z = 0.0102, h = 6.5756e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 24s (41% in total). ETA : 2021-01-28 16:09:37 (3444 steps). z = 0.0083, h = 1.7968e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 11s (60% in total). ETA : 2021-01-28 16:09:25 (3443 steps). z = 0.0121, h = 2.9227e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 17s (50% in total). ETA : 2021-01-28 16:09:32 (3706 steps). z = 0.0102, h = 2.8346e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 7s (71% in total). ETA : 2021-01-28 16:09:22 (3664 steps). z = 0.0143, h = 1.1425e-05
wavelength_8.35e-07_num_0: remaining : 0h 0min 12s (60% in total). ETA : 2021-01-28 16:09:28 (3915 steps). z = 0.0121, h = 8.8081e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 4s (80% in total). ETA : 2021-01-28 16:09:20 (3833 steps). z = 0.0162, h = 1.0515e-05
wavelength_8.35e-07_num_0: remaining : 0h 0min 8s (71% in total). ETA : 2021-01-28 16:09:25 (4135 steps). z = 0.0143, h = 5.7601e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:09:19 (4003 steps). z = 0.0181, h = 7.4644e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 5s (80% in total). ETA : 2021-01-28 16:09:23 (4307 steps). z = 0.0162, h = 6.6994e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:09:18 (4181 steps). z = 0.0200, h = 1.21e-05
wavelength_8.3e-07_num_0: propagation finished in 4181 steps (20.752765 seconds)
wavelength_8.35e-07_num_0: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:09:21 (4466 steps). z = 0.0181, h = 5.4078e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:09:19 (4634 steps). z = 0.0200, h = 7.4967e-06
wavelength_8.35e-07_num_0: propagation finished in 4634 steps (22.817256 seconds)
scgenerator.physics.simulate: Merging data...
scgenerator.physics.simulate: Finished simulations from config full anomalous !
scgenerator.physics.simulate: 1 node in the Ray cluster : ['Obento.fritz.box']
scgenerator.initialize: computed initial N = 8.66
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 0)], job : df5a1a828c9685d3ffffffff0100000001000000
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 1)], job : cb230a572350ff44ffffffff0100000001000000
wavelength_8.35e-07_num_0: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_0: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_0: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_1: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_1: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_1: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_0: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:21:10 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:21:11 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 16:21:15 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 16:21:16 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 30s (30% in total). ETA : 2021-01-28 16:21:54 (2638 steps). z = 0.0060, h = 9.7856e-07
wavelength_8.35e-07_num_1: remaining : 0h 0min 30s (30% in total). ETA : 2021-01-28 16:21:54 (2638 steps). z = 0.0060, h = 9.7856e-07
wavelength_8.35e-07_num_0: remaining : 0h 0min 24s (41% in total). ETA : 2021-01-28 16:21:52 (3444 steps). z = 0.0083, h = 1.7968e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 24s (41% in total). ETA : 2021-01-28 16:21:52 (3444 steps). z = 0.0083, h = 1.7968e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 17s (50% in total). ETA : 2021-01-28 16:21:46 (3706 steps). z = 0.0102, h = 2.8346e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 17s (50% in total). ETA : 2021-01-28 16:21:47 (3706 steps). z = 0.0102, h = 2.8346e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 12s (60% in total). ETA : 2021-01-28 16:21:42 (3915 steps). z = 0.0121, h = 8.8081e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 12s (60% in total). ETA : 2021-01-28 16:21:43 (3915 steps). z = 0.0121, h = 8.8081e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 8s (71% in total). ETA : 2021-01-28 16:21:39 (4135 steps). z = 0.0143, h = 5.7601e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 8s (71% in total). ETA : 2021-01-28 16:21:39 (4135 steps). z = 0.0143, h = 5.7601e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 5s (80% in total). ETA : 2021-01-28 16:21:37 (4307 steps). z = 0.0162, h = 6.6994e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 5s (80% in total). ETA : 2021-01-28 16:21:37 (4307 steps). z = 0.0162, h = 6.6994e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:21:35 (4466 steps). z = 0.0181, h = 5.4078e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:21:35 (4466 steps). z = 0.0181, h = 5.4078e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:21:33 (4634 steps). z = 0.0200, h = 7.4967e-06
wavelength_8.35e-07_num_0: propagation finished in 4634 steps (23.470734 seconds)
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 2)], job : 7bbd90284b71e599ffffffff0100000001000000
wavelength_8.35e-07_num_1: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:21:34 (4634 steps). z = 0.0200, h = 7.4967e-06
wavelength_8.35e-07_num_1: propagation finished in 4634 steps (23.325504 seconds)
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 3)], job : bd37d2621480fc7dffffffff0100000001000000
scgenerator.initialize: computed initial N = 8.66
wavelength_8.35e-07_num_2: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_2: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_2: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_3: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_3: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_3: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_2: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:21:35 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:21:35 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 16:21:40 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 16:21:40 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 28s (30% in total). ETA : 2021-01-28 16:22:15 (2638 steps). z = 0.0060, h = 9.7856e-07
wavelength_8.35e-07_num_3: remaining : 0h 0min 28s (30% in total). ETA : 2021-01-28 16:22:15 (2638 steps). z = 0.0060, h = 9.7856e-07
wavelength_8.35e-07_num_2: remaining : 0h 0min 23s (41% in total). ETA : 2021-01-28 16:22:13 (3444 steps). z = 0.0083, h = 1.7968e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 23s (41% in total). ETA : 2021-01-28 16:22:14 (3444 steps). z = 0.0083, h = 1.7968e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 16s (50% in total). ETA : 2021-01-28 16:22:08 (3706 steps). z = 0.0102, h = 2.8346e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 16s (50% in total). ETA : 2021-01-28 16:22:09 (3706 steps). z = 0.0102, h = 2.8346e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 12s (60% in total). ETA : 2021-01-28 16:22:05 (3915 steps). z = 0.0121, h = 8.8081e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 12s (60% in total). ETA : 2021-01-28 16:22:05 (3915 steps). z = 0.0121, h = 8.8081e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 8s (71% in total). ETA : 2021-01-28 16:22:01 (4135 steps). z = 0.0143, h = 5.7601e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 8s (71% in total). ETA : 2021-01-28 16:22:02 (4135 steps). z = 0.0143, h = 5.7601e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 5s (80% in total). ETA : 2021-01-28 16:21:59 (4307 steps). z = 0.0162, h = 6.6994e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 5s (80% in total). ETA : 2021-01-28 16:22:00 (4307 steps). z = 0.0162, h = 6.6994e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:21:58 (4466 steps). z = 0.0181, h = 5.4078e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:21:58 (4466 steps). z = 0.0181, h = 5.4078e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:21:56 (4634 steps). z = 0.0200, h = 7.4967e-06
wavelength_8.35e-07_num_2: propagation finished in 4634 steps (22.065082 seconds)
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.3e-07), ('num', 0)], job : 88866c7daffdd00effffffff0100000001000000
wavelength_8.35e-07_num_3: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:21:57 (4634 steps). z = 0.0200, h = 7.4967e-06
wavelength_8.35e-07_num_3: propagation finished in 4634 steps (22.22073 seconds)
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.3e-07), ('num', 1)], job : d251967856448cebffffffff0100000001000000
wavelength_8.3e-07_num_0: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.3e-07_num_0: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.3e-07_num_0: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.3e-07_num_1: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.3e-07_num_1: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.3e-07_num_1: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.3e-07_num_0: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:21:58 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.3e-07_num_1: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:21:59 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 16:22:03 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.3e-07_num_1: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 16:22:04 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.3e-07_num_0: step 997 rejected with h = 2.7537e-06, doing over
wavelength_8.3e-07_num_1: step 997 rejected with h = 2.7537e-06, doing over
wavelength_8.3e-07_num_0: remaining : 0h 0min 23s (30% in total). ETA : 2021-01-28 16:22:31 (2162 steps). z = 0.0060, h = 3.8127e-08
wavelength_8.3e-07_num_1: remaining : 0h 0min 23s (30% in total). ETA : 2021-01-28 16:22:31 (2162 steps). z = 0.0060, h = 3.8127e-08
wavelength_8.3e-07_num_0: remaining : 0h 0min 20s (41% in total). ETA : 2021-01-28 16:22:32 (2991 steps). z = 0.0083, h = 5.8562e-06
wavelength_8.3e-07_num_1: remaining : 0h 0min 20s (41% in total). ETA : 2021-01-28 16:22:32 (2991 steps). z = 0.0083, h = 5.8562e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 15s (50% in total). ETA : 2021-01-28 16:22:28 (3242 steps). z = 0.0102, h = 6.5756e-06
wavelength_8.3e-07_num_1: remaining : 0h 0min 15s (50% in total). ETA : 2021-01-28 16:22:28 (3242 steps). z = 0.0102, h = 6.5756e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 11s (60% in total). ETA : 2021-01-28 16:22:25 (3443 steps). z = 0.0121, h = 2.9227e-06
wavelength_8.3e-07_num_1: remaining : 0h 0min 11s (60% in total). ETA : 2021-01-28 16:22:25 (3443 steps). z = 0.0121, h = 2.9227e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 7s (71% in total). ETA : 2021-01-28 16:22:22 (3664 steps). z = 0.0143, h = 1.1425e-05
wavelength_8.3e-07_num_1: remaining : 0h 0min 7s (71% in total). ETA : 2021-01-28 16:22:23 (3664 steps). z = 0.0143, h = 1.1425e-05
wavelength_8.3e-07_num_0: remaining : 0h 0min 4s (80% in total). ETA : 2021-01-28 16:22:20 (3833 steps). z = 0.0162, h = 1.0515e-05
wavelength_8.3e-07_num_1: remaining : 0h 0min 4s (80% in total). ETA : 2021-01-28 16:22:21 (3833 steps). z = 0.0162, h = 1.0515e-05
wavelength_8.3e-07_num_0: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:22:19 (4003 steps). z = 0.0181, h = 7.4644e-06
wavelength_8.3e-07_num_1: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:22:20 (4003 steps). z = 0.0181, h = 7.4644e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:22:18 (4181 steps). z = 0.0200, h = 1.21e-05
wavelength_8.3e-07_num_0: propagation finished in 4181 steps (20.406912 seconds)
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.3e-07), ('num', 2)], job : 3bf0c856ace5a4d8ffffffff0100000001000000
wavelength_8.3e-07_num_1: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:22:18 (4181 steps). z = 0.0200, h = 1.21e-05
wavelength_8.3e-07_num_1: propagation finished in 4181 steps (20.386092 seconds)
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.3e-07), ('num', 3)], job : 72e11b46e93d91e4ffffffff0100000001000000
wavelength_8.3e-07_num_2: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.3e-07_num_2: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.3e-07_num_2: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.3e-07_num_3: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.3e-07_num_3: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.3e-07_num_3: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.3e-07_num_2: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:22:20 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.3e-07_num_3: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:22:20 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.3e-07_num_2: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 16:22:25 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.3e-07_num_3: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 16:22:25 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.3e-07_num_2: step 997 rejected with h = 2.7537e-06, doing over
wavelength_8.3e-07_num_3: step 997 rejected with h = 2.7537e-06, doing over
wavelength_8.3e-07_num_2: remaining : 0h 0min 23s (30% in total). ETA : 2021-01-28 16:22:52 (2162 steps). z = 0.0060, h = 3.8127e-08
wavelength_8.3e-07_num_3: remaining : 0h 0min 23s (30% in total). ETA : 2021-01-28 16:22:53 (2162 steps). z = 0.0060, h = 3.8127e-08
wavelength_8.3e-07_num_2: remaining : 0h 0min 20s (41% in total). ETA : 2021-01-28 16:22:53 (2991 steps). z = 0.0083, h = 5.8562e-06
wavelength_8.3e-07_num_3: remaining : 0h 0min 20s (41% in total). ETA : 2021-01-28 16:22:54 (2991 steps). z = 0.0083, h = 5.8562e-06
wavelength_8.3e-07_num_2: remaining : 0h 0min 15s (50% in total). ETA : 2021-01-28 16:22:49 (3242 steps). z = 0.0102, h = 6.5756e-06
wavelength_8.3e-07_num_3: remaining : 0h 0min 15s (50% in total). ETA : 2021-01-28 16:22:50 (3242 steps). z = 0.0102, h = 6.5756e-06
wavelength_8.3e-07_num_2: remaining : 0h 0min 11s (60% in total). ETA : 2021-01-28 16:22:46 (3443 steps). z = 0.0121, h = 2.9227e-06
wavelength_8.3e-07_num_3: remaining : 0h 0min 11s (60% in total). ETA : 2021-01-28 16:22:47 (3443 steps). z = 0.0121, h = 2.9227e-06
wavelength_8.3e-07_num_2: remaining : 0h 0min 7s (71% in total). ETA : 2021-01-28 16:22:43 (3664 steps). z = 0.0143, h = 1.1425e-05
wavelength_8.3e-07_num_3: remaining : 0h 0min 7s (71% in total). ETA : 2021-01-28 16:22:44 (3664 steps). z = 0.0143, h = 1.1425e-05
wavelength_8.3e-07_num_2: remaining : 0h 0min 4s (80% in total). ETA : 2021-01-28 16:22:42 (3833 steps). z = 0.0162, h = 1.0515e-05
wavelength_8.3e-07_num_3: remaining : 0h 0min 4s (80% in total). ETA : 2021-01-28 16:22:42 (3833 steps). z = 0.0162, h = 1.0515e-05
wavelength_8.3e-07_num_2: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:22:40 (4003 steps). z = 0.0181, h = 7.4644e-06
wavelength_8.3e-07_num_3: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:22:41 (4003 steps). z = 0.0181, h = 7.4644e-06
wavelength_8.3e-07_num_2: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:22:39 (4181 steps). z = 0.0200, h = 1.21e-05
wavelength_8.3e-07_num_2: propagation finished in 4181 steps (20.253876 seconds)
wavelength_8.3e-07_num_3: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:22:40 (4181 steps). z = 0.0200, h = 1.21e-05
wavelength_8.3e-07_num_3: propagation finished in 4181 steps (20.165082 seconds)
scgenerator.physics.simulate: Merging data...
scgenerator.physics.simulate: Finished simulations from config full anomalous !
scgenerator.physics.simulate: 1 node in the Ray cluster : ['Obento.fritz.box']
scgenerator.initialize: computed initial N = 8.66
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 0)], job : df5a1a828c9685d3ffffffff0100000001000000
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 1)], job : cb230a572350ff44ffffffff0100000001000000
wavelength_8.35e-07_num_0: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_0: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_0: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_1: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_1: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_1: step 1 rejected with h = 7.9365e-05, doing over
scgenerator.physics.simulate: 1 node in the Ray cluster : ['Obento.fritz.box']
scgenerator.initialize: computed initial N = 8.66
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 0)], job : df5a1a828c9685d3ffffffff0100000001000000
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 1)], job : cb230a572350ff44ffffffff0100000001000000
wavelength_8.35e-07_num_0: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_0: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_0: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_0: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:45:21 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_1: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_1: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_1: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_1: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:45:22 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 16:45:26 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 16:45:27 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 28s (30% in total). ETA : 2021-01-28 16:46:00 (2638 steps). z = 0.0060, h = 9.7856e-07
wavelength_8.35e-07_num_1: remaining : 0h 0min 28s (30% in total). ETA : 2021-01-28 16:46:01 (2638 steps). z = 0.0060, h = 9.7856e-07
wavelength_8.35e-07_num_0: remaining : 0h 0min 21s (41% in total). ETA : 2021-01-28 16:45:57 (3444 steps). z = 0.0083, h = 1.7968e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 21s (41% in total). ETA : 2021-01-28 16:45:58 (3444 steps). z = 0.0083, h = 1.7968e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 16s (50% in total). ETA : 2021-01-28 16:45:53 (3706 steps). z = 0.0102, h = 2.8346e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 16s (50% in total). ETA : 2021-01-28 16:45:53 (3706 steps). z = 0.0102, h = 2.8346e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 11s (60% in total). ETA : 2021-01-28 16:45:49 (3915 steps). z = 0.0121, h = 8.8081e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 11s (60% in total). ETA : 2021-01-28 16:45:50 (3915 steps). z = 0.0121, h = 8.8081e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 7s (71% in total). ETA : 2021-01-28 16:45:46 (4135 steps). z = 0.0143, h = 5.7601e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 7s (71% in total). ETA : 2021-01-28 16:45:47 (4135 steps). z = 0.0143, h = 5.7601e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 4s (80% in total). ETA : 2021-01-28 16:45:44 (4307 steps). z = 0.0162, h = 6.6994e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 4s (80% in total). ETA : 2021-01-28 16:45:45 (4307 steps). z = 0.0162, h = 6.6994e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:45:43 (4466 steps). z = 0.0181, h = 5.4078e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:45:44 (4466 steps). z = 0.0181, h = 5.4078e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:45:42 (4634 steps). z = 0.0200, h = 7.4967e-06
wavelength_8.35e-07_num_0: propagation finished in 4634 steps (21.323236 seconds)
scgenerator.physics.simulate: remaining : 0h 2min 41s (12% in total). ETA : 2021-01-28 16:48:23
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 2)], job : 7bbd90284b71e599ffffffff0100000001000000
wavelength_8.35e-07_num_1: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:45:42 (4634 steps). z = 0.0200, h = 7.4967e-06
wavelength_8.35e-07_num_1: propagation finished in 4634 steps (21.296093 seconds)
scgenerator.physics.simulate: remaining : 0h 1min 9s (25% in total). ETA : 2021-01-28 16:46:51
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 3)], job : bd37d2621480fc7dffffffff0100000001000000
scgenerator.initialize: computed initial N = 8.66
wavelength_8.35e-07_num_2: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_2: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_2: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_3: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_3: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_3: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_2: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:45:43 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:45:44 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 16:45:48 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 16:45:49 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 28s (30% in total). ETA : 2021-01-28 16:46:23 (2638 steps). z = 0.0060, h = 9.7856e-07
wavelength_8.35e-07_num_3: remaining : 0h 0min 28s (30% in total). ETA : 2021-01-28 16:46:24 (2638 steps). z = 0.0060, h = 9.7856e-07
wavelength_8.35e-07_num_2: remaining : 0h 0min 23s (41% in total). ETA : 2021-01-28 16:46:22 (3444 steps). z = 0.0083, h = 1.7968e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 23s (41% in total). ETA : 2021-01-28 16:46:23 (3444 steps). z = 0.0083, h = 1.7968e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 16s (50% in total). ETA : 2021-01-28 16:46:17 (3706 steps). z = 0.0102, h = 2.8346e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 16s (50% in total). ETA : 2021-01-28 16:46:17 (3706 steps). z = 0.0102, h = 2.8346e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 12s (60% in total). ETA : 2021-01-28 16:46:13 (3915 steps). z = 0.0121, h = 8.8081e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 12s (60% in total). ETA : 2021-01-28 16:46:14 (3915 steps). z = 0.0121, h = 8.8081e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 8s (71% in total). ETA : 2021-01-28 16:46:10 (4135 steps). z = 0.0143, h = 5.7601e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 8s (71% in total). ETA : 2021-01-28 16:46:11 (4135 steps). z = 0.0143, h = 5.7601e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 5s (80% in total). ETA : 2021-01-28 16:46:08 (4307 steps). z = 0.0162, h = 6.6994e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 5s (80% in total). ETA : 2021-01-28 16:46:08 (4307 steps). z = 0.0162, h = 6.6994e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:46:06 (4466 steps). z = 0.0181, h = 5.4078e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:46:07 (4466 steps). z = 0.0181, h = 5.4078e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:46:05 (4634 steps). z = 0.0200, h = 7.4967e-06
wavelength_8.35e-07_num_2: propagation finished in 4634 steps (22.109413 seconds)
scgenerator.physics.simulate: remaining : 0h 1min 17s (37% in total). ETA : 2021-01-28 16:47:21
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.3e-07), ('num', 0)], job : 88866c7daffdd00effffffff0100000001000000
wavelength_8.35e-07_num_3: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:46:05 (4634 steps). z = 0.0200, h = 7.4967e-06
wavelength_8.35e-07_num_3: propagation finished in 4634 steps (22.079782 seconds)
scgenerator.physics.simulate: remaining : 0h 0min 47s (50% in total). ETA : 2021-01-28 16:46:52
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.3e-07), ('num', 1)], job : d251967856448cebffffffff0100000001000000
wavelength_8.3e-07_num_0: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.3e-07_num_0: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.3e-07_num_0: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.3e-07_num_1: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.3e-07_num_1: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.3e-07_num_1: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.3e-07_num_0: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:46:06 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.3e-07_num_1: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:46:07 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 16:46:11 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.3e-07_num_1: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 16:46:12 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.3e-07_num_0: step 997 rejected with h = 2.7537e-06, doing over
wavelength_8.3e-07_num_1: step 997 rejected with h = 2.7537e-06, doing over
wavelength_8.3e-07_num_0: remaining : 0h 0min 21s (30% in total). ETA : 2021-01-28 16:46:36 (2162 steps). z = 0.0060, h = 3.8127e-08
wavelength_8.3e-07_num_1: remaining : 0h 0min 21s (30% in total). ETA : 2021-01-28 16:46:37 (2162 steps). z = 0.0060, h = 3.8127e-08
wavelength_8.3e-07_num_0: remaining : 0h 0min 19s (41% in total). ETA : 2021-01-28 16:46:38 (2991 steps). z = 0.0083, h = 5.8562e-06
wavelength_8.3e-07_num_1: remaining : 0h 0min 19s (41% in total). ETA : 2021-01-28 16:46:38 (2991 steps). z = 0.0083, h = 5.8562e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 14s (50% in total). ETA : 2021-01-28 16:46:34 (3242 steps). z = 0.0102, h = 6.5756e-06
wavelength_8.3e-07_num_1: remaining : 0h 0min 14s (50% in total). ETA : 2021-01-28 16:46:35 (3242 steps). z = 0.0102, h = 6.5756e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 10s (60% in total). ETA : 2021-01-28 16:46:31 (3443 steps). z = 0.0121, h = 2.9227e-06
wavelength_8.3e-07_num_1: remaining : 0h 0min 10s (60% in total). ETA : 2021-01-28 16:46:32 (3443 steps). z = 0.0121, h = 2.9227e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 6s (71% in total). ETA : 2021-01-28 16:46:29 (3664 steps). z = 0.0143, h = 1.1425e-05
wavelength_8.3e-07_num_1: remaining : 0h 0min 6s (71% in total). ETA : 2021-01-28 16:46:29 (3664 steps). z = 0.0143, h = 1.1425e-05
wavelength_8.3e-07_num_0: remaining : 0h 0min 4s (80% in total). ETA : 2021-01-28 16:46:27 (3833 steps). z = 0.0162, h = 1.0515e-05
wavelength_8.3e-07_num_1: remaining : 0h 0min 4s (80% in total). ETA : 2021-01-28 16:46:28 (3833 steps). z = 0.0162, h = 1.0515e-05
wavelength_8.3e-07_num_0: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:46:26 (4003 steps). z = 0.0181, h = 7.4644e-06
wavelength_8.3e-07_num_1: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:46:26 (4003 steps). z = 0.0181, h = 7.4644e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:46:25 (4181 steps). z = 0.0200, h = 1.21e-05
wavelength_8.3e-07_num_0: propagation finished in 4181 steps (19.058624 seconds)
scgenerator.physics.simulate: remaining : 0h 0min 40s (62% in total). ETA : 2021-01-28 16:47:04
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.3e-07), ('num', 2)], job : 3bf0c856ace5a4d8ffffffff0100000001000000
wavelength_8.3e-07_num_1: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:46:25 (4181 steps). z = 0.0200, h = 1.21e-05
wavelength_8.3e-07_num_1: propagation finished in 4181 steps (19.191059 seconds)
scgenerator.physics.simulate: remaining : 0h 0min 22s (75% in total). ETA : 2021-01-28 16:46:48
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.3e-07), ('num', 3)], job : 72e11b46e93d91e4ffffffff0100000001000000
wavelength_8.3e-07_num_2: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.3e-07_num_2: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.3e-07_num_2: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.3e-07_num_3: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.3e-07_num_3: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.3e-07_num_3: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.3e-07_num_2: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:46:27 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.3e-07_num_3: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 16:46:27 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.3e-07_num_2: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 16:46:32 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.3e-07_num_3: remaining : 0h 0min 8s (20% in total). ETA : 2021-01-28 16:46:36 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.3e-07_num_2: step 997 rejected with h = 2.7537e-06, doing over
wavelength_8.3e-07_num_3: step 997 rejected with h = 2.7537e-06, doing over
wavelength_8.3e-07_num_2: remaining : 0h 0min 25s (30% in total). ETA : 2021-01-28 16:47:02 (2162 steps). z = 0.0060, h = 3.8127e-08
wavelength_8.3e-07_num_3: remaining : 0h 0min 25s (30% in total). ETA : 2021-01-28 16:47:03 (2162 steps). z = 0.0060, h = 3.8127e-08
wavelength_8.3e-07_num_2: remaining : 0h 0min 21s (41% in total). ETA : 2021-01-28 16:47:02 (2991 steps). z = 0.0083, h = 5.8562e-06
wavelength_8.3e-07_num_3: remaining : 0h 0min 20s (41% in total). ETA : 2021-01-28 16:47:01 (2991 steps). z = 0.0083, h = 5.8562e-06
wavelength_8.3e-07_num_2: remaining : 0h 0min 16s (50% in total). ETA : 2021-01-28 16:46:58 (3242 steps). z = 0.0102, h = 6.5756e-06
wavelength_8.3e-07_num_3: remaining : 0h 0min 16s (50% in total). ETA : 2021-01-28 16:46:58 (3242 steps). z = 0.0102, h = 6.5756e-06
wavelength_8.3e-07_num_2: remaining : 0h 0min 11s (60% in total). ETA : 2021-01-28 16:46:54 (3443 steps). z = 0.0121, h = 2.9227e-06
wavelength_8.3e-07_num_3: remaining : 0h 0min 11s (60% in total). ETA : 2021-01-28 16:46:55 (3443 steps). z = 0.0121, h = 2.9227e-06
wavelength_8.3e-07_num_2: remaining : 0h 0min 7s (71% in total). ETA : 2021-01-28 16:46:51 (3664 steps). z = 0.0143, h = 1.1425e-05
wavelength_8.3e-07_num_3: remaining : 0h 0min 7s (71% in total). ETA : 2021-01-28 16:46:52 (3664 steps). z = 0.0143, h = 1.1425e-05
wavelength_8.3e-07_num_2: remaining : 0h 0min 4s (80% in total). ETA : 2021-01-28 16:46:49 (3833 steps). z = 0.0162, h = 1.0515e-05
wavelength_8.3e-07_num_3: remaining : 0h 0min 4s (80% in total). ETA : 2021-01-28 16:46:50 (3833 steps). z = 0.0162, h = 1.0515e-05
wavelength_8.3e-07_num_2: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:46:48 (4003 steps). z = 0.0181, h = 7.4644e-06
wavelength_8.3e-07_num_3: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 16:46:48 (4003 steps). z = 0.0181, h = 7.4644e-06
wavelength_8.3e-07_num_2: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:46:47 (4181 steps). z = 0.0200, h = 1.21e-05
wavelength_8.3e-07_num_2: propagation finished in 4181 steps (20.62597 seconds)
scgenerator.physics.simulate: remaining : 0h 0min 13s (87% in total). ETA : 2021-01-28 16:46:59
wavelength_8.3e-07_num_3: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:46:47 (4181 steps). z = 0.0200, h = 1.21e-05
wavelength_8.3e-07_num_3: propagation finished in 4181 steps (20.517408 seconds)
scgenerator.physics.simulate: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 16:46:47
scgenerator.physics.simulate: Merging data...
scgenerator.physics.simulate: Finished simulations from config full anomalous !
scgenerator.initialize: using computed ɣ = 2.00e-02 W/m^2
scgenerator.initialize: computed initial N = 17.4
scgenerator.initialize: intensity noise of 0.0005
scgenerator.initialize: computed initial N = 8.66
scgenerator.initialize: computed initial N = 8.66
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 0)]
wavelength_8.35e-07_num_0: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_0: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_0: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_0: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 19:54:08 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 19:54:13 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 23s (30% in total). ETA : 2021-01-28 19:54:41 (2638 steps). z = 0.0060, h = 9.7856e-07
wavelength_8.35e-07_num_0: remaining : 0h 0min 19s (41% in total). ETA : 2021-01-28 19:54:39 (3444 steps). z = 0.0083, h = 1.7968e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 14s (50% in total). ETA : 2021-01-28 19:54:36 (3706 steps). z = 0.0102, h = 2.8346e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 10s (60% in total). ETA : 2021-01-28 19:54:33 (3915 steps). z = 0.0121, h = 8.8081e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 6s (71% in total). ETA : 2021-01-28 19:54:30 (4135 steps). z = 0.0143, h = 5.7601e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 4s (80% in total). ETA : 2021-01-28 19:54:28 (4307 steps). z = 0.0162, h = 6.6994e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 19:54:27 (4466 steps). z = 0.0181, h = 5.4078e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 19:54:26 (4634 steps). z = 0.0200, h = 7.4967e-06
wavelength_8.35e-07_num_0: propagation finished in 4634 steps (18.270374 seconds)
scgenerator.physics.simulate: remaining : 0h 2min 6s (12% in total). ETA : 2021-01-28 19:56:32
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 1)]
scgenerator.initialize: computed initial N = 8.66
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 0)]
wavelength_8.35e-07_num_0: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_0: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_0: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_0: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 19:56:42 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 19:56:47 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 23s (30% in total). ETA : 2021-01-28 19:57:15 (2638 steps). z = 0.0060, h = 9.7856e-07
wavelength_8.35e-07_num_0: remaining : 0h 0min 19s (41% in total). ETA : 2021-01-28 19:57:14 (3444 steps). z = 0.0083, h = 1.7968e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 14s (50% in total). ETA : 2021-01-28 19:57:10 (3706 steps). z = 0.0102, h = 2.8346e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 10s (60% in total). ETA : 2021-01-28 19:57:07 (3915 steps). z = 0.0121, h = 8.8081e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 6s (71% in total). ETA : 2021-01-28 19:57:04 (4135 steps). z = 0.0143, h = 5.7601e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 4s (80% in total). ETA : 2021-01-28 19:57:03 (4307 steps). z = 0.0162, h = 6.6994e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 19:57:01 (4466 steps). z = 0.0181, h = 5.4078e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 19:57:00 (4634 steps). z = 0.0200, h = 7.4967e-06
wavelength_8.35e-07_num_0: propagation finished in 4634 steps (18.345039 seconds)
scgenerator.physics.simulate: remaining : 0h 2min 6s (12% in total). ETA : 2021-01-28 19:59:06
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 1)]
scgenerator.initialize: computed initial N = 8.66
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 0)]
wavelength_8.35e-07_num_0: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_0: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_0: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_0: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 19:59:41 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 19:59:46 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 23s (30% in total). ETA : 2021-01-28 20:00:15 (2638 steps). z = 0.0060, h = 9.7856e-07
wavelength_8.35e-07_num_0: remaining : 0h 0min 20s (41% in total). ETA : 2021-01-28 20:00:15 (3444 steps). z = 0.0083, h = 1.7968e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 15s (50% in total). ETA : 2021-01-28 20:00:10 (3706 steps). z = 0.0102, h = 2.8346e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 10s (60% in total). ETA : 2021-01-28 20:00:07 (3915 steps). z = 0.0121, h = 8.8081e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 6s (71% in total). ETA : 2021-01-28 20:00:04 (4135 steps). z = 0.0143, h = 5.7601e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 4s (80% in total). ETA : 2021-01-28 20:00:02 (4307 steps). z = 0.0162, h = 6.6994e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 20:00:01 (4466 steps). z = 0.0181, h = 5.4078e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 20:00:00 (4634 steps). z = 0.0200, h = 7.4967e-06
wavelength_8.35e-07_num_0: propagation finished in 4634 steps (18.838541 seconds)
scgenerator.physics.simulate: remaining : 0h 2min 6s (12% in total). ETA : 2021-01-28 20:02:06
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 1)]
scgenerator.initialize: computed initial N = 8.66
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 0)]
wavelength_8.35e-07_num_0: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_0: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_0: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_0: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 20:01:06 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 20:01:11 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 23s (30% in total). ETA : 2021-01-28 20:01:40 (2638 steps). z = 0.0060, h = 9.7856e-07
wavelength_8.35e-07_num_0: remaining : 0h 0min 20s (41% in total). ETA : 2021-01-28 20:01:40 (3444 steps). z = 0.0083, h = 1.7968e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 15s (50% in total). ETA : 2021-01-28 20:01:36 (3706 steps). z = 0.0102, h = 2.8346e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 11s (60% in total). ETA : 2021-01-28 20:01:33 (3915 steps). z = 0.0121, h = 8.8081e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 7s (71% in total). ETA : 2021-01-28 20:01:30 (4135 steps). z = 0.0143, h = 5.7601e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 4s (80% in total). ETA : 2021-01-28 20:01:28 (4307 steps). z = 0.0162, h = 6.6994e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 20:01:26 (4466 steps). z = 0.0181, h = 5.4078e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 20:01:25 (4634 steps). z = 0.0200, h = 7.4967e-06
wavelength_8.35e-07_num_0: propagation finished in 4634 steps (19.588611 seconds)
scgenerator.physics.simulate: remaining : 0h 2min 13s (12% in total). ETA : 2021-01-28 20:03:38
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 1)]
wavelength_8.35e-07_num_1: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_1: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_1: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_1: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 20:01:26 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 20:01:31 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 28s (30% in total). ETA : 2021-01-28 20:02:06 (2638 steps). z = 0.0060, h = 9.7856e-07
wavelength_8.35e-07_num_1: remaining : 0h 0min 21s (41% in total). ETA : 2021-01-28 20:02:03 (3444 steps). z = 0.0083, h = 1.7968e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 16s (50% in total). ETA : 2021-01-28 20:01:59 (3706 steps). z = 0.0102, h = 2.8346e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 11s (60% in total). ETA : 2021-01-28 20:01:55 (3915 steps). z = 0.0121, h = 8.8081e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 7s (71% in total). ETA : 2021-01-28 20:01:51 (4135 steps). z = 0.0143, h = 5.7601e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 4s (80% in total). ETA : 2021-01-28 20:01:49 (4307 steps). z = 0.0162, h = 6.6994e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 20:01:48 (4466 steps). z = 0.0181, h = 5.4078e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 20:01:46 (4634 steps). z = 0.0200, h = 7.4967e-06
wavelength_8.35e-07_num_1: propagation finished in 4634 steps (20.783914 seconds)
scgenerator.physics.simulate: remaining : 0h 2min 0s (25% in total). ETA : 2021-01-28 20:03:46
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 2)]
wavelength_8.35e-07_num_2: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_2: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_2: step 1 rejected with h = 7.9365e-05, doing over
scgenerator.initialize: computed initial N = 8.66
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 0)]
wavelength_8.35e-07_num_0: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_0: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_0: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_0: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 20:03:06 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 4s (20% in total). ETA : 2021-01-28 20:03:11 (353 steps). z = 0.0041, h = 4.8586e-06
scgenerator.physics.simulate: 1 node in the Ray cluster : ['Obento.fritz.box']
scgenerator.initialize: computed initial N = 8.66
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 0)], job : df5a1a828c9685d3ffffffff0100000001000000
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 1)], job : cb230a572350ff44ffffffff0100000001000000
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 2)], job : 7bbd90284b71e599ffffffff0100000001000000
wavelength_8.35e-07_num_0: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_0: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_0: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_1: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_1: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_2: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_1: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_2: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_2: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_0: remaining : 0h 0min 8s (11% in total). ETA : 2021-01-28 20:03:57 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 8s (11% in total). ETA : 2021-01-28 20:03:57 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 8s (11% in total). ETA : 2021-01-28 20:03:57 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 8s (20% in total). ETA : 2021-01-28 20:03:58 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 8s (20% in total). ETA : 2021-01-28 20:03:58 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 8s (20% in total). ETA : 2021-01-28 20:03:58 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 37s (30% in total). ETA : 2021-01-28 20:04:41 (2638 steps). z = 0.0060, h = 9.7856e-07
wavelength_8.35e-07_num_2: remaining : 0h 0min 37s (30% in total). ETA : 2021-01-28 20:04:41 (2638 steps). z = 0.0060, h = 9.7856e-07
wavelength_8.35e-07_num_0: remaining : 0h 0min 37s (30% in total). ETA : 2021-01-28 20:04:41 (2638 steps). z = 0.0060, h = 9.7856e-07
wavelength_8.35e-07_num_2: remaining : 0h 0min 30s (41% in total). ETA : 2021-01-28 20:04:39 (3444 steps). z = 0.0083, h = 1.7968e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 30s (41% in total). ETA : 2021-01-28 20:04:39 (3444 steps). z = 0.0083, h = 1.7968e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 30s (41% in total). ETA : 2021-01-28 20:04:39 (3444 steps). z = 0.0083, h = 1.7968e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 22s (50% in total). ETA : 2021-01-28 20:04:33 (3706 steps). z = 0.0102, h = 2.8346e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 22s (50% in total). ETA : 2021-01-28 20:04:33 (3706 steps). z = 0.0102, h = 2.8346e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 22s (50% in total). ETA : 2021-01-28 20:04:33 (3706 steps). z = 0.0102, h = 2.8346e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 16s (60% in total). ETA : 2021-01-28 20:04:28 (3915 steps). z = 0.0121, h = 8.8081e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 16s (60% in total). ETA : 2021-01-28 20:04:28 (3915 steps). z = 0.0121, h = 8.8081e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 16s (60% in total). ETA : 2021-01-28 20:04:28 (3915 steps). z = 0.0121, h = 8.8081e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 10s (71% in total). ETA : 2021-01-28 20:04:24 (4135 steps). z = 0.0143, h = 5.7601e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 10s (71% in total). ETA : 2021-01-28 20:04:24 (4135 steps). z = 0.0143, h = 5.7601e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 10s (71% in total). ETA : 2021-01-28 20:04:24 (4135 steps). z = 0.0143, h = 5.7601e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 6s (80% in total). ETA : 2021-01-28 20:04:21 (4307 steps). z = 0.0162, h = 6.6994e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 6s (80% in total). ETA : 2021-01-28 20:04:21 (4307 steps). z = 0.0162, h = 6.6994e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 6s (80% in total). ETA : 2021-01-28 20:04:21 (4307 steps). z = 0.0162, h = 6.6994e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 3s (90% in total). ETA : 2021-01-28 20:04:18 (4466 steps). z = 0.0181, h = 5.4078e-06
wavelength_8.35e-07_num_0: remaining : 0h 0min 3s (90% in total). ETA : 2021-01-28 20:04:19 (4466 steps). z = 0.0181, h = 5.4078e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 3s (90% in total). ETA : 2021-01-28 20:04:19 (4466 steps). z = 0.0181, h = 5.4078e-06
wavelength_8.35e-07_num_2: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 20:04:17 (4634 steps). z = 0.0200, h = 7.4967e-06
wavelength_8.35e-07_num_2: propagation finished in 4634 steps (28.97378 seconds)
scgenerator.physics.simulate: remaining : 0h 3min 30s (12% in total). ETA : 2021-01-28 20:07:47
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.35e-07), ('num', 3)], job : bd37d2621480fc7dffffffff0100000001000000
scgenerator.initialize: computed initial N = 8.66
wavelength_8.35e-07_num_0: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 20:04:17 (4634 steps). z = 0.0200, h = 7.4967e-06
wavelength_8.35e-07_num_1: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 20:04:17 (4634 steps). z = 0.0200, h = 7.4967e-06
wavelength_8.35e-07_num_0: propagation finished in 4634 steps (29.131892 seconds)
wavelength_8.35e-07_num_1: propagation finished in 4634 steps (29.093002 seconds)
scgenerator.physics.simulate: remaining : 0h 1min 33s (25% in total). ETA : 2021-01-28 20:05:50
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.3e-07), ('num', 0)], job : 88866c7daffdd00effffffff0100000001000000
scgenerator.physics.simulate: remaining : 0h 0min 52s (37% in total). ETA : 2021-01-28 20:05:08
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.3e-07), ('num', 1)], job : d251967856448cebffffffff0100000001000000
wavelength_8.35e-07_num_3: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.35e-07_num_3: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.35e-07_num_3: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.3e-07_num_0: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.3e-07_num_0: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.3e-07_num_0: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.3e-07_num_1: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.3e-07_num_1: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.3e-07_num_1: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_3: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 20:04:18 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 20:04:19 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.3e-07_num_1: remaining : 0h 0min 0s (11% in total). ETA : 2021-01-28 20:04:19 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 8s (20% in total). ETA : 2021-01-28 20:04:28 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 8s (20% in total). ETA : 2021-01-28 20:04:28 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.3e-07_num_1: remaining : 0h 0min 8s (20% in total). ETA : 2021-01-28 20:04:28 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.3e-07_num_0: step 997 rejected with h = 2.7537e-06, doing over
wavelength_8.3e-07_num_1: step 997 rejected with h = 2.7537e-06, doing over
wavelength_8.3e-07_num_0: remaining : 0h 0min 30s (30% in total). ETA : 2021-01-28 20:05:01 (2162 steps). z = 0.0060, h = 3.8127e-08
wavelength_8.3e-07_num_1: remaining : 0h 0min 30s (30% in total). ETA : 2021-01-28 20:05:01 (2162 steps). z = 0.0060, h = 3.8127e-08
wavelength_8.35e-07_num_3: remaining : 0h 0min 35s (30% in total). ETA : 2021-01-28 20:05:08 (2638 steps). z = 0.0060, h = 9.7856e-07
wavelength_8.3e-07_num_0: remaining : 0h 0min 24s (41% in total). ETA : 2021-01-28 20:05:00 (2991 steps). z = 0.0083, h = 5.8562e-06
wavelength_8.3e-07_num_1: remaining : 0h 0min 24s (41% in total). ETA : 2021-01-28 20:05:00 (2991 steps). z = 0.0083, h = 5.8562e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 18s (50% in total). ETA : 2021-01-28 20:04:56 (3242 steps). z = 0.0102, h = 6.5756e-06
wavelength_8.3e-07_num_1: remaining : 0h 0min 18s (50% in total). ETA : 2021-01-28 20:04:56 (3242 steps). z = 0.0102, h = 6.5756e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 28s (41% in total). ETA : 2021-01-28 20:05:07 (3444 steps). z = 0.0083, h = 1.7968e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 13s (60% in total). ETA : 2021-01-28 20:04:52 (3443 steps). z = 0.0121, h = 2.9227e-06
wavelength_8.3e-07_num_1: remaining : 0h 0min 13s (60% in total). ETA : 2021-01-28 20:04:52 (3443 steps). z = 0.0121, h = 2.9227e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 9s (71% in total). ETA : 2021-01-28 20:04:49 (3664 steps). z = 0.0143, h = 1.1425e-05
wavelength_8.3e-07_num_1: remaining : 0h 0min 9s (71% in total). ETA : 2021-01-28 20:04:49 (3664 steps). z = 0.0143, h = 1.1425e-05
wavelength_8.35e-07_num_3: remaining : 0h 0min 21s (50% in total). ETA : 2021-01-28 20:05:01 (3706 steps). z = 0.0102, h = 2.8346e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 5s (80% in total). ETA : 2021-01-28 20:04:46 (3833 steps). z = 0.0162, h = 1.0515e-05
wavelength_8.3e-07_num_1: remaining : 0h 0min 5s (80% in total). ETA : 2021-01-28 20:04:46 (3833 steps). z = 0.0162, h = 1.0515e-05
wavelength_8.35e-07_num_3: remaining : 0h 0min 16s (60% in total). ETA : 2021-01-28 20:04:58 (3915 steps). z = 0.0121, h = 8.8081e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 3s (90% in total). ETA : 2021-01-28 20:04:46 (4003 steps). z = 0.0181, h = 7.4644e-06
wavelength_8.3e-07_num_1: remaining : 0h 0min 3s (90% in total). ETA : 2021-01-28 20:04:46 (4003 steps). z = 0.0181, h = 7.4644e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 10s (71% in total). ETA : 2021-01-28 20:04:55 (4135 steps). z = 0.0143, h = 5.7601e-06
wavelength_8.3e-07_num_0: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 20:04:45 (4181 steps). z = 0.0200, h = 1.21e-05
wavelength_8.3e-07_num_1: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 20:04:45 (4181 steps). z = 0.0200, h = 1.21e-05
wavelength_8.3e-07_num_0: propagation finished in 4181 steps (27.237882 seconds)
wavelength_8.3e-07_num_1: propagation finished in 4181 steps (27.326729 seconds)
scgenerator.physics.simulate: remaining : 0h 0min 59s (50% in total). ETA : 2021-01-28 20:05:44
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.3e-07), ('num', 2)], job : 3bf0c856ace5a4d8ffffffff0100000001000000
scgenerator.physics.simulate: remaining : 0h 0min 35s (62% in total). ETA : 2021-01-28 20:05:21
scgenerator.physics.simulate: launching simulation with [('wavelength', 8.3e-07), ('num', 3)], job : 72e11b46e93d91e4ffffffff0100000001000000
wavelength_8.3e-07_num_2: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.3e-07_num_2: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.3e-07_num_2: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.3e-07_num_3: Storing 63 new spectra, first one at 0.00031746031746031746m
wavelength_8.3e-07_num_3: step 1 rejected with h = 1.5873e-04, doing over
wavelength_8.3e-07_num_3: step 1 rejected with h = 7.9365e-05, doing over
wavelength_8.35e-07_num_3: remaining : 0h 0min 7s (80% in total). ETA : 2021-01-28 20:04:54 (4307 steps). z = 0.0162, h = 6.6994e-06
wavelength_8.3e-07_num_2: remaining : 0h 0min 8s (11% in total). ETA : 2021-01-28 20:04:56 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.3e-07_num_3: remaining : 0h 0min 8s (11% in total). ETA : 2021-01-28 20:04:56 (132 steps). z = 0.0022, h = 5.5668e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 3s (90% in total). ETA : 2021-01-28 20:04:52 (4466 steps). z = 0.0181, h = 5.4078e-06
wavelength_8.3e-07_num_2: remaining : 0h 0min 8s (20% in total). ETA : 2021-01-28 20:04:57 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.35e-07_num_3: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 20:04:50 (4634 steps). z = 0.0200, h = 7.4967e-06
wavelength_8.35e-07_num_3: propagation finished in 4634 steps (31.918563 seconds)
scgenerator.physics.simulate: remaining : 0h 0min 21s (75% in total). ETA : 2021-01-28 20:05:11
wavelength_8.3e-07_num_3: remaining : 0h 0min 8s (20% in total). ETA : 2021-01-28 20:04:57 (353 steps). z = 0.0041, h = 4.8586e-06
wavelength_8.3e-07_num_3: step 997 rejected with h = 2.7537e-06, doing over
wavelength_8.3e-07_num_2: step 997 rejected with h = 2.7537e-06, doing over
wavelength_8.3e-07_num_2: remaining : 0h 0min 25s (30% in total). ETA : 2021-01-28 20:05:24 (2162 steps). z = 0.0060, h = 3.8127e-08
wavelength_8.3e-07_num_3: remaining : 0h 0min 25s (30% in total). ETA : 2021-01-28 20:05:24 (2162 steps). z = 0.0060, h = 3.8127e-08
wavelength_8.3e-07_num_2: remaining : 0h 0min 21s (41% in total). ETA : 2021-01-28 20:05:24 (2991 steps). z = 0.0083, h = 5.8562e-06
wavelength_8.3e-07_num_3: remaining : 0h 0min 21s (41% in total). ETA : 2021-01-28 20:05:24 (2991 steps). z = 0.0083, h = 5.8562e-06
wavelength_8.3e-07_num_2: remaining : 0h 0min 16s (50% in total). ETA : 2021-01-28 20:05:20 (3242 steps). z = 0.0102, h = 6.5756e-06
wavelength_8.3e-07_num_3: remaining : 0h 0min 16s (50% in total). ETA : 2021-01-28 20:05:21 (3242 steps). z = 0.0102, h = 6.5756e-06
wavelength_8.3e-07_num_2: remaining : 0h 0min 12s (60% in total). ETA : 2021-01-28 20:05:17 (3443 steps). z = 0.0121, h = 2.9227e-06
wavelength_8.3e-07_num_3: remaining : 0h 0min 12s (60% in total). ETA : 2021-01-28 20:05:17 (3443 steps). z = 0.0121, h = 2.9227e-06
wavelength_8.3e-07_num_2: remaining : 0h 0min 8s (71% in total). ETA : 2021-01-28 20:05:14 (3664 steps). z = 0.0143, h = 1.1425e-05
wavelength_8.3e-07_num_3: remaining : 0h 0min 8s (71% in total). ETA : 2021-01-28 20:05:14 (3664 steps). z = 0.0143, h = 1.1425e-05
wavelength_8.3e-07_num_2: remaining : 0h 0min 5s (80% in total). ETA : 2021-01-28 20:05:12 (3833 steps). z = 0.0162, h = 1.0515e-05
wavelength_8.3e-07_num_3: remaining : 0h 0min 5s (80% in total). ETA : 2021-01-28 20:05:13 (3833 steps). z = 0.0162, h = 1.0515e-05
wavelength_8.3e-07_num_2: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 20:05:11 (4003 steps). z = 0.0181, h = 7.4644e-06
wavelength_8.3e-07_num_3: remaining : 0h 0min 2s (90% in total). ETA : 2021-01-28 20:05:11 (4003 steps). z = 0.0181, h = 7.4644e-06
wavelength_8.3e-07_num_2: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 20:05:10 (4181 steps). z = 0.0200, h = 1.21e-05
wavelength_8.3e-07_num_2: propagation finished in 4181 steps (22.964871 seconds)
scgenerator.physics.simulate: remaining : 0h 0min 12s (87% in total). ETA : 2021-01-28 20:05:22
wavelength_8.3e-07_num_3: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 20:05:10 (4181 steps). z = 0.0200, h = 1.21e-05
wavelength_8.3e-07_num_3: propagation finished in 4181 steps (22.72864 seconds)
scgenerator.physics.simulate: remaining : 0h 0min 0s (100% in total). ETA : 2021-01-28 20:05:10
scgenerator.physics.simulate: Merging data...
scgenerator.physics.simulate: Finished simulations from config full anomalous !

36
setup.cfg Normal file
View File

@@ -0,0 +1,36 @@
[metadata]
name = scgenerator
version = 0.0.1
description = Simulated PCFs and supercontinuum generation !
author = Benoit Sierro
author_email = benoit.sierro@iap.unibe.ch
long_description = file: README.md
long_description_content_type = text/markdown
keywords = supercontinuum, nonlinear optics, GNLSE
license = MIT
classifiers =
License :: OSI Approved :: MIT
Programming Language :: Python :: 3
[options]
zip_safe = False
include_package_data = True
packages = find:
package_dir =
= src
install_requires =
numpy
matplotlib
scipy
ray
[options.package_data]
scgenerator =
data/hr_t.npz
data/default_params.json
data/gas.json
data/silica.json
[options.packages.find]
where = src

3
setup.py Normal file
View File

@@ -0,0 +1,3 @@
from setuptools import setup
setup()

View File

@@ -0,0 +1,7 @@
from . import initialize, io, math, plotting, state, utilities
from .initialize import compute_init_parameters
from .io import Paths, iter_load_sim_data, load_toml, load_sim_data
from .math import abs2, argclosest, span
from .physics import fiber, materials, pulse, simulate, units
from .physics.simulate import RK4IP, parallel_simulations
from .plotting import plot_avg, plot_results_1D, plot_results_2D, plot_spectrogram

View File

@@ -0,0 +1,25 @@
import argparse
def create_parser():
parser = argparse.ArgumentParser(
description="scgenerator command",
prog="scgenerator"
)
return parser
def main():
parser = create_parser()
subparsers = parser.add_subparsers(
help="sub-command help"
)
newconfig = subparsers.add_parser(
"newconfig",
help="create a new configuration file"
)
if __name__ == "__main__":
main()

195
src/scgenerator/const.py Normal file
View File

@@ -0,0 +1,195 @@
import numpy as np
def num(n):
"""must be a single, real, non-negative number"""
return isinstance(n, (float, int)) and n >= 0
def integer(n):
"""must be a strictly positive integer"""
return isinstance(n, int) and n > 0
def boolean(b):
"""must be a boolean"""
return type(b) == bool
def behaviors(l):
"""must be a valid list of behaviors"""
for s in l:
if s.lower() not in ["spm", "raman", "ss"]:
return False
return True
def beta(l):
"""must be a valid beta array"""
for n in l:
if not isinstance(n, (float, int)):
return False
return True
def field_0(f):
return isinstance(f, (str, tuple, list, np.ndarray))
def he_mode(mode):
"""must be a valide HE mode"""
if not isinstance(mode, (list, tuple)):
return False
if not len(mode) == 2:
return False
for m in mode:
if not integer(m):
return False
return True
def fit_parameters(param):
"""must be a valide fitting parameter tuple of the mercatili_adjusted model"""
if not isinstance(param, (list, tuple)):
return False
if not len(param) == 2:
return False
for n in param:
if not integer(n):
return False
return True
def string(l):
def _string(s):
return isinstance(s, str) and s.lower() in l
_string.__doc__ = f"must be a str matching one of {l}"
return _string
def capillary_resonance_strengths(l):
"""must be a list of non-zero, real number"""
if not isinstance(l, (list, tuple)):
return False
for m in l:
if not num(m):
return False
return True
def capillary_nested(n):
"""must be a non negative integer"""
return isinstance(n, int) and n >= 0
# def find_parent(param):
# """find the parent dictionary name of param"""
# for k, v in valid_param_types.items():
# if param in v:
# return k
# raise ValueError(f"'{param}' is an invalid parameter name")
valid_param_types = dict(
root=dict(
name=lambda s: isinstance(s, str),
),
fiber=dict(
gamma=num,
pitch=num,
pitch_ratio=num,
core_radius=num,
he_mode=he_mode,
fit_parameters=fit_parameters,
beta=beta,
model=string(["pcf", "marcatili", "marcatili_adjusted", "hasan"]),
length=num,
capillary_num=integer,
capillary_outer_d=num,
capillary_thickness=num,
capillary_spacing=num,
capillary_resonance_strengths=capillary_resonance_strengths,
capillary_nested=capillary_nested,
),
gas=dict(
gas_name=string(["vacuum", "helium", "air"]),
pressure=num,
temperature=num,
plasma_density=num,
),
pulse=dict(
field_0=field_0,
power=num,
energy=num,
soliton_num=num,
quantum_noise=boolean,
shape=string(["gaussian", "sech"]),
wavelength=num,
intensity_noise=num,
width=num,
t0=num,
),
simulation=dict(
behaviors=behaviors,
parallel=integer,
raman_type=string(["measured", "agrawal", "stolen"]),
ideal_gas=boolean,
repeat=integer,
t_num=integer,
z_num=integer,
time_window=num,
dt=num,
tolerated_error=num,
step_size=num,
lower_wavelength_interp_limit=num,
upper_wavelength_interp_limit=num,
),
)
hc_model_specific_parameters = dict(
marcatili=["core_radius", "he_mode"],
marcatili_adjusted=["core_radius", "he_mode", "fit_parameters"],
hasan=[
"core_radius",
"capillary_num",
"capillary_thickness",
"capillary_resonance_strengths",
"capillary_nested",
"capillary_spacing",
"capillary_outer_d",
],
)
"""dependecy map only includes actual fiber parameters and exclude gas parameters"""
valid_varying = dict(
fiber=[
"beta",
"gamma",
"pitch",
"pitch_ratio",
"core_radius",
"capillary_num",
"capillary_outer_d",
"capillary_thickness",
"capillary_spacing",
"capillary_resonance_strengths",
"capillary_nested",
"he_mode",
"fit_parameters",
],
gas=["pressure", "temperature", "gas_name", "plasma_density"],
pulse=[
"power",
"quantum_noise",
"shape",
"wavelength",
"intensity_noise",
"width",
"soliton_num",
],
simulation=["behaviors", "raman_type", "tolerated_error", "step_size", "ideal_gas"],
)
TMP_FOLDER_KEY_BASE = "SCGENERATOR_TMP"

View File

@@ -0,0 +1,10 @@
# source of the Sellmeir coefficients
Air, Nitrogen, Helium, Neon, Argon, Krypton, Xenon : A. Börzsönyi et al. "Dispersion measurement of inert gases and gas mixtures at 800 nm" (2008)
Silica : I. H. Malitson. Interspecimen comparison of the refractive index of fused silica (1965)
In sellmeierRefractiveIndex : see refractiveindex.info
Van der Waals constants from https://en.wikipedia.org/wiki/Van_der_Waals_constants_(data_page)
Chi3 : Wahlstrand 2012

View File

@@ -0,0 +1,29 @@
DTf.
file /Users/benoitsierro/Desktop/Capture décran 2019-09-12 à 13.38.10.png
mainx 0
mainy 0
window 0 23 1920 1177
axis 00 lin X - lin Y
state 1
continuous 1
tools 1
a4mode 0
distance 0.01
mode 4
precision 4
texts \z , , , \z
colors -8388608 -16777024 -13598720 -8372032 -16724992 -3407872 -16777012 -16777216 -16777216 -16777216 -1 -16777216
params 7 1 1 2 3 1 4
dump show 1028.0 185.0
point 109.5 1241.0 124.35055146851568 1235.0450758962884 124.35055146851568 1235.0450758962884 2 0 -16724992
point 3101.0 1234.0 3101.000000000001 1234.0 3101.000000000001 1234.0 0 0 -3407872
point 955.0 321.5 955.0000000000001 321.5 955.0000000000001 321.5 0 0 -16777012
point 109.25 1241.25 109.25000000000003 1241.25 109.25000000000003 1241.25 0 1 -8388608
point 3100.75 1242.75 3100.750000000001 1242.75 3100.750000000001 1242.75 0 1 -16777024
point 108.75 216.0 108.75000000000003 216.0 108.75000000000003 216.0 0 1 -13598720
point 260.0 160.0 260.0 160.0 260.0 160.0 0 1 -8372032
point 1174.25 265.25 1185.25 246.5 1176.25 258.25 6 3
point 1208.5 192.25 1219.5 220.75 1112.5 192.0 6 3
refs 0 0 40 0 0 6 0 0
xtrans none
ytrans none

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
{
"lambda0": 835e-9,
"P0": 10e3,
"T0": 28.4e-15,
"z_targets": [0, 1, 128],
"chirp": 0,
"alpha": 0,
"fiber_type": "PCF",
"pitch": 1.55e-6,
"pitch_ratio": 0.37,
"gamma": 0.11,
"pulse_shape":"gaussian",
"dt": 1e-15,
"nt": 16384,
"frep": 80e6,
"behaviors": ["spm", "ss", "raman"],
"raman_rf": "stolen",
"adapt_step_size": true,
"error_ok": 1e-8,
"vmin_log": -40
}

View File

@@ -0,0 +1,198 @@
{
"air": {
"sellmeier": {
"B": [57921050000.0, 1679170000.0],
"C": [238018500000000.0, 57362000000000.0],
"kind": 2,
"P0": 101325,
"T0": 288.15
}
},
"nitrogen": {
"a": 0.137,
"b": 1.709e-05,
"sellmeier": {
"B": [32431570000.0],
"C": [144000000000000.0],
"kind": 2,
"P0": 101325,
"T0": 273.15,
"const": 6.8552e-05
},
"kerr": {
"source": "Wahlstrand, J. K., Cheng, Y. H., & Milchberg, H. M. (2012). High field optical nonlinearity and the Kramers-Kronig relations. Physical review letters, 109(11), 113904.",
"P0": 30.4e3,
"T0": 273.15,
"n2": 2.2e-23
}
},
"helium": {
"a": 0.00346,
"b": 2.38e-05,
"sellmeier": {
"source": "A. Ermolov, K. F. Mak, M. H. Frosz, J. C. Travers, P. St. J. Russell, Supercontinuum generation in the vacuum ultraviolet through dispersive-wave and soliton-plasma interaction in a noble-gas-filled hollow-core photonic crystal fiber, Phys. Rev. A 92, 033821 (2015)",
"B": [2.16463842e-5, 2.10561127e-7, 4.7509272e-5],
"C": [-6.80769781e-16, 5.13251289e-15, 3.18621354e-15],
"kind": 1,
"P0": 101325,
"T0": 273.15
},
"kerr": {
"source": "Wahlstrand, J. K., Cheng, Y. H., & Milchberg, H. M. (2012). High field optical nonlinearity and the Kramers-Kronig relations. Physical review letters, 109(11), 113904.",
"P0": 30.4e3,
"T0": 273.15,
"n2": 3.1e-25
}
},
"helium_alt": {
"a": 0.00346,
"b": 2.38e-05,
"sellmeier": {
"source": " C. Cuthbertson and M. Cuthbertson. The refraction and dispersion of neon and helium. Proc. R. Soc. London A 135, 40-47 (1936)",
"B": [14755297000.0],
"C": [426297400000000.0],
"kind": 2,
"P0": 101325,
"T0": 273.15
},
"kerr": {
"P0": 30.4e3,
"T0": 273.15,
"n2": 3.1e-25
}
},
"hydrogen": {
"a": 0.02453,
"b": 2.651e-05,
"sellmeier": {
"source": "E. R. Peck and S. Hung. Refractivity and dispersion of hydrogen in the visible and near infrared, J. Opt. Soc. Am. 67, 1550-1554 (1977)",
"B": [0.0148956, 0.0049037],
"C": [180.7e-12, 92e-12],
"kind": 2,
"P0": 101325,
"T0": 273.15
},
"kerr": {
"source": "Shelton, D. P., & Rice, J. E. (1994). Measurements and calculations of the hyperpolarizabilities of atoms and small molecules in the gas phase. Chemical Reviews, 94(1), 3-29",
"P0": 30.4e3,
"T0": 273.15,
"n2": 6.36e-24
}
},
"neon": {
"a": 0.02135,
"b": 1.709e-05,
"sellmeier": {
"B": [1281450000.0, 22048600000.0],
"C": [184661000000000.0, 376840000000000.0],
"kind": 2,
"P0": 101325,
"T0": 273.15
},
"kerr": {
"source": "Wahlstrand, J. K., Cheng, Y. H., & Milchberg, H. M. (2012). High field optical nonlinearity and the Kramers-Kronig relations. Physical review letters, 109(11), 113904.",
"P0": 30.4e3,
"T0": 273.15,
"n2": 8.7e-25
}
},
"argon": {
"a": 0.1355,
"b": 3.201e-05,
"sellmeier": {
"source": "A. Bideau-Mehu, Y. Guern, R. Abjean, A. Johannin-Gilles. Measurement of refractive indices of neon, argon, krypton and xenon in the 253.7-140.4 nm wavelength range. Dispersion relations and estimated oscillator strengths of the resonance lines. J. Quant. Spectrosc. Rad. Transfer 25, 395-402 (1981)",
"B": [2501410000.0, 500283000.0, 52234300000.0],
"C": [91012000000000.0, 87892000000000.0, 214020000000000.0],
"kind": 2,
"P0": 101325,
"T0": 273.15
},
"kerr": {
"source": "Wahlstrand, J. K., Cheng, Y. H., & Milchberg, H. M. (2012). High field optical nonlinearity and the Kramers-Kronig relations. Physical review letters, 109(11), 113904.",
"P0": 30.4e3,
"T0": 273.15,
"n2": 9.7e-24
}
},
"argon_alt": {
"a": 0.1355,
"b": 3.201e-05,
"sellmeier": {
"source": "A. Börzsönyi, Z. Heiner, M. P. Kalashnikov, A. P. Kovács, and K. Osvay, Dispersion measurement of inert gases and gas mixtures at 800 nm, Appl. Opt. 47, 4856-4863 (2008)",
"B": [20332.29e-8, 34458.31e-8],
"C": [206.12e-18, 8.066e-15],
"kind": 1,
"P0": 101325,
"T0": 273.15
},
"kerr": {
"source": "Wahlstrand, J. K., Cheng, Y. H., & Milchberg, H. M. (2012). High field optical nonlinearity and the Kramers-Kronig relations. Physical review letters, 109(11), 113904.",
"P0": 30.4e3,
"T0": 273.15,
"n2": 9.7e-24
}
},
"argon_alt2": {
"a": 0.1355,
"b": 3.201e-05,
"sellmeier": {
"source": "E. R. Peck and D. J. Fisher. Dispersion of argon, J. Opt. Soc. Am. 54, 1362-1364 (1964)",
"B": [3.0182943e-2],
"C": [144e12],
"const": 6.7867e-5,
"kind": 2,
"P0": 101325,
"T0": 273.15
},
"kerr": {
"source": "Wahlstrand, J. K., Cheng, Y. H., & Milchberg, H. M. (2012). High field optical nonlinearity and the Kramers-Kronig relations. Physical review letters, 109(11), 113904.",
"P0": 30.4e3,
"T0": 273.15,
"n2": 9.7e-24
}
},
"krypton": {
"a": 0.2349,
"b": 3.978e-05,
"sellmeier": {
"B": [2536370000.0, 2736490000.0, 62080200000.0],
"C": [65474200000000.0, 73698000000000.0, 181080000000000.0],
"kind": 2,
"P0": 101325,
"T0": 273.15
},
"kerr": {
"source": "Wahlstrand, J. K., Cheng, Y. H., & Milchberg, H. M. (2012). High field optical nonlinearity and the Kramers-Kronig relations. Physical review letters, 109(11), 113904.",
"P0": 30.4e3,
"T0": 273.15,
"n2": 2.2e-23
}
},
"xenon": {
"a": 0.425,
"b": 5.105e-05,
"sellmeier": {
"B": [3228690000.0, 3553930000.0, 60676400000.0],
"C": [46301000000000.0, 59578000000000.0, 112740000000000.0],
"kind": 2,
"P0": 101325,
"T0": 273.15
},
"kerr": {
"source": "Wahlstrand, J. K., Cheng, Y. H., & Milchberg, H. M. (2012). High field optical nonlinearity and the Kramers-Kronig relations. Physical review letters, 109(11), 113904.",
"P0": 30.4e3,
"T0": 273.15,
"n2": 5.8e-23
}
}
}

View File

@@ -0,0 +1,183 @@
[nitrogen]
a = 0.137
b = 1.709e-5
[helium]
a = 0.00346
b = 2.38e-5
[helium_alt]
a = 0.00346
b = 2.38e-5
[hydrogen]
a = 0.02453
b = 2.651e-5
[neon]
a = 0.02135
b = 1.709e-5
[argon]
a = 0.1355
b = 3.201e-5
[argon_alt]
a = 0.1355
b = 3.201e-5
[argon_alt2]
a = 0.1355
b = 3.201e-5
[krypton]
a = 0.2349
b = 3.978e-5
[xenon]
a = 0.425
b = 5.105e-5
[air.sellmeier]
B = [57921050000.0, 1679170000.0]
C = [238018500000000.0, 57362000000000.0]
P0 = 101325
T0 = 288.15
kind = 2
[nitrogen.sellmeier]
B = [32431570000.0]
C = [144000000000000.0]
P0 = 101325
T0 = 273.15
const = 6.8552e-5
kind = 2
[nitrogen.kerr]
P0 = 30400.0
T0 = 273.15
n2 = 2.2e-23
source = "Wahlstrand, J. K., Cheng, Y. H., & Milchberg, H. M. (2012). High field optical nonlinearity and the Kramers-Kronig relations. Physical review letters, 109(11), 113904."
[helium.sellmeier]
B = [2.16463842e-5, 2.10561127e-7, 4.7509272e-5]
C = [-6.80769781e-16, 5.13251289e-15, 3.18621354e-15]
P0 = 101325
T0 = 273.15
kind = 1
source = "A. Ermolov, K. F. Mak, M. H. Frosz, J. C. Travers, P. St. J. Russell, Supercontinuum generation in the vacuum ultraviolet through dispersive-wave and soliton-plasma interaction in a noble-gas-filled hollow-core photonic crystal fiber, Phys. Rev. A 92, 033821 (2015)"
[helium.kerr]
P0 = 30400.0
T0 = 273.15
n2 = 3.1e-25
source = "Wahlstrand, J. K., Cheng, Y. H., & Milchberg, H. M. (2012). High field optical nonlinearity and the Kramers-Kronig relations. Physical review letters, 109(11), 113904."
[helium_alt.sellmeier]
B = [14755297000.0]
C = [426297400000000.0]
P0 = 101325
T0 = 273.15
kind = 2
source = " C. Cuthbertson and M. Cuthbertson. The refraction and dispersion of neon and helium. Proc. R. Soc. London A 135, 40-47 (1936)"
[helium_alt.kerr]
P0 = 30400.0
T0 = 273.15
n2 = 3.1e-25
[hydrogen.sellmeier]
B = [0.0148956, 0.0049037]
C = [1.807e-10, 9.2e-11]
P0 = 101325
T0 = 273.15
kind = 2
source = "E. R. Peck and S. Hung. Refractivity and dispersion of hydrogen in the visible and near infrared, J. Opt. Soc. Am. 67, 1550-1554 (1977)"
[hydrogen.kerr]
P0 = 30400.0
T0 = 273.15
n2 = 6.36e-24
source = "Shelton, D. P., & Rice, J. E. (1994). Measurements and calculations of the hyperpolarizabilities of atoms and small molecules in the gas phase. Chemical Reviews, 94(1), 3-29"
[neon.sellmeier]
B = [1281450000.0, 22048600000.0]
C = [184661000000000.0, 376840000000000.0]
P0 = 101325
T0 = 273.15
kind = 2
[neon.kerr]
P0 = 30400.0
T0 = 273.15
n2 = 8.7e-25
source = "Wahlstrand, J. K., Cheng, Y. H., & Milchberg, H. M. (2012). High field optical nonlinearity and the Kramers-Kronig relations. Physical review letters, 109(11), 113904."
[argon.sellmeier]
B = [2501410000.0, 500283000.0, 52234300000.0]
C = [91012000000000.0, 87892000000000.0, 214020000000000.0]
P0 = 101325
T0 = 273.15
kind = 2
source = "A. Bideau-Mehu, Y. Guern, R. Abjean, A. Johannin-Gilles. Measurement of refractive indices of neon, argon, krypton and xenon in the 253.7-140.4 nm wavelength range. Dispersion relations and estimated oscillator strengths of the resonance lines. J. Quant. Spectrosc. Rad. Transfer 25, 395-402 (1981)"
[argon.kerr]
P0 = 30400.0
T0 = 273.15
n2 = 9.7e-24
source = "Wahlstrand, J. K., Cheng, Y. H., & Milchberg, H. M. (2012). High field optical nonlinearity and the Kramers-Kronig relations. Physical review letters, 109(11), 113904."
[argon_alt.sellmeier]
B = [0.0002033229, 0.0003445831]
C = [2.0612e-16, 8.066e-15]
P0 = 101325
T0 = 273.15
kind = 1
source = "A. Börzsönyi, Z. Heiner, M. P. Kalashnikov, A. P. Kovács, and K. Osvay, Dispersion measurement of inert gases and gas mixtures at 800 nm, Appl. Opt. 47, 4856-4863 (2008)"
[argon_alt.kerr]
P0 = 30400.0
T0 = 273.15
n2 = 9.7e-24
source = "Wahlstrand, J. K., Cheng, Y. H., & Milchberg, H. M. (2012). High field optical nonlinearity and the Kramers-Kronig relations. Physical review letters, 109(11), 113904."
[argon_alt2.sellmeier]
B = [0.030182943]
C = [144000000000000.0]
P0 = 101325
T0 = 273.15
const = 6.7867e-5
kind = 2
source = "E. R. Peck and D. J. Fisher. Dispersion of argon, J. Opt. Soc. Am. 54, 1362-1364 (1964)"
[argon_alt2.kerr]
P0 = 30400.0
T0 = 273.15
n2 = 9.7e-24
source = "Wahlstrand, J. K., Cheng, Y. H., & Milchberg, H. M. (2012). High field optical nonlinearity and the Kramers-Kronig relations. Physical review letters, 109(11), 113904."
[krypton.sellmeier]
B = [2536370000.0, 2736490000.0, 62080200000.0]
C = [65474200000000.0, 73698000000000.0, 181080000000000.0]
P0 = 101325
T0 = 273.15
kind = 2
[krypton.kerr]
P0 = 30400.0
T0 = 273.15
n2 = 2.2e-23
source = "Wahlstrand, J. K., Cheng, Y. H., & Milchberg, H. M. (2012). High field optical nonlinearity and the Kramers-Kronig relations. Physical review letters, 109(11), 113904."
[xenon.sellmeier]
B = [3228690000.0, 3553930000.0, 60676400000.0]
C = [46301000000000.0, 59578000000000.0, 112740000000000.0]
P0 = 101325
T0 = 273.15
kind = 2
[xenon.kerr]
P0 = 30400.0
T0 = 273.15
n2 = 5.8e-23
source = "Wahlstrand, J. K., Cheng, Y. H., & Milchberg, H. M. (2012). High field optical nonlinearity and the Kramers-Kronig relations. Physical review letters, 109(11), 113904."

Binary file not shown.

View File

@@ -0,0 +1,69 @@
{
"silica" : {
"B" : [0.696166300, 0.407942600, 0.897479400],
"C" : [4.67914826e-15, 1.35120631e-14, 97.9340025e-12],
"kind": 1
},
"air" : {
"B" : [0.05792105e12, 0.00167917e12],
"C" : [238.0185e12, 57.362e12],
"kind": 2,
"P0": 101325,
"T0": 288.15
},
"nitrogen" : {
"B" : [3.243157e10],
"C" : [144e12],
"a" : 0.137,
"b" : 0.01709e-3,
"const": 6.8552e-5,
"kind": 2,
"P0": 101325,
"T0": 273.15
},
"helium" : {
"B" : [0.014755297e12],
"C" : [426.2974e12],
"a" : 0.00346,
"b" : 0.0238e-3,
"kind": 2,
"P0": 101325,
"T0": 273.15
},
"neon" : {
"B" : [0.00128145e12, 0.0220486e12],
"C" : [184.661e12, 376.84e12],
"a" : 0.02135,
"b" : 0.01709e-3,
"kind": 2,
"P0": 101325,
"T0": 273.15
},
"argon" : {
"B" : [2.50141e9, 5.00283e8, 5.22343e10],
"C" : [91.012e12, 87.892e12, 214.02e12],
"a" : 0.1355,
"b" : 0.03201e-3,
"kind": 2,
"P0": 101325,
"T0": 273.15
},
"krypton" : {
"B" : [0.00253637e12, 0.00273649e12, 0.0620802e12],
"C" : [65.4742e12, 73.698e12, 181.08e12],
"a" : 0.2349,
"b" : 0.03978e-3,
"kind": 2,
"P0": 101325,
"T0": 273.15
},
"xenon" : {
"B" : [0.00322869e12, 0.00355393e12, 0.0606764e12],
"C" : [46.301e12, 59.578e12, 112.74e12],
"a" : 0.425,
"b" : 0.05105e-3,
"kind": 2,
"P0": 101325,
"T0": 273.15
}
}

View File

@@ -0,0 +1,34 @@
{
"silica" : {
"B" : [0.696166300, 0.407942600, 0.897479400],
"C" : [4.67914826e-15, 1.35120631e-14, 97.9340025e-12]
},
"air" : {
"B" : [14926.44e-8, 41807.57e-8],
"C" : [19.36e-18, 7.434e-15]
},
"nitrogen" : {
"B" : [39209.95e-8, 18806.48e-8],
"C" : [1146.24e-18, 13.476e-15]
},
"helium" : {
"B" : [4977.77e-8, 1856.94e-8],
"C" : [28.54e-18, 7.760e-15]
},
"neon" : {
"B" : [9154.48e-8, 4018.63e-8],
"C" : [656.97e-18, 5.728e-15]
},
"argon" : {
"B" : [20332.29e-8, 34458.31e-8],
"C" : [206.12e-18, 8.066e-15]
},
"krypton" : {
"B" : [26102.88e-8, 56946.82e-8],
"C" : [2.01e-18, 10.043e-15]
},
"xenon" : {
"B" : [103701.61e-8, 31228.61e-8],
"C" : [12.75e-15, 0.561e-15]
}
}

View File

@@ -0,0 +1,8 @@
{
"sellmeier": {
"B": [0.6961663, 0.4079426, 0.8974794],
"C": [4.67914826e-15, 1.35120631e-14, 9.79340025e-11],
"kind": 1
}
}

View File

@@ -0,0 +1,4 @@
[sellmeier]
B = [ 0.6961663, 0.4079426, 0.8974794,]
C = [ 4.67914826e-15, 1.35120631e-14, 9.79340025e-11,]
kind = 1

102
src/scgenerator/defaults.py Normal file
View File

@@ -0,0 +1,102 @@
import numpy as np
from .errors import MissingParameterError
default_parameters = dict(
name="no name",
he_mode=(1, 1),
fit_parameters=(0.08, 200e-9),
model="pcf",
length=1,
capillary_resonance_strengths=[],
capillary_nested=0,
gas_name="vacuum",
plasma_density=0,
pressure=1e5,
temperature=300,
quantum_noise=False,
intensity_noise=0,
shape="gaussian",
frep=80e6,
behaviors=["spm", "ss"],
raman_type="agrawal",
parallel=1,
repeat=1,
tolerated_error=1e-11,
lower_wavelength_interp_limit=0,
upper_wavelength_interp_limit=1900e-9,
ideal_gas=False,
)
def get(section_dict, param, **kwargs):
"""checks if param is in the parameter section dict and attempts to fill in a default value
Parameters
----------
section_dict : dict
the parameters section {fiber, pulse, simulation, root} sub-dictionary
param : str
the name of the parameter (dict key)
kwargs : any
key word arguments passed to the MissingParameterError constructor
Returns
-------
dict
the updated section_dict dictionary
Raises
------
MissingFiberParameterError
raised when a parameter is missing and no default exists
"""
# whether the parameter is in the right place and valid is checked elsewhere,
# here, we just make sure it is present.
if param not in section_dict and param not in section_dict.get("varying", {}):
try:
section_dict[param] = default_parameters[param]
# LOG
except KeyError:
raise MissingParameterError(param, **kwargs)
return section_dict
def get_fiber(section_dict, param, **kwargs):
"""wrapper for fiber parameters that depend on fiber model"""
return get(section_dict, param, fiber_model=section_dict["model"], **kwargs)
def get_multiple(section_dict, params, num, **kwargs):
"""similar to th get method but works with several parameters
Parameters
----------
section_dict : dict
the parameters section {fiber, pulse, simulation, root}, sub-dictionary
params : list of str
names of the required parameters
num : int
how many of the parameters in params are required
Returns
-------
dict
the updated section_dict
Raises
------
MissingParameterError
raised when not enough parameters are provided and no defaults exist
"""
gotten = 0
for param in params:
try:
section_dict = get(section_dict, param, **kwargs)
gotten += 1
except MissingParameterError:
pass
if gotten >= num:
return section_dict
raise MissingParameterError(params, num_required=num, **kwargs)

51
src/scgenerator/errors.py Normal file
View File

@@ -0,0 +1,51 @@
class MissingParameterError(Exception):
def __init__(self, param, **kwargs):
self.param = param
# initial message
message = ""
if isinstance(param, str):
message += f"'{self.param}' is a required parameter "
elif isinstance(param, (tuple, list)):
num = kwargs["num_required"]
message += f"{num} of '{self.param}' {'is' if num == 1 else 'are'} required "
else:
raise TypeError(f"don't know what to do with param as {param}")
# complementary information
if "fiber_model" in kwargs:
message += f"for fiber model '{kwargs['fiber_model']}' "
if "specified_parameters" in kwargs:
if len(kwargs["specified_parameters"]) == 0:
pass
elif len(kwargs["specified_parameters"]) == 1:
message += f"when '{kwargs['specified_parameters'][0]}' is specified "
else:
message += f"when {kwargs['specified_parameters']} are specified "
# closing statement
message += "and no defaults have been set"
super().__init__(message)
class DuplicateParameterError(Exception):
pass
class IncompleteDataFolderError(Exception):
pass
# class MissingFiberParameterError(MissingParameterError):
# def __init__(self, param, model):
# self.param = param
# self.model = model
# super().__init__(
# f"'{self.param}' is a required parameter for fiber model '{self.model}' and no default value is set"
# )
# class MissingPulseParameterError(MissingParameterError):
# def __init__(self, param):
# self.param = param
# super().__init__(f"'{self.param}' is a required pulse parameter and no default value is set")

View File

@@ -0,0 +1,775 @@
from os import path
from typing import Iterator, Mapping, Tuple
import numpy as np
from numpy import pi
from numpy.core.fromnumeric import var
from ray.state import current_node_id
from . import io, state, defaults
from .math import length, power_fact
from .physics import fiber, pulse, units
from .const import valid_param_types, valid_varying, hc_model_specific_parameters
from .errors import *
from .io import get_logger
from .utilities import varying_iterator, count_variations
class ParamSequence(Mapping):
def __init__(self, config):
validate_types(config)
self.config = ensure_consistency(config)
self.name = self.config["name"]
self.num_sim, self.num_varying = count_variations(self.config)
self.single_sim = self.num_sim == 1
def get_pulse(self, key):
return self.config["pulse"][key]
def get_fiber(self, key):
return self.config["fiber"][key]
def get_simulation(self, key):
return self.config["pulse"][key]
def __iter__(self) -> Iterator[Tuple[list, dict]]:
"""iterates through all possible parameters, yielding a config as welle as a flattened
computed parameters set each time"""
for only_varying, full_config in varying_iterator(self.config):
yield only_varying, compute_init_parameters(full_config)
def __len__(self):
return self.num_sim
def __getitem__(self, key):
return self.config[key[0]][key[1]]
def __str__(self) -> str:
return f"dispatcher generated from config {self.name}"
def wspace(t, t_num=0):
"""frequency array such that x(t) <-> np.fft(x)(w)
Parameters
----------
t : float or array
float : total width of the time window
array : time array
t_num : int-
if t is a float, specifies the number of points
Returns
----------
w : array
linspace of frencies corresponding to t
"""
if isinstance(t, (np.ndarray, list, tuple)):
dt = t[1] - t[0]
t_num = len(t)
t = t[-1] - t[0] + dt
else:
dt = t / t_num
w = 2 * pi * np.arange(t_num) / t
w = np.where(w >= pi / dt, w - 2 * pi / dt, w)
return w
def tspace(time_window=None, t_num=None, dt=None):
"""returns a time array centered on 0
Parameters
----------
time_window : float
total time spanned
t_num : int
number of points
dt : float
time resolution
at least 2 arguments must be given. They are prioritize as such
t_num > time_window > dt
Returns
-------
t : array
a linearily spaced time array
Raises
------
TypeError
missing at least 1 argument
"""
if t_num is not None:
if isinstance(time_window, (float, int)):
return np.linspace(-time_window / 2, time_window / 2, int(t_num))
elif isinstance(dt, (float, int)):
time_window = (t_num - 1) * dt
return np.linspace(-time_window / 2, time_window / 2, t_num)
elif isinstance(time_window, (float, int)) and isinstance(dt, (float, int)):
t_num = int(time_window / dt) + 1
return np.linspace(-time_window / 2, time_window / 2, t_num)
else:
raise TypeError("not enough parameter to determine time vector")
def validate_single_parameter(parent, key, value):
try:
func = valid_param_types[parent][key]
except KeyError:
s = f"The parameter '{key}' does not belong "
if parent == "root":
s += "at the root of the config file"
else:
s += f"in the category '{parent}'"
s += ". Make sure it is a valid parameter in the first place"
raise TypeError(s)
if not func(value):
raise TypeError(
f"value '{value}' of type {type(value)} for key '{key}' is not valid, {func.__doc__}"
)
return
def validate_types(config):
"""validates the data types in the initial config dictionary
Parameters
----------
config : dict
the initial config dictionary
Raises
------
TypeError
raised when a parameter has the wrong type
"""
for domain, parameters in config.items():
if isinstance(parameters, dict):
for param_name, param_value in parameters.items():
if param_name == "varying":
for k_vary, v_vary in param_value.items():
if not isinstance(v_vary, list):
raise TypeError(f"Varying parameters should be specified in a list")
if len(v_vary) < 1:
raise ValueError(
f"Varying parameters lists should contain at least 1 element"
)
if k_vary not in valid_varying[domain]:
raise TypeError(f"'{k_vary}' is not a valid variable parameter")
[
validate_single_parameter(domain, k_vary, v_vary_indiv)
for v_vary_indiv in v_vary
]
else:
validate_single_parameter(domain, param_name, param_value)
else:
validate_single_parameter("root", domain, parameters)
def _contains(sub_conf, param):
return param in sub_conf or param in sub_conf.get("varying", {})
def _ensure_consistency_fiber(fiber):
"""ensure the fiber sub-dictionary of the parameter set is consistent
Parameters
----------
fiber : dict
dictionary containing the fiber parameters
Returns
-------
dict
the updated dictionary
Raises
------
MissingParameterError
When at least one required parameter with no default is missing
"""
if _contains(fiber, "beta"):
fiber = defaults.get(fiber, "gamma", specified_parameters=["beta"])
fiber["model"] = fiber.get("model", "custom")
else:
fiber = defaults.get(fiber, "model")
if fiber["model"] == "pcf":
fiber = defaults.get_fiber(fiber, "pitch")
fiber = defaults.get_fiber(fiber, "pitch_ratio")
elif fiber["model"] == "hasan":
fiber = defaults.get_multiple(
fiber, ["capillary_spacing", "capillary_outer_d"], 1, fiber_model="hasan"
)
for param in [
"core_radius",
"capillary_num",
"capillary_thickness",
"capillary_resonance_strengths",
"capillary_nested",
]:
fiber = defaults.get_fiber(fiber, param)
else:
for param in hc_model_specific_parameters[fiber["model"]]:
fiber = defaults.get_fiber(fiber, param)
fiber = defaults.get(fiber, "length")
return fiber
def _ensure_consistency_gas(gas):
"""ensure the gas sub-dictionary of the parameter set is consistent
Parameters
----------
gas : dict
dictionary containing the gas parameters
Returns
-------
dict
the updated dictionary
Raises
------
MissingParameterError
When at least one required parameter with no default is missing
"""
for param in ["gas_name", "temperature", "pressure", "plasma_density"]:
gas = defaults.get(gas, param, specified_params=["gas"])
return gas
def _ensure_consistency_pulse(pulse):
"""ensure the pulse sub-dictionary of the parameter set is consistent
Parameters
----------
pulse : dict
dictionary of the pulse section of parameters
Returns
-------
dict
the updated pulse dictionary
Raises
------
MissingParameterError
When at least one required parameter with no default is missing
"""
for param in ["wavelength", "shape", "quantum_noise", "intensity_noise"]:
pulse = defaults.get(pulse, param)
if _contains(pulse, "soliton_num"):
pulse = defaults.get_multiple(
pulse, ["power", "energy", "width", "t0"], 1, specified_parameters=["soliton_num"]
)
else:
pulse = defaults.get_multiple(pulse, ["t0", "width"], 1)
pulse = defaults.get_multiple(pulse, ["power", "energy"], 1)
return pulse
def _ensure_consistency_simulation(simulation):
"""ensure the simulation sub-dictionary of the parameter set is consistent
Parameters
----------
pulse : dict
dictionary of the pulse section of parameters
Returns
-------
dict
the updated pulse dictionary
Raises
------
MissingParameterError
When at least one required parameter with no default is missing
"""
simulation = defaults.get_multiple(simulation, ["dt", "t_num", "time_window"], 2)
for param in [
"behaviors",
"z_num",
"frep",
"tolerated_error",
"parallel",
"repeat",
"lower_wavelength_interp_limit",
"upper_wavelength_interp_limit",
"ideal_gas",
]:
simulation = defaults.get(simulation, param)
if "raman" in simulation["behaviors"] or any(
["raman" in l for l in simulation.get("varying", {}).get("behaviors", [])]
):
simulation = defaults.get(simulation, "raman_type", specified_parameters=["raman"])
return simulation
def ensure_consistency(config):
"""ensure the config dictionary is consistent and that certain parameters are set,
either by filling in defaults or by raising an error. This is not where new values are calculated.
Parameters
----------
config : dict
original config dict loaded from the toml file
Returns
-------
dict
the consistent config dict
"""
validate_types(config)
# ensure parameters are not specified multiple times
for sub_dict in valid_param_types.values():
for param_name in sub_dict:
for set_param in config.values():
if isinstance(set_param, dict):
if param_name in set_param and param_name in set_param.get("varying", {}):
raise DuplicateParameterError(
f"got multiple values for parameter '{param_name}'"
)
# ensure every required parameter has a value
config["name"] = config.get("name", "no name")
config["fiber"] = _ensure_consistency_fiber(config.get("fiber", {}))
if config["fiber"]["model"] in hc_model_specific_parameters:
config["gas"] = _ensure_consistency_gas(config.get("gas", {}))
config["pulse"] = _ensure_consistency_pulse(config.get("pulse", {}))
config["simulation"] = _ensure_consistency_simulation(config.get("simulation", {}))
return config
def compute_init_parameters(config):
"""computes all derived values from a config dictionary
Parameters
----------
config : dict
a configuration dictionary containing the pulse, fiber and simulation sections with no varying parameter.
Note : checking the validity of the configuration shall be done before calling this function.
Returns
-------
dict
a flattened dictionary (no fiber, pulse, simulation subsections) with all the necessary values to run RK4IP
"""
logger = get_logger(__name__)
# copy and flatten the config
params = dict(name=config["name"])
for section in ["pulse", "fiber", "simulation", "gas"]:
for key, value in config.get(section, {}).items():
params[key] = value
params = _generate_sim_grid(params)
# FIBER
params["interp_range"] = _interp_range(
params["w"],
params["upper_wavelength_interp_limit"],
params["lower_wavelength_interp_limit"],
)
if "beta" in params:
params["beta"] = np.array(params["beta"])
temp_gamma = 0
params["dynamic_dispersion"] = False
else:
params["dynamic_dispersion"] = fiber.is_dynamic_dispersion(params)
params["beta"], temp_gamma = fiber.dispersion_central(params["model"], params)
if params["dynamic_dispersion"]:
params["gamma_func"] = temp_gamma
params["beta_func"] = params["beta"]
params["beta"] = params["beta_func"](0)
temp_gamma = temp_gamma(0)
if "gamma" not in params:
params["gamma"] = temp_gamma
logger.info(f"using computed \u0263 = {params['gamma']:.2e} W/m^2")
# Raman response
if "raman" in params["behaviors"]:
params["hr_w"] = fiber.delayed_raman_w(params["t"], params["dt"], params["raman_type"])
# PULSE
params = _update_pulse_parameters(params)
logger.info(f"computed initial N = {params['soliton_num']:.3g}")
params["L_D"] = params["t0"] ** 2 / abs(params["beta"][0])
params["L_NL"] = 1 / (params["gamma"] * params["power"]) if params["gamma"] else np.inf
params["L_sol"] = pi / 2 * params["L_D"]
# Technical noise
if "intensity_noise" in params:
params = _technical_noise(params)
# Initial field
if "field_0" in params:
params = _validate_custom_init_field(params)
else:
params["field_0"] = pulse.initial_field(
params["t"], params["shape"], params["t0"], params["power"]
)
if params["quantum_noise"]:
params["field_0"] = params["field_0"] + pulse.shot_noise(
params["w_c"], params["w0"], params["time_window"], params["dt"]
)
return params
def _update_pulse_parameters(params):
(
params["width"],
params["t0"],
params["power"],
params["energy"],
params["soliton_num"],
) = pulse.conform_pulse_params(
shape=params["shape"],
width=params.get("width", None),
t0=params.get("t0", None),
power=params.get("power", None),
energy=params.get("energy", None),
gamma=params["gamma"],
beta2=params["beta"][0],
)
return params
def _validate_custom_init_field(params):
if isinstance(params["field_0"], str):
field_0 = evaluate_field_equation(params["field_0"], **params)
params["field_0"] = field_0
elif len(params["field_0"]) != params["t_num"]:
raise ValueError(
"initial field is given but doesn't match size and type with the time array"
)
return params
def _technical_noise(params):
logger = get_logger(__name__)
if params["intensity_noise"] > 0:
logger.info(f"intensity noise of {params['intensity_noise']}")
delta_int, delta_T0 = pulse.technical_noise(params["intensity_noise"])
params["power"] *= delta_int
params["t0"] *= delta_T0
params["width"] *= delta_T0
params = _update_pulse_parameters(params)
return params
def _interp_range(w, upper, lower):
# by default, the interpolation range of the dispersion polynomial stops exactly
# at the boundary of the frequency window we consider
interp_range = [
max(lower, units.m.inv(np.max(w[w > 0]))),
min(upper, units.m.inv(np.min(w[w > 0]))),
]
return interp_range
def _generate_sim_grid(params):
"""computes a bunch of values that relate to the simulation grid
Parameters
----------
params : dict
flattened parameter dictionary
Returns
-------
dict
updated parameter dictionary
"""
t = tspace(
time_window=params.get("time_window", None),
t_num=params.get("t_num", None),
dt=params.get("dt", None),
)
params["t"] = t
params["time_window"] = length(t)
params["dt"] = t[1] - t[0]
params["t_num"] = len(t)
w_c = wspace(t)
w0 = units.m(params["wavelength"])
params["w0"] = w0
params["w_c"] = w_c
params["w"] = w_c + w0
params["w_power_fact"] = [power_fact(w_c, k) for k in range(2, 11)]
params["z_targets"] = np.linspace(0, params["length"], params["z_num"])
params["store_num"] = len(params["z_targets"])
return params
# def compute_init_parameters_old(dictionary):
# """
# computes the initial parameters required and sorts them in 3 categories : simulations, pulse and fiber
# Parameters
# ----------
# dictionary : dict, optional
# dictionary containing parameters for a single simulation
# Returns
# -------
# params : dict
# dictionary of parameters
# Note
# ----
# Parameter computation occurs in 3 different stages
# Simulation-specific parameters
# Fiber-specific parameters
# Initial pulse parameters
# """
# logger = state.CurrentLogger
# param_dico = dictionary.copy()
# if "name" not in param_dico:
# param_dico["name"] = "untitled_parameters"
# # convert units
# param_dico = units.standardize_dictionary(param_dico)
# #### SIMULATION SPECIFIC VALUES
# # time/frequency grid
# lambda0 = param_dico["lambda0"]
# t = tspace(
# T=param_dico.get("T", None), t_num=param_dico.get("nt", None), dt=param_dico.get("dt", None)
# ) # time grid
# w_c = wspace(t)
# w0 = units.m(lambda0)
# param_dico["T"] = length(t)
# param_dico["dt"] = t[1] - t[0]
# param_dico["nt"] = len(t)
# param_dico["w0"] = w0
# param_dico["w_c"] = w_c
# param_dico["w"] = w_c + w0
# param_dico["t"] = t
# # precompute (w - w0)^k / k!
# param_dico["w_power_fact"] = [power_fact(w_c, k) for k in range(2, 11)]
# logger.log(
# f"time window : {1e15*param_dico['T']:.1f}fs with {1e15*param_dico['dt']:.1f}fs resolution"
# )
# logger.log(
# f"wl window : {units.nm.inv(np.max(param_dico['w']))}nm to {units.nm.inv(np.min(param_dico['w'][param_dico['w'] > 0]))}nm"
# )
# # High frequencies can be a problem
# # We can ignore them with a lower_wavelength_interp_limit parameter
# if units.nm.inv(np.max(w_c + w0)) < 450 and "lower_wavelength_interp_limit" not in param_dico:
# pass
# if "lower_wavelength_interp_limit" in param_dico:
# lower_wavelength_interp_limit = units.nm(param_dico["lower_wavelength_interp_limit"])
# logger.log(
# f"ignoring wavelength below {lower_wavelength_interp_limit} for dispersion and photon number computation"
# )
# del param_dico["lower_wavelength_interp_limit"]
# else:
# lower_wavelength_interp_limit = np.inf
# #### FIBER PARAMETERS
# # Unify data whether we want spectra stored at certain places or just
# # a certain number of them uniformly spaced
# if "z_targets" not in param_dico:
# param_dico["z_targets"] = np.linspace(0, 1, state.default_z_target_size)
# else:
# param_dico["z_targets"] = sanitize_z_targets(param_dico["z_targets"])
# param_dico["store_num"] = len(param_dico["z_targets"])
# # Dispersion parameters of the fiber, as well as gamma
# if "interp_range" not in param_dico:
# # the interpolation range of the dispersion polynomial stops exactly
# # at the boundary of the frequency window we consider
# param_dico["interp_range"] = [
# units.nm.inv(max(np.max(w_c + w0), units.nm(lower_wavelength_interp_limit))),
# 1900,
# ]
# if "beta" in param_dico:
# param_dico["beta"] = np.array(param_dico["beta"])
# temp_gamma = 0
# param_dico["dynamic_dispersion"] = False
# else:
# param_dico["dynamic_dispersion"] = fiber.is_dynamic_dispersion(param_dico)
# param_dico["beta"], temp_gamma = fiber.dispersion_central(
# param_dico.get("fiber_model", "PCF"), param_dico
# )
# if param_dico["dynamic_dispersion"]:
# param_dico["gamma_func"] = temp_gamma
# param_dico["beta_func"] = param_dico["beta"]
# param_dico["beta"] = param_dico["beta_func"](0)
# temp_gamma = temp_gamma(0)
# if "gamma" not in param_dico:
# param_dico["gamma"] = temp_gamma
# logger.log(f"computed gamma coefficient of {temp_gamma:.2e}")
# # Raman response
# if "raman" in param_dico["behaviors"]:
# param_dico["hr_w"] = fiber.delayed_raman_w(
# t, param_dico["dt"], param_dico.get("raman_type", "stolen")
# )
# #### PULSE PARAMETERS
# # Convert if pulse energy is given
# if "E0" in param_dico:
# param_dico["P0"] = pulse.E0_to_P0(
# param_dico["E0"], param_dico["T0_FWHM"], param_dico.get("pulse_shape", "gaussian")
# )
# logger.log(
# f"Pulse energy of {1e6 * param_dico['E0']:.2f} microjoules converted to peak power of {1e-3 * param_dico['P0']:.0f}kW."
# )
# # Soliton Number
# param_dico["t0"] = (
# param_dico["T0_FWHM"] * pulse.fwhm_to_T0_fac[param_dico.get("pulse_shape", "gaussian")]
# )
# if "N" in param_dico:
# param_dico["P0"] = (
# param_dico["N"] ** 2
# * np.abs(param_dico["beta"][0])
# / (param_dico["gamma"] * param_dico["t0"] ** 2)
# )
# logger.log(
# "Pump power adjusted to {:.2f} W to match solition number {}".format(
# param_dico["P0"], param_dico["N"]
# )
# )
# else:
# param_dico["N"] = np.sqrt(
# param_dico["P0"]
# * param_dico["gamma"]
# * param_dico["t0"] ** 2
# / np.abs(param_dico["beta"][0])
# )
# logger.log("Soliton number : {:.2f}".format(param_dico["N"]))
# # Other caracteristic quantities
# param_dico["L_D"] = param_dico["t0"] ** 2 / np.abs(param_dico["beta"][0])
# param_dico["L_NL"] = 1 / (param_dico["gamma"] * param_dico["P0"])
# param_dico["L_sol"] = pi / 2 * param_dico["L_D"]
# # Technical noise
# if "delta_I" in param_dico:
# if param_dico["delta_I"] > 0:
# logger.log(f"intensity noise of {param_dico['delta_I']}")
# delta_int, delta_T0 = pulse.technical_noise(param_dico["delta_I"])
# param_dico["P0"] *= delta_int
# param_dico["t0"] *= delta_T0
# param_dico["T0_FWHM"] *= delta_T0
# # Initial field
# field_0 = np.zeros(param_dico["nt"], dtype="complex")
# # check validity if an array is given, otherwise compute it according to given values
# if "field_0" in param_dico:
# if isinstance(param_dico["field_0"], str):
# field_0 = evaluate_field_equation(param_dico["field_0"], **param_dico)
# elif len(param_dico["field_0"]) != param_dico["nt"] or not isinstance(
# param_dico["field_0"], (tuple, list, np.ndarray)
# ):
# raise ValueError(
# "initial field is given but doesn't match size and type with the time array"
# )
# else:
# shape = param_dico.get("pulse_shape", "gaussian")
# if shape.lower() == "gaussian":
# field_0 = pulse.gauss_pulse(param_dico["t"], param_dico["T0_FWHM"], param_dico["P0"])
# elif shape.lower() == "sech":
# field_0 = pulse.sech_pulse(param_dico["t"], param_dico["T0_FWHM"], param_dico["P0"])
# # Shot noise
# if "q_noise" in param_dico["behaviors"]:
# field_0 = field_0 + pulse.shot_noise(w_c, w0, param_dico["T"], param_dico["dt"])
# param_dico["field_0"] = np.array(field_0, dtype="complex")
# return param_dico
def sanitize_z_targets(z_targets):
"""
processes the 'z_targets' arguments and guarantees that:
- it is sorted
- it doesn't contain the same value twice
- it starts with 0
Parameters
----------
z_targets : float, int or array-like
float or int : end point of the fiber starting from 0
array-like of len(.) == 3 : `numpy.linspace` arguments
array-like of other length : target distances at which to store the spectra
Returns
----------
z_targets : list (mutability is important)
"""
if isinstance(z_targets, (float, int)):
z_targets = np.linspace(0, z_targets, state.default_z_target_size)
else:
z_targets = np.array(z_targets).flatten()
if len(z_targets) == 3:
z_targets = np.linspace(*z_targets[:2], int(z_targets[2]))
z_targets = list(set(value for value in z_targets if value >= 0))
z_targets.sort()
if 0 not in z_targets:
z_targets = [0] + z_targets
return z_targets
def evaluate_field_equation(eq, **kwargs):
return eval(
eq,
dict(
sin=np.sin,
cos=np.cos,
tan=np.tan,
exp=np.exp,
pi=np.pi,
sqrt=np.sqrt,
**kwargs,
),
)

874
src/scgenerator/io.py Normal file
View File

@@ -0,0 +1,874 @@
import itertools
import json
import logging
import os
from datetime import datetime
from glob import glob
from typing import Any, Dict, Iterable, List, Tuple
import matplotlib.pyplot as plt
import numpy as np
import pkg_resources as pkg
import toml
from matplotlib.gridspec import GridSpec
from scgenerator import utilities
from scgenerator.const import TMP_FOLDER_KEY_BASE, num
from scgenerator.errors import IncompleteDataFolderError
from . import state
def load_toml(path):
"""returns a dictionary parsed from the specified toml file"""
if not path.lower().endswith(".toml"):
path += ".toml"
with open(path, mode="r") as file:
dico = toml.load(file)
return dico
def save_toml(path, dico):
"""saves a dictionary into a toml file"""
if not path.lower().endswith(".toml"):
path += ".toml"
with open(path, mode="w") as file:
toml.dump(dico, file)
return dico
def get_logger(name=None):
"""returns a logging.Logger instance. This function is there because if scgenerator
is used with ray, workers are not aware of any configuration done with the logging
and so it must be reconfigured.
Parameters
----------
name : str, optional
name of the logger, by default None
Returns
-------
logging.Logger obj
logger
"""
name = __name__ if name is None else name
logger = logging.getLogger(name)
return configure_logger(logger)
def configure_logger(logger, logfile="scgenerator.log"):
"""configures a logging.Logger obj
Parameters
----------
logger : logging.Logger
logger to configure
logfile : str or None, optional
path to log file
Returns
-------
logging.Logger obj
updated logger
"""
if not hasattr(logger, "already_configured"):
if logfile is not None:
file_handler = logging.FileHandler("scgenerator.log", "a+")
file_handler.setFormatter(logging.Formatter("{name}: {message}", style="{"))
logger.addHandler(file_handler)
stream_handler = logging.StreamHandler()
logger.addHandler(stream_handler)
logger.setLevel(logging.INFO)
logger.already_configured = True
return logger
class Paths:
home = os.path.expanduser("~")
_data_files = ["silica.toml", "gas.toml", "hr_t.npz"]
paths = {
f.split(".")[0]: os.path.abspath(
pkg.resource_filename("scgenerator", os.path.join("data", f))
)
for f in _data_files
}
@classmethod
def get(cls, key):
if key not in cls.paths:
if os.path.exists("paths.json"):
with open("paths.json") as file:
paths_dico = json.load(file)
for k, v in paths_dico.items():
cls.paths[k] = os.path.abspath(os.path.expanduser(v))
if key not in cls.paths:
print(f"{key} was not found in path index, returning current working directory.")
cls.paths[key] = os.getcwd()
return cls.paths[key]
@classmethod
def gets(cls, key):
"""returned the specified file as a string"""
with open(cls.get(key)) as file:
return file.read()
@staticmethod
def tmp(task_id=0):
suffix = "" if task_id == 0 else str(task_id)
return ".scgenerator_tmp" + suffix
@classmethod
def plot(cls, name):
"""returns the paths to the specified plot. Used to save new plot
example
---------
fig.savefig(Paths.plot("figure5.pdf"))
"""
return os.path.join(cls.get("plots"), name)
def serializable(val):
"""returns True if val is serializable into a Json file"""
types = (np.ndarray, float, int, str, list, tuple)
out = isinstance(val, types)
if isinstance(val, np.ndarray):
out &= val.dtype != "complex"
return out
def _prepare_for_serialization(dico):
"""prepares a dictionary for serialization. Some keys may not be preserved
(dropped due to no conversion available)
Parameters
----------
dico : dict
dictionary
"""
forbiden_keys = ["w_c", "w_power_fact", "field_0", "w"]
types = (np.ndarray, float, int, str, list, tuple, dict)
out = {}
for key, value in dico.items():
if key in forbiden_keys:
continue
if not isinstance(value, types):
continue
if isinstance(value, dict):
out[key] = _prepare_for_serialization(value)
elif isinstance(value, np.ndarray) and value.dtype == complex:
continue
else:
out[key] = value
return out
def save_parameters(param_dict, file_name="param"):
"""Writes the flattened parameters dictionary specific to a single simulation into a toml file
Parameters
----------
param_dict : dictionary of parameters. Only floats, int and arrays of
non complex values are stored in the json
folder_name : folder where to save the files (relative to cwd)
file_name : name of the readable file.
"""
param = param_dict.copy()
folder_name, file_name = os.path.split(file_name)
folder_name = "tmp" if folder_name == "" else folder_name
file_name = os.path.splitext(file_name)[0]
if not os.path.exists(folder_name):
os.makedirs(folder_name)
param = _prepare_for_serialization(param)
param["datetime"] = datetime.now()
# save toml of the simulation
with open(os.path.join(folder_name, file_name + ".toml"), "w") as file:
toml.dump(param, file, encoder=toml.TomlNumpyEncoder())
return os.path.join(folder_name, file_name)
def load_previous_parameters(path):
"""loads a parameters json files and converts data to appropriate type
Parameters
----------
path : path-like
path to the json
Returns
----------
params : dict
"""
params = load_toml(path)
for k, v in params.items():
if isinstance(v, list):
if isinstance(v[0], (float, int)):
params[k] = np.array(v)
return params
def load_material_dico(name):
"""loads a material dictionary
Parameters
----------
name : str
name of the material
Returns
----------
material_dico : dict
"""
if name == "silica":
return toml.loads(Paths.gets("silica"))
else:
return toml.loads(Paths.gets("gas"))[name]
def load_sim_data(folder_name, ind=None, load_param=True):
"""
loads the data already simulated.
defauft shape is (z_targets, n, nt)
Parameters
----------
folder_name : (string) folder where the simulation data is stored
ind : list of indices if only certain spectra are desired.
- If left to None, returns every spectrum
- If only 1 int, will cast the (1, n, nt) array into a (n, nt) array
load_param : (bool) return the parameter dictionary as well. returns None
if not available
dico_name : name of the params dict stored in state.Params
Returns
----------
spectra : array
squeezed array of complex spectra (n simulation on a nt size grid at each ind)
Raises
----------
FileNotFoundError : folder does not exist or does not contain sufficient
data
"""
print(f"opening {folder_name}")
# Check if file exists and assert how many z positions there are
if not os.path.exists(folder_name):
raise FileNotFoundError(f"Folder {folder_name} does not exist")
nmax = len(glob(os.path.join(folder_name, "spectra_*.npy")))
if nmax <= 0:
raise FileNotFoundError(f"No appropriate file in specified folder {folder_name}")
if ind is None:
ind = range(nmax)
elif isinstance(ind, int):
ind = [ind]
# Load the spectra
spectra = []
for i in ind:
spectra.append(load_single_spectrum(folder_name, i))
spectra = np.array(spectra)
# Load the parameters dictionary
try:
params = load_previous_parameters(os.path.join(folder_name, "params.toml"))
except FileNotFoundError:
print(f"parameters corresponding to {folder_name} not found")
params = None
print("data successfully loaded")
if load_param:
return spectra.squeeze(), params
else:
return spectra.squeeze()
def get_all_environ() -> Dict[str, str]:
"""returns a dictionary of all environment variables set by any instance of scgenerator"""
return dict(filter(lambda el: el[0].startswith(TMP_FOLDER_KEY_BASE), os.environ.items()))
def load_single_spectrum(folder, index) -> np.ndarray:
return np.load(os.path.join(folder, f"spectra_{index}.npy"))
def iter_load_sim_data(folder_name, with_params=False) -> Iterable[np.ndarray]:
"""
similar to load_sim_data but works as an iterator
"""
if not os.path.exists(folder_name):
raise FileNotFoundError(f"Folder {folder_name} does not exist")
nmax = len(glob(os.path.join(folder_name, "spectra_*.npy")))
if nmax <= 0:
raise FileNotFoundError(f"No appropriate file in specified folder {folder_name}")
params = {}
if with_params:
try:
params = load_previous_parameters(os.path.join(folder_name, "params.toml"))
except FileNotFoundError:
print(f"parameters corresponding to {folder_name} not found")
params = None
print(f"iterating through {folder_name}")
for i in range(nmax):
if with_params:
yield load_single_spectrum(folder_name, i), params
else:
yield load_single_spectrum(folder_name, i)
def _get_data_subfolders(path: str) -> List[str]:
"""returns a list of subfolders in the specified directory
Parameters
----------
path : str
path to directory containing the initial config file and the spectra sub folders
Returns
-------
List[str]
paths to sub folders
"""
sub_folders = glob(os.path.join(path, "*"))
sub_folders = list(filter(os.path.isdir, sub_folders))
return sub_folders
# def _sort_subfolder_list(
# sub_folders: List[str], varying_lists: List[Tuple[str, Any]]
# ) -> Tuple[list]:
# """sorts the two lists in parallel according to parameter values
# Parameters
# ----------
# sub_folders : List[str]
# paths to where spectra are loaded
# varying_lists : List[Tuple]
# (param_name, value) tuples corresponding to the sub_folders
# Returns
# -------
# Tuple[list]
# the input, sorted
# """
# both_lists = list(zip(sub_folders, varying_lists))
# for i in range(len(varying_lists[0])):
# both_lists.sort(key=lambda el: el[1][i][1])
# return tuple(zip(*both_lists))
def check_data_integrity(sub_folders: List[str], init_z_num: int):
"""checks the integrity and completeness of a simulation data folder
Parameters
----------
path : str
path to the data folder
init_z_num : int
z_num as specified by the initial configuration file
Raises
------
IncompleteDataFolderError
raised if not all spectra are present in any folder
"""
for sub_folder in sub_folders:
params = load_toml(os.path.join(sub_folder, "params.toml"))
z_num = params["z_num"]
num_spectra = len(glob(os.path.join(sub_folder, "spectrum*.npy")))
if z_num != init_z_num:
raise IncompleteDataFolderError(
f"initial config specifies {init_z_num} spectra per"
+ f" but the parameter file in {sub_folder} specifies {z_num}"
)
if num_spectra != z_num:
raise IncompleteDataFolderError(
f"only {num_spectra} spectra found in {sub_folder} instead of the specified {z_num}"
)
# def preprocess_data_folder(path: str) -> bool:
# config = load_toml(os.path.join(path, "initial_config.toml"))
# num_sims, _ = utilities.count_variations(config)
# sub_folders = _get_data_subfolders(path)
# init_z_num = config["simulation"]["z_num"]
# if len(sub_folders) != num_sims:
# raise IncompleteDataFolderError(
# f"only {len(sub_folders)} simulations out of {num_sims} have been made"
# )
# varying_lists = [utilities.varying_list_from_path(os.path.split(s)[1]) for s in sub_folders]
# varying_params = [el[0] for el in varying_lists[0]]
# sub_folders, varying_lists = _update_varying_lists(
# sub_folders, varying_lists, varying_params, init_z_num
# )
# possible_values = []
# for i, p in enumerate(varying_params):
# tmp = set()
# for v_list in varying_lists:
# tmp.add(v_list[i][1])
# tmp = list(tmp)
# possible_values.append(tmp)
# return sub_folders, varying_lists, varying_params, possible_values, init_z_num
# def merge_data(path: str):
# sub_folders, varying_lists, varying_params, possible_values, z_num = preprocess_data_folder(
# path
# )
# z_values = list(range(z_num))
# pt = utilities.ProgressTracker(z_num, auto_print=True)
# shape = tuple((len(l) for l in possible_values))
# for z_num in z_values:
# to_save = []
# for i in range(np.product(shape)):
# to_save.append(np.load(os.path.join(sub_folders[i], f"spectrum_{z_num}.npy")))
# out = np.array(to_save).reshape((*shape, len(to_save[0])))
# np.save(os.path.join(path, f"spectra_{z_num}.npy"), out)
# pt.update()
# _create_reference_file(varying_params, possible_values)
# return
def merge_same_simulations(path: str):
num_separator = "_num_"
sub_folders = _get_data_subfolders(path)
config = load_toml(os.path.join(path, "initial_config.toml"))
repeat = config["simulation"].get("repeat", 1)
z_num = config["simulation"]["z_num"]
check_data_integrity(sub_folders, z_num)
base_folders = set()
for sub_folder in sub_folders:
splitted_base_path = sub_folder.split(num_separator)[:-1]
base_folder = num_separator.join(splitted_base_path)
if len(base_folder) > 0:
base_folders.add(base_folder)
print(base_folders)
for base_folder in base_folders:
for j in range(z_num):
spectra = []
for i in range(repeat):
spectra.append(
np.load(os.path.join(f"{base_folder}{num_separator}{i}/spectrum_{j}.npy"))
)
dest_folder = ensure_folder(base_folder, prevent_overwrite=False)
spectra = np.array(spectra).reshape(repeat, len(spectra[0]))
np.save(os.path.join(dest_folder, f"spectra_{j}.npy"), spectra)
# class tmp_index_manager:
# """Manages a temporary index of files while the simulation is running
# and merge them at the end automatically"""
# def __init__(self, config_name="untitled", task_id=0, varying_keys=None):
# self.path = os.path.join(Paths.tmp(task_id), "index.json")
# self.config_name = config_name
# self.varying_keys = varying_keys
# # set up the directories
# if not os.path.exists(Paths.tmp(task_id)):
# os.makedirs(Paths.tmp(task_id))
# file_num = 0
# while os.path.exists(self.path):
# self.path = os.path.join(Paths.tmp(task_id), f"index_{file_num}.json")
# file_num += 1
# self.index = dict(spectra={}, z={}, params={})
# self.ids = set()
# with open(self.path, "w") as file:
# json.dump(self.index, file)
# def get_path(self):
# return self.path
# def append_to_index(self, param_id, spectra_file_name="", params_file_name=""):
# """add one or two files to the index
# Parameters
# ----------
# param_id : id of the parameter set
# spectra_file_name : name of the spectra file
# params_file_name : name of the parameters file
# Returns
# ----------
# None
# """
# # names of the recorded values in order
# # here : {"spectra":spectra_file_name, "params":params_file_name}
# file_names = [spectra_file_name, params_file_name]
# file_names_dict = dict(zip(state.recorded_types, file_names))
# param_id = str(param_id)
# self.ids.add(param_id)
# with open(self.path, "r") as file:
# self.index = json.loads(file.read())
# for type_name, file_name in file_names_dict.items():
# if file_name != "":
# if param_id not in self.index[type_name]:
# self.index[type_name][param_id] = []
# self.index[type_name][param_id].append(file_name)
# with open(self.path, "w") as file:
# json.dump(self.index, file)
# def convert_sim_data(self):
# return convert_sim_data(self.path, name=self.config_name, varying_keys=self.varying_keys)
# def convert_sim_data(path, name="untitled", ids=None, varying_keys=[], delete_temps=True):
# """Converts simulation data that are stored as 1 file/simulation to 1 file
# per parameters set
# Parameters
# ----------
# path : path to the index containing infos about how to group files together
# name : name of the final folder
# ids : list of ids, 1 per set of parameters
# Returns
# ----------
# path to the converted data
# """
# with open(path, "r") as file:
# index = json.loads(file.read())
# folder_0 = os.path.join(Paths.get("data"), name)
# folder_0 = ensure_folder(folder_0) # related to the set of simulation / job
# # find the ids if not stored already
# if ids is None:
# ids = set()
# for key in state.recorded_types:
# for k in index[key]:
# ids.add(k)
# not_found = []
# for param_id in ids:
# print("ids", ids)
# # Load the spectra
# spectra = []
# for f in index["spectra"][param_id]:
# try:
# spectra.append(np.load(f))
# except FileNotFoundError:
# not_found.append(f)
# index["spectra"][param_id].remove(f)
# spectra = np.array(spectra)
# # Load the params
# main_param_name = index["params"][param_id][0] + ".json"
# try:
# with open(main_param_name, "r") as file:
# params = json.load(file)
# except FileNotFoundError:
# print(f"no parameters for id {param_id} found. Skipping this one")
# not_found += index["params"][param_id]
# continue
# if len(not_found) > 0:
# print(f"{len(not_found)} files not found:")
# for file_not_found in not_found:
# print("\t" + file_not_found)
# # create sub folder
# if len(ids) > 1:
# complement = [param_id]
# for key in varying_keys:
# if key in ["T0_FWHM", "P0"]:
# key = "init_" + key
# complement.append(key)
# complement.append(format(params.get(key, 0), ".2e").split("e")[0])
# folder_1 = "_".join(complement) # related to specific parameter
# folder_name = os.path.join(folder_0, folder_1)
# else:
# folder_name = folder_0
# if not os.path.exists(folder_name):
# os.makedirs(folder_name)
# os.rename(main_param_name, os.path.join(folder_name, "param.json"))
# # Save the data in a more easily manageable format (one file per z position)
# for k in range(len(spectra[0])):
# np.save(os.path.join(folder_name, f"spectra_{k}"), spectra[:, k])
# print(f"{len(spectra)} simulations converted. Data saved in {folder_name}")
# deleted = 0
# if delete_temps:
# # once everything is saved, delete the temporary files to free up space
# param_file_names = [f + ".json" for f in index["params"][param_id]]
# try:
# param_file_names.remove(main_param_name)
# except ValueError:
# pass
# fail_list = []
# for f in index["spectra"][param_id] + param_file_names:
# try:
# os.remove(f)
# deleted += 1
# except FileNotFoundError:
# fail_list.append(f)
# if len(fail_list) > 0:
# print(f"could not remove {len(fail_list)} temporary files :")
# for failed in fail_list:
# print("\t" + failed)
# print(f"Merge finished, deleted {deleted} temporary files.")
# if delete_temps:
# os.remove(path)
# delete_tmp_folder()
# return folder_0
# def delete_tmp_folder():
# """deletes temporary folders if they are empty"""
# for folder in glob(Paths.tmp()):
# try:
# os.rmdir(folder)
# except OSError as err:
# print(err)
def get_data_folder(task_id: int, name_if_new: str = ""):
idstr = str(int(task_id))
tmp = os.getenv(TMP_FOLDER_KEY_BASE + idstr)
if tmp is None:
tmp = ensure_folder("scgenerator_" + name_if_new + idstr)
tmp = os.path.abspath(tmp)
os.environ[TMP_FOLDER_KEY_BASE + idstr] = tmp
return tmp
def generate_file_path(file_name: str, task_id: int, sub_folder: str = "") -> str:
"""generates a path for the desired file name
Parameters
----------
file_name : str
desired file name. May be altered if it already exists
task_id : int
unique id of the process
sub_folder : str
subfolder in which to store the file. default : ""
Returns
-------
str
the full path
"""
base_name, ext = os.path.splitext(file_name)
folder = get_data_folder(task_id)
folder = os.path.join(folder, sub_folder)
folder = ensure_folder(folder, prevent_overwrite=False)
i = 0
base_name = os.path.join(folder, base_name)
new_name = base_name + ext
while os.path.exists(new_name):
print(f"{i=}")
new_name = f"{base_name}_{i}{ext}"
i += 1
return new_name
def save_data(data: np.ndarray, file_name: str, task_id: int, subfolder: str = ""):
"""saves numpy array to disk
Parameters
----------
data : np.ndarray
data to save
file_name : str
file name
task_id : int
id that uniquely identifies the process
subfolder : str, optional
subfolder in the main data folder of the task, by default ""
"""
path = generate_file_path(file_name, task_id, subfolder)
np.save(path, data)
def generate_tmp_file_name_old(file_name, job_id=0, param_id=0, task_id=0, ext=""):
"""returns a guaranteed available file name"""
main_suffix = f"_JOBID{job_id}_PARAMID{param_id}"
suffix = main_suffix + "_" + str(0)
no_dup = 1
while os.path.exists(os.path.join(Paths.tmp(task_id), file_name + suffix + ext)):
suffix = main_suffix + "_" + str(no_dup)
no_dup += 1
return os.path.join(Paths.tmp(task_id), file_name + suffix + ext)
def ensure_folder(name, i=0, suffix="", prevent_overwrite=True):
"""creates a folder for simulation data named name and prevents overwrite
by adding a suffix if necessary and returning the name"""
prefix, last_dir = os.path.split(os.path.abspath(name))
exploded = [prefix]
sub_prefix = prefix
while sub_prefix != os.path.abspath("/"):
sub_prefix, _ = os.path.split(sub_prefix)
exploded.append(sub_prefix)
if any(os.path.isfile(el) for el in exploded):
prefix = ensure_folder(prefix)
name = os.path.join(prefix, last_dir)
folder_name = name
if i > 0:
folder_name += f"_{i}"
folder_name += suffix
if not os.path.exists(folder_name):
os.makedirs(folder_name)
else:
if prevent_overwrite:
return ensure_folder(name, i + 1)
else:
return folder_name
return folder_name
# class Logger:
# def __init__(self, print_level=10000):
# """
# Parameters
# ----------
# print_level : messages above this priority will be printed as well as recorded
# """
# log_file_name = (
# "scgenerator_log_"
# + format(datetime.today())[:-7].replace(" ", "_").replace(":", "-")
# + ".txt"
# )
# self.log_file = os.path.join(Paths.get("logs"), log_file_name)
# self.print_level = print_level
# self.prefix_length = 0
# self.default_prefix = "Main Thread"
# if not os.path.exists(self.log_file):
# with open(self.log_file, "w"):
# pass
# with open(self.log_file, "a") as file:
# file.write(
# f"\n---------------------------\nNew Log {str(datetime.today()):19.19}\n---------------------------\n"
# )
# def log(self, s, priority=0, prefix=None):
# """logs a message
# Parameters
# ----------
# s : the string to log
# priority : will be compared to the logger's print_level to decide whether to print the string
# prefix : string identifying which thread or part of the program is giving the message
# Returns
# ----------
# nothing
# """
# if prefix is None:
# prefix = self.default_prefix
# if priority >= self.print_level:
# print(s)
# with open(self.log_file, "a") as file:
# if len(prefix) > self.prefix_length:
# self.prefix_length = len(prefix)
# prefix = format(prefix[: self.prefix_length], str(self.prefix_length))
# file.write(prefix + " : " + str(s) + "\n")
def plot_setup(
folder_name=None,
file_name=None,
file_type="png",
figsize=state.plot_default_figsize,
params=None,
mode="default",
):
"""It should return :
- a folder_name
- a file name
- a fig
- an axis
"""
file_name = state.plot_default_name if file_name is None else file_name
if params is not None:
folder_name = params.get("plot.folder_name", folder_name)
file_name = params.get("plot.file_name", file_name)
file_type = params.get("plot.file_type", file_type)
figsize = params.get("plot.figsize", figsize)
# ensure output folder_name exists
folder_name, file_name = (
os.path.split(file_name)
if folder_name is None
else (folder_name, os.path.split(file_name)[1])
)
folder_name = os.path.join(Paths.get("plots"), folder_name)
if not os.path.exists(os.path.abspath(folder_name)):
os.makedirs(os.path.abspath(folder_name))
# ensure no overwrite
ind = 0
while os.path.exists(os.path.join(folder_name, file_name + "_" + str(ind) + "." + file_type)):
ind += 1
file_name = file_name + "_" + str(ind) + "." + file_type
if mode == "default":
fig, ax = plt.subplots(figsize=figsize)
elif mode == "coherence":
n = state.plot_avg_default_main_to_coherence_ratio
gs1 = GridSpec(n + 1, 1, hspace=0.4)
fig = plt.figure(figsize=state.plot_default_figsize)
top = fig.add_subplot(gs1[:n])
top.tick_params(labelbottom=False)
bot = fig.add_subplot(gs1[n], sharex=top)
bot.set_ylim(-0.1, 1.1)
bot.set_ylabel(r"|$g_{12}$|")
ax = (top, bot)
elif mode == "coherence_T":
n = state.plot_avg_default_main_to_coherence_ratio
gs1 = GridSpec(1, n + 1, wspace=0.4)
fig = plt.figure(figsize=state.plot_default_figsize)
top = fig.add_subplot(gs1[:n])
top.tick_params(labelleft=False, left=False, right=True)
bot = fig.add_subplot(gs1[n], sharey=top)
bot.set_xlim(1.1, -0.1)
bot.set_xlabel(r"|$g_{12}$|")
ax = (top, bot)
else:
raise ValueError(f"mode {mode} not understood")
return folder_name, file_name, fig, ax

125
src/scgenerator/math.py Normal file
View File

@@ -0,0 +1,125 @@
from typing import Type
import numpy as np
from scipy.special import jn_zeros
from scipy.interpolate import interp1d, griddata
def span(*vec):
"""returns the min and max of whatever array-like is given. can accept many args"""
out = (np.inf, -np.inf)
for x in vec:
x = np.atleast_1d(x)
if len(x.shape) > 1:
x = x.ravel()
minx = np.min(x)
maxx = np.max(x)
out = (np.min([minx, out[0]]), np.max([maxx, out[1]]))
if out[0] == np.inf or out[1] == -np.inf:
out = (0, 1)
print(f"failed to evaluate the span of {vec}")
return out
def argclosest(array, target):
"""returns the index/indices corresponding to the closest matches of target in array"""
min_dist = np.inf
index = None
if isinstance(target, (list, tuple, np.ndarray)):
return np.array([argclosest(array, t) for t in target])
for k, val in enumerate(array):
dist = abs(val - target)
if dist < min_dist:
min_dist = dist
index = k
return index
def length(x):
return np.max(x) - np.min(x)
def power_fact(x, n):
"""
returns x ^ n / n!
"""
if isinstance(x, (int, float)):
x = float(x)
result = 1.0
elif isinstance(x, np.ndarray):
if x.dtype == int:
x = np.array(x, dtype=float)
result = np.ones(len(x))
else:
raise TypeError(f"type {type(x)} of x not supported.")
for k in range(n):
result = result * x / (n - k)
return result
def abs2(z):
return z.real ** 2 + z.imag ** 2
def sigmoid(x):
return 1 / (np.exp(-x) + 1)
def u_nm(n, m):
"""returns the mth zero of the Bessel function of order n-1
Parameters
----------
n-1 : order of the Bessel function
m : order of the zero
Returns
----------
float
"""
return jn_zeros(n - 1, m)[-1]
def make_uniform_2D(values, x_axis, y_axis, n=1024, method="linear"):
"""Interpolates a 2D array with the help of griddata
Parameters
----------
values : 2D array of real values
x_axis : x-coordinates of values
y_axis : y-coordinates of values
method : method of interpolation to be passed to griddata
Returns
----------
array of shape n
"""
xx, yy = np.meshgrid(x_axis, y_axis)
xx = xx.flatten()
yy = yy.flatten()
if not isinstance(n, tuple):
n = (n, n)
# old_points = np.array([gridx.ravel(), gridy.ravel()])
newx, newy = np.meshgrid(np.linspace(*span(x_axis), n[0]), np.linspace(*span(y_axis), n[1]))
print("interpolating")
out = griddata((xx, yy), values.flatten(), (newx, newy), method=method, fill_value=0)
print("interpolating done!")
return out.reshape(n[1], n[0])
def make_uniform_1D(values, x_axis, n=1024, method="linear"):
"""Interpolates a 2D array with the help of interp1d
Parameters
----------
values : 1D array of real values
x_axis : x-coordinates of values
method : method of interpolation to be passed to interp1d
Returns
----------
array of length n
"""
xx = np.linspace(*span(x_axis), len(x_axis))
return interp1d(x_axis, values, kind=method)(xx)

View File

View File

@@ -0,0 +1,962 @@
import numpy as np
import toml
from numpy.fft import fft, ifft
from numpy.polynomial.chebyshev import Chebyshev, cheb2poly
from scipy.interpolate import interp1d
from scgenerator.state import _DEBUG
from .. import io
from ..const import hc_model_specific_parameters
from ..math import abs2, argclosest, power_fact, u_nm
from . import materials as mat
from . import units
from .units import c, pi
def lambda_for_dispersion():
"""Returns a wl vector for dispersion calculation
Returns
-------
array of wl values
"""
return np.linspace(190e-9, 3000e-9, 4000)
def is_dynamic_dispersion(params):
"""tests if the parameter dictionary implies that the dispersion profile of the fiber changes with z
Parameters
----------
params : dict
flattened parameters dict
Returns
-------
bool : True if dispersion is supposed to change with z
"""
out = False
if "pressure" in params:
out |= isinstance(params["pressure"], (tuple, list)) and len(params["pressure"]) == 2
return out
def HCARF_gap(core_radius, capillary_num, capillary_outer_d):
"""computes the gap length between capillaries of a hollow core anti-resonance fiber
Parameters
----------
core_radius : float
radius of the core (m) (from cented to edge of a capillary)
capillary_num : int
number of capillaries
capillary_outer_d : float
diameter of the capillaries including the wall thickness(m). The core together with the microstructure has a diameter of 2R + 2d
Returns
-------
gap : float
"""
return (core_radius + capillary_outer_d / 2) * 2 * np.sin(
pi / capillary_num
) - capillary_outer_d
def dispersion_parameter(n_eff, lambda_):
"""computes the dispersion parameter D from an effective index of refraction n_eff
Since computing gradients/derivatives of discrete arrays is not well defined on the boundary, it is
advised to chop off the two values on either end of the returned array
Parameters
----------
n_eff : 1D array
a wl-dependent index of refraction
lambda_ : 1D array
the wavelength array (len must match n_eff)
Returns
-------
D : 1D array
wl-dependent dispersion parameter as function of lambda_
"""
return -lambda_ / c * (np.gradient(np.gradient(n_eff, lambda_), lambda_))
def beta2_to_D(beta2, lambda_):
"""returns the beta2 parameters corresponding to D(lambda_)"""
return -(2 * pi * c) / (lambda_ ** 2) * beta2
def D_to_beta2(D, lambda_):
"""returns the D parameter corresponding to beta2(lambda_)"""
return -(lambda_ ** 2) / (2 * pi * c) * D
def plasma_dispersion(lambda_, number_density, simple=False):
"""computes dispersion (beta2) for constant plasma
Parameters
----------
lambda_ : array-like
wavelengths over which to calculate the dispersion
number_density : number of ionized atoms /m^3
Returns
-------
beta2 : ndarray
WL-dependent dispersion parameter
"""
e2_me_e0 = 3182.60735 # e^2 /(m_e * epsilon_0)
w = units.m(lambda_)
if simple:
w_pl = number_density * e2_me_e0
return -(w_pl ** 2) / (c * w ** 2)
beta = w / c * np.sqrt(1 - number_density * e2_me_e0 / w ** 2)
beta2 = np.gradient(np.gradient(beta, w), w)
return beta2
def n_eff_marcatili(lambda_, n_gas_2, core_radius, he_mode=(1, 1)):
"""computes the effective refractive index according to the Marcatili model of a capillary
Parameters
----------
lambda_ : ndarray, shape (n, )
wavelengths array (m)
n_gas_2 : ndarray, shape (n, )
refractive index of the gas as function of lambda_
core_radius : float
inner radius of the capillary (m)
he_mode : tuple, shape (2, ), optional
n and m value of the HE_nm mode. 1 and 1 corresponds to the fundamental mode
Returns
-------
n_eff : ndarray, shape (n, )
Reference
---------
Marcatili, E., and core_radius. Schmeltzer, 1964, Bell Syst. Tech. J. 43, 1783.
"""
u = u_nm(*he_mode)
return np.sqrt(n_gas_2 - (lambda_ * u / (2 * pi * core_radius)) ** 2)
def n_eff_marcatili_adjusted(lambda_, n_gas_2, core_radius, he_mode=(1, 1), fit_parameters=()):
"""computes the effective refractive index according to the Marcatili model of a capillary but adjusted at longer wavelengths
Parameters
----------
lambda_ : ndarray, shape (n, )
wavelengths array (m)
n_gas_2 : ndarray, shape (n, )
refractive index of the gas as function of lambda_
core_radius : float
inner radius of the capillary (m)
he_mode : tuple, shape (2, ), optional
n and m value of the HE_nm mode. 1 and 1 corresponds to the fundamental mode
fit_parameters : tuple, shape (2, ), optional
fitting parameters (s, h). See reference for more info
Returns
-------
n_eff : ndarray, shape (n, )
Reference
----------
Köttig, F., et al. "Novel mid-infrared dispersive wave generation in gas-filled PCF by transient ionization-driven changes in dispersion." arXiv preprint arXiv:1701.04843 (2017).
"""
u = u_nm(*he_mode)
corrected_radius = effective_core_radius(lambda_, core_radius, *fit_parameters)
return np.sqrt(n_gas_2 - (lambda_ * u / (2 * pi * corrected_radius)) ** 2)
def n_eff_hasan(
lambda_,
n_gas_2,
core_radius,
capillary_num,
capillary_thickness,
capillary_outer_d=None,
capillary_spacing=None,
capillary_resonance_strengths=[],
capillary_nested=0,
):
"""computes the effective refractive index of the fundamental mode according to the Hasan model for a anti-resonance fiber
Parameters
----------
lambda_
wavelenghs array (m)
n_gas_2 : ndarray, shape (n, )
squared refractive index of the gas as a function of lambda_
core_radius : float
radius of the core (m) (from cented to edge of a capillary)
capillary_num : int
number of capillaries
capillary_thickness : float
thickness of the capillaries (m)
capillary_outer_d : float, optional if capillary_spacing is given
diameter of the capillaries including the wall thickness(m). The core together with the microstructure has a diameter of 2R + 2d
capillary_spacing : float, optional if capillary_outer_d is given
spacing between capillaries (m)
capillary_resonance_strengths : list or tuple, optional
strengths of the resonance lines. default : []
capillary_nested : int, optional
number of levels of nested capillaries. default : 0
Returns
-------
n_eff : ndarray, shape (n, )
the effective refractive index as function of wavelength
Reference
----------
Hasan, Md Imran, Nail Akhmediev, and Wonkeun Chang. "Empirical formulae for dispersion and effective mode area in hollow-core antiresonant fibers." Journal of Lightwave Technology 36.18 (2018): 4060-4065.
"""
u = u_nm(1, 1)
if capillary_spacing is None:
capillary_spacing = HCARF_gap(core_radius, capillary_num, capillary_outer_d)
elif capillary_outer_d is None:
capillary_outer_d = (2 * core_radius * np.sin(pi / capillary_num) - capillary_spacing) / (
1 - np.sin(pi / capillary_num)
)
Rg = core_radius / capillary_spacing
f1 = 1.095 * np.exp(0.097041 / Rg)
f2 = 0.007584 * capillary_num * np.exp(0.76246 / Rg) - capillary_num * 0.002 + 0.012
if capillary_nested > 0:
f2 += 0.0045 * np.exp(-4.1589 / (capillary_nested * Rg))
R_eff = f1 * core_radius * (1 - f2 * lambda_ ** 2 / (core_radius * capillary_thickness))
n_eff_2 = n_gas_2 - (u * lambda_ / (2 * pi * R_eff)) ** 2
chi_sil = mat.sellmeier(lambda_, io.load_material_dico("silica"))
with np.errstate(divide="ignore", invalid="ignore"):
for m, strength in enumerate(capillary_resonance_strengths):
n_eff_2 += (
strength
* lambda_ ** 2
/ (lambda_ ** 2 - chi_sil * (2 * capillary_thickness / (m + 1)) ** 2)
)
return np.sqrt(n_eff_2)
def A_eff_hasan(core_radius, capillary_num, capillary_spacing):
"""computed the effective mode area
Parameters
----------
core_radius : float
radius of the core (m) (from cented to edge of a capillary)
capillary_num : int
number of capillaries
capillary_spacing : float
spacing between capillaries (m)
Returns
-------
A_eff : float
"""
M_f = 1.5 / (1 - 0.5 * np.exp(-0.245 * capillary_num))
return M_f * core_radius ** 2 * np.exp((capillary_spacing / 22e-6) ** 2.5)
def HCPCF_find_with_given_ZDW(
varying,
target,
search_range,
material_dico,
model="marcatili",
model_params={},
pressure=None,
temperature=None,
ideal=False,
):
"""finds the parameters (pressure or temperature) to yield the target ZDW. assign the string value 'vary' to the parameter
Parameters
----------
varying : str {"pressure", "temperature"}
which parameter to vary
target : float
the ZDW target, in m
search_range : array, shape (2,)
(min, max) of the search range
other parameters : see HCPCF_dispersion. Pressure or temperature is used as initial value if it is varying
Returns
-------
the parameter that satisfies the ZDW
"""
from scipy import optimize
l_search = [120e-9, 6000e-9]
#
fixed = [material_dico, model, model_params, ideal]
if varying == "pressure":
fixed.append(temperature)
x0 = 1e5 if pressure is None else pressure
def zdw(x, *args):
current_ZDW = HCPF_ZDW(
l_search,
args[0],
model=args[1],
model_params=args[2],
pressure=x,
temperature=args[4],
ideal=args[3],
)
out = current_ZDW - target
return out
elif varying == "temperature":
fixed.append(pressure)
x0 = 273.15 if temperature is None else temperature
def zdw(x, *args):
current_ZDW = HCPF_ZDW(
l_search,
args[0],
model=args[1],
model_params=args[2],
pressure=args[4],
temperature=x,
ideal=args[3],
)
out = current_ZDW - target
return out
else:
raise AttributeError(f"'varying' arg must be 'pressure' or 'temperature', not {varying}")
optimized = optimize.root_scalar(
zdw, x0=x0, args=tuple(fixed), method="brentq", bracket=search_range
)
return optimized.root
def HCPF_ZDW(
search_range,
material_dico,
model="marcatili",
model_params={},
pressure=None,
temperature=None,
ideal=False,
max_iter=10,
threshold=1e-36,
):
"""finds one Zero Dispersion Wavelength (ZDW) of a given HC-PCF fiber
Parameters
----------
see HCPCF_dispersion for description of most arguments
max_iter : float
How many iterations are allowed at most to reach the threashold
threshold : float
upper bound of what counts as beta2 == 0 (in si units)
Returns
-------
float:
the ZDW in m
"""
prev_find = np.inf
l = np.linspace(*search_range, 50)
core_radius = model_params["core_radius"]
zdw_ind = 0
for i in range(max_iter):
beta2 = HCPCF_dispersion(
l,
material_dico,
model=model,
model_params=model_params,
pressure=pressure,
temperature=temperature,
ideal=ideal,
)
zdw_ind = argclosest(beta2, 0)
if beta2[zdw_ind] < threshold:
break
elif beta2[zdw_ind] < prev_find:
l = np.linspace(
l[zdw_ind] - (100 / (i + 1)) * 1e-9, l[zdw_ind] + (100 / (i + 1)) * 1e-9, 50
)
prev_find = beta2[zdw_ind]
else:
raise RuntimeError(
f"Could not find a ZDW with parameters {1e6*core_radius} um, {1e-5 * pressure} bar, {temperature} K."
)
else:
print(f"Could not get to threshold in {max_iter} iterations")
return l[zdw_ind]
def beta2(w, n_eff):
"""computes the dispersion parameter beta2 according to the effective refractive index of the fiber and the frequency range
Parameters
----------
w : ndarray, shape (n, )
angular frequencies over which to calculate the dispersion
n_eff : ndarray_ shape (n, )
effective refractive index of the fiber computed with one of the n_eff_* methods
Returns
-------
beta2 : ndarray, shape (n, )
"""
return np.gradient(np.gradient(n_eff * w / c, w), w)
def HCPCF_dispersion(
lambda_,
material_dico=None,
model="marcatili",
model_params={},
pressure=None,
temperature=None,
ideal=False,
):
"""returns the dispersion profile (beta_2) of a hollow-core photonic crystal fiber.
Parameters
----------
lambda_ : ndarray, shape (n, )
wavelengths over which to calculate the dispersion
material_dico : dict
material dictionary respecting standard format explained in FIXME
model : string {"marcatili", "marcatili_adjusted", "hasan"}
which model of effective refractive index to use
model_params : tuple
to be cast to the function in charge of computing the effective index of the fiber. Every n_eff_* function has a signature
n_eff_(lambda_, n_gas_2, radius, *args) and model_params corresponds to args
temperature : float
Temperature of the material
pressure : float
constant pressure
FIXME tupple : a pressure gradient from pressure[0] to pressure[1] is computed
Returns
-------
out : 1D array
beta2 as function of wavelength
"""
w = units.m(lambda_)
if material_dico is None:
n_gas_2 = np.ones_like(lambda_)
else:
if ideal:
n_gas_2 = mat.sellmeier(lambda_, material_dico, pressure, temperature) + 1
else:
N_1 = mat.number_density_van_der_waals(
pressure=pressure, temperature=temperature, material_dico=material_dico
)
N_0 = mat.number_density_van_der_waals(material_dico=material_dico)
n_gas_2 = mat.sellmeier(lambda_, material_dico) * N_1 / N_0 + 1
n_eff_func = dict(
marcatili=n_eff_marcatili, marcatili_adjusted=n_eff_marcatili_adjusted, hasan=n_eff_hasan
)[model]
n_eff = n_eff_func(lambda_, n_gas_2, **model_params)
return beta2(w, n_eff)
def dynamic_HCPCF_dispersion(lambda_, params, material_dico, deg):
"""returns functions for beta2 coefficients and gamma instead of static values
Parameters
----------
lambda_ : wavelength array
params : dict
flattened parameter dictionary
material_dico : dict
material dictionary (see README for details)
Returns
-------
beta2_coef : func(r), r is the relative position in the fiber
a function that returns an array of coefficients as function of the relative position in the fiber
to be used in disp_op
gamma : func(r), r is the relative position in the fiber
a function that returns a float corresponding to the nonlinear parameter at the relative position
in the fiber
"""
# store values because storing functions acts weird with dict
pressure_values = params["pressure"]
a = params["core_radius"]
fiber_model = params["fiber_model"]
model_params = {k: params[k] for k in hc_model_specific_parameters[fiber_model]}
temp = params["temperature"]
ideal_gas = params["ideal_gas"]
w0 = params["w0"]
interp_range = params["interp_range"]
A_eff = 1.5 * a ** 2
# defining function instead of storing every possilble value
pressure = lambda r: mat.pressure_from_gradient(r, *pressure_values)
beta2 = lambda r: HCPCF_dispersion(
lambda_, a, material_dico, fiber_model, model_params, pressure(r), temp, ideal_gas
)
n2 = lambda r: mat.non_linear_refractive_index(material_dico, pressure(r), temp)
ratio_range = np.linspace(0, 1, 256)
gamma_grid = np.array([n2(r) * w0 / (A_eff * c) for r in ratio_range])
gamma_interp = interp1d(ratio_range, gamma_grid)
beta2_grid = np.array(
[dispersion_coefficients(lambda_, beta2(r), w0, interp_range, deg) for r in ratio_range]
)
beta2_interp = [
interp1d(ratio_range, beta2_grid[:, i], assume_sorted=True) for i in range(deg + 1)
]
def beta2_func(r):
return [beta2_interp[i](r)[()] for i in range(deg + 1)]
def gamma_func(r):
return gamma_interp(r)[()]
return beta2_func, gamma_func
def PCF_dispersion(lambda_, pitch, ratio_d, w0=None):
"""
semi-analytical computation of the dispersion profile of a triangular Index-guiding PCF
Parameters
----------
lambda_ : 1D array-like
wavelengths over which to calculate the dispersion
pitch : float
distance between air holes in m
ratio_d : float
ratio diameter of hole / pitch
w0 : float, optional
pump angular frequency. If given, the gamma value is also returned in adition to the GVD. default : None
Returns
-------
beta2 : 1D array
Dispersion parameter as function of wavelength
gamma : float
non-linear coefficient
Reference
---------
Formulas and values are from Saitoh K and Koshiba M, "Empirical relations for simple design of photonic crystal fibers" (2005)
"""
# Check validity
if ratio_d < 0.2 or ratio_d > 0.8:
print("WARNING : Fitted formula valid only for pitch ratio between 0.2 and 0.8")
n_co = 1.45
a_eff = pitch / np.sqrt(3)
pi2a = 2 * pi * a_eff
ratio_l = lambda_ / pitch
# Table 1 and 2 in Saitoh2005
ai0 = np.array([0.54808, 0.71041, 0.16904, -1.52736])
ai1 = np.array([5.00401, 9.73491, 1.85765, 1.06745])
ai2 = np.array([-10.43248, 47.41496, 18.96849, 1.93229])
ai3 = np.array([8.22992, -437.50962, -42.4318, 3.89])
bi1 = np.array([5, 1.8, 1.7, -0.84])
bi2 = np.array([7, 7.32, 10, 1.02])
bi3 = np.array([9, 22.8, 14, 13.4])
ci0 = np.array([-0.0973, 0.53193, 0.24876, 5.29801])
ci1 = np.array([-16.70566, 6.70858, 2.72423, 0.05142])
ci2 = np.array([67.13845, 52.04855, 13.28649, -5.18302])
ci3 = np.array([-50.25518, -540.66947, -36.80372, 2.7641])
di1 = np.array([7, 1.49, 3.85, -2])
di2 = np.array([9, 6.58, 10, 0.41])
di3 = np.array([10, 24.8, 15, 6])
A = ai0 + ai1 * ratio_d ** bi1 + ai2 * ratio_d ** bi2 + ai3 * ratio_d ** bi3
B = ci0 + ci1 * ratio_d ** di1 + ci2 * ratio_d ** di2 + ci3 * ratio_d ** di3
V = A[0] + A[1] / (1 + A[2] * np.exp(A[3] * ratio_l))
W = B[0] + B[1] / (1 + B[2] * np.exp(B[3] * ratio_l))
n_FSM2 = 1.45 ** 2 - (lambda_ * V / (pi2a)) ** 2
n_eff2 = (lambda_ * W / (pi2a)) ** 2 + n_FSM2
n_eff = np.sqrt(n_eff2)
D_wave_guide = dispersion_parameter(n_eff, lambda_)
material_dico = io.load_material_dico("silica")
chi_mat = mat.sellmeier(lambda_, material_dico)
D_mat = dispersion_parameter(np.sqrt(chi_mat + 1), lambda_)
# material index of refraction (Sellmeier formula)
D = D_wave_guide + D_mat
beta2 = D_to_beta2(D, lambda_)
if w0 is None:
return beta2
else:
# effective mode field area (koshiba2004)
V_eff = pi2a / lambda_ * np.sqrt(n_co ** 2 - n_FSM2)
w_eff = a_eff * (0.65 + 1.619 / V_eff ** 1.5 + 2.879 / V_eff ** 6)
A_eff = interp1d(lambda_, w_eff, kind="linear")(units.m.inv(w0)) ** 2 * pi
n2 = 2.6e-20 # FIXME
gamma = n2 * w0 / (A_eff * c)
return beta2, gamma
def dispersion_central(fiber_model, params, deg=8):
"""dispatch function depending on what type of fiber is used
Parameters
----------
fiber_model : str {"PCF", "HCPCF"}
describes the type of fiber
- PCF : triangular Index-guiding photonic crystal fiber
- HCPCF : hollow core fiber (filled with gas, or not)
params : dict
parameter dictionary as in `parameters.toml`
Returns
-------
beta2_coef : 1D array of size deg
beta coefficients to be used in disp_op
gamma : float
nonlinear parameter
"""
lambda_ = lambda_for_dispersion()
beta2 = np.zeros_like(lambda_)
fiber_model = fiber_model.lower()
if fiber_model == "pcf":
beta2, gamma = PCF_dispersion(
lambda_,
params["pitch"],
params["pitch_ratio"],
w0=params["w0"],
)
else:
# Load material info
gas_name = params["gas_name"]
if gas_name == "vacuum":
material_dico = None
else:
material_dico = toml.loads(io.Paths.gets("gas"))[gas_name]
# compute dispersion
if params.get("dynamic_dispersion", False):
return dynamic_HCPCF_dispersion(lambda_, params, material_dico, deg)
else:
# actually compute the dispersion
beta2 = HCPCF_dispersion(
lambda_,
material_dico,
fiber_model,
{k: params[k] for k in hc_model_specific_parameters[fiber_model]},
params["pressure"],
params["temperature"],
params["ideal_gas"],
)
if material_dico is not None:
A_eff = 1.5 * params["core_radius"] ** 2
n2 = mat.non_linear_refractive_index(
material_dico, params["pressure"], params["temperature"]
)
gamma = n2 * params["w0"] / (A_eff * c)
else:
gamma = 0
# add plasma if wanted
if params["plasma_density"] > 0:
beta2 += plasma_dispersion(lambda_, params["plasma_density"])
beta2_coef = dispersion_coefficients(lambda_, beta2, params["w0"], params["interp_range"], deg)
return beta2_coef, gamma
def dispersion_coefficients(lambda_, beta2, w0, interp_range=None, deg=8):
"""Computes the taylor expansion of beta2 to be used in dispersion_op
Parameters
----------
lambda_ : 1D array
wavelength
beta2 : 1D array
beta2 as function of lambda_
w0 : float
pump angular frequency
interp_range : slice-like
index-style specifying wl range over which to fit to get beta2 coefficients
deg : int
degree of polynomial fit. Will return deg+1 coefficients
Returns
-------
beta2_coef : 1D array
Taylor coefficients in decreasing order
"""
if interp_range is None:
r = slice(2, -2)
else:
# 2 discrete gradients are computed before getting to
# beta2, so we need to make sure coefficients are not affected
# by edge effects
r = (lambda_ > max(lambda_[2], interp_range[0])) & (
lambda_ < min(lambda_[-2], interp_range[1])
)
# we get the beta2 Taylor coeffiecients by making a fit around w0
w_c = units.m(lambda_) - w0
fit = Chebyshev.fit(w_c[r], beta2[r], deg)
beta2_coef = cheb2poly(fit.convert().coef) * np.cumprod([1] + list(range(1, deg + 1)))
return beta2_coef
def delayed_raman_t(t, raman_type="stolen"):
"""
computes the unnormalized temporal Raman response function applied to the array t
Parameters
----------
t : 1D array
time in the co-moving frame of reference
raman_type : str {"stolen", "agrawal", "measured"}
indicates what type of Raman effect modelization to use
default : "stolen"
Returns
-------
hr_arr : 1D array
temporal response function
"""
tau1 = 12.2e-15
tau2 = 32e-15
t_ = t - t[0]
t = t_
if raman_type == "stolen":
hr_arr = (tau1 / tau2 ** 2 + 1 / tau1) * np.exp(-t_ / tau2) * np.sin(t_ / tau1)
elif raman_type == "agrawal":
taub = 96e-15
h_a = (tau1 / tau2 ** 2 + 1 / tau1) * np.exp(-t_ / tau2) * np.sin(t_ / tau1)
h_b = (2 * taub - t_) / taub ** 2 * np.exp(-t_ / taub)
hr_arr = 0.79 * h_a + 0.21 * h_b
elif raman_type == "measured":
try:
path = io.Paths.get("hr_t")
loaded = np.load(path)
except FileNotFoundError:
print(
f"Not able to find the measured Raman response function. Going with agrawal model"
)
return delayed_raman_t(t, raman_type="agrawal")
t_stored, hr_arr_stored = loaded["t"], loaded["hr_arr"]
hr_arr = interp1d(t_stored, hr_arr_stored, bounds_error=False, fill_value=0)(t)
else:
print("invalid raman response function, aborting")
quit()
return hr_arr
def delayed_raman_w(t, dt, raman_type="stolen"):
"""returns the delayed raman response function as function of w
see delayed_raman_t for detailes"""
return fft(delayed_raman_t(t, raman_type)) * dt
def create_non_linear_op(behaviors, w_c, w0, gamma, raman_type="stolen", f_r=None, hr_w=None):
"""
Creates a non-linear operator with the desired features
Parameters
----------
behaviors : list of str
behaviors wanted
w_c : 1d array
symetric frequency array generated by scgenerator.initialize.wspace
w0 : float
pump angular frenquency
gamma : float
nonlinear parameter
raman_type : str, optional
name of the raman response function model. default : "stolen"
hr_w : 1d array, optional unless "raman" in behaviors
pre-calculated frequency-dependent delayed raman response function
f_r : float, optional
overwrite fractional contribution of the delayed raman effect. default : None
returns
-------
func
a function to be passed to RK4IP which takes a spectrum as input and returns
a new spectrum modified with the non-linear interactions.
"""
# Compute raman response function if necessary
if "raman" in behaviors:
if "hr_w" == None:
raise TypeError("freq-dependent Raman response must be give")
else:
if f_r is None:
if raman_type in ["stolen", "measured"]:
f_r = 0.18
elif raman_type == "agrawal":
f_r = 0.245
# Define the non linear operator
def N_func(spectrum, r=0):
field = ifft(spectrum)
ss_part = w_c / w0 if "ss" in behaviors else 0
spm_part = (1 - f_r) * abs2(field) if "spm" in behaviors else 0
raman_part = f_r * ifft(hr_w * fft(abs2(field))) if "raman" in behaviors else 0
raman_noise_part = 1j * 0
if isinstance(gamma, (float, int)):
return (
-1j
* gamma
* (1 + ss_part)
* fft(field * (spm_part + raman_part) + raman_noise_part)
)
else:
return (
-1j
* gamma(r)
* (1 + ss_part)
* fft(field * (spm_part + raman_part) + raman_noise_part)
)
return N_func
def fast_dispersion_op(w_c, beta_arr, power_fact, where=slice(None)):
"""
dispersive operator
Parameters
----------
w_c : 1d array
angular frequencies centered around 0
beta_arr : 1d array
beta coefficients returned by scgenerator.physics.fiber.dispersion_coefficients
power_fact : list of arrays of len == len(w_c)
precomputed values for w_c^k / k!
where : slice-like
indices over which to apply the operator, otherwise 0
Returns
-------
array of len == len(w_c)
dispersive component
"""
dispersion = np.zeros_like(w_c)
for k, beta in reversed(list(enumerate(beta_arr))):
dispersion = dispersion + beta * power_fact[k]
out = np.zeros_like(dispersion)
out[where] = dispersion[where]
return -1j * out
def dispersion_op(w_c, beta_arr, where=None):
"""
dispersive operator
Parameters
----------
w_c : 1d array
angular frequencies centered around 0
beta_arr : 1d array
beta coefficients returned by scgenerator.physics.fiber.dispersion_coefficients
where : indices over which to apply the operatory, otherwise 0
Returns
-------
disp_arr : dispersive component as an array of len = len(w_c)
"""
dispersion = np.zeros_like(w_c)
for k, beta in reversed(list(enumerate(beta_arr))):
dispersion = dispersion + beta * power_fact(w_c, k + 2)
out = np.zeros_like(dispersion)
out[where] = dispersion[where]
return -1j * out
def _get_radius(radius_param, lambda_=None):
if isinstance(radius_param, tuple) and lambda_ is not None:
return effective_core_radius(lambda_, *radius_param)
else:
return radius_param
def effective_core_radius(lambda_, core_radius, s=0.08, h=200e-9):
"""return the variable core radius according to Eq. S2.2 from Köttig2017
Parameters
----------
lambda_ : ndarray, shape (n, )
array of wl over which to calculate the effective core radius
core_radius : float
physical core radius in m
s : float
s parameter from the equation S2.2
h : float
wall thickness in m
Returns
-------
effective_core_radius : ndarray, shape (n, )
"""
return core_radius / (1 + s * lambda_ ** 2 / (core_radius * h))
def effective_radius_HCARF(core_radius, t, f1, f2, lambda_):
"""eq. 3 in Hasan 2018"""
return f1 * core_radius * (1 - f2 * lambda_ ** 2 / (core_radius * t))

View File

@@ -0,0 +1,167 @@
import numpy as np
from .. import state
from . import units
from .units import NA, c, kB
def pressure_from_gradient(ratio, p0, p1):
"""returns the pressure as function of distance with eq. 20 in Markos et al. (2017)
Parameters
----------
ratio : relative position in the fiber (0 = start, 1 = end)
p0 : pressure at the start
p1 : pressure at the end
Returns
----------
the pressure (float)
"""
return np.sqrt(p0 ** 2 - ratio * (p0 ** 2 - p1 ** 2))
def number_density_van_der_waals(
a=None, b=None, pressure=None, temperature=None, material_dico=None
):
"""returns the number density of a gas
Parameters
----------
P : pressure
T : temperature
for pressure and temperature, the default
a : Van der Waals a coefficient
b : Van der Waals b coefficient
material_dico : optional. If passed, will compute the number density at given reference values found in material_dico
Returns
----------
the numbers density (/m^3)
Raises
----------
ValueError : Since the Van der Waals equation is a cubic one, there could be more than one real, positive solution
"""
if pressure == 0:
return 0
if material_dico is not None:
a = material_dico.get("a", 0) if a is None else a
b = material_dico.get("b", 0) if b is None else b
pressure = material_dico["sellmeier"].get("P0", 101325) if pressure is None else pressure
temperature = (
material_dico["sellmeier"].get("t0", 273.15) if temperature is None else temperature
)
else:
a = 0 if a is None else a
b = 0 if b is None else b
pressure = 101325 if pressure is None else pressure
temperature = 273.15 if temperature is None else temperature
ap = a / NA ** 2
bp = b / NA
# setup van der Waals equation for the number density
p3 = -ap * bp
p2 = ap
p1 = -(pressure * bp + kB * temperature)
p0 = pressure
# filter out unwanted matches
roots = np.roots([p3, p2, p1, p0])
roots = roots[np.isreal(roots)].real
roots = roots[roots > 0]
if len(roots) != 1:
s = f"Van der Waals eq with parameters P={pressure}, T={temperature}, a={a}, b={b}"
s += f"\nThere is more than one possible number density : {roots}."
s += f"\n{np.min(roots)} was returned"
state.CurrentLogger.log(s)
return np.min(roots)
def sellmeier(lambda_, material_dico, pressure=None, temperature=None):
"""reads a file containing the Sellmeier values corresponding to the choses material and returns the real susceptibility
pressure and temperature adjustments are made according to ideal gas law.
Parameters
----------
lambda_ : wl vector over which to compute the refractive index
material_dico : material dictionary as explained in scgenerator.io.load_material_dico
pressure : pressure in mbar if material is a gas. Can be a constant or a tupple if a presure gradient is considered
temperature : temperature of the gas in Kelvin
Returns
----------
an array n(lambda_)^2 - 1
"""
WL_THRESHOLD = 8.285e-6
temp_l = lambda_[lambda_ < WL_THRESHOLD]
kind = 1
B = material_dico["sellmeier"]["B"]
C = material_dico["sellmeier"]["C"]
const = material_dico["sellmeier"].get("const", 0)
P0 = material_dico["sellmeier"].get("P0", 1e5)
t0 = material_dico["sellmeier"].get("t0", 273.15)
kind = material_dico["sellmeier"].get("kind", 1)
# Sellmeier equation
chi = np.zeros_like(lambda_) # = n^2 - 1
if kind == 1:
for b, c in zip(B, C):
chi[lambda_ < WL_THRESHOLD] += temp_l ** 2 * b / (temp_l ** 2 - c)
elif kind == 2: # gives n-1
for b, c in zip(B, C):
chi[lambda_ < WL_THRESHOLD] += b / (c - 1 / temp_l ** 2)
chi += const
chi = (chi + 1) ** 2 - 1
else:
raise ValueError(f"kind {kind} is not recognized.")
if temperature is not None:
chi *= t0 / temperature
if pressure is not None:
chi *= pressure / P0
return chi
def delta_gas(w, material_dico):
"""returns the value delta_t (eq. 24 in Markos(2017))
Parameters
----------
w : angular frequency array
material_dico : material dictionary as explained in scgenerator.io.load_material_dico
Returns
----------
delta_t
since 2 gradients are computed, it is recommended to exclude the 2 extremum values
"""
chi = sellmeier(units.m.inv(w), material_dico)
N0 = number_density_van_der_waals(material_dico=material_dico)
dchi_dw = np.gradient(chi, w)
return 1 / (N0 * c) * (dchi_dw + w / 2 * np.gradient(dchi_dw, w))
def non_linear_refractive_index(material_dico, pressure=None, temperature=None):
"""returns the non linear refractive index n2 adjusted for pressure and temperature
NOTE : so far, there is no adjustment made for wavelength
Parameters
----------
lambda_ : wavelength array
material_dico :
pressure : pressure in Pa
temperature : temperature in Kelvin
Returns
----------
n2
"""
n2_ref = material_dico["kerr"]["n2"]
# if pressure and/or temperature are specified, adjustment is made according to number density ratio
if pressure is not None or temperature is not None:
N0 = number_density_van_der_waals(material_dico=material_dico)
N = number_density_van_der_waals(
pressure=pressure, temperature=temperature, material_dico=material_dico
)
ratio = N / N0
else:
ratio = 1
return ratio * n2_ref

View File

@@ -0,0 +1,795 @@
"""
This files incluedes funcitons used by the scgenerator module to compute properties of pulses.
This include computing initial pulse shape and pulse noise as well as transforming the pulse
or measuring its properties.
NOTE
the term `sc-ordering` is used throughout this module. An array that follows sc-ordering is
of shape `([what, ever,] n, nt)` (could be just `(n, nt)` for 2D sc-ordered array) such that
n is the number of spectra at the same z position and nt is the size of the time/frequency grid
"""
import itertools
import os
import matplotlib.pyplot as plt
import numpy as np
from numpy import pi
from numpy.fft import fft, fftshift, ifft
from scipy.interpolate import UnivariateSpline
from .. import state
from ..io import plot_setup
from ..math import *
c = 299792458.0
hbar = 1.05457148e-34
#
fwhm_to_T0_fac = dict(
sech=1 / (2 * np.log(1 + np.sqrt(2))),
gaussian=1 / (np.sqrt(2 * np.log(2))),
)
"""relates the fwhm of the intensity profile (amplitue^2) to the t0 parameter of the amplitude"""
P0T0_to_E0_fac = dict(
sech=2, # int(a * sech(x / b)^2 * dx) from -inf to inf = 2 * a * b
gaussian=np.sqrt(pi / 2), # int(a * exp(-(x/b)^2)^2 * dx) from -inf to inf = sqrt(pi/2) * a * b
)
"""relates the total energy (amplitue^2) to the t0 parameter of the amplitude and the peak intensity (peak_amplitude^2)"""
def initial_field(t, shape, t0, power):
"""returns the initial field
Parameters
----------
t : 1d array
time array
shape : str {"gaussian", "sech"}
shape of the pulse
t0 : float
time parameters. Can be obtained by dividing the FWHM by
`scgenerator.physics.pulse.fwhm_to_T0_fac[shape]`
power : float
peak power
Returns
-------
1d array
field array
Raises
------
ValueError
raised when shape is not recognized
"""
if shape == "gaussian":
return gauss_pulse(t, t0, power)
elif shape == "sech":
return sech_pulse(t, t0, power)
else:
raise ValueError(f"shape '{shape}' not understood")
def conform_pulse_params(
shape,
width=None,
t0=None,
power=None,
energy=None,
soliton_num=None,
gamma=None,
beta2=None,
):
"""makes sure all parameters of the pulse are set and consistent
Parameters
----------
shape : str {"gaussian", "sech"}
shape of the pulse
width : float, optional
fwhm of the intensity pulse, by default None
t0 : float, optional
time parameter of the amplitude pulse, by default None
power : float, optional
peak power, by default None
energy : float, optional
total energy of the pulse, by default None
soliton_num : float, optional
soliton number, by default None
gamma : float, optional
nonlinear parameter, by default None
beta2 : float, optional
second order dispersion coefficient, by default None
if more parameters than required are specified, the order of precedence
indicated by the order in which the parameters are enumerated below holds,
meaning the superflous parameters will be overwritten.
choose one of the possible combinations :
1 of (width, t0), 1 of (power, energy), gamma and beta2 together optional (not one without the other)
soliton_num, gamma, 1 of (width, power, energy, t0)
examples :
specify width, power and energy -> t0 and energy will be computed
specify soliton_num, gamma, power, t0 -> width, t0 and energy will be computed
Returns
-------
width, t0, power, energy
when no gamma is specified
width, t0, power, energy, soliton_num
when gamma is specified
Raises
------
TypeError
[description]
"""
if gamma is not None and beta2 is None or beta2 is not None and gamma is None:
raise TypeError("when soliton number is desired, both gamma and beta2 must be specified")
if soliton_num is not None:
if gamma is None:
raise TypeError("gamma must be specified when soliton_num is")
if width is not None:
power = soliton_num ** 2 * abs(beta2) / (gamma * t0 ** 2)
elif power is not None:
t0 = np.sqrt(soliton_num ** 2 * abs(beta2) / (power * gamma))
elif energy is not None:
t0 = P0T0_to_E0_fac[shape] * soliton_num ** 2 * abs(beta2) / (energy * gamma)
elif t0 is not None:
width = t0 / fwhm_to_T0_fac[shape]
power = soliton_num ** 2 * abs(beta2) / (gamma * t0 ** 2)
else:
raise TypeError("not enough parameters to determine pulse")
if width is not None:
t0 = width * fwhm_to_T0_fac[shape]
else:
width = t0 / fwhm_to_T0_fac[shape]
if power is not None:
energy = P0_to_E0(power, t0, shape)
else:
power = E0_to_P0(energy, t0, shape)
if gamma is None:
return width, t0, power, energy
else:
if soliton_num is None:
soliton_num = np.sqrt(power * gamma * t0 ** 2 / abs(beta2))
return width, t0, power, energy, soliton_num
def E0_to_P0(E0, t0, shape="gaussian"):
"""convert an initial total pulse energy to a pulse peak power"""
return E0 / (t0 * P0T0_to_E0_fac[shape])
def P0_to_E0(P0, t0, shape="gaussian"):
"""converts initial peak power to pulse energy"""
return P0 * t0 * P0T0_to_E0_fac[shape]
def sech_pulse(t, t0, P0, offset=0):
return np.sqrt(P0) / np.cosh((t - offset) / t0)
def gauss_pulse(t, t0, P0, offset=0):
return np.sqrt(P0) * np.exp(-(((t - offset) / t0) ** 2))
def photon_number(spectrum, w, dw, gamma):
return np.sum(1 / gamma * abs2(spectrum) / w * dw)
def pulse_energy(spectrum, w, dw, _):
return np.sum(abs2(spectrum) * dw)
def technical_noise(rms_noise, relative_factor=0.4):
"""
To implement technical noise as described in Grenier2019, we need to know the
noise properties of the laser, summarized into the RMS amplitude noise
Parameters
----------
rms_noise : float
RMS amplitude noise of the laser
relative factor : float
magnitude of the anticorrelation between power and pulse width noise
Returns
----------
delta_int : float
delta_T0 : float
"""
psy = np.random.normal(1, rms_noise)
return psy, 1 - relative_factor * (psy - 1)
def shot_noise(w_c, w0, T, dt):
"""
Parameters
----------
w_c : 1D array
angular frequencies centered around 0
w0 : float
pump angular frequency
T : float
length of the time windows
dt : float
resolution of time grid
Returns
----------
out : 1D array of size len(w_c)
noise field to be added on top of initial field in time domain
"""
rand_phase = np.random.rand(len(w_c)) * 2 * pi
A_oppm = np.sqrt(hbar * (np.abs(w_c + w0)) * T) * np.exp(-1j * rand_phase)
out = ifft(A_oppm / dt * np.sqrt(2 * pi))
return out
def mean_phase(spectra):
"""computes the mean phase of spectra
Parameter
----------
spectra : 2D array
The mean is taken on the 0th axis. This means the array has to be of shape (n, nt)
Returns
----------
mean_phase : 1D array of shape (len(spectra[0]))
array of complex numbers of unit length representing the mean phase
Example
----------
>>> x = np.array([[1 + 1j, 0 + 2j, -3 - 1j],
[1 + 0j, 2 + 3j, -3 + 1j]])
>>> mean_phase(x)
array([ 0.92387953+0.38268343j, 0.28978415+0.95709203j, -1. +0.j ])
"""
total_phase = np.sum(
spectra / np.abs(spectra),
axis=0,
where=spectra != 0,
out=np.zeros(len(spectra[0]), dtype="complex"),
)
return (total_phase) / np.abs(total_phase)
def flatten_phase(spectra):
"""
takes the mean phase out of an array of complex numbers
Parameters
----------
spectra : 2D array of shape (n, nt)
spectra arranged in the same fashion as in `scgenerator.physics.pulse.mean_phase`
Returns
----------
output : array of same dimensions and amplitude, but with a flattened phase
"""
mean_theta = mean_phase(spectra)
tiled = np.tile(mean_theta, (len(spectra), 1))
output = spectra * np.conj(tiled)
return output
def compress_pulse(spectra):
"""given some complex spectrum, returns the compressed pulse in the time domain
Parameters
----------
spectra : ND array
spectra to compress. The shape must be at least 2D. Compression occurs along the -2th axis.
This means spectra have to be of shape ([what, ever,] n, nt) where n is the number of spectra
brought together for one compression operation and nt the resolution of the grid.
Returns
----------
out : array of shape ([what, ever,] nt)
compressed inverse Fourier-transformed pulse
"""
if spectra.ndim > 2:
return np.array([compress_pulse(spec) for spec in spectra])
else:
return fftshift(ifft(flatten_phase(spectra)), axes=1)
def ideal_compressed_pulse(spectra):
"""returns the ideal compressed pulse assuming flat phase
Parameters
----------
spectra : 2D array, sc-ordering
Returns
----------
compressed : 1D array
time envelope of the compressed field
"""
return abs2(fftshift(ifft(np.sqrt(np.mean(abs2(spectra), axis=0)))))
def spectrogram(time, values, t_res=256, t_win=24e-12, gate_width=200e-15, shift=False):
"""
returns the spectorgram of the field given in values
Parameters
----------
time : 1D array-like
time in the co-moving frame of reference
values : 1D array-like
field array that matches the time array
t_res : int, optional
how many "bins" the time array is subdivided into. Default : 256
t_win : float, optional
total time window (=length of time) over which the spectrogram is computed. Default : 24e-12
gate_width : float, optional
width of the gaussian gate function (=sqrt(2 log(2)) * FWHM). Default : 200e-15
Returns
----------
spec : 2D array
real 2D spectrogram
delays : 1D array of size t_res
new time axis
"""
t_lim = t_win / 2
delays = np.linspace(-t_lim, t_lim, t_res)
spec = np.zeros((t_res, len(time)))
for i, delay in enumerate(delays):
masked = values * np.exp(-(((time - delay) / gate_width) ** 2))
spec[i] = abs2(fft(masked))
if shift:
spec[i] = fftshift(spec[i])
return spec, delays
def g12(values):
"""
computes the first order coherence function of a ensemble of values
Parameters
----------
values : 2D array
complex values following sc-ordering
return:
g12_arr : coherence function as a n-D array
"""
# Create all the possible pairs of values
n = len(values)
field_pairs = itertools.combinations(values, 2)
corr = np.zeros_like(values[0])
for pair in field_pairs:
corr += pair[0].conj() * pair[1]
g12_arr = corr / (n * (n - 1) / 2 * np.mean(abs2(values), axis=0))
return np.abs(g12_arr)
def avg_g12(values):
"""
comptutes the average of the coherence function weighted by amplitude of spectrum
Parameters
----------
values : (m, n)-D array containing m complex values
Returns
----------
(float) average g12
"""
if len(values.shape) > 2:
pass
avg_values = np.mean(abs2(values), axis=0)
coherence = g12(values)
return np.sum(coherence * avg_values) / np.sum(avg_values)
def fwhm_ind(values, mam=None):
"""returns the indices where values is bigger than half its maximum
Parameters
----------
values : array
real values with ideally only one smooth peak
mam : tupple (float, int)
(maximum value, index of the maximum value)
Returns
----------
left_ind, right_ind : int
indices of the the left and right spots where values drops below 1/2 the maximum
"""
if mam is None:
m = np.max(values)
am = np.argmax(values)
else:
m, am = mam
left_ind = am - np.where(values[am::-1] < m / 2)[0][0]
right_ind = am + np.where(values[am:] < m / 2)[0][0]
return left_ind - 1, right_ind + 1
def peak_ind(values, mam=None):
"""returns the indices that encapsulate the entire peak
Parameters
----------
values : array
real values with ideally only one smooth peak
mam : tupple (float, int)
(maximum value, index of the maximum value)
Returns
----------
left_ind, right_ind : int
indices of the the left and right spots where values starts rising again, with a margin of 3
"""
if mam is None:
m = np.max(values)
am = np.argmax(values)
else:
m, am = mam
left_ind = (
am
- np.where((values[am:0:-1] - values[am - 1 :: -1] < 0) & (values[am:0:-1] < m / 2))[0][0]
)
right_ind = (
am + np.where((values[am:-1] - values[am + 1 :] < 0) & (values[am:-1] < m / 2))[0][0]
)
return left_ind - 3, right_ind + 3
def setup_splines(x_axis, values, mam=None):
"""sets up spline interpolation to better measure a peak. Different splines with different orders are
necessary because derivatives and second derivatives are computed to find extremea and inflection points
Parameters
----------
x_axis : 1D array
domain of values
values : 1D array
real values that ideally contain only one smooth peak to measure
mam : tupple (float, int)
(maximum value, index of the maximum value)
Returns
----------
small_spline : scipy.interpolate.UnivariateSpline
order 3 spline that interpolates `values - m/2` around the peak
spline_4 : scipy.interpolate.UnivariateSpline
order 4 spline that interpolate values around the peak
spline 5 : scipy.interpolate.UnivariateSpline
order 5 spline that interpolates values around the peak
d_spline : scipy.interpolate.UnivariateSpline
order 3 spline that interpolates the derivative of values around the peak
d_roots : list
roots of d_spline
dd_roots : list
inflection points of spline_5
l_ind, r_ind : int
return values of peak_ind
"""
# Isolate part thats roughly above max/2
l_ind_h, r_ind_h = fwhm_ind(values, mam)
l_ind, r_ind = peak_ind(values, mam)
if mam is None:
mm = np.max(values)
else:
mm, _ = mam
# Only roots of deg=3 splines can be computed, so we need 3 splines to find
# zeros, local extrema and inflection points
small_spline = UnivariateSpline(
x_axis[l_ind_h : r_ind_h + 1], values[l_ind_h : r_ind_h + 1] - mm / 2, k=3, s=0
)
spline_4 = UnivariateSpline(x_axis[l_ind : r_ind + 1], values[l_ind : r_ind + 1], k=4, s=0)
spline_5 = UnivariateSpline(x_axis[l_ind : r_ind + 1], values[l_ind : r_ind + 1], k=5, s=0)
d_spline = spline_4.derivative()
d_roots = spline_4.derivative().roots()
dd_roots = spline_5.derivative(2).roots()
return small_spline, spline_4, spline_5, d_spline, d_roots, dd_roots, l_ind, r_ind
def find_lobe_limits(x_axis, values, debug="", already_sorted=True):
"""find the limits of the centra lobe given 2 derivatives of the values and
the position of the FWHM
Parameters
----------
x_axis : 1D array
domain of values
values : 1D array
real values that present a peak whose properties we want to meausure
debug : str
if the peak is not distinct, a plot is made to assess the measurement
providing a debug label can help identify which plot correspond to which function call
sorted : bool
faster computation if arrays are already sorted
Returns
----------
peak_lim : 1D array (left_lim, right_lim, peak_pos)
values that delimit the left, right and maximum of the peak in units of x_axis
fwhm_pos : 1D array (left_pos, right_pos)
values corresponding to fwhm positions in units of x_axis
good_roots : 1D array
all candidate values that could delimit the peak position
spline_4 : scipy.interpolate.UnivariateSpline
order 4 spline that interpolate values around the peak
"""
if not already_sorted:
x_axis, values = x_axis.copy(), values.copy()
values = values[np.argsort(x_axis)]
x_axis.sort()
debug_str = f"debug : {debug}" if debug != "" else ""
small_spline, spline_4, spline_5, d_spline, d_roots, dd_roots, l_ind, r_ind = setup_splines(
x_axis, values
)
# get premliminary values for fwhm limits and peak limits
# if the peak is distinct, it should be sufficient
fwhm_pos = np.array(small_spline.roots())
peak_pos = d_roots[np.argmax(spline_4(d_roots))]
# if there are more than 2 fwhm position, a detailed analysis can help
# determining the true ones. If that fails, there is no meaningful peak to measure
detailed_measurement = len(fwhm_pos) > 2 or state._DEBUG.get(
"find_lobe_limits.always_plot", False
)
if detailed_measurement:
print("trouble measuring the peak.{}".format(debug_str))
(
spline_4,
d_spline,
d_roots,
dd_roots,
fwhm_pos,
peak_pos,
folder_name,
file_name,
fig,
ax,
color,
) = _detailed_find_lobe_limits(
x_axis,
values,
debug,
debug_str,
spline_4,
spline_5,
fwhm_pos,
peak_pos,
d_spline,
d_roots,
dd_roots,
l_ind,
r_ind,
)
good_roots, left_lim, right_lim = _select_roots(d_spline, d_roots, dd_roots, fwhm_pos)
ax.scatter(
[left_lim, right_lim],
spline_4([left_lim, right_lim]),
marker="|",
label="lobe pos",
c=color[5],
)
if not "find_lobe_limits.ax" in state._DEBUG:
ax.legend()
fig.savefig(os.path.join(folder_name, file_name), bbox_inches="tight")
plt.close(fig)
else:
good_roots, left_lim, right_lim = _select_roots(d_spline, d_roots, dd_roots, fwhm_pos)
return np.array([left_lim, right_lim, peak_pos]), fwhm_pos, np.array(good_roots), spline_4
def _select_roots(d_spline, d_roots, dd_roots, fwhm_pos):
"""selects the limits of a lobe
Parameters
----------
d_spline : scipy.interpolate.UnivariateSpline
spline of the first derivative of the lobe
d_roots : list
roots of the first derivarive (extrema of the original function)
dd_roots : list
roots of the second derivative (inflection points of the original function)
fwhm_pos : list
locations where the lobe is half of its maximum
Returns
-------
good_roots : list
valid roots
left_lim : list
location of the left limit
right_lim : list
location of the right limit
"""
# includes inflection points when slope is low (avoids considering the inflection points around fwhm limits)
all_roots = np.append(d_roots, dd_roots)
good_roots = all_roots[np.abs(d_spline(all_roots)) < np.max(d_spline(all_roots)) / 10]
try:
left_lim = np.max(good_roots[good_roots < np.min(fwhm_pos)])
except ValueError:
left_lim = np.min(good_roots)
try:
right_lim = np.min(good_roots[good_roots > np.max(fwhm_pos)])
except ValueError:
right_lim = np.max(good_roots)
return good_roots, left_lim, right_lim
def _detailed_find_lobe_limits(
x_axis,
values,
debug,
debug_str,
spline_4,
spline_5,
fwhm_pos,
peak_pos,
d_spline,
d_roots,
dd_roots,
l_ind,
r_ind,
):
left_pos = fwhm_pos[fwhm_pos < peak_pos]
right_pos = fwhm_pos[fwhm_pos > peak_pos]
iterations = 0
# spline maximum may not be on same peak as the original one. In this
# case it means that there is no distinct peak, but we try to
# compute everything again anyway. If spline inaccuracies lead to a cycle,
# we break it by choosing two values arbitrarily
while len(left_pos) == 0 or len(right_pos) == 0:
if iterations > 4:
left_pos, right_pos = [np.min(peak_pos)], [np.max(peak_pos)]
print(
"Cycle had to be broken. Peak measurement is probably wrong : {}".format(debug_str)
)
break
else:
iterations += 1
mam = (spline_4(peak_pos), argclosest(x_axis, peak_pos))
small_spline, spline_4, spline_5, d_spline, d_roots, dd_roots, l_ind, r_ind = setup_splines(
x_axis, values, mam
)
fwhm_pos = np.array(small_spline.roots())
peak_pos = d_roots[np.argmax(spline_4(d_roots))]
left_pos = fwhm_pos[fwhm_pos < peak_pos]
right_pos = fwhm_pos[fwhm_pos > peak_pos]
# if measurement of the peak is not straightforward, we plot the situation to see
# if the final measurement is good or not
folder_name, file_name, fig, ax = plot_setup(
file_name=f"it_{iterations}_{debug}", folder_name="measurements_errors_plots"
)
ax = state._DEBUG.get("find_lobe_limits.ax", ax)
plt.sca(ax)
state._DEBUG["x"] = x_axis
new_fwhm_pos = np.array([np.max(left_pos), np.min(right_pos)])
# PLOT
newx = np.linspace(*span(x_axis[l_ind : r_ind + 1]), 1000)
color = state._DEBUG.get("color", state.plot_default_color_cycle)
if state._DEBUG.get("find_lobe_limits.draw_raw_data", True):
ax.plot(x_axis[l_ind - 5 : r_ind + 6], values[l_ind - 5 : r_ind + 6], c=color[0])
ax.plot(newx, spline_5(newx), c=color[1])
ax.scatter(fwhm_pos, spline_4(fwhm_pos), marker="+", label="all fwhm", c=color[2])
ax.scatter(peak_pos, spline_4(peak_pos), marker=".", label="peak pos", c=color[3])
ax.scatter(new_fwhm_pos, spline_4(new_fwhm_pos), marker="_", label="2 chosen", c=color[4])
fwhm_pos = new_fwhm_pos
return (
spline_4,
d_spline,
d_roots,
dd_roots,
fwhm_pos,
peak_pos,
folder_name,
file_name,
fig,
ax,
color,
)
def measure_properties(spectra, t, compress=True, debug=""):
"""measure the quality factor, the fwhm variation, the peak power variation,
Parameters
----------
spectra : 2D array
set of n spectra in sc-ordering that differ only by noise
t : 1D array
time axis of the simulation
compress : bool, optional
whether to perform pulse compression. Default value is True, but this
should be set to False to measure the initial pulse as output by gaussian_pulse
or sech_pulse because compressing it would result in glitches and wrong measurements
Returns
----------
qf : float
quality factor of the pulse ensemble
mean_g12 : float
mean coherence of the spectra ensemble
fwhm_var : float
relative noise in temporal width of the compressed pulse
fwhm_abs : float
width of the mean compressed pulse
int_var : flaot
relative noise in the compressed pulse peak intensity
t_jitter : float
standard deviantion in absolute temporal peak position
"""
if compress:
fields = abs2(compress_pulse(spectra))
else:
print("Skipping compression")
fields = abs2(ifft(spectra))
field = np.mean(fields, axis=0)
ideal_field = abs2(fftshift(ifft(np.sqrt(np.mean(abs2(spectra), axis=0)))))
# Isolate whole central lobe of bof mean and ideal field
lobe_lim, fwhm_lim, _, big_spline = find_lobe_limits(t, field, debug)
lobe_lim_i, _, _, big_spline_i = find_lobe_limits(t, ideal_field, debug)
# Compute quality factor
energy_fraction = (big_spline.integral(*span(lobe_lim[:2]))) / np.trapz(field, x=t)
energy_fraction_i = (big_spline_i.integral(*span(lobe_lim_i[:2]))) / np.trapz(ideal_field, x=t)
qf = energy_fraction / energy_fraction_i
# Compute mean coherence
mean_g12 = avg_g12(spectra)
fwhm_abs = length(fwhm_lim)
# To compute amplitude and fwhm fluctuations, we need to measure every single peak
P0 = []
fwhm = []
t_offset = []
for f in fields:
lobe_lim, fwhm_lim, _, big_spline = find_lobe_limits(t, f, debug)
P0.append(big_spline(lobe_lim[2]))
fwhm.append(length(fwhm_lim))
t_offset.append(lobe_lim[2])
fwhm_var = np.std(fwhm) / np.mean(fwhm)
int_var = np.std(P0) / np.mean(P0)
t_jitter = np.std(t_offset)
return qf, mean_g12, fwhm_var, fwhm_abs, int_var, t_jitter

View File

@@ -0,0 +1,722 @@
import json
import os
from datetime import datetime
from typing import List
import numpy as np
from numpy.fft import fft, ifft
from .. import initialize
from .. import io, state
from .. import utilities
from ..io import generate_file_path, get_logger
from ..math import abs2
from ..utilities import ProgressTracker, format_varying_list
from . import pulse, units
from .fiber import create_non_linear_op, fast_dispersion_op
using_ray = False
try:
import ray
using_ray = True
except ModuleNotFoundError:
pass
class Simulations:
"""The recommended way to run simulations.
New Simulations child classes can be written and must implement the following
"""
_available_simulation_methods = []
def __init_subclass__(cls, available: bool, priority=0, **kwargs):
cls._available = available
if available:
Simulations._available_simulation_methods.append((cls, priority))
Simulations._available_simulation_methods.sort(key=lambda el: el[1])
super().__init_subclass__(**kwargs)
@classmethod
def get_best_method(cls):
return Simulations._available_simulation_methods[-1][0]
def __init__(self, param_seq: initialize.ParamSequence, task_id=0, data_folder="scgenerator/"):
"""
Parameters
----------
param_seq : scgenerator.initialize.ParamSequence obj
parameter sequence
task_id : int, optional
a unique id that identifies the simulation, by default 0
data_folder : str, optional
path to the folder where data is saved, by default "scgenerator/"
"""
self.logger = io.get_logger(__name__)
self.id = int(task_id)
self.param_seq = param_seq
self.name = param_seq.name
self.data_folder = io.get_data_folder(self.id, name_if_new=self.name)
io.save_toml(os.path.join(self.data_folder, "initial_config.toml"), self.param_seq.config)
self.using_ray = False
self.sim_jobs = 1
self.propagation_func = lambda params, varying_list: RK4IP(
params,
save_data=True,
job_identifier=utilities.format_varying_list(varying_list),
task_id=self.id,
)
self.progress_tracker = utilities.ProgressTracker(
max=len(self.param_seq),
auto_print=True,
percent_incr=1,
callback=lambda s, logger: logger.info(s),
)
def run(self):
for varying_params, params in self.param_seq:
for i in range(self.param_seq["simulation", "repeat"]):
varying = varying_params + [("num", i)]
io.save_parameters(
params,
io.generate_file_path(
"params.toml", self.id, utilities.format_varying_list(varying)
),
)
self.new_sim(varying, params.copy())
self.finish()
self.logger.info(f"Merging data...")
self.merge_data()
self.logger.info(f"Finished simulations from config {self.name} !")
def new_sim(self, varying_list: List[tuple], params: dict):
"""responsible to launch a new simulation
Parameters
----------
varying_list : list[tuple]
list of tuples (name, value) where name is the name of a
varying parameter and value is its current value
params : dict
a flattened parameter dictionary, as returned by scgenerator.initialize.compute_init_parameters
"""
raise NotImplementedError()
def finish(self):
"""called once all the simulations are launched."""
raise NotImplementedError()
def stop(self):
raise NotImplementedError()
def merge_data(self):
io.merge_data(self.data_folder)
class SequencialSimulations(Simulations, available=True, priority=0):
def new_sim(self, varying_list: List[tuple], params: dict):
self.logger.info(f"launching simulation with {varying_list}")
self.propagation_func(params, varying_list)
self.progress_tracker.update(1, [self.logger])
def finish(self):
pass
def stop(self):
pass
class RaySimulations(Simulations, available=using_ray, priority=1):
"""runs simulation with the help of the ray module. ray must be initialized before creating an instance of RaySimulations"""
def __init__(self, param_seq: initialize.ParamSequence, task_id=0, data_folder="scgenerator/"):
super().__init__(param_seq, task_id, data_folder)
self._init_ray()
def _init_ray(self):
nodes = ray.nodes()
nodes_num = len(nodes)
self.logger.info(
f"{nodes_num} node{'s' if nodes_num > 1 else ''} in the Ray cluster : "
+ str([node.get("NodeManagerHostname", "unknown") for node in nodes])
)
self.sim_jobs = min(self.param_seq.num_sim, self.param_seq["simulation", "parallel"])
self.propagation_func = ray.remote(self.propagation_func).options(
override_environment_variables=io.get_all_environ()
)
self.jobs = []
def new_sim(self, varying_list: List[tuple], params: dict):
if len(self.jobs) >= self.sim_jobs:
# wait for a slot to free before starting a new job
_, self.jobs = ray.wait(self.jobs)
ray.get(_)
self.progress_tracker.update(1, [self.logger])
self.jobs.append(self.propagation_func.remote(params, varying_list))
self.logger.info(f"launching simulation with {varying_list}, job : {self.jobs[-1].hex()}")
def finish(self):
for job in self.jobs:
ray.get(job)
self.progress_tracker.update(1, [self.logger])
def stop(self):
ray.shutdown()
def new_simulations(config_file: str, task_id: int, data_folder="scgenerator/"):
config = io.load_toml(config_file)
param_seq = initialize.ParamSequence(config)
if param_seq.num_sim > 1 and param_seq["simulation", "parallel"] > 1 and using_ray:
return Simulations.get_best_method()(param_seq, task_id, data_folder=data_folder)
else:
return SequencialSimulations(param_seq, task_id, data_folder=data_folder)
def RK4IP(sim_params, save_data=False, job_identifier="", task_id=0, n_percent=10):
"""Computes the spectrum of a pulse as it propagates through a PCF
Parameters
----------
sim_params : a dictionary containing the following :
w_c : array
angular frequencies centered around 0 generated with scgenerator.initialize.wspace
w0 : float
central angular frequency of the pulse
t : array
time
dt : float
time resolution
field_0 : array
initial field envelope as function of w_c
z_targets : list
target distances
beta : array
beta coeficients (Taylor expansion of beta(w))
gamma : float
non-linear parameter
behaviors : list(str {'ss', 'raman', 'spm'})
behaviors to include in the simulation given as a list of strings
raman_type : str, optional
type of raman modelisation if raman effect is present
f_r, hr_w : (opt) arguments of delayed_raman_t (see there for infos)
adapt_step_size : bool, optional
if True (default), adapts the step size with conserved quantity methode
error_ok : float
tolerated relative error for the adaptive step size if adaptive
step size is turned on, otherwise length of fixed steps in m
save_data : bool
False : return the spectra (recommended, save manually later if necessary)
True : save in a temporary folder and return the folder name
to be used for merging later
job_id : int
id of this particular simulation
param_id : int
id corresponding to the set of paramters. Files created with the same param_id will be
merged if an indexer is passed (this feature is mainly used for automated parallel simulations
using the parallel_simulations function).
task_id : int
id of the whole program (useful when many python instances run at once). None if not running in parallel
n_percent : int, float
log message every n_percent of the simulation done
pt : scgenerator.progresstracker.ProgressTracker object
indexer : indexer object
debug_return : bool
if True and save_data False, will return photon number and step sizes as well as the spectra.
Returns
----------
stored_spectra : (store_num, nt) array
spectrum aligned on w_c array
h_stored : 1D array
length of each valid step
cons_qty : 1D array
conserved quantity at each valid step
cons_qty_change : 1D array
conserved quantity change at each valid step
"""
# DEBUG
debug = False
w_c = sim_params.pop("w_c")
w0 = sim_params.pop("w0")
w_power_fact = sim_params.pop("w_power_fact")
field_0 = sim_params.pop("field_0")
z_targets = sim_params.pop("z_targets")
z_final = sim_params.pop("length")
beta = sim_params.pop("beta_func", sim_params.pop("beta"))
gamma = sim_params.pop("gamma_func", sim_params.pop("gamma"))
behaviors = sim_params.pop("behaviors")
raman_type = sim_params.pop("raman_type", "stolen")
f_r = sim_params.pop("f_r", 0)
hr_w = sim_params.pop("hr_w", None)
adapt_step_size = sim_params.pop("adapt_step_size", True)
error_ok = sim_params.pop("error_ok", 1e-10)
dynamic_dispersion = sim_params.pop("dynamic_dispersion", False)
del sim_params
logger = get_logger(job_identifier)
# Initial setup of both non linear and linear operators
N_func = create_non_linear_op(behaviors, w_c, w0, gamma, raman_type, f_r, hr_w)
if dynamic_dispersion:
disp = lambda r: fast_dispersion_op(w_c, beta(r), w_power_fact)
else:
disp = lambda r: fast_dispersion_op(w_c, beta, w_power_fact)
# Set up which quantity is conserved for adaptive step size
if adapt_step_size:
if "raman" in behaviors:
conserved_quantity_func = pulse.photon_number
else:
print("energy conserved")
conserved_quantity_func = pulse.pulse_energy
else:
conserved_quantity_func = lambda a, b, c, d: 0
# making sure to keep only the z that we want
z_targets = list(set(value for value in z_targets if value > 0))
z_targets.sort()
store_num = len(z_targets)
# Initial setup of simulation parameters
d_w = w_c[1] - w_c[0] # resolution of the frequency grid
z_stored, z = [0], 0 # position of each stored spectrum (for display)
pt = utilities.ProgressTracker(
z_final,
auto_print=True,
percent_incr=n_percent,
callback=_gen_RK4IP_progress_callback(),
)
# Setup initial values for every physical quantity that we want to track
current_spectrum = fft(field_0)
stored_spectra = [current_spectrum.copy()]
stored_field = [ifft(current_spectrum.copy())]
cons_qty = [conserved_quantity_func(current_spectrum, w_c + w0, d_w, gamma), 0]
cons_qty_change = [0, 0]
size_fac = 2 ** (1 / 5)
if save_data:
_save_current_spectrum(current_spectrum, 0, task_id, job_identifier)
# Initial step size
if adapt_step_size:
h = z_targets[0] / 2
else:
h = error_ok
newh = h
# Print introduction
logger.info("Storing {} new spectra, first one at {}m".format(store_num, z_targets[0]))
# Start of the integration
step = 1
keep = True # keep a step
store = False # store a spectrum
time_start = datetime.today()
while z < z_final:
h = newh
z_ratio = z / z_final
# Store Exp(h/2 * disp) to be used several times
expD = np.exp(h / 2 * disp(z_ratio))
# RK4 algorithm
A_I = expD * current_spectrum
k1 = expD * (h * N_func(current_spectrum, z_ratio))
k2 = h * N_func(A_I + k1 / 2, z_ratio)
k3 = h * N_func(A_I + k2 / 2, z_ratio)
k4 = h * N_func(expD * (A_I + k3), z_ratio)
end_spectrum = expD * (A_I + k1 / 6 + k2 / 3 + k3 / 3) + k4 / 6
# Check relative error and adjust next step size
if adapt_step_size:
cons_qty[step] = conserved_quantity_func(end_spectrum, w_c + w0, d_w, gamma)
curr_p_change = np.abs(cons_qty[step - 1] - cons_qty[step])
cons_qty_change[step] = cons_qty_change[step - 1] + curr_p_change
cons_qty_change_ok = error_ok * cons_qty[step - 1]
if curr_p_change > 2 * cons_qty_change_ok:
keep = False
newh = h / 2
elif cons_qty_change_ok < curr_p_change <= 2 * cons_qty_change_ok:
keep = True
newh = h / size_fac
elif curr_p_change < 0.1 * cons_qty_change_ok:
keep = True
newh = h * size_fac
else:
keep = True
newh = h
# consider storing anythin only if the step was valid
if keep:
# If step is accepted, z becomes the current position
z += h
step += 1
cons_qty.append(0)
cons_qty_change.append(0)
current_spectrum = end_spectrum.copy()
# Whether the current spectrum has to be stored depends on previous step
if store:
pt.set(z, [logger, step, z, h])
stored_spectra.append(end_spectrum)
stored_field.append(ifft(end_spectrum))
if save_data:
_save_current_spectrum(
end_spectrum, len(stored_spectra) - 1, task_id, job_identifier
)
z_stored.append(z)
del z_targets[0]
# No more spectrum to store
if len(z_targets) == 0:
break
store = False
# reset the constant step size after a spectrum is stored
if not adapt_step_size:
newh = error_ok
# if the next step goes over a position at which we want to store
# a spectrum, we shorten the step to reach this position exactly
if z + newh >= z_targets[0]:
store = True
newh = z_targets[0] - z
else:
progress_str = f"step {step} rejected with h = {h:.4e}, doing over"
logger.info(progress_str)
logger.info(
"propagation finished in {} steps ({} seconds)".format(
step, (datetime.today() - time_start).total_seconds()
)
)
if save_data:
io.save_data(z_stored, "z.npy", task_id, job_identifier)
return stored_spectra
def _save_current_spectrum(spectrum: np.ndarray, num: int, task_id: int, job_identifier: str):
base_name = f"spectrum_{num}.npy"
io.save_data(spectrum, base_name, task_id, job_identifier)
def _gen_RK4IP_progress_callback():
def callback(s, logger, step, z, h):
progress_str = " ({} steps). z = {:.4f}, h = {:.5g}".format(step, z, h)
logger.info(s + progress_str)
return callback
def _RK4IP_extract_params(sim_params):
"""extracts the right parameters from the the flattened params dict
Parameters
----------
sim_params : dict
flattened parameters dictionary
Returns
-------
tuple
all the necessary parameters
"""
w_c = sim_params.pop("w_c")
w0 = sim_params.pop("w0")
w_power_fact = sim_params.pop("w_power_fact")
field_0 = sim_params.pop("field_0")
z_targets = sim_params.pop("z_targets")
beta = sim_params.pop("beta_func", sim_params.pop("beta"))
gamma = sim_params.pop("gamma_func", sim_params.pop("gamma"))
behaviors = sim_params.pop("behaviors")
raman_type = sim_params.pop("raman_type", "stolen")
f_r = sim_params.pop("f_r", 0)
hr_w = sim_params.pop("hr_w", None)
adapt_step_size = sim_params.pop("adapt_step_size", True)
error_ok = sim_params.pop("error_ok", 1e-10)
dynamic_dispersion = sim_params.pop("dynamic_dispersion", False)
del sim_params
return (
behaviors,
w_c,
w0,
gamma,
raman_type,
f_r,
hr_w,
dynamic_dispersion,
beta,
w_power_fact,
adapt_step_size,
z_targets,
field_0,
error_ok,
)
def _prepare_grid(z_targets, w_c):
"""prepares some derived values for the propagation
Parameters
----------
z_targets : array
array of target z positions
w_c : array
angular frequency array (centered on 0)
Returns
-------
d_w : float
angular frequency grid size
z_targets : list
list of target z positions
store_num : int
number of spectra to store
z_final : float
final z position
z_sored : list
where the spectra are already stored
"""
# making sure to keep only the z that we want
z_targets = list(set(value for value in z_targets if value > 0))
z_targets.sort()
z_final = z_targets[-1]
store_num = len(z_targets)
# Initial setup of simulation parameters
d_w = w_c[1] - w_c[0] # resolution of the frequency grid
z_stored = [0] # position of each stored spectrum (for display)
return d_w, z_targets, store_num, z_final, z_stored
def parallel_simulations(config_file, num_cpu_per_task=1, task_id=0):
"""runs simulations in parallel thanks to Ray
Parameters
----------
config_file : str
name of the config file
should be a json containing all necessary parameters for the simulation. Varying parameters should be placed in a subdictionary
called "varying" (see scgenerator.utilities.dictionary_iterator for details)
num_cpu_per_task : int
number of concurrent job per node
task_id : give an id for book keeping purposes (must be set if multiple ray instances run at once so their files do not overlap)
Returns
----------
name of the folder where the data is stored
"""
logger = ray.remote(io.Logger).remote()
state.CurrentLogger.focus_logger(logger)
print("Nodes in the Ray cluster:", len(ray.nodes()))
for node in ray.nodes():
print(" " + node.get("NodeManagerHostname", "unknown"))
config_name, config_dict, store_num, n, m = _sim_preps(config_file)
# Override number of simultaneous jobs if provided by config file
sim_jobs = config_dict.pop("sim_jobs", len(ray.nodes()) * num_cpu_per_task)
print(f"number of simultaneous jobs : {sim_jobs}")
if n * m < sim_jobs:
sim_jobs = n * m
# Initiate helper workers (a logger, a progress tracker to give estimates of
# completion time and an indexer to keep track of the individual files
# created after each simulation. The indexer can then automatically merge them)
pt = ray.remote(ProgressTracker).remote(max=n * m * store_num, auto_print=True, percent_incr=1)
indexer = ray.remote(io.tmp_index_manager).remote(
config_name=config_name, task_id=task_id, varying_keys=config_dict.get("varying", None)
)
ray.get(
logger.log.remote(f"CRITICAL FILE at {ray.get(indexer.get_path.remote())}, do not touch it")
)
RK4IP_parallel = ray.remote(RK4IP)
jobs = []
# we treat loops over different parameters differently
for k, dico in enumerate(utilities.dictionary_iterator(config_dict, varying_dict="varying")):
# loop over same parameter set
for i in range(n):
# because of random processes, initial conditions are recalculated every time
params = initialize.compute_init_parameters(dictionary=config_dict, replace=dico)
# make sure initial conditions are saved
params["init_P0"] = dico.get("P0", config_dict.get("P0", 0))
params["init_T0_FWHM"] = dico.get("T0_FWHM", config_dict.get("T0_FWHM", 0))
params["param_id"] = k
params_file_name = io.generate_file_path("param", i, k, task_id, "")
io.save_parameters(params, params_file_name)
ray.get(indexer.append_to_index.remote(k, params_file_name=params_file_name))
if len(jobs) >= sim_jobs:
# update the number of jobs if new nodes connect
sim_jobs = min(n * (m - k) - i, len(ray.nodes()) * num_cpu_per_task)
# print(f"Nodes in the Ray cluster: {len(ray.nodes())}, {sim_jobs} simultaneous jobs")
# for node in ray.nodes():
# print(" " + node.get("NodeManagerHostname", "unknown"))
# wait for a slot to free before starting a new job
_, jobs = ray.wait(jobs)
ray.get(_)
# start a new simulation
ray.get(
logger.log.remote(
f"Launching propagation of a {params.get('t0', 0) * 1e15:.2f}fs pulse with {np.max(abs2(params['field_0'])):.0f}W peak power over {np.max(params['z_targets'])}m"
)
)
jobs.append(
RK4IP_parallel.remote(
params,
save_data=True,
job_id=i,
param_id=k,
task_id=task_id,
pt=pt,
indexer=indexer,
logger=logger,
n_percent=1,
)
)
ray.get(logger.log.remote("number of running jobs : {}".format(len(jobs))))
ray.get(logger.log.remote(ray.get(pt.get_eta.remote())))
# wait for the last jobs to finish
ray.get(jobs)
# merge the data properly
folder_0 = ray.get(indexer.convert_sim_data.remote())
print(f"{config_name} successfully finished ! data saved in {folder_0}")
return folder_0
def simulate(config_file, task_id=0, n_percent=1):
"""runs simulations one after another
Parameters
----------
config_file : str
name of the config file
should be a json containing all necessary parameters for the simulation. Varying parameters should be placed in a subdictionary
called "varying" (see scgenerator.utilities.dictionary_iterator for details)
task_id : any formatable (int, string, float, ...)
give an id for book keeping purposes (must be set if multiple ray instances run at once so their files do not overlap)
n_percent : int or float
each individual simulation reports its progress every n_percent percent.
Returns
----------
name of the folder where the data is stored
"""
logger = io.Logger()
state.CurrentLogger.focus_logger(logger)
config_name, config_dict, store_num, n, m = _sim_preps(config_file)
# Initiate helper workers (a logger, a progress tracker to give estimates of
# completion time and an indexer to keep track of the individual files
# created after each simulation. The indexer can then automatically merge them)
pt = ProgressTracker(max=n * m * store_num, auto_print=True, percent_incr=1)
indexer = io.tmp_index_manager(
config_name=config_name, task_id=task_id, varying_keys=config_dict.get("varying", None)
)
logger.log(f"CRITICAL FILE at {indexer.get_path()}, do not touch it")
# we treat loops over different parameters differently
for k, dico in enumerate(utilities.dictionary_iterator(config_dict, varying_dict="varying")):
# loop over same parameter set
for i in range(n):
# because of random processes, initial conditions are recalculated every time
params = initialize.compute_init_parameters(dictionary=config_dict, replace=dico)
# make sure initial conditions are saved
params["init_P0"] = dico.get("P0", config_dict.get("P0", 0))
params["init_T0_FWHM"] = dico.get("T0_FWHM", config_dict.get("T0_FWHM", 0))
params["param_id"] = k
params_file_name = io.generate_file_path("param", i, k, task_id, "")
io.save_parameters(params, params_file_name)
indexer.append_to_index(k, params_file_name=params_file_name)
# start a new simulation
logger.log(
f"Launching propagation of a {params.get('t0', 0) * 1e15:.2f}fs pulse with {np.max(abs2(params['field_0'])):.0f}W peak power over {np.max(params['z_targets'])}m"
)
RK4IP(
params,
save_data=True,
job_id=i,
param_id=k,
task_id=task_id,
pt=pt,
indexer=indexer,
logger=logger,
n_percent=n_percent,
)
logger.log(pt.get_eta())
# merge the data properly
folder_0 = indexer.convert_sim_data()
print(f"{config_name} successfully finished ! data saved in {folder_0}")
return folder_0
def _sim_preps(config_file):
# Load the config file
try:
with open(config_file, "r") as file:
config_dict = json.loads(file.read())
except FileNotFoundError:
print("No config file named {} found".format(config_file))
raise
# Store a master dictionary of parameters to generate file names and such
config_name = config_dict.pop("name", os.path.split(config_file)[-1][:-5])
# make sure we store spectra every time at the exact same place
if "z_targets" not in config_dict:
config_dict["z_targets"] = np.linspace(0, 1, 128)
config_dict["z_targets"] = initialize.sanitize_z_targets(config_dict["z_targets"])
config_dict = units.standardize_dictionary(config_dict)
store_num = len(config_dict["z_targets"])
# How many total simulations
n = int(config_dict.pop("n", 1))
m = np.prod([len(np.atleast_1d(ls)) for _, ls in config_dict.get("varying", {1: 1}).items()])
return config_name, config_dict, store_num, n, m

View File

@@ -0,0 +1,323 @@
# series of functions to convert different values to angular frequencies
# For example, nm(X) means "I give the number X in nm, figure out the ang. freq."
# to be used especially when giving plotting ranges : (400, 1400, nm), (-4, 8, ps), ...
import numpy as np
from numpy import pi
c = 299792458.0
hbar = 1.05457148e-34
NA = 6.02214076e23
R = 8.31446261815324
kB = 1.380649e-23
epsilon0 = 8.85418781e-12
prefix = dict(P=1e12, G=1e9, M=1e6, k=1e3, d=1e-1, c=1e-2, m=1e-3, u=1e-6, n=1e-9, p=1e-12, f=1e-15)
"""
Below are common units. You can define your own unit function
this function must have a few porperties:
inv : function
inverse of the function. example :
um(1) -> 883651567308853.2
um.inv(883651567308853.2) -> 1.0
label : str
label to be displayed on plot
type : ("WL", "FREQ", "AFREQ", "TIME", "OTHER")
"""
def m(l):
return 2 * pi * c / l
m.inv = m
m.label = r"Wavelength $\lambda$ (m)"
m.type = "WL"
def nm(l):
return 2 * pi * c / (l * 1e-9)
nm.inv = nm
nm.label = r"Wavelength $\lambda$ (nm)"
nm.type = "WL"
def um(l):
return 2 * pi * c / (l * 1e-6)
um.inv = um
um.label = r"Wavelength $\lambda$ ($\mathrm{\mu}$m)"
um.type = "WL"
def THz(f):
return 1e12 * 2 * pi * f
THz.inv = lambda w: w / (1e12 * 2 * pi)
THz.label = r"Frequency $f$ (THz)"
THz.type = "FREQ"
def PHz(f):
return 1e15 * 2 * pi * f
PHz.inv = lambda w: w / (1e15 * 2 * pi)
PHz.label = r"Frequency $f$ (PHz)"
PHz.type = "FREQ"
def rad_s(w):
return w
rad_s.inv = rad_s
rad_s.label = r"Angular frequency $\omega$ ($\frac{\mathrm{rad}}{\mathrm{s}}$)"
rad_s.type = "AFREQ"
def Prad_s(w):
return w * 1e15
Prad_s.inv = lambda w: 1e-15 * w
Prad_s.label = r"Angular frequency $\omega$ ($\frac{\mathrm{Prad}}{\mathrm{s}}$)"
Prad_s.type = "AFREQ"
def rel_time(t):
return t
rel_time.inv = rel_time
rel_time.label = r"relative time ${\tau}/{\tau_\mathrm{0, FWHM}}$"
rel_time.type = "TIME"
def rel_freq(f):
return f
rel_freq.inv = rel_freq
rel_freq.label = r"relative angular freq. $(\omega - \omega_0)/\Delta\omega_0$"
rel_freq.type = "FREQ"
def s(t):
return t
s.inv = s
s.label = r"Time $t$ (s)"
s.type = "TIME"
def us(t):
return t * 1e-6
us.inv = lambda t: t * 1e6
us.label = r"Time $t$ (us)"
us.type = "TIME"
def ns(t):
return t * 1e-9
ns.inv = lambda t: t * 1e9
ns.label = r"Time $t$ (ns)"
ns.type = "TIME"
def ps(t):
return t * 1e-12
ps.inv = lambda t: t * 1e12
ps.label = r"Time $t$ (ps)"
ps.type = "TIME"
def fs(t):
return t * 1e-15
fs.inv = lambda t: t * 1e15
fs.label = r"Time $t$ (fs)"
fs.type = "TIME"
def inv(x):
return 1 / x
inv.inv = inv
inv.label = "inverse"
inv.type = "WL"
def bar(p):
return 1e5 * p
bar.inv = lambda p: 1e-5 * p
bar.label = "Pressure (bar)"
bar.type = "PRESSURE"
def beta2_fs_cm(b2):
return 1e-28 * b2
beta2_fs_cm.inv = lambda b2: 1e28 * b2
beta2_fs_cm.label = r"$\beta_2$ (fs$^2$/cm)"
beta2_fs_cm.type = "OTHER"
def beta2_ps_km(b2):
return 1e-27 * b2
beta2_ps_km.inv = lambda b2: 1e27 * b2
beta2_ps_km.label = r"$\beta_2$ (ps$^2$/km)"
beta2_ps_km.type = "OTHER"
def D_ps_nm_km(D):
return 1e-6 * D
D_ps_nm_km.inv = lambda D: 1e6 * D
D_ps_nm_km.label = r"$D$ (ps/(nm km))"
D_ps_nm_km.type = "OTHER"
def beta2_coef(beta):
fac = 1e27
out = np.zeros_like(beta)
for i, b in enumerate(beta):
out[i] = fac * b
fac *= 1e12
return out
def standardize_dictionary(dico):
"""convert lists of number and units into a float with SI units inside a dictionary
Parameters
----------
dico : a dictionary
Returns
----------
same dictionary with units converted
Example
----------
standardize_dictionary({"power": [23, "kW"], "points": [1, 2, 3]})
{"power": 23000, "points": [1, 2, 3]})
"""
for key, item in dico.items():
if isinstance(item, list) and len(item) == 2 and isinstance(item[0], (int, float)) and isinstance(item[1], str):
num, unit = item
fac = 1
if len(unit) == 2:
fac = prefix[unit[0]]
elif unit == "bar":
fac = 1e5
dico[key] = num * fac
return dico
def sort_axis(axis, plt_range):
"""
given an axis, returns this axis cropped according to the given range, converted and sorted
Parameters
----------
axis : 1D array containing the original axis (usual the w or t array)
plt_range : tupple (min, max, conversion_function) used to crop the axis
Returns
----------
cropped : the axis cropped, converted and sorted
indices : indices to use to slice and sort other array in the same fashion
extent : tupple with min and max of cropped
Example
----------
w = np.append(np.linspace(0, -10, 20), np.linspace(0, 10, 20))
t = np.linspace(-10, 10, 400)
W, T = np.meshgrid(w, t)
y = np.exp(-W**2 - T**2)
# Define ranges
rw = (-4, 4, s)
rt = (-2, 6, s)
w, cw = sort_axis(w, rw)
t, ct = sort_axis(t, rt)
# slice y according to the given ranges
y = y[ct][:, cw]
"""
r = np.array(plt_range[:2], dtype="float")
func = plt_range[2]
indices = np.arange(len(axis))[(axis <= np.max(func(r))) & (axis >= np.min(func(r)))]
cropped = axis[indices]
order = np.argsort(func.inv(cropped))
indices = indices[order]
cropped = cropped[order]
out_ax = func.inv(cropped)
return out_ax, indices, (out_ax[0], out_ax[-1])
def to_WL(spectrum, frep, lambda_):
"""
When a spectrogram is displayed as function of wl instead of frequency, we
need to adjust the amplitude of each bin for the integral over the whole frequency
range to match.
"""
m = 2 * pi * c / (lambda_ ** 2) * frep * spectrum
return m
def to_log(arr, ref=None):
"""takes the log of each 1D array relative to the max of said array. Useful
to plot spectrum evolution, but use to_log2D for spectrograms
Parameters
----------
arr : 1D array of real numbers. >1D array : operation is applied on axis=0
ref : reference value corresponding to 0dB (default : max(arr))
Returns
----------
arr array in dB
"""
if arr.ndim > 1:
return np.apply_along_axis(to_log, -1, arr, ref)
else:
if ref is None:
ref = np.max(arr)
m = arr / ref
m = 10 * np.log10(m, out=np.zeros_like(m) - 100, where=m > 0)
return m
def to_log2D(arr, ref=None):
"""computes the log of a 2D array
Parameters
----------
arr : 2D array of real numbers
ref : reference value corresponding to 0dB
Returns
----------
arr array in dB
"""
if ref is None:
ref = np.max(arr)
m = arr / ref
m = 10 * np.log10(m, out=np.zeros_like(m) - 100, where=m > 0)
return m

927
src/scgenerator/plotting.py Normal file
View File

@@ -0,0 +1,927 @@
import os
import matplotlib.gridspec as gs
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import ListedColormap
from scipy.interpolate import UnivariateSpline
from . import io, math, state
from .math import abs2, make_uniform_1D, span
from .physics import pulse, units
def draw_across(ax1, xy1, ax2, xy2, clip_on=False, **kwargs):
"""draws a line across 2 axes
Parameters
----------
ax1, ax2 : axis objects
xy1, xy2 : tupple (float, float)
The end positions in data coordinates (from their respective axis)
**kwargs : arrowprops kwargs
Returns
----------
None
"""
ax1.annotate(
"",
xy=xy1,
xytext=xy2,
xycoords=ax1.transData,
textcoords=ax2.transData,
arrowprops=dict(arrowstyle="-", clip_on=clip_on, **kwargs),
)
def zoom(ax, zoom_ax, clip_on=False, **kwargs):
l, r = zoom_ax.get_xlim()
b, t = zoom_ax.get_ylim()
draw_across(ax, (l, b), zoom_ax, (l, b), clip_on=clip_on, **kwargs)
draw_across(ax, (l, t), zoom_ax, (l, t), clip_on=clip_on, **kwargs)
draw_across(ax, (r, l), zoom_ax, (r, l), clip_on=clip_on, **kwargs)
draw_across(ax, (r, t), zoom_ax, (r, t), clip_on=clip_on, **kwargs)
ax.plot([l, l, r, r], [b, t, t, b], **kwargs)
def create_zoom_axis(
axis, xlim, ylim=None, width_ratios=[1, 1, 1], height_ratios=[1, 1, 1], frame_style=dict(c="k", lw=0.5), plot=True
):
"""creates a zoomed in plot inside a plot. Should be called as a last step as parent axis limits will be locked
Parameters
----------
axis : parent axis object
xlim : tupple
limits in parent axis data coordinates
ylim : tupple, optional
width_ratios, height_ration : lists of len 3
cut the parent axis in 3x3 cells with these ratios, the center one will be the new axis
frame_style : dict, optional
plot : bool, optional
whether to copy the lines or return an empty axis
Returns
----------
the new axis
"""
axis.set_xlim(axis.get_xlim())
axis.set_ylim(axis.get_ylim())
# set up the axis
grid = gs.GridSpecFromSubplotSpec(
3, 3, subplot_spec=axis, width_ratios=width_ratios, height_ratios=height_ratios, hspace=0, wspace=0
)
inset = axis.get_figure().add_subplot(grid[1, 1])
width_ratios = np.cumsum(np.array(width_ratios) / np.sum(width_ratios))
height_ratios = np.cumsum(np.array(height_ratios) / np.sum(height_ratios))
# copy the plot content
if plot:
lines = axis.get_lines()
ymin, ymax = 0, 0
for line in lines:
xdata = line.get_xdata()
xdata, ind, _ = units.sort_axis(xdata, (*xlim, units.s))
ydata = line.get_ydata()[ind]
inset.plot(xdata, ydata, c=line.get_color(), ls=line.get_linestyle(), lw=line.get_linewidth())
inset.set_xlim(xlim)
if ylim is not None:
inset.set_ylim(ylim)
ylim = inset.get_ylim()
elif ylim is None:
raise ValueError("ylim is mandatory when not plotting")
# draw the box in parent axis
dx = math.length(axis.get_xlim())
dy = math.length(axis.get_ylim())
l, r = xlim
b, t = ylim
axis.plot([l, l, r, r, l], [b, t, t, b, b], **frame_style)
# draw lines connecting the box to the new axis
ll = axis.get_xlim()[0] + width_ratios[0] * dx
rr = axis.get_xlim()[0] + width_ratios[1] * dx
bb = axis.get_ylim()[1] - height_ratios[1] * dy
tt = axis.get_ylim()[1] - height_ratios[0] * dy
axis.plot([l, ll], [t, tt], **frame_style)
axis.plot([l, ll], [b, bb], **frame_style)
axis.plot([r, rr], [t, tt], **frame_style)
axis.plot([r, rr], [b, bb], **frame_style)
return inset
def corner_annotation(text, ax, position="tl", rel_x_offset=0.05, rel_y_offset=0.05, **text_kwargs):
"""puts an annotatin in a corner of an ax
Parameters
----------
text : str
text to put in the corner
ax : matplotlib axis object
position : str {"tl", "tr", "bl", "br"}
Returns
----------
nothing
"""
# xlim = ax.get_xlim()
# ylim = ax.get_ylim()
# xoff = length(xlim) * rel_x_offset
# yoff = length(ylim) * rel_y_offset
if position[0] == "t":
y = 1 - rel_y_offset
va = "top"
else:
y = 0 + rel_y_offset
va = "bottom"
if position[1] == "l":
x = 0 + rel_x_offset
ha = "left"
else:
x = 1 - rel_x_offset
ha = "right"
ax.annotate(
text,
(x, y),
(x, y),
xycoords="axes fraction",
textcoords="axes fraction",
verticalalignment=va,
horizontalalignment=ha,
**text_kwargs,
)
return None
def _finish_plot_2D(
values,
x_axis,
x_label,
y_axis,
y_label,
log,
vmin,
vmax,
transpose,
cmap,
cbar_label,
ax,
file_name,
file_type,
params,
):
# apply log transform if required
if log != False:
vmax = state.plot_default_vmax if vmax is None else vmax
vmin = state.plot_default_vmin if vmin is None else vmin
if isinstance(log, (float, int)) and log != True:
values = units.to_log(values, ref=log)
elif log == "2D":
values = units.to_log2D(values)
elif log == "1D":
values = np.apply_along_axis(units.to_log, 1, values)
elif log == "smooth 1D":
ref = np.max(values, axis=1)
ind = np.argmax((ref[:-1] - ref[1:]) < 0)
values = units.to_log(values, ref=np.max(ref[ind:]))
elif log == "unique 1D":
try:
ref = _finish_plot_2D.ref
print(f"recovered reference value {ref} for log plot")
except AttributeError:
ref = np.max(values, axis=1)
ind = np.argmax((ref[:-1] - ref[1:]) < 0)
ref = np.max(ref[ind:])
_finish_plot_2D.ref = ref
values = units.to_log(values, ref=ref)
cmap = state.plot_default_cmap if cmap is None else cmap
is_new_plot = ax is None
cbar_ax = None
if isinstance(ax, tuple) and len(ax) > 1:
ax, cbar_ax = ax[0], ax[1]
folder_name = ""
if is_new_plot:
folder_name, file_name, fig, ax = io.plot_setup(file_name=file_name, file_type=file_type, params=params)
else:
fig = ax.get_figure()
# Determine grid extent and spacing to be able to center
# each pixel since by default imshow draws values at the lower-left corner
if transpose:
dy = x_axis[1] - x_axis[0]
ext_y = span(x_axis)
dx = y_axis[1] - y_axis[0]
ext_x = span(y_axis)
values = values.T
ax.set_xlabel(y_label)
ax.set_ylabel(x_label)
else:
dx = x_axis[1] - x_axis[0]
ext_x = span(x_axis)
dy = y_axis[1] - y_axis[0]
ext_y = span(y_axis)
ax.set_ylabel(y_label)
ax.set_xlabel(x_label)
ax.set_xlim(*ext_x)
ax.set_ylim(*ext_y)
interpolation = params.get("plot.interpolation", state.plot_default_2D_interpolation)
im = ax.imshow(
values,
extent=[ext_x[0] - dx / 2, ext_x[1] + dx / 2, ext_y[0] - dy / 2, ext_y[1] + dy / 2],
cmap=cmap,
vmin=vmin,
vmax=vmax,
origin="lower",
interpolation=interpolation,
aspect="auto",
)
cbar = None
if cbar_label is not None:
if cbar_ax is None:
cbar = fig.colorbar(im, ax=ax, orientation="vertical")
else:
cbar = fig.colorbar(im, cax=cbar_ax, orientation="vertical")
cbar.ax.set_ylabel(cbar_label)
if is_new_plot:
fig.savefig(os.path.join(folder_name, file_name), bbox_inches="tight", dpi=200)
print(f"plot saved in {os.path.join(folder_name, file_name)}")
if cbar_label is not None:
return fig, ax, cbar.ax
else:
return fig, ax
def plot_spectrogram(
values,
x_range,
y_range,
params,
t_res=None,
gate_width=None,
log=True,
vmin=None,
vmax=None,
cbar_label="normalized intensity (dB)",
file_type="png",
file_name=None,
cmap=None,
ax=None,
):
"""Plots a spectrogram given a complex field in the time domain
Parameters
----------
values : 2D array
axis 0 defines the position in the fiber and axis 1 the position in time, frequency or wl
example : [[1, 2, 3], [0, 1, 0]] describes a quantity at 3 different freq/time and at two locations in the fiber
x_range, y_range : tupple (min, max, units)
one of them must be time, the other one must be wl/freq
min, max : int or float
minimum and maximum values given in the desired units
units : function to convert from the desired units to rad/s or to time.
common functions are already defined in scgenerator.physics.units
look there for more details
params : dict
parameters of the simulations
log : bool, optional
whether to compute the logarithm of the spectrogram
vmin : float, optional
min value of the colorbar
vmax : float, optional
max value of the colorbar
cbar_label : str or None
label of the colorbar. Will not draw colorbar if None
file_type : str, optional
usually pdf or png
plt_name : str, optional
special name to give to the plot. A name is automatically assigned anyway
cmap : str, optional
colormap to be used in matplotlib.pyplot.imshow
ax : matplotlib.axes._subplots.AxesSubplot object or tupple of 2 axis objects, optional
axis on which to draw the plot
if only one is given, a new one will be created to draw the colorbar
"""
if values.ndim != 1:
print("plot_spectrogram can only plot 1D arrays")
return
if (x_range[2].type == "TIME") == (y_range[2].type == "TIME"):
print("exactly one range must be a time range")
return
log = "2D" if log in ["2D", True] else False
# 0 axis means x-axis -> determine final orientation of spectrogram
time_axis = 0 if x_range[2].type not in ["WL", "FREQ", "AFREQ"] else 1
if time_axis == 0:
t_range, f_range = x_range, y_range
else:
t_range, f_range = y_range, x_range
# Actually compute the spectrogram
t_win = 2 * np.max(t_range[2](np.abs(t_range[:2])))
spec_kwargs = dict(t_res=t_res, t_win=t_win, gate_width=gate_width, shift=False)
spec, new_t = pulse.spectrogram(
params["t"].copy(), values, **{k: v for k, v in spec_kwargs.items() if v is not None}
)
# Crop and reoder axis
new_t, ind_t, _ = units.sort_axis(new_t, t_range)
new_f, ind_f, _ = units.sort_axis(params["w"], f_range)
values = spec[ind_t][:, ind_f]
if f_range[2].type == "WL":
values = np.apply_along_axis(units.to_WL, 1, values, params["frep"], units.m(f_range[2].inv(new_f)))
values = np.apply_along_axis(make_uniform_1D, 1, values, new_f)
if time_axis == 0:
x_axis, y_axis = new_t, new_f
values = values.T
else:
x_axis, y_axis = new_f, new_t
return _finish_plot_2D(
values,
x_axis,
x_range[2].label,
y_axis,
y_range[2].label,
log,
vmin,
vmax,
False,
cmap,
cbar_label,
ax,
file_name,
file_type,
params,
)
def plot_results_2D(
values,
plt_range,
params,
log="1D",
skip=16,
vmin=None,
vmax=None,
transpose=False,
cbar_label="normalized intensity (dB)",
file_type="png",
file_name=None,
cmap=None,
ax=None,
):
"""
plots 2D arrays and automatically saves the plots, as well as returns it
Parameters
----------
values : 2D array
axis 0 defines the position in the fiber and axis 1 the position in time, frequency or wl
example : [[1, 2, 3], [0, 1, 0]] describes a quantity at 3 different freq/time and at two locations in the fiber
plt_range : tupple (min, max, units)
min, max : int or float
minimum and maximum values given in the desired units
units : function to convert from the desired units to rad/s or to time.
common functions are already defined in scgenerator.physics.units
look there for more details
params : dict
parameters of the simulations
log : str {"1D", "2D", "smooth 1D"} or int, float or bool, optional
str : plot in dB
1D : takes the log for every slice
2D : takes the log for the whole 2D array
smooth 1D : figures out a smart reference value for the whole 2D array
int, float : plot in dB
reference value
bool : whether to use 1D variant or nothing
skip : int, optional
take 1 every skip values along the -1 axis
vmin : float, optional
min value of the colorbar
vmax : float, optional
max value of the colorbar
cbar_label : str or None
label of the colorbar. Will not draw colorbar if None
file_type : str, optional
usually pdf or png
plt_name : str, optional
special name to give to the plot. A name is automatically assigned anyway
cmap : str, optional
colormap to be used in matplotlib.pyplot.imshow
ax : matplotlib.axes._subplots.AxesSubplot object or tupple of 2 axis objects, optional
axis on which to draw the plot
if only one is given, a new one will be created to draw the colorbar
returns
fig, ax : matplotlib objects containing the plots
example:
if spectra is a (m, n, nt) array, one can plot a spectrum evolution as such:
>>> fig, ax = plot_results_2D(spectra[:, -1], (600, 1600, nm), log=True, "Heidt2017")
"""
if values.ndim != 2:
print(f"Shape was {values.shape}. plot_results_2D can only plot 2D arrays")
return
is_spectrum = values.dtype == "complex"
if plt_range[2].type in ["WL", "FREQ", "AFREQ"]:
x_axis = params["w"].copy()
else:
x_axis = params["t"].copy()
# crop and convert
x_axis, ind, ext = units.sort_axis(x_axis[::skip], plt_range)
values = values[:, ::skip][:, ind]
if is_spectrum:
values = abs2(values)
# make uniform if converting to wavelength
if plt_range[2].type == "WL":
if is_spectrum:
values = np.apply_along_axis(units.to_WL, 1, values, params.get("frep", 1), x_axis)
values = np.array([make_uniform_1D(v, x_axis, n=len(x_axis), method="linear") for v in values])
return _finish_plot_2D(
values,
x_axis,
plt_range[2].label,
params["z_targets"],
"propagation distance (m)",
log,
vmin,
vmax,
transpose,
cmap,
cbar_label,
ax,
file_name,
file_type,
params,
)
def plot_results_1D(
values,
plt_range,
params,
log=False,
spacing=1,
vmin=None,
vmax=None,
ylabel=None,
yscaling=1,
file_type="pdf",
file_name=None,
ax=None,
line_label=None,
transpose=False,
**line_kwargs,
):
"""
Parameters
----------
values : 1D array
if values are complex, the abs^2 is computed before plotting
plt_range : tupple (min, max, units)
min, max : int or float
minimum and maximum values given in the desired units
units : function to convert from the desired units to rad/s or to time.
common functions are already defined in scgenerator.physics.units
look there for more details
params : dict
parameters of the simulations
log : str {"1D"} or int, float or bool, optional
str : plot in dB
1D : takes the log for every slice
int, float : plot in dB
reference value
bool : whether to use 1D variant or nothing
spacing : int, float, optional
tells the function to take one value every `spacing` one available. If a float is given, it will interpolate with a spline.
vmin : float, optional
min value of the colorbar
vmax : float, optional
max value of the colorbar
ylabel : str, optional
label of the y axis (x axis in transposed mode). Default is "normalized intensity (dB)" for log plots
yscaling : float, optional
scale the y values by this amount
file_type : str, optional
usually pdf or png
plt_name : str, optional
special name to give to the plot. A name is automatically assigned anyway
ax : matplotlib.axes._subplots.AxesSubplot object, optional
axis on which to draw the plot
line_label : str, optional
label of the line
transpose : bool, optional
transpose the plot
line_kwargs : to be passed to plt.plot
returns
fig, ax : matplotlib objects containing the plots
example:
if spectra is a (m, n, nt) array, one can plot a spectrum evolution as such:
>>> fig, ax = plot_results_2D(spectra[:, -1], (600, 1600, nm), log=True, "Heidt2017")
"""
if len(values.shape) != 1:
print(f"Shape was {values.shape}. plot_results_1D can only plot 1D arrays")
return
is_spectrum = values.dtype == "complex"
if plt_range[2].type in ["WL", "FREQ", "AFREQ"]:
x_axis = params["w"].copy()
else:
x_axis = params["t"].copy()
# crop and convert
x_axis, ind, ext = units.sort_axis(x_axis, plt_range)
values = values[ind]
if is_spectrum:
values = abs2(values)
values *= yscaling
# make uniform if converting to wavelength
if plt_range[2].type == "WL":
if is_spectrum:
values = units.to_WL(values, params["frep"], units.m.inv(params["w"][ind]))
# change the resolution
if isinstance(spacing, float):
new_x_axis = np.linspace(*span(x_axis), int(len(x_axis) / spacing))
values = UnivariateSpline(x_axis, values, k=4, s=0)(new_x_axis)
x_axis = new_x_axis
elif isinstance(spacing, int) and spacing > 1:
values = values[::spacing]
x_axis = x_axis[::spacing]
# apply log transform if required
if log == False:
pass
else:
ylabel = "normalized intensity (dB)" if ylabel is None else ylabel
vmax = state.plot_default_vmax_with_headroom if vmax is None else vmax
vmin = state.plot_default_vmin if vmin is None else vmin
if isinstance(log, (float, int)) and log != True:
values = units.to_log(values, ref=log)
else:
values = units.to_log(values)
is_new_plot = ax is None
folder_name = ""
if is_new_plot:
folder_name, file_name, fig, ax = io.plot_setup(file_name=file_name, file_type=file_type, params=params)
else:
fig = ax.get_figure()
if transpose:
ax.plot(values, x_axis, label=line_label, **line_kwargs)
ax.yaxis.tick_right()
ax.yaxis.set_label_position("right")
ax.set_xlim(vmax, vmin)
ax.set_xlabel(ylabel)
ax.set_ylabel(plt_range[2].label)
else:
ax.plot(x_axis, values, label=line_label, **line_kwargs)
ax.set_ylim(vmin, vmax)
ax.set_ylabel(ylabel)
ax.set_xlabel(plt_range[2].label)
if is_new_plot:
fig.savefig(os.path.join(folder_name, file_name), bbox_inches="tight", dpi=200)
print(f"plot saved in {os.path.join(folder_name, file_name)}")
return fig, ax
def plot_avg(
values,
plt_range,
params,
log=False,
spacing=1,
vmin=None,
vmax=None,
ylabel=None,
yscaling=1,
renormalize=True,
add_coherence=False,
file_type="png",
file_name=None,
ax=None,
line_labels=None,
legend=True,
legend_kwargs={},
transpose=False,
):
"""
plots 1D arrays and there mean and automatically saves the plots, as well as returns it
Parameters
----------
values : 2D array
axis 0 defines the position in the fiber and axis 1 the position in time, frequency or wl
example : [[1, 2, 3], [0, 1, 0]] describes a quantity at 3 different freq/time and at two locations in the fiber
plt_range : tupple (min, max, units)
min, max : int or float
minimum and maximum values given in the desired units
units : function to convert from the desired units to rad/s or to time.
common functions are already defined in scgenerator.physics.units
look there for more details
params : dict
parameters of the simulations
log : str {"1D"} or int, float or bool, optional
str : plot in dB
1D : takes the log for every slice
int, float : plot in dB
reference value
bool : whether to use 1D variant or nothing
spacing : int, float, optional
tells the function to take one value every `spacing` one available. If a float is given, it will interpolate with a spline.
vmin : float, optional
min value of the colorbar
vmax : float, optional
max value of the colorbar
ylabel : str, optional
label of the y axis (x axis in transposed mode). Default is 'normalized intensity (dB)' for log plots
yscaling : float, optional
scale the y values by this amount
renormalize : bool, optional
if converting to wl scale, renormalize with to_WL function to ensure energy is conserved
add_coherence : bool, optional
whether to add a subplot with coherence
file_type : str, optional
usually pdf or png
plt_name : str, optional
special name to give to the plot. A name is automatically assigned anyway
ax : matplotlib.axes._subplots.AxesSubplot object, optional
axis on which to draw the plot
line_labels : tupple(str), optional
label of the lines. line_labels[0] is the label of the mean and line_labels[1] is the label of the indiv. values
legend : bool, optional
whether to draw the legend
transpose : bool, optional
transpose the plot
returns
fig, ax : matplotlib objects containing the plots
example:
if spectra is a (m, n, nt) array, one can plot a spectrum evolution as such:
>>> fig, ax = plot_results_2D(spectra[:, -1], (600, 1600, nm), log=True, "Heidt2017")
"""
if len(values.shape) != 2:
print(f"Shape was {values.shape}. plot_avg can only plot 2D arrays")
return
is_spectrum = values.dtype == "complex"
if plt_range[2].type in ["WL", "FREQ", "AFREQ"]:
x_axis = params["w"].copy()
else:
x_axis = params["t"].copy()
# crop and convert
x_axis, ind, ext = units.sort_axis(x_axis, plt_range)
if add_coherence:
coherence = pulse.g12(values)
coherence = coherence[ind]
else:
coherence = None
values = values[:, ind]
is_new_plot = ax is None
folder_name = ""
original_lines = []
# compute the mean spectrum
if is_spectrum:
values = abs2(values)
values *= yscaling
mean_values = np.mean(values, axis=0)
if plt_range[2].type == "WL" and renormalize:
values = np.apply_along_axis(units.to_WL, 1, values, params["frep"], x_axis)
mean_values = units.to_WL(mean_values, params["frep"], x_axis)
# change the resolution
if isinstance(spacing, float):
new_x_axis = np.linspace(*span(x_axis), int(len(x_axis) / spacing))
values = np.array([UnivariateSpline(x_axis, value, k=4, s=0)(new_x_axis) for value in values])
if add_coherence:
coherence = UnivariateSpline(x_axis, coherence, k=4, s=0)(new_x_axis)
mean_values = np.mean(values, axis=0)
x_axis = new_x_axis
elif isinstance(spacing, int) and spacing > 1:
values = values[:, ::spacing]
mean_values = mean_values[::spacing]
x_axis = x_axis[::spacing]
if add_coherence:
coherence = coherence[::spacing]
# apply log transform if required
if log != False:
ylabel = "normalized intensity (dB)" if ylabel is None else ylabel
vmax = state.plot_default_vmax_with_headroom if vmax is None else vmax
vmin = state.plot_default_vmin if vmin is None else vmin
if isinstance(log, (float, int)) and log != True:
ref = log
else:
ref = np.max(mean_values)
values = units.to_log(values, ref=ref)
mean_values = units.to_log(mean_values, ref=ref)
if is_new_plot:
if add_coherence:
mode = "coherence_T" if transpose else "coherence"
folder_name, file_name, fig, (top, bot) = io.plot_setup(
file_name=file_name, file_type=file_type, params=params, mode=mode
)
else:
folder_name, file_name, fig, top = io.plot_setup(file_name=file_name, file_type=file_type, params=params)
bot = top
else:
if isinstance(ax, (tuple, list)):
top, bot = ax
if transpose:
bot.set_xlim(1.1, -0.1)
bot.set_xlabel(r"|$g_{12}$|")
else:
bot.set_ylim(-0.1, 1.1)
bot.set_ylabel(r"|$g_{12}$|")
else:
bot, top = ax, ax
fig = top.get_figure()
original_lines = top.get_lines()
# Actual Plotting
gray_style = state.plot_muted_style
highlighted_style = state.plot_highlighted_style
if transpose:
for value in values:
top.plot(value, x_axis, **gray_style)
top.plot(mean_values, x_axis, **highlighted_style)
if add_coherence:
bot.plot(coherence, x_axis, c=state.plot_default_color_cycle[0])
top.set_xlim(left=vmax, right=vmin)
top.yaxis.tick_right()
top.set_xlabel(ylabel)
top.set_ylim(*ext)
bot.yaxis.tick_right()
bot.yaxis.set_label_position("right")
bot.set_ylabel(plt_range[2].label)
bot.set_ylim(*ext)
else:
for value in values:
top.plot(x_axis, value, **gray_style)
top.plot(x_axis, mean_values, **highlighted_style)
if add_coherence:
bot.plot(x_axis, coherence, c=state.plot_default_color_cycle[0])
top.set_ylim(bottom=vmin, top=vmax)
top.set_ylabel(ylabel)
top.set_xlim(*ext)
bot.set_xlabel(plt_range[2].label)
bot.set_xlim(*ext)
custom_lines = [plt.Line2D([0], [0], lw=2, c=gray_style["c"]), plt.Line2D([0], [0], lw=2, c=highlighted_style["c"])]
line_labels = state.plot_avg_default_line_labels if line_labels is None else line_labels
line_labels = list(line_labels)
if not is_new_plot:
custom_lines += original_lines
line_labels += [l.get_label() for l in original_lines]
if legend:
top.legend(custom_lines, line_labels, **legend_kwargs)
if is_new_plot:
fig.savefig(os.path.join(folder_name, file_name), bbox_inches="tight", dpi=200)
print(f"plot saved in {os.path.join(folder_name, file_name)}")
if top is bot:
return fig, top
else:
return fig, (top, bot)
def prepare_plot_1D(values, plt_range, x_axis, yscaling=1, spacing=1, frep=80e6):
"""prepares the values for plotting
Parameters
----------
values : array
the values to plot.
if complex, will take the abs^2
if 2D, will consider it a as a list of values, each corresponding to the same x_axis
plt_range : tupple (float, float, fct)
fct as defined in scgenerator.physics.units
x_axis : 1D array
the corresponding x_axis
yscaling : float, optional
scale the y values by this amount
spacing : int, float, optional
tells the function to take one value every `spacing` one available. If a float is given, it will interpolate with a spline.
frep : float
used for conversion between frequency and wavelength if necessary
Returns
----------
new_x_axis : array
new_values : array
"""
is_spectrum = values.dtype == "complex"
unique = len(values.shape) == 1
values = np.atleast_2d(values)
x_axis, ind, ext = units.sort_axis(x_axis, plt_range)
if is_spectrum:
values = abs2(values)
values *= yscaling
values = values[:, ind]
if plt_range[2].type == "WL":
values = np.apply_along_axis(units.to_WL, -1, values, frep, x_axis)
if isinstance(spacing, float):
new_x_axis = np.linspace(*span(x_axis), int(len(x_axis) / spacing))
values = np.array([UnivariateSpline(x_axis, value, k=4, s=0)(new_x_axis) for value in values])
x_axis = new_x_axis
elif isinstance(spacing, int) and spacing > 1:
values = values[:, ::spacing]
x_axis = x_axis[::spacing]
return x_axis, np.squeeze(values)
def plot_dispersion_parameter(params, plt_range):
"""
Plots the dispersion parameter D as well as the beta2 parameter over the given range
"""
# TODO allow several curves, with legends, to be plotted
x_axis = np.linspace(*plt_range[:2], 1000)
w_axis = plt_range[2](x_axis)
if "disp_obj" in params:
D = params["disp_obj"].D_w(w_axis)
beta2 = params["disp_obj"].beta2_w(w_axis)
else:
print("no dispersion information given")
return
fig, (ax_D, ax_beta2) = plt.subplots(1, 2)
ax_D.plot(x_axis, 1e6 * D)
ax_D.plot(
x_axis,
0 * x_axis,
":",
c="k",
)
ax_D.set_xlabel(plt_range[2].label)
ax_D.set_ylabel(r"Dispersion parameter $D$ ($\frac{\mathrm{ps}}{\mathrm{nm\ km}}$)")
ax_beta2.plot(x_axis, 1e27 * beta2)
ax_beta2.plot(
x_axis,
0 * x_axis,
":",
c="k",
)
ax_beta2.set_xlabel(plt_range[2].label)
ax_beta2.set_ylabel(r"$\beta_2$ parameter ($\frac{\mathrm{ps}^2}{\mathrm{km}}$)")
plt.show()
def white_bottom_cmap(name, start=0, end=1, new_name="white_background", c_back=(1, 1, 1, 1)):
"""returns a new colormap based on "name" but that has a solid bacground (default=white)"""
top = plt.get_cmap(name, 1024)
n_bottom = 80
bottom = np.ones((n_bottom, 4))
for i in range(4):
bottom[:, i] = np.linspace(c_back[i], top(start)[i], n_bottom)
return ListedColormap(np.vstack((bottom, top(np.linspace(start, end, 1024)))), name=new_name)

103
src/scgenerator/state.py Normal file
View File

@@ -0,0 +1,103 @@
import matplotlib.pyplot as plt
from . import utilities as util
"""
This File is used as a public global variable storage solutions. Main functions are having a centralised
parameters index
plotting parameters
logger
This is not a solution when functions are accessing this module in parallel threads, as
changes made by a thread are not reflected in other threads, which is why another solution should be used when dealing
with parameters or paths (if those paths are not stored in paths.json)
"""
class CurrentLogger:
_current_logger = None
@classmethod
def focus_logger(cls, logger):
cls._current_logger = logger
@classmethod
def log(cls, *args, **kwargs):
if cls._current_logger is not None:
util.ray_safe(cls._current_logger.log, *args, **kwargs)
else:
print(*args)
# WILL BREAK SIMULATION SAVING AND MERGING IF CHANGED
recorded_types = ["spectra", "params"] # nickname of the objects saved and tracked when doing automatic simulations
# --------SIMULATION VARIABLES--------#
default_z_target_size = 128
# ---------PLOTTING VARIABLES---------#
def plot_arrowstyle(direction=1, color="white"):
return dict(
arrowprops=dict(arrowstyle="->", connectionstyle=f"arc3,rad={direction*0.3}", color=color),
color=color,
backgroundcolor=(0.5, 0.5, 0.5, 0.5),
)
plot_default_figsize = (10, 7)
plot_default_2D_interpolation = "bicubic"
plot_default_vmin = -40
plot_default_vmax = 0
plot_default_vmax_with_headroom = 2
plot_default_name = "plot"
plot_avg_default_main_to_coherence_ratio = 4
plot_avg_default_line_labels = ["individual values", "mean"]
plot_muted_style = dict(linewidth=0.5, c=(0.8, 0.8, 0.8, 0.4))
plot_highlighted_style = dict(c="red")
plot_default_color_cycle = plt.rcParams["axes.prop_cycle"].by_key()["color"]
plot_default_light_color = (1, 1, 1, 0.7)
plot_default_markers = ["*", "+", ".", "D", "x", "d", "v", "s", "1", "^"]
plot_default_cmap = "viridis"
plot_label_quality_factor = r"$F_\mathrm{Q}$"
plot_label_mean_g12 = r"$\langle | g_{12} |\rangle$"
plot_label_g12 = r"|$g_{12}$|"
plot_label_z = "propagation distance z (m)"
plot_label_fwhm = r"$T_\mathrm{FWHM}$ (fs)"
plot_label_wb_distance = r"$L_\mathrm{WB}$"
plot_label_t_jitter = "timing jitter (fs)"
plot_label_fwhm_noise = "FWHM noise (%)"
plot_label_int_noise = "RIN (%)"
plot_text_topright_style = dict(verticalalignment="top", horizontalalignment="right")
plot_text_topleft_style = dict(verticalalignment="top", horizontalalignment="left")
# ------------------------------------#
# plotting choices
dist = 1.5
# plotting variables
def style(k):
return dict(
marker=plot_default_markers[k], markerfacecolor="none", linestyle=":", lw=1, c=plot_default_color_cycle[k]
)
default_width = 10
def fs(ratio):
return (default_width, default_width * ratio)
# store global variables for debug purposes
_DEBUG = {}

View File

@@ -0,0 +1,235 @@
"""
This files includes utility functions designed more or less to be used specifically with the
scgenerator module but some function may be used in any python program
"""
import datetime as dt
import itertools
from typing import Callable, List, Tuple, Union, Any
import numpy as np
import ray
from .math import *
from .const import valid_varying
# XXX ############################################
# XXX ############### Pure Python ################
# XXX ############################################
class ProgressTracker:
def __init__(
self,
max: Union[int, float],
auto_print: bool = False,
percent_incr: Union[int, float] = 5,
default_update: Union[int, float] = 1,
callback: Callable[[str, Any], None] = None,
):
self.max = max
self.current = 0
self.start_time = dt.datetime.now()
self.auto_print = auto_print
self.next_percent = percent_incr
self.percent_incr = percent_incr
self.default_update = default_update
self.callback = callback
def _update(self, callback_args):
if self.auto_print and self.current / self.max >= self.next_percent / 100:
self.next_percent += self.percent_incr
if self.callback is None:
print(self.ETA)
else:
self.callback(self.ETA, *callback_args)
def update(self, num=None, callback_args=[]):
if num is None:
num = self.default_update
self.current += num
self._update(callback_args)
def set(self, value, callback_args=[]):
self.current = value
self._update(callback_args)
@property
def ETA(self):
if self.current <= 0:
return "\033[31mETA : unknown\033[0m"
eta = (
(dt.datetime.now() - self.start_time).seconds / self.current * (self.max - self.current)
)
H = eta // 3600
M = (eta - H * 3600) // 60
S = eta % 60
percent = int(100 * self.current / self.max)
return "\033[34mremaining : {:.0f}h {:.0f}min {:.0f}s ({:.0f}% in total). \033[31mETA : {:%Y-%m-%d %H:%M:%S}\033[0m".format(
H, M, S, percent, dt.datetime.now() + dt.timedelta(seconds=eta)
)
def get_eta(self):
return self.ETA
def __str__(self):
return "{}/{}".format(self.current, self.max)
# def ray_safe(func, *args, **kwargs):
# """evaluates functions that return None whether they are Ray workers or normal functions
# Parameters
# ----------
# func : the function or Worker id
# args : arguments to give to the functions
# Returns
# ----------
# nothing
# """
# if hasattr(func, "remote"):
# ray.get(func.remote(*args, **kwargs))
# else:
# func(*args, **kwargs)
def count_variations(config: dict) -> Tuple[int, int]:
"""returns True if the config specified by the config dict requires only on simulation run"""
num = 1
varying_params = 0
for section_name in valid_varying:
for array in config.get(section_name, {}).get("varying", {}).values():
num *= len(array)
varying_params += 1
num *= config["simulation"].get("repeat", 1)
return num, varying_params
def format_varying_list(l: List[tuple], joints: List[str] = ""):
while len(joints) < 2:
joints += "_"
str_list = []
for p_name, p_value in l:
ps = p_name.replace("/", "").replace(joints[0], "").replace(joints[1], "")
vs = format_value(p_value).replace("/", "").replace(joints[0], "").replace(joints[1], "")
str_list.append(ps + joints[1] + vs)
return joints[0].join(str_list)
def varying_list_from_path(s: str) -> List[tuple]:
s = s.replace("/", "")
str_list = s.split("_")
out = []
for i in range(0, len(str_list) // 2 * 2, 2):
out.append((str_list[i], get_value(str_list[i + 1])))
return out
def format_value(value):
if type(value) == type(False):
return str(value)
elif isinstance(value, (float, int)):
return format(value, ".5g")
elif isinstance(value, (list, tuple, np.ndarray)):
return "-".join([format_value(v) for v in value])
else:
return str(value)
def get_value(s: str):
if s.lower() == "true":
return True
if s.lower() == "false":
return False
try:
return int(s)
except ValueError:
pass
try:
return float(s)
except ValueError:
pass
return s
def varying_iterator(config):
varying_dict = {
section_name: config.get(section_name, {}).pop("varying", {})
for section_name in valid_varying
}
possible_keys = []
possible_ranges = []
for section_name, section in varying_dict.items():
for key in section:
arr = np.atleast_1d(varying_dict[section_name][key])
varying_dict[section_name][key] = arr
possible_keys.append((section_name, key))
possible_ranges.append(range(len(arr)))
combinations = itertools.product(*possible_ranges)
for combination in combinations:
out = config.copy()
only_varying = []
for i, key in enumerate(possible_keys):
parameter_value = varying_dict[key[0]][key[1]][combination[i]]
out[key[0]][key[1]] = parameter_value
only_varying.append((key[1], parameter_value))
yield only_varying, out
def parallelize(func, arg_iter, sim_jobs=4, progress_tracker_kwargs=None, const_kwarg={}):
"""given a function and an iterator of arguments, runs the function in parallel
Parameters
----------
func : a function
arg_iter : an iterator that yields a tuple to be unpacked to the function as argument(s)
sim_jobs : number of simultaneous runs
progress_tracker_kwargs : key word arguments to be passed to the ProgressTracker
const_kwarg : keyword arguments to be passed to the function on every run
Returns
----------
a list of the result ordered like arg_iter
"""
pt = None
if progress_tracker_kwargs is not None:
progress_tracker_kwargs["auto_print"] = True
pt = ray.remote(ProgressTracker).remote(**progress_tracker_kwargs)
# Initial setup
func = ray.remote(func)
jobs = []
results = []
dico = {} # to keep track of the order, as tasks may no finish in order
for k, args in enumerate(arg_iter):
if not isinstance(args, tuple):
print("iterator must return a tuple")
quit()
# as we got through the iterator, wait for first one to finish before
# adding a new job
if len(jobs) >= sim_jobs:
res, jobs = ray.wait(jobs)
results[dico[res[0].task_id()]] = ray.get(res[0])
if pt is not None:
ray.get(pt.update.remote())
newJob = func.remote(*args, **const_kwarg)
jobs.append(newJob)
dico[newJob.task_id()] = k
results.append(None)
# still have to wait for the last few jobs when there is no more new jobs
for j in jobs:
results[dico[j.task_id()]] = ray.get(j)
if pt is not None:
ray.get(pt.update.remote())
return np.array(results)

BIN
test_config.npz Normal file

Binary file not shown.

View File

@@ -0,0 +1,26 @@
name = "test config"
[fiber]
# gamma = 0.018
length = 1
model = "pcf"
pitch = 1.5e-6
pitch_ratio = 0.37
[pulse]
intensity_noise = 0.05e-2
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
width = 50e-15
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
raman_type = "agrawal"
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,29 @@
#t0 or width missing
name = "test config"
[fiber]
gamma = 0.018
length = 1
model = "pcf"
pitch = 1.5e-6
pitch_ratio = 0.37
[pulse]
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
intensity_noise = [0.05e-2, 0.1e-2]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
raman_type = "agrawal"
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,29 @@
#t0, width, power or energy missing
name = "test config"
[fiber]
gamma = 0.018
length = 1
model = "pcf"
pitch = 1.5e-6
pitch_ratio = 0.37
[pulse]
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
intensity_noise = [0.05e-2, 0.1e-2]
soliton_num = [1, 2, 3, 4]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
raman_type = "agrawal"
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,29 @@
# window size or dt missing
name = "test config"
[fiber]
gamma = 0.018
length = 1
model = "pcf"
pitch = 1.5e-6
pitch_ratio = 0.37
[pulse]
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
intensity_noise = [0.05e-2, 0.1e-2]
width = [50e-15, 100e-15, 200e-15]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
raman_type = "agrawal"
repeat = 4
t_num = 16384
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,31 @@
#multiple width parameters
name = "test config"
[fiber]
gamma = 0.018
length = 1
model = "pcf"
pitch = 1.5e-6
pitch_ratio = 0.37
[pulse]
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
width = 120e-15
[pulse.varying]
intensity_noise = [0.05e-2, 0.1e-2]
width = [50e-15, 100e-15, 200e-15]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
raman_type = "agrawal"
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,32 @@
# missing capillary_thickness
name = "test config"
[fiber]
capillary_num = 6
capillary_outer_d = 2e-6
capillary_spacing = 4e-6
core_radius = 50e-6
gamma = 0.018
length = 1
model = "hasan"
[pulse]
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
intensity_noise = [0.05e-2, 0.1e-2]
width = [50e-15, 100e-15, 200e-15]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
raman_type = "agrawal"
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,31 @@
# missing capillary_outer_d and capillary_spacing
name = "test config"
[fiber]
capillary_num = 6
capillary_thickness = 4e-6
core_radius = 50e-6
gamma = 0.018
length = 1
model = "hasan"
[pulse]
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
intensity_noise = [0.05e-2, 0.1e-2]
width = [50e-15, 100e-15, 200e-15]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
raman_type = "agrawal"
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,29 @@
# model 'pcf' should be added and no gas dico
name = "test config"
[fiber]
gamma = 0.018
length = 1
pitch = 1.5e-6
pitch_ratio = 0.37
[pulse]
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
intensity_noise = [0.05e-2, 0.1e-2]
width = [50e-15, 100e-15, 200e-15]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
raman_type = "agrawal"
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,29 @@
# raman_type should be added
name = "test config"
[fiber]
gamma = 0.018
length = 1
model = "pcf"
pitch = 1.5e-6
pitch_ratio = 0.37
[pulse]
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
intensity_noise = [0.05e-2, 0.1e-2]
width = [50e-15, 100e-15, 200e-15]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,28 @@
# name should be added
[fiber]
gamma = 0.018
length = 1
model = "pcf"
pitch = 1.5e-6
pitch_ratio = 0.37
[pulse]
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
intensity_noise = [0.05e-2, 0.1e-2]
width = [50e-15, 100e-15, 200e-15]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
raman_type = "agrawal"
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,38 @@
# should add capillary_nested and capillary_resonance_strengths
name = "test config"
[fiber]
capillary_num = 6
capillary_outer_d = 2e-6
capillary_spacing = 4e-6
capillary_thickness = 1e-7
core_radius = 50e-6
length = 1
model = "hasan"
[gas]
gas_name = "helium"
[gas.varying]
temperature = [300, 350, 400]
[pulse]
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
intensity_noise = [0.05e-2, 0.1e-2]
width = [50e-15, 100e-15, 200e-15]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
raman_type = "agrawal"
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,29 @@
# should add he_mode and gas
name = "test config"
[fiber]
core_radius = 50e-6
gamma = 0.018
length = 1
model = "marcatili"
[pulse]
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
intensity_noise = [0.05e-2, 0.1e-2]
width = [50e-15, 100e-15, 200e-15]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
raman_type = "agrawal"
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,31 @@
# should not touch simulation parameters
# should add wavelength ranges
name = "test config"
[fiber]
gamma = 0.018
length = 1
model = "pcf"
pitch = 1.5e-6
pitch_ratio = 0.37
[pulse]
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
intensity_noise = [0.05e-2, 0.1e-2]
width = [50e-15, 100e-15, 200e-15]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
raman_type = "agrawal"
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,32 @@
name = "full anomalous"
[fiber]
beta = [
-1.183e-26,
8.1038e-41,
-9.5205e-56,
2.0737e-70,
-5.3943e-85,
1.3486e-99,
-2.5495e-114,
3.0524e-129,
-1.714e-144,
]
gamma = 0.11
length = 0.02
[pulse]
power = 10000
t0 = 2.84e-14
[pulse.varying]
wavelength = [835e-9, 830e-9]
[simulation]
dt = 1e-15
parallel = 3
raman_type = "measured"
repeat = 4
t_num = 16384
tolerated_error = 1e-10
z_num = 64

View File

@@ -0,0 +1,20 @@
[fiber]
core_radius = 50e-6
length = 50e-2
model = "marcatili"
[gas]
gas_name = "air"
[pulse]
wavelength = 800e-9
width = 250e-15
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
repeat = 2
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,23 @@
[fiber]
core_radius = 50e-6
length = 50e-2
model = "marcatili"
[gas]
gas_name = "air"
[pulse]
wavelength = 800e-9
width = 250e-15
[pulse.varying]
shape = ["gaussian", "sech"]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
repeat = 1
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,20 @@
[fiber]
core_radius = 50e-6
length = 50e-2
model = "marcatili"
[gas]
gas_name = "air"
[pulse]
wavelength = 800e-9
width = 250e-15
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
repeat = 1
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,30 @@
# pitch in wrong section
name = "test config"
[fiber]
gamma = 0.018
length = 1
model = "pcf"
pitch_ratio = 0.37
[pulse]
pitch = 1.5e-6
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
intensity_noise = [0.05e-2, 0.1e-2]
width = [50e-15, 100e-15, 200e-15]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
raman_type = "agrawal"
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,30 @@
# wrong value in behaviors
name = "test config"
[fiber]
gamma = 0.018
length = 1
model = "pcf"
pitch = 1.5e-6
pitch_ratio = 0.37
[pulse]
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
intensity_noise = [0.05e-2, 0.1e-2]
width = [50e-15, 100e-15, 200e-15]
[simulation]
behaviors = ["spm", "raman", "ss", "q_noise"]
parallel = 2
raman_type = "agrawal"
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,30 @@
# wrong type in width
name = "test config"
[fiber]
gamma = 0.018
length = 1
model = "pcf"
pitch = 1.5e-6
pitch_ratio = 0.37
[pulse]
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
intensity_noise = [0.05e-2, 0.1e-2]
width = ["gaussian", "sech"]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
raman_type = "agrawal"
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,32 @@
# parallel should not be varying
name = "test config"
[fiber]
gamma = 0.018
length = 1
model = "pcf"
pitch = 1.5e-6
pitch_ratio = 0.37
[pulse]
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
intensity_noise = [0.05e-2, 0.1e-2]
width = [50e-15, 100e-15, 200e-15]
[simulation]
behaviors = ["spm", "raman", "ss"]
raman_type = "agrawal"
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 1
[simulation.varying]
parallel = [2, 4]

View File

@@ -0,0 +1,30 @@
#varying parameters should be lists
name = "test config"
[fiber]
gamma = 0.018
length = 1
model = "pcf"
pitch = 1.5e-6
pitch_ratio = 0.37
[pulse]
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
intensity_noise = 0.05e-2
width = [50e-15, 100e-15, 200e-15]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
raman_type = "agrawal"
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,30 @@
#repeat should not be 0
name = "test config"
[fiber]
gamma = 0.018
length = 1
model = "pcf"
pitch = 1.5e-6
pitch_ratio = 0.37
[pulse]
intensity_noise = 0.05e-2
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
width = [50e-15, 100e-15, 200e-15]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
raman_type = "agrawal"
repeat = 0
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,28 @@
# gamma missing
name = "test config"
[fiber]
beta = [1, 2, 3]
gamma = 0.018
length = 1
[pulse]
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
intensity_noise = []
width = [50e-15, 100e-15, 200e-15]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
raman_type = "agrawal"
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

View File

@@ -0,0 +1,28 @@
name = "test config"
[fiber]
gamma = 0.018
length = 1
model = "pcf"
pitch = 1.5e-6
pitch_ratio = 0.37
[pulse]
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
intensity_noise = [0.05e-2, 0.1e-2]
width = [50e-15, 100e-15, 200e-15]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
raman_type = "agrawal"
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

155
testing/test_initialize.py Normal file
View File

@@ -0,0 +1,155 @@
import unittest
import toml
import scgenerator.initialize as init
from scgenerator.errors import *
from prettyprinter import pprint
def load_conf(name):
with open("testing/configs/" + name + ".toml") as file:
conf = toml.load(file)
return conf
def conf_maker(folder):
def conf(name):
return load_conf(folder + "/" + name)
return conf
class TestInitializeMethods(unittest.TestCase):
def test_validate_types(self):
conf = lambda s: load_conf("validate_types/" + s)
with self.assertRaisesRegex(TypeError, "belong"):
init.validate_types(conf("bad1"))
with self.assertRaisesRegex(TypeError, "valid list of behaviors"):
init.validate_types(conf("bad2"))
with self.assertRaisesRegex(TypeError, "single, real, non-negative number"):
init.validate_types(conf("bad3"))
with self.assertRaisesRegex(TypeError, "'parallel' is not a valid variable parameter"):
init.validate_types(conf("bad4"))
with self.assertRaisesRegex(TypeError, "Varying parameters should be specified in a list"):
init.validate_types(conf("bad5"))
with self.assertRaisesRegex(
TypeError,
"value '0' of type <class 'int'> for key 'repeat' is not valid, must be a strictly positive integer",
):
init.validate_types(conf("bad6"))
with self.assertRaisesRegex(
ValueError,
r"Varying parameters lists should contain at least 1 element",
):
init.ensure_consistency(conf("bad7"))
self.assertIsNone(init.validate_types(conf("good")))
def test_ensure_consistency(self):
conf = lambda s: load_conf("ensure_consistency/" + s)
with self.assertRaisesRegex(
MissingParameterError,
r"1 of '\['t0', 'width'\]' is required and no defaults have been set",
):
init.ensure_consistency(conf("bad1"))
with self.assertRaisesRegex(
MissingParameterError,
r"1 of '\['power', 'energy', 'width', 't0'\]' is required when 'soliton_num' is specified and no defaults have been set",
):
init.ensure_consistency(conf("bad2"))
with self.assertRaisesRegex(
MissingParameterError,
r"2 of '\['dt', 't_num', 'time_window'\]' are required and no defaults have been set",
):
init.ensure_consistency(conf("bad3"))
with self.assertRaisesRegex(
DuplicateParameterError,
r"got multiple values for parameter 'width'",
):
init.ensure_consistency(conf("bad4"))
with self.assertRaisesRegex(
MissingParameterError,
r"'capillary_thickness' is a required parameter for fiber model 'hasan' and no defaults have been set",
):
init.ensure_consistency(conf("bad5"))
with self.assertRaisesRegex(
MissingParameterError,
r"1 of '\['capillary_spacing', 'capillary_outer_d'\]' is required for fiber model 'hasan' and no defaults have been set",
):
init.ensure_consistency(conf("bad6"))
self.assertLessEqual(
{"model": "pcf"}.items(), init.ensure_consistency(conf("good1"))["fiber"].items()
)
self.assertNotIn("gas", init.ensure_consistency(conf("good1")))
self.assertNotIn("gamma", init.ensure_consistency(conf("good4"))["fiber"])
self.assertLessEqual(
{"raman_type": "agrawal"}.items(),
init.ensure_consistency(conf("good2"))["simulation"].items(),
)
self.assertLessEqual(
{"name": "no name"}.items(), init.ensure_consistency(conf("good3")).items()
)
self.assertLessEqual(
{"capillary_nested": 0, "capillary_resonance_strengths": []}.items(),
init.ensure_consistency(conf("good4"))["fiber"].items(),
)
self.assertLessEqual(
dict(he_mode=(1, 1)).items(),
init.ensure_consistency(conf("good5"))["fiber"].items(),
)
self.assertLessEqual(
dict(temperature=300, pressure=1e5, gas_name="vacuum", plasma_density=0).items(),
init.ensure_consistency(conf("good5"))["gas"].items(),
)
self.assertLessEqual(
dict(
t_num=16384,
time_window=37e-12,
lower_wavelength_interp_limit=0,
upper_wavelength_interp_limit=1900e-9,
).items(),
init.ensure_consistency(conf("good6"))["simulation"].items(),
)
def test_single_sim(self):
conf = conf_maker("single_sim")
self.assertTrue(init.single_sim(conf("true1")))
self.assertFalse(init.single_sim(conf("false1")))
self.assertFalse(init.single_sim(conf("false2")))
# def test_compute_init_parameters(self):
# conf = lambda s: load_conf("compute_init_parameters/" + s)
if __name__ == "__main__":
conf = conf_maker("validate_types")
config = conf("good")
pprint(config)
config = init.ensure_consistency(config)
pprint(config)
params = init.compute_init_parameters(config)
pprint(params)
unittest.main()

41
testing/test_pulse.py Normal file
View File

@@ -0,0 +1,41 @@
import unittest
from scgenerator.physics.pulse import conform_pulse_params
class TestPulseMethods(unittest.TestCase):
def test_conform_pulse_params(self):
self.assertNotIn(None, conform_pulse_params("gaussian", t0=5, energy=6))
self.assertNotIn(None, conform_pulse_params("gaussian", width=5, energy=6))
self.assertNotIn(None, conform_pulse_params("gaussian", t0=5, power=6))
self.assertNotIn(None, conform_pulse_params("gaussian", width=5, power=6))
self.assertEqual(4, len(conform_pulse_params("gaussian", t0=5, energy=6)))
self.assertEqual(4, len(conform_pulse_params("gaussian", width=5, energy=6)))
self.assertEqual(4, len(conform_pulse_params("gaussian", t0=5, power=6)))
self.assertEqual(4, len(conform_pulse_params("gaussian", width=5, power=6)))
with self.assertRaisesRegex(
TypeError, "when soliton number is desired, both gamma and beta2 must be specified"
):
conform_pulse_params("gaussian", t0=5, energy=6, gamma=0.01)
with self.assertRaisesRegex(
TypeError, "when soliton number is desired, both gamma and beta2 must be specified"
):
conform_pulse_params("gaussian", t0=5, energy=6, beta2=0.01)
self.assertEqual(
5, len(conform_pulse_params("gaussian", t0=5, energy=6, gamma=0.01, beta2=2e-6))
)
self.assertEqual(
5, len(conform_pulse_params("gaussian", width=5, energy=6, gamma=0.01, beta2=2e-6))
)
self.assertEqual(
5, len(conform_pulse_params("gaussian", t0=5, power=6, gamma=0.01, beta2=2e-6))
)
self.assertEqual(
5, len(conform_pulse_params("gaussian", width=5, power=6, gamma=0.01, beta2=2e-6))
)
if __name__ == "__main__":
unittest.main()

84
tests.py Normal file
View File

@@ -0,0 +1,84 @@
import numpy as np
import scgenerator as sc
import matplotlib.pyplot as plt
def convert(l, beta2):
return l[2:-2] * 1e9, sc.units.beta2_fs_cm.inv(beta2[2:-2])
def test_empty_marcatili():
l = np.linspace(250, 1200, 500) * 1e-9
beta2 = sc.fiber.HCPCF_dispersion(l, 15e-6)
plt.plot(*convert(l, beta2))
plt.show()
def test_empty_hasan_no_resonance():
l = np.linspace(250, 1200, 500) * 1e-9
beta2 = sc.fiber.HCPCF_dispersion(
l, 12e-6, model="hasan", model_params=dict(t=0.2e-6, g=1e-6, n=6)
)
plt.plot(*convert(l, beta2))
plt.show()
def test_empty_hasan():
l = np.linspace(250, 1200, 500) * 1e-9
fig, (ax, ax2) = plt.subplots(2, 1, figsize=(6, 7), gridspec_kw=dict(height_ratios=[3, 1]))
ax.set_ylim(-40, 20)
ax2.set_ylim(-100, 0)
beta2 = sc.fiber.HCPCF_dispersion(
l,
12e-6,
model="hasan",
model_params=dict(t=0.2e-6, g=1e-6, n=6, resonance_strength=(2e-6,)),
)
ax.plot(*convert(l, beta2))
beta2 = sc.fiber.HCPCF_dispersion(
l, 12e-6, model="hasan", model_params=dict(t=0.2e-6, g=1e-6, n=6)
)
ax.plot(*convert(l, beta2))
l = np.linspace(500, 1500, 500) * 1e-9
beta2 = sc.fiber.HCPCF_dispersion(
l, 12e-6, model="hasan", model_params=dict(t=0.2e-6, g=1e-6, n=10)
)
ax2.plot(*convert(l, beta2))
plt.show()
def test_custom_initial_field():
param = {
"name": "test",
"lambda0": [1030, "nm"],
"E0": [6, "uJ"],
"T0_FWHM": [27, "fs"],
"frep": 151e3,
"z_targets": [0, 0.07, 128],
"gas": "argon",
"pressure": 4e5,
"temperature": 293,
"pulse_shape": "sech",
"behaviors": [],
"fiber_model": "marcatili",
"model_params": {"core_radius": 18e-6},
"field_0": "exp(-(t/t0)**2)*P0 + P0/10 * cos(t/t0)*2*exp(-(0.05*t/t0)**2)",
"nt": 16384,
"T": 2e-12,
"adapt_step_size": True,
"error_ok": 1e-10,
"interp_range": [120, 2000],
"n_percent": 2,
}
p = sc.compute_init_parameters(dictionary=param)
fig, ax = plt.subplots()
ax.plot(p["t"], abs(p["field_0"]))
plt.show()
if __name__ == "__main__":
# test_empty_marcatili()
# test_empty_hasan()
test_custom_initial_field()

6
tests/numpy.toml Normal file
View File

@@ -0,0 +1,6 @@
a = [ 0, 1, 2, 3, 4,]
c = [ 0, 1, 2, 3, 4,]
b = "<built-in function print>"
aa = [ "0j", "(1+0j)", "(2+0j)", "(3+0j)", "(4+0j)",]
bb = [ 0.0, 1.0, 2.0, 3.0, 4.0,]
ddd = 2021-01-21T07:55:18.881790

49343
tests/param.json Normal file

File diff suppressed because it is too large Load Diff

4
tests/param.toml Normal file
View File

@@ -0,0 +1,4 @@
a = [ 0, 1, 2, 3, 4,]
c = [ 0, 1, 2, 3, 4,]
bb = [ 0.0, 1.0, 2.0, 3.0, 4.0,]
datetime = 2021-01-21T07:55:18.882279

15
tests/playground.py Normal file
View File

@@ -0,0 +1,15 @@
from scgenerator import io
from scgenerator.io import _get_data_subfolders
import numpy as np
from glob import glob
from scgenerator.math import abs2
from matplotlib import pyplot as plt
path = "scgenerator_full anomalous123/wavelength_8.35e-07"
for i in [0, 63]:
dat = np.load(f"{path}/spectra_{i}.npy")
for d in dat:
plt.plot(abs2(d))
plt.show()

15
tests/test_Config.py Normal file
View File

@@ -0,0 +1,15 @@
from scgenerator.initialize import ParamSequence
from logging import StreamHandler
from scgenerator import io
from scgenerator import utilities
# dispatcher = ParamSequence(io.load_toml("testing/configs/ensure_consistency/good4"))
dispatcher = ParamSequence(io.load_toml("testing/configs/compute_init_parameters/good"))
print(dispatcher)
for only, params in dispatcher:
print(only, params["width"])
print(len(dispatcher))
print(dispatcher["fiber", "length"])
print(utilities.varying_list_from_path("/a_5_b_asdf"))

View File

@@ -0,0 +1,13 @@
from prettyprinter import pprint
from scgenerator import initialize as init
from scgenerator.io import load_toml
debug = 56
config = load_toml("testing/configs/compute_init_parameters/good.toml")
config = init.ensure_consistency(config)
try:
params = init.compute_init_parameters(config)
except:
raise
pprint(params)

38
tests/test_config.json Normal file
View File

@@ -0,0 +1,38 @@
{
"name": "test config",
"fiber": {
"gamma": 0.018,
"pitch": 1.5e-06,
"pitch_ratio": 0.37,
"type": "pcf"
},
"pulse": {
"power": 100000.0,
"quantum_noise": true,
"shape": "gaussian",
"wavelength": 1.05e-06,
"varying": {
"intensity_noise": [
0.0005,
0.001
],
"pulse_width": [
5e-14,
1e-13,
2e-13
]
}
},
"simulation": {
"behaviors": [
"spm",
"raman",
"ss"
],
"nt": 16384,
"raman_type": "agrawal",
"repeat": 4,
"time_window": 3.7e-11,
"tolerated_error": 1e-11
}
}

28
tests/test_config.toml Normal file
View File

@@ -0,0 +1,28 @@
name = "test config"
[fiber]
gamma = 0.018
length = 1
model = "pcf"
pitch = 1.5e-6
pitch_ratio = 0.37
[pulse]
power = 100e3
quantum_noise = true
shape = "gaussian"
wavelength = 1050e-9
[pulse.varying]
intensity_noise = [0.05e-2, 0.1e-2]
pulse_width = [50e-15, 100e-15, 200e-15]
[simulation]
behaviors = ["spm", "raman", "ss"]
parallel = 2
raman_type = "agrawal"
repeat = 4
t_num = 16384
time_window = 37e-12
tolerated_error = 1e-11
z_num = 128

11
tests/test_load_config.py Normal file
View File

@@ -0,0 +1,11 @@
import toml
from prettyprinter import pprint
import json
config = toml.load("tests/test_config.toml")
pprint(config)
with open("tests/test_config.toml") as file:
config = toml.load(file)
# with open("tests/test_config.json", "w") as file:
# json.dump(config, file)

29
tests/test_merge_data.py Normal file
View File

@@ -0,0 +1,29 @@
from scgenerator.io import merge_same_simulations
from prettyprinter import pprint
# a, b = [
# "scgenerator_full anomalous123_1/wavelength_8.35e-07_num_3",
# "scgenerator_full anomalous123_1/wavelength_8.35e-07_num_2",
# "scgenerator_full anomalous123_1/wavelength_8.3e-07_num_1",
# "scgenerator_full anomalous123_1/wavelength_8.3e-07_num_0",
# "scgenerator_full anomalous123_1/wavelength_8.35e-07_num_0",
# "scgenerator_full anomalous123_1/wavelength_8.35e-07_num_1",
# "scgenerator_full anomalous123_1/wavelength_8.3e-07_num_2",
# "scgenerator_full anomalous123_1/wavelength_8.3e-07_num_3",
# ], [
# [("wavelength", 8.3e-07), ("num", 0)],
# [("wavelength", 8.35e-07), ("num", 0)],
# [("wavelength", 8.3e-07), ("num", 1)],
# [("wavelength", 8.35e-07), ("num", 1)],
# [("wavelength", 8.3e-07), ("num", 2)],
# [("wavelength", 8.35e-07), ("num", 2)],
# [("wavelength", 8.3e-07), ("num", 3)],
# [("wavelength", 8.35e-07), ("num", 3)],
# ]
# pprint(list(zip(a, b)))
all = merge_same_simulations("scgenerator_full anomalous123")
pprint(all)

View File

@@ -0,0 +1,8 @@
from scgenerator.physics.simulate import new_simulations
from scgenerator import io
import ray
ray.init()
sim = new_simulations("testing/configs/run_simulations/full_anomalous.toml", 123)
sim.run()

17
tests/test_save_config.py Normal file
View File

@@ -0,0 +1,17 @@
from json import encoder
import toml
import numpy as np
from datetime import datetime
from scgenerator.io import save_parameters
x = np.arange(5)
y = np.arange(5, dtype="complex")
z = np.arange(5, dtype="float")
dico = dict(a=x, c=list(x), b=print, aa=y, bb=z, ddd=datetime.now())
with open("tests/numpy.toml", "w") as file:
toml.dump(dico, file, encoder=toml.TomlNumpyEncoder())
save_parameters(dico, "tests/param")
save_parameters(toml.load("tests/test_config.toml"), "tests/test_save_config")

View File

@@ -0,0 +1,26 @@
name = "test config"
datetime = 2021-01-21T07:55:18.883204
[fiber]
gamma = 0.018
pitch = 1.5e-6
pitch_ratio = 0.37
type = "pcf"
[pulse]
power = 100000.0
quantum_noise = true
shape = "gaussian"
wavelength = 1.05e-6
[simulation]
behaviors = [ "spm", "raman", "ss",]
nt = 16384
raman_type = "agrawal"
repeat = 4
time_window = 3.7e-11
tolerated_error = 1e-11
[pulse.varying]
intensity_noise = [ 0.0005, 0.001,]
pulse_width = [ 5e-14, 1e-13, 2e-13,]

View File

@@ -0,0 +1,6 @@
from scgenerator.initialize import validate_types
from scgenerator.io import load_toml
config = load_toml("tests/test_config.toml")
validate_types(config)