working direct dispersion

This commit is contained in:
Benoît Sierro
2021-10-21 14:28:34 +02:00
parent 4c60835d3d
commit 039357bff2
7 changed files with 9359 additions and 102 deletions

View File

@@ -4,24 +4,40 @@ It is recommended to import scgenerator in the following manner :
# How to run a set of simulations
create a config file
run `sc.parallel_simulations(config_file)` or `sc.simulate(config_file)`
run `sc.run_simulation(config_file)`
# How to analyse a simulation
load data with the load_sim_data method
spectra, params = load_sim_data("varyTechNoise100kW_sim_data")
to plot
plot_results_2D(spectra[0], (600, 1450, nm), params)
Load completed simulations with the SimulationSeries class. The path argument should be that of the last fiber in the series (of the form `xx fiber A...` where `xx` is an integer)
The SimulationSeries class has basic plotting functions available. See the class documentation for more info
```
series = sc.SimulationSeries(path_to_data_folder)
fig, ax = plt.subplots()
series.plot_2D(800, 1600, "nm", ax)
plt.show()
```
# Environment variables
SCGENERATOR_PBAR_POLICY : "none", "file", "print", "both", optional
`SCGENERATOR_PBAR_POLICY` : "none", "file", "print", "both", optional
whether progress should be printed to a file ("file"), to the standard output ("print") or both, default : print
`SCGENERATOR_LOG_FILE_LEVEL` : "debug", "info", "warning", "error", "critical", optional
level of logging printed in $PWD/scgenerator.log
`SCGENERATOR_LOG_PRINT_LEVEL` : "debug", "info", "warning", "error", "critical", optional
level of logging printed in the cli.
# Configuration
You can load parameters by simply 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 root of the file has information concerning the whole simulation : name, grid information, input pulse, ...
Then, there must be a `[[Fiber]]` array with at least one fiber with fiber-specific parameters.
Parameters can be variable (either in the root or in one fiber). if at most one single `[variable]` dict is specified by section, all the possible combinations of those variable parameters are considered. Another possibility is to specify a `[[variable]]` array where the length of each set of parameter is the same so they're coupled to each other.
Examples :
```
@@ -38,11 +54,14 @@ n2 = 2.2e-20
[variable]
n2 = [2.1e-20, 2.4e-20, 2.6e-20]
```
NOT ALLOWED
NOT ALLOWED. You cannot specified the same parameter twice.
Here is an example of a configuration file
```
# these be applied to the whole simulation fiber PM1550_1 only
# fiber parameters specified here would apply to the whole simulation as well
# unless overridden in one of the individual fiber
name = "Test/Compound 1"
field_file = "Toptica/init_field.npz"
@@ -57,11 +76,9 @@ z_num = 32
mean_power = 200e-3
repeat = 3
# will be applied to the whole simulation fiber PM1550_1 only
# fiber parameters specified here would apply to the whole simulation as well
# unless overridden in one of the individual fiber
[variable]
behaviors = [["spm", "ss", "raman"], ["spm", "ss"]]
[[variable]]
spm = [true, false]
raman_type = ["agrawal", "stolen"]
[[Fiber]]
@@ -82,8 +99,7 @@ dispersion_file = "PM2000D/Dispersion/PM2000D_1 extrapolated 0 4.npz"
input_transmission = [0.9, 0.95]
```
note : internally, another structure with a flattened dictionary is used
this means that only `(spm=true, raman_type="agrawal")` and `(spm=false, raman_type="stolen")` are considered and not `(spm=false, raman_type="agrawal")` for example. In the end, 12 simulations are ran with this configuration.

37
make_new_dispersion.py Normal file
View File

@@ -0,0 +1,37 @@
import numpy as np
import scgenerator as sc
import matplotlib.pyplot as plt
def main():
params = sc.Configuration(
"/Users/benoitsierro/tests/test_sc/Travers2019 Fig2 dev/initial_config.toml"
).first
evalu = sc.Evaluator.default()
evalu.set(**params.prepare_for_dump())
n_eff = evalu.compute("n_eff")
wl = evalu.compute("wl_for_disp")
w = evalu.compute("w_for_disp")
print(w.max(), w.min())
disp_inf = evalu.compute("dispersion_ind")
# quit()
params.interpolation_degree = 6
params.compute()
current_disp = params.linear_operator.dispersion_op
beta = n_eff * w / 3e8
beta1 = np.gradient(beta, w)
ind = sc.argclosest(w, params.w0)
disp = -1j * (beta - beta1[ind] * (w - params.w0) - beta[ind])
disp2 = sc.fiber.fast_direct_dispersion(w, params.w0, n_eff, ind)
plt.plot(params.l * 1e9, current_disp(None).imag)
plt.plot(wl * 1e9, disp.imag)
plt.plot(wl * 1e9, disp2.imag)
plt.xlim(100, 3000)
plt.ylim(-1000, 4000)
plt.show()
if __name__ == "__main__":
main()

View File

@@ -19,3 +19,4 @@ from .plotting import (
from .spectra import SimulationSeries, Spectrum
from .utils import Paths, _open_config, open_single_config
from .variationer import DescriptorDict, VariationDescriptor, Variationer, VariationSpecsError
from .evaluator import Evaluator

View File

@@ -131,9 +131,24 @@ class Evaluator:
self.rules[t].append(r)
self.rules[t].sort(key=lambda el: el.targets[t], reverse=True)
def set(self, **params: Any):
self.params.update(params)
for k in params:
def set(self, dico: dict[str, Any] = None, **params: Any):
"""sets the internal set of parameters
Parameters
----------
dico : dict, optional
if given, replace current dict of parameters with this one
(not a copy of it), by default None
params : Any
if dico is None, update the internal dict of parameters with params
"""
if dico is None:
dico = params
self.params.update(dico)
else:
self.reset()
self.params = dico
for k in dico:
self.eval_stats[k].priority = np.inf
def reset(self):
@@ -175,7 +190,9 @@ class Evaluator:
self.__curent_lookup.append(target)
if len(self.rules[target]) == 0:
error = EvaluatorError(f"no rule for {target}")
error = EvaluatorError(
f"no rule for {target}, trying to evaluate {self.__curent_lookup!r}"
)
else:
error = None
@@ -312,13 +329,9 @@ default_rules: list[Rule] = [
Rule("L_NL", pulse.L_NL),
Rule("L_sol", pulse.L_sol),
# Fiber Dispersion
Rule("wl_for_disp", fiber.lambda_for_dispersion),
Rule(["wl_for_disp", "dispersion_ind"], fiber.lambda_for_dispersion),
Rule("w_for_disp", units.m, ["wl_for_disp"]),
Rule(
"beta2_coefficients",
fiber.dispersion_coefficients,
["wl_for_disp", "beta2_arr", "w0", "interpolation_range", "interpolation_degree"],
),
Rule("beta2_coefficients", fiber.dispersion_coefficients),
Rule("beta2_arr", fiber.beta2),
Rule("beta2_arr", fiber.dispersion_from_coefficients),
Rule("beta2", lambda beta2_coefficients: beta2_coefficients[0]),

View File

@@ -75,8 +75,8 @@ class Operator(ABC):
class NoOp:
def __init__(self, w: np.ndarray):
self.arr_const = np.zeros_like(w)
def __init__(self, t_num: int):
self.arr_const = np.zeros(t_num)
##################################################
@@ -112,23 +112,45 @@ class ConstantPolyDispersion(AbstractDispersion):
def __init__(
self,
wl_for_disp: np.ndarray,
w_for_disp: np.ndarray,
beta2_arr: np.ndarray,
w0: float,
w_c: np.ndarray,
interpolation_range: tuple[float, float],
interpolation_degree: int,
):
self.coefs = fiber.dispersion_coefficients(
wl_for_disp, beta2_arr, w0, interpolation_range, interpolation_degree
)
self.coefs = fiber.dispersion_coefficients(w_for_disp, beta2_arr, w0, interpolation_degree)
self.w_c = w_c
self.w_power_fact = np.array(
[math.power_fact(w_c, k) for k in range(2, interpolation_degree + 3)]
)
def __call__(self, state: CurrentState) -> np.ndarray:
return fiber.fast_dispersion_op(self.w_c, self.coefs, self.w_power_fact)
def __call__(self, state: CurrentState = None) -> np.ndarray:
return fiber.fast_poly_dispersion_op(self.w_c, self.coefs, self.w_power_fact)
class ConstantDirectDispersion(AbstractDispersion):
"""
Direct dispersion for when the refractive index is known
"""
disp_arr_const: np.ndarray
def __init__(
self,
w_for_disp: np.ndarray,
w0: np.ndarray,
t_num: int,
n_eff: np.ndarray,
dispersion_ind: np.ndarray,
):
self.disp_arr_const = np.zeros(t_num)
w0_ind = math.argclosest(w_for_disp, w0)
self.disp_arr_const[dispersion_ind] = fiber.fast_direct_dispersion(
w_for_disp, w0, n_eff, w0_ind
)[2:-2]
def __call__(self, state: CurrentState = None):
return self.disp_arr_const
##################################################
@@ -286,8 +308,8 @@ class AbstractGamma(Operator):
class ConstantScalarGamma(AbstractGamma):
def __init__(self, gamma: np.ndarray, w: np.ndarray):
self.arr_const = gamma * np.ones_like(w)
def __init__(self, gamma: np.ndarray, t_num: int):
self.arr_const = gamma * np.ones(t_num)
def __call__(self, state: CurrentState) -> np.ndarray:
return self.arr_const
@@ -373,8 +395,8 @@ class AbstractLoss(Operator):
class ConstantLoss(AbstractLoss):
arr_const: np.ndarray
def __init__(self, alpha: float, w: np.ndarray):
self.arr_const = alpha * np.ones_like(w)
def __init__(self, alpha: float, t_num: int):
self.arr_const = alpha * np.ones(t_num)
def __call__(self, state: CurrentState = None) -> np.ndarray:
return self.arr_const
@@ -388,15 +410,15 @@ class NoLoss(ConstantLoss):
class CapillaryLoss(ConstantLoss):
def __init__(
self,
l: np.ndarray,
wl_for_disp: np.ndarray,
dispersion_ind: np.ndarray,
t_num: int,
core_radius: float,
interpolation_range: tuple[float, float],
he_mode: tuple[int, int],
):
mask = (l < interpolation_range[1]) & (l > interpolation_range[0])
alpha = fiber.capillary_loss(l[mask], he_mode, core_radius)
self.arr = np.zeros_like(l)
self.arr[mask] = alpha
alpha = fiber.capillary_loss(wl_for_disp, he_mode, core_radius)
self.arr = np.zeros(t_num)
self.arr[dispersion_ind] = alpha[2:-2]
def __call__(self, state: CurrentState) -> np.ndarray:
return self.arr

View File

@@ -5,11 +5,11 @@ from numpy import e
from numpy.fft import fft
from numpy.polynomial.chebyshev import Chebyshev, cheb2poly
from scipy.interpolate import interp1d
from sympy import re
from .. import utils
from ..cache import np_cache
from ..logger import get_logger
from ..math import argclosest, power_fact, u_nm
from ..math import argclosest, u_nm
from . import materials as mat
from . import units
from .units import c, pi
@@ -18,14 +18,28 @@ pipi = 2 * pi
T = TypeVar("T")
def lambda_for_dispersion(interpolation_range: tuple[float, float]) -> np.ndarray:
def lambda_for_dispersion(
l: np.ndarray, interpolation_range: tuple[float, float]
) -> tuple[np.ndarray, np.ndarray]:
"""Returns a wl vector for dispersion calculation
Returns
-------
array of wl values
np.ndarray
subset of l in the interpolation range with two extra values on each side
to accomodate for taking gradients
np.ndarray
indices of the original l where the values are valid (i.e. without the two extra on each side)
"""
return np.arange(interpolation_range[0] - 2e-9, interpolation_range[1] + 3e-9, 1e-9)
su = np.where((l >= interpolation_range[0]) & (l <= interpolation_range[1]))[0]
ind_above_cond = su >= len(l) // 2
ind_above = su[ind_above_cond]
ind_below = su[~ind_above_cond]
fu = np.concatenate((ind_below, (ind_below + 2)[-2:], (ind_above - 2)[:2], ind_above))
fs = fu[np.argsort(l[fu])[::-1]]
l_out = l[fs]
ind_out = fs[2:-2]
return l_out, ind_out
def is_dynamic_dispersion(pressure=None):
@@ -107,9 +121,8 @@ def plasma_dispersion(wl_for_disp, number_density, simple=False):
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
beta2_arr = beta2(w, np.sqrt(1 - number_density * e2_me_e0 / w ** 2))
return beta2_arr
def n_eff_marcatili(wl_for_disp, n_gas_2, core_radius, he_mode=(1, 1)):
@@ -561,6 +574,14 @@ def HCPF_ZDW(
return l[zdw_ind]
def beta(w_for_disp: np.ndarray, n_eff: np.ndarray) -> np.ndarray:
return n_eff * w_for_disp / c
def beta1(w_for_disp: np.ndarray, n_eff: np.ndarray) -> np.ndarray:
return np.gradient(beta(w_for_disp, n_eff), w_for_disp)
def beta2(w_for_disp: np.ndarray, n_eff: np.ndarray) -> np.ndarray:
"""computes the dispersion parameter beta2 according to the effective refractive index of the fiber and the frequency range
@@ -575,7 +596,7 @@ def beta2(w_for_disp: np.ndarray, n_eff: np.ndarray) -> np.ndarray:
-------
beta2 : ndarray, shape (n, )
"""
return np.gradient(np.gradient(n_eff * w_for_disp / c, w_for_disp), w_for_disp)
return np.gradient(np.gradient(beta(w_for_disp, n_eff), w_for_disp), w_for_disp)
def HCPCF_dispersion(
@@ -849,11 +870,10 @@ def load_custom_loss(l: np.ndarray, loss_file: str) -> np.ndarray:
@np_cache
def dispersion_coefficients(
wl_for_disp: np.ndarray,
w_for_disp: np.ndarray,
beta2_arr: np.ndarray,
w0: float,
interpolation_range=None,
interpolation_degree=8,
interpolation_degree: int,
):
"""Computes the taylor expansion of beta2 to be used in dispersion_op
@@ -865,8 +885,6 @@ def dispersion_coefficients(
beta2 as function of wl_for_disp
w0 : float
pump angular frequency
interpolation_range : slice-like
index-style specifying wl range over which to fit to get beta2 coefficients
interpolation_degree : int
degree of polynomial fit. Will return deg+1 coefficients
@@ -875,29 +893,13 @@ def dispersion_coefficients(
beta2_coef : 1D array
Taylor coefficients in decreasing order
"""
logger = get_logger()
if interpolation_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 = (wl_for_disp >= interpolation_range[0]) & (wl_for_disp <= interpolation_range[1])
logger.debug(
f"interpolating dispersion between {wl_for_disp[r].min()*1e9:.1f}nm and {wl_for_disp[r].max()*1e9:.1f}nm"
)
# we get the beta2 Taylor coeffiecients by making a fit around w0
w_c = units.m(wl_for_disp) - w0
interp = interp1d(w_c[r], beta2_arr[r])
w_c = np.linspace(w_c[r].min(), w_c[r].max(), len(w_c[r]))
w_c = w_for_disp - w0
# import matplotlib.pyplot as plt
# ax = plt.gca()
# ax.plot(w_c, interp(w_c) * 1e28)
fit = Chebyshev.fit(w_c, interp(w_c), interpolation_degree)
w_c = w_c[2:-2]
beta2_arr = beta2_arr[2:-2]
fit = Chebyshev.fit(w_c, beta2_arr, interpolation_degree)
poly_coef = cheb2poly(fit.convert().coef)
beta2_coef = poly_coef * np.cumprod([1] + list(range(1, interpolation_degree + 1)))
@@ -981,7 +983,7 @@ def delayed_raman_w(t: np.ndarray, raman_type: str) -> np.ndarray:
return fft(delayed_raman_t(t, raman_type)) * (t[1] - t[0])
def fast_dispersion_op(w_c, beta_arr, power_fact_arr, where=slice(None)):
def fast_poly_dispersion_op(w_c, beta_arr, power_fact_arr, where=slice(None)):
"""
dispersive operator
@@ -1015,39 +1017,32 @@ def _fast_disp_loop(dispersion: np.ndarray, beta_arr, power_fact_arr):
return dispersion
def dispersion_op(w_c, beta2_coefficients, where=None):
"""
dispersive operator
def direct_dispersion(w: np.ndarray, w0: float, n_eff: np.ndarray) -> np.ndarray:
"""returns the dispersive operator in direct form (without polynomial interpolation)
i.e. -1j * (beta(w) - beta1 * (w - w0) - beta0)
Parameters
----------
w_c : 1d array
angular frequencies centered around 0
beta2_coefficients : 1d array
beta coefficients returned by scgenerator.physics.fiber.dispersion_coefficients
where : indices over which to apply the operatory, otherwise 0
w : np.ndarray
angular frequency array
w0 : float
center frequency
n_eff : np.ndarray
effectiv refractive index
Returns
-------
disp_arr : dispersive component as an array of len = len(w_c)
np.ndarray
dispersive operator
"""
dispersion = np.zeros_like(w_c)
for k, beta in reversed(list(enumerate(beta2_coefficients))):
dispersion = dispersion + beta * power_fact(w_c, k + 2)
out = np.zeros_like(dispersion)
out[where] = dispersion[where]
return -1j * out
w0_ind = argclosest(w, w0)
return fast_direct_dispersion(w, w0, n_eff, w0_ind)
def _get_radius(radius_param, wl_for_disp=None):
if isinstance(radius_param, tuple) and wl_for_disp is not None:
return effective_core_radius(wl_for_disp, *radius_param)
else:
return radius_param
def fast_direct_dispersion(w: np.ndarray, w0: float, n_eff: np.ndarray, w0_ind: int) -> np.ndarray:
beta_arr = beta(w, n_eff)
beta1_arr = np.gradient(beta_arr, w)
return -1j * (beta_arr - beta1_arr[w0_ind] * (w - w0) - beta_arr[w0_ind])
def effective_core_radius(wl_for_disp, core_radius, s=0.08, h=200e-9):

9173
test_ind.txt Normal file

File diff suppressed because it is too large Load Diff