diff --git a/README.md b/README.md index 58b2d4a..5c0f724 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,28 @@ spectra, params = load_sim_data("varyTechNoise100kW_sim_data") # 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 +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 configuration file can have a ```name``` parameter at the root and must otherwise contain the following sections with the specified parameters. Every section ("fiber", "gas", "pulse", "simulation") can have a "variable" subsection where parameters are specified in a list INSTEAD of being specified as a single value in the main section. This has the effect of running one simulation per value in the list. If many parameters are variable, all possible combinations are ran. +Examples : + +``` +[fiber] +n2 = 2.2e-20 +``` +a single simulation is ran with this value +``` +[fiber.variable] +n2 = [2.1e-20, 2.4e-20, 2.6e-20] +``` +3 simulations are ran, one for each value +``` +[fiber] +n2 = 2.2e-20 +[fiber.variable] +n2 = [2.1e-20, 2.4e-20, 2.6e-20] +``` +NOT ALLOWED + note : internally, another structure with a flattened dictionary is used @@ -29,6 +49,11 @@ If you already know the Taylor coefficients corresponding to the expansion of th beta: list-like list of Taylor coefficients for the beta_2 function +If you already have a dispersion curve, you can convert it to a npz file with the wavelength (key : 'wavelength') in m and the D parameter (key : 'dispersion') in s/m/m. You the refer to this file as + +dispersion_file : str + path to the npz dispersion file + else, you can choose a mathematical fiber model @@ -81,7 +106,7 @@ capillary_thickness : float capillary_spacing : float, optional if d is specified spacing between the capillary -capillray_resonance_strengths : list, optional +capillary_resonance_strengths : list, optional list of resonance strengths. Default is [] capillary_nested : int, optional @@ -93,14 +118,20 @@ capillary_nested : int, optional gamma: float, optional unless beta is directly provided nonlinear parameter in m^2 / W. Will overwrite any computed gamma parameter. +effective_mode_diameter : float, optional + effective mode field diameter in m + +n2 : float, optional + non linear refractive index + +A_eff : float, optional + effective mode field area + length: float, optional length of the fiber in m. default : 1 -fiber_id : int - in case multiple fibers are chained together, indicates the index of this particular fiber, default : 0 - input_transmission : float - number between 0 and 1 indicating how much light enters the fiber, default : 1 + number between 0 and 1 indicating how much light enters the fiber, useful when chaining many fibers together, default : 1 ## Gas parameters @@ -124,11 +155,17 @@ plasma_density: float 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) +To specify the initial pulse properties, either use one of 3 in (peak_power, energy, mean_power) together with one of 2 in (width, t0), or use soliton_num together with one of 5 in (peak_power, mean_power, energy, width, t0) -power: float +peak_power : float peak power in W +mean_power : float + mean power of the pulse train in W. if specified, repetition_rate must also be specified + +repetition_rate : float + repetition rate of the pulse train in Hz + energy: float total pulse energy in J @@ -138,11 +175,14 @@ width: float t0: float pulse width parameter -solition_num: float +soliton_num: float soliton number ### optional +field_file : str + if you have an initial field to use, convert it to a npz file with time (key : 'time') in s and electric field (key : 'field') in sqrt(W) (can be complex). You the use it with this config key. You can then scale it by settings any 1 of mean_power, energy and peak_power (priority is in this order) + quantum_noise: bool whether or not one-photon-per-mode quantum noise is activated. default : False diff --git a/src/scgenerator/__init__.py b/src/scgenerator/__init__.py index e3f5c27..845244b 100644 --- a/src/scgenerator/__init__.py +++ b/src/scgenerator/__init__.py @@ -2,5 +2,5 @@ from .initialize import compute_init_parameters from .io import Paths, load_toml from .math import abs2, argclosest, span from .physics import fiber, materials, pulse, simulate, units -from .physics.simulate import RK4IP, new_simulations, resume_simulations +from .physics.simulate import RK4IP, new_simulation, resume_simulations from .plotting import plot_avg, plot_results_1D, plot_results_2D, plot_spectrogram diff --git a/src/scgenerator/cli/cli.py b/src/scgenerator/cli/cli.py index 8b0e421..f803f8a 100644 --- a/src/scgenerator/cli/cli.py +++ b/src/scgenerator/cli/cli.py @@ -6,7 +6,7 @@ import sys import ray from scgenerator import initialize -from ..physics.simulate import new_simulations, resume_simulations, SequencialSimulations +from ..physics.simulate import run_simulation_sequence, resume_simulations, SequencialSimulations from .. import io @@ -37,11 +37,14 @@ def create_parser(): run_parser.add_argument("configs", help="path(s) to the toml configuration file(s)", nargs="+") run_parser.add_argument( - "appendto", + "--append-to", + "-a", help="optional directory where a compatible simulation has already been ran", - nargs="?", default=None, ) + run_parser.add_argument( + "--output-name", "--o", help="path to the final output folder", default=None + ) run_parser.set_defaults(func=run_sim) resume_parser = subparsers.add_parser("resume", help="resume a simulation") @@ -53,11 +56,10 @@ def create_parser(): merge_parser = subparsers.add_parser("merge", help="merge simulation results") merge_parser.add_argument( - "paths", - nargs="+", - help="path(s) to simulation folder(s) containing 'initial_config.toml'. If more " - "than one path is given, simulations are appended to each other as if they're " - "physically the continuation of the previous one.", + "path", help="path to the final simulation folder containing 'initial_config.toml'" + ) + merge_parser.add_argument( + "--output-name", "--o", help="path to the final output folder", default=None ) merge_parser.set_defaults(func=merge) @@ -73,29 +75,11 @@ def main(): def run_sim(args): method = prep_ray(args) - configs = args.configs.copy() - first_config = configs.pop(0) - - if args.appendto is None: - sim = new_simulations(first_config, method=method) - else: - sim = new_simulations( - first_config, prev_data_folder=args.appendto, method=method, initial=False - ) - sim.run() - data_folders = [sim.data_folder] - for config in configs: - print("launching", config) - sim = new_simulations( - config, prev_data_folder=data_folders[-1], method=method, initial=False - ) - sim.run() - data_folders.append(sim.data_folder) - io.merge(data_folders) + run_simulation_sequence(*args.configs, method=method, final_name=args.output_name) def merge(args): - io.merge(args.paths) + io.append_and_merge(args.path, args.output_name) def prep_ray(args): diff --git a/src/scgenerator/const.py b/src/scgenerator/const.py index b637b1f..ecffb29 100644 --- a/src/scgenerator/const.py +++ b/src/scgenerator/const.py @@ -1,13 +1,31 @@ import numpy as np +def pbar_format(worker_id: int): + if worker_id == 0: + return dict( + position=0, + bar_format="{l_bar}{bar}" "|[{elapsed}<{remaining}, " "{rate_fmt}{postfix}]", + ) + else: + return dict( + total=1, + desc=f"Worker {worker_id}", + position=worker_id, + bar_format="{l_bar}{bar}" "|[{elapsed}<{remaining}, " "{rate_fmt}{postfix}]", + ) + + +##### + + def in_range_excl(func, r): def _in_range(n): if not func(n): return False return n > r[0] and n < r[1] - _in_range.__doc__ = func.__doc__ + f" between {r[0]} and {r[1]}" + _in_range.__doc__ = func.__doc__ + f" between {r[0]} and {r[1]} (exclusive) " return _in_range @@ -17,7 +35,7 @@ def in_range_incl(func, r): return False return n >= r[0] and n <= r[1] - _in_range.__doc__ = func.__doc__ + f" between {r[0]} and {r[1]}" + _in_range.__doc__ = func.__doc__ + f" between {r[0]} and {r[1]} (inclusive)" return _in_range @@ -122,7 +140,7 @@ valid_param_types = dict( beta=beta, dispersion_file=lambda s: isinstance(s, str), model=string(["pcf", "marcatili", "marcatili_adjusted", "hasan", "custom"]), - length=num, + length=in_range_excl(num, (0, 1e9)), capillary_num=integer, capillary_outer_d=in_range_excl(num, (0, 1e-3)), capillary_thickness=in_range_excl(num, (0, 1e-3)), @@ -139,7 +157,9 @@ valid_param_types = dict( pulse=dict( field_0=field_0, field_file=lambda s: isinstance(s, str), - power=num, + repetition_rate=num, + peak_power=num, + mean_power=num, energy=num, soliton_num=num, quantum_noise=boolean, @@ -201,7 +221,9 @@ valid_variable = dict( ], gas=["pressure", "temperature", "gas_name", "plasma_density"], pulse=[ - "power", + "peak_power", + "mean_power", + "energy", "quantum_noise", "shape", "wavelength", diff --git a/src/scgenerator/data/submit_job_template.txt b/src/scgenerator/data/submit_job_template.txt index 35b5604..b872a5e 100644 --- a/src/scgenerator/data/submit_job_template.txt +++ b/src/scgenerator/data/submit_job_template.txt @@ -50,5 +50,5 @@ done ############################################################################################## #### call your code below -scgenerator run {config} +scgenerator run {configs_list} exit \ No newline at end of file diff --git a/src/scgenerator/defaults.py b/src/scgenerator/defaults.py index 8e80693..530862e 100644 --- a/src/scgenerator/defaults.py +++ b/src/scgenerator/defaults.py @@ -18,10 +18,10 @@ default_parameters = dict( quantum_noise=False, intensity_noise=0, shape="gaussian", - frep=80e6, + frep=40e6, behaviors=["spm", "ss"], raman_type="agrawal", - parallel=False, + parallel=True, repeat=1, tolerated_error=1e-11, lower_wavelength_interp_limit=300e-9, diff --git a/src/scgenerator/initialize.py b/src/scgenerator/initialize.py index ea945eb..59687d3 100644 --- a/src/scgenerator/initialize.py +++ b/src/scgenerator/initialize.py @@ -4,7 +4,6 @@ from typing import Any, Dict, Iterator, List, Set, Tuple import numpy as np from numpy import pi -from numpy.core.numeric import full from scipy.interpolate.interpolate import interp1d from tqdm import tqdm from pathlib import Path @@ -15,7 +14,7 @@ from .errors import * from .logger import get_logger from .math import length, power_fact from .physics import fiber, pulse, units -from .utils import count_variations, required_simulations +from .utils import count_variations, override_config, required_simulations class ParamSequence(Mapping): @@ -66,7 +65,7 @@ class ContinuationParamSequence(ParamSequence): for variable_list, _ in required_simulations(init_config) ] - new_config = utils.override_config(init_config, new_config) + new_config = utils.override_config(new_config, init_config) super().__init__(new_config) def __iter__(self) -> Iterator[Tuple[List[Tuple[str, Any]], Dict[str, Any]]]: @@ -75,7 +74,6 @@ class ContinuationParamSequence(ParamSequence): for variable_list, full_config in required_simulations(self.config): prev_sim_folder = self.find_prev_data_folder(variable_list) full_config["prev_data_dir"] = str(prev_sim_folder.resolve()) - yield variable_list, compute_subsequent_paramters(prev_sim_folder, full_config) def find_prev_data_folder(self, new_variable_list: List[Tuple[str, Any]]) -> Path: @@ -170,6 +168,28 @@ def validate(config: dict) -> dict: return _ensure_consistency(config) +def validate_config_sequence(*configs: os.PathLike) -> Dict[str, Any]: + """validates a sequence of configs where all but the first one may have + parameters missing + + Parameters + ---------- + configs : os.PathLike + sequence of paths to toml config files + + Returns + ------- + Dict[str, Any] + the final config as would be simulated, but of course missing input fields in the middle + """ + previous = None + for config in configs: + dico = io.load_toml(config) + previous = override_config(dico, previous) + validate(previous) + return previous + + def wspace(t, t_num=0): """frequency array such that x(t) <-> np.fft(x)(w) Parameters @@ -312,16 +332,14 @@ def _ensure_consistency_fiber(fiber: Dict[str, Any]): When at least one required parameter with no default is missing """ - if _contains(fiber, "beta") and not ( - _contains(fiber, "n2") and _contains(fiber, "effective_mode_diameter") - ): - fiber = defaults.get(fiber, "gamma", specified_parameters=["beta"]) + if _contains(fiber, "beta"): + if not (_contains(fiber, "A_eff") or _contains(fiber, "effective_mode_diameter")): + fiber = defaults.get(fiber, "gamma", specified_parameters=["beta"]) fiber.setdefault("model", "custom") - elif _contains(fiber, "dispersion_file") and not ( - _contains(fiber, "n2") and _contains(fiber, "effective_mode_diameter") - ): - fiber = defaults.get(fiber, "gamma", specified_parameters=["dispersion_file"]) + elif _contains(fiber, "dispersion_file"): + if not (_contains(fiber, "A_eff") or _contains(fiber, "effective_mode_diameter")): + fiber = defaults.get(fiber, "gamma", specified_parameters=["dispersion_file"]) fiber.setdefault("model", "custom") else: @@ -400,12 +418,17 @@ def _ensure_consistency_pulse(pulse): if _contains(pulse, "soliton_num"): pulse = defaults.get_multiple( - pulse, ["power", "energy", "width", "t0"], 1, specified_parameters=["soliton_num"] + pulse, + ["peak_power", "mean_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) + pulse = defaults.get_multiple(pulse, ["peak_power", "energy", "mean_power"], 1) + if _contains(pulse, "mean_power"): + pulse = defaults.get(pulse, "repetition_rate", specified_parameters=["mean_power"]) return pulse @@ -565,6 +588,9 @@ def compute_init_parameters(config: Dict[str, Any]) -> Dict[str, Any]: params["hr_w"] = fiber.delayed_raman_w(params["t"], params["dt"], params["raman_type"]) # PULSE + if "mean_power" in params: + params["energy"] = params["mean_power"] / params["repetition_rate"] + if "field_file" in params: field_data = np.load(params["field_file"]) field_interp = interp1d( @@ -581,7 +607,7 @@ def compute_init_parameters(config: Dict[str, Any]) -> Dict[str, Any]: 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_NL"] = 1 / (params["gamma"] * params["peak_power"]) if params["gamma"] else np.inf params["L_sol"] = pi / 2 * params["L_D"] # Technical noise @@ -589,7 +615,7 @@ def compute_init_parameters(config: Dict[str, Any]) -> Dict[str, Any]: params = _technical_noise(params) params["field_0"] = pulse.initial_field( - params["t"], params["shape"], params["t0"], params["power"] + params["t"], params["shape"], params["t0"], params["peak_power"] ) if params["quantum_noise"]: @@ -605,17 +631,22 @@ def compute_init_parameters(config: Dict[str, Any]) -> Dict[str, Any]: def compute_subsequent_paramters(sim_folder: str, config: Dict[str, Any]) -> Dict[str, Any]: params = compute_init_parameters(config) - params["spec_0"] = io.load_last_spectrum(sim_folder)[1] - params["field_0"] = np.fft.ifft(params["spec_0"]) * params["input_transmission"] + spec = io.load_last_spectrum(sim_folder)[1] + params["field_0"] = np.fft.ifft(spec) * params["input_transmission"] + params["spec_0"] = np.fft.fft(params["field_0"]) return params def _comform_custom_field(params): params["field_0"] = params["field_0"] * pulse.modify_field_ratio( - params["field_o"], params.get("power"), params.get("intensity_noise") + params["t"], + params["field_0"], + params.get("peak_power"), + params.get("energy"), + params.get("intensity_noise"), ) - params["width"], params["power"], params["energy"] = pulse.measure_field( + params["width"], params["peak_power"], params["energy"] = pulse.measure_field( params["t"], params["field_0"] ) return params @@ -625,14 +656,14 @@ def _update_pulse_parameters(params): ( params["width"], params["t0"], - params["power"], + params["peak_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), + peak_power=params.get("peak_power", None), energy=params.get("energy", None), gamma=params["gamma"], beta2=params["beta"][0], @@ -658,7 +689,7 @@ def _technical_noise(params): 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["peak_power"] *= delta_int params["t0"] *= delta_T0 params["width"] *= delta_T0 params = _update_pulse_parameters(params) diff --git a/src/scgenerator/io.py b/src/scgenerator/io.py index ea3841f..95331c0 100644 --- a/src/scgenerator/io.py +++ b/src/scgenerator/io.py @@ -5,10 +5,8 @@ from glob import glob from typing import Any, Dict, Iterable, List, Tuple, Union import numpy as np -from numpy.lib import delete import pkg_resources as pkg import toml -from ray import util from send2trash import TrashPermissionError, send2trash from tqdm import tqdm from pathlib import Path @@ -139,6 +137,7 @@ def load_toml(path: os.PathLike): def save_toml(path, dico): """saves a dictionary into a toml file""" + path = str(path) if not path.lower().endswith(".toml"): path += ".toml" with open(path, mode="w") as file: @@ -366,42 +365,11 @@ def load_last_spectrum(path: str) -> Tuple[int, np.ndarray]: return num, np.load(os.path.join(path, f"spectrum_{num}.npy")) -def merge(paths: Union[str, List[str]]): - if isinstance(paths, str): +def merge(paths: Union[str, List[str]], delete=False): + if isinstance(paths, (str, Path)): paths = [paths] for path in paths: - merge_same_simulations(path, delete=False) - - -def append_simulations(paths: List[os.PathLike]): - paths: List[Path] = [Path(p).resolve() for p in paths] - master_sim_path = paths[-1] - merged_path = master_sim_path.parent / "merged_sims" - merged_path.mkdir(exist_ok=True) - for i, path in enumerate(paths): - shutil.copy(path / "initial_config.toml", merged_path / f"initial_config{i}.toml") - for sim in master_sim_path.glob("*"): - if not sim.is_dir() or not str(sim).endswith("merged"): - continue - sim_name = sim.name - merge_sim_path = merged_path / sim_name - merge_sim_path.mkdir(exist_ok=True) - shutil.copy(sim / "params.toml", merge_sim_path / f"params.toml") - z = [] - z_num = 0 - last_z = 0 - for path in paths: - curr_z_num = load_toml(str(path / sim_name / "params.toml"))["z_num"] - for i in range(curr_z_num): - shutil.copy( - path / sim_name / f"spectra_{i}.npy", - merge_sim_path / f"spectra_{i + z_num}.npy", - ) - z_arr = np.load(path / sim_name / "z.npy") - z.append(z_arr + last_z) - last_z += z_arr[-1] - z_num += curr_z_num - np.save(merge_sim_path / "z.npy", np.concatenate(z)) + merge_same_simulations(path, delete=delete) def append_and_merge(final_sim_path: os.PathLike, new_name=None): @@ -409,20 +377,56 @@ def append_and_merge(final_sim_path: os.PathLike, new_name=None): if new_name is None: new_name = final_sim_path.name + " appended" - appended_path = final_sim_path.parent / new_name - appended_path.mkdir(exist_ok=True) + destination_path = final_sim_path.parent / new_name + destination_path.mkdir(exist_ok=True) - for sim_path in final_sim_path.glob("id*num*"): + for sim_path in tqdm(list(final_sim_path.glob("id*num*")), position=0, desc="Appending"): path_tree = [sim_path] sim_name = sim_path.name - appended_sim_path = appended_path / sim_name + appended_sim_path = destination_path / sim_name appended_sim_path.mkdir(exist_ok=True) - while (prev_sim_path := load_toml(path_tree[-1] / "params.toml")).get( - "prev_sim_dir" + while ( + prev_sim_path := load_toml(path_tree[-1] / "params.toml").get("prev_data_dir") ) is not None: path_tree.append(Path(prev_sim_path).resolve()) + z: List[np.ndarray] = [] + z_num = 0 + last_z = 0 + for path in tqdm(list(reversed(path_tree)), position=1, leave=False): + curr_z_num = load_toml(path / "params.toml")["z_num"] + for i in range(curr_z_num): + shutil.copy( + path / f"spectrum_{i}.npy", + appended_sim_path / f"spectrum_{i + z_num}.npy", + ) + z_arr = np.load(path / "z.npy") + z.append(z_arr + last_z) + last_z += z_arr[-1] + z_num += curr_z_num + z_arr = np.concatenate(z) + update_appended_params(sim_path / "params.toml", appended_sim_path / "params.toml", z_arr) + np.save(appended_sim_path / "z.npy", z_arr) + + update_appended_params( + final_sim_path / "initial_config.toml", destination_path / "initial_config.toml", z_arr + ) + + merge(destination_path, delete=True) + + +def update_appended_params(param_path, new_path, z): + z_num = len(z) + params = load_toml(param_path) + if "simulation" in params: + params["simulation"]["z_num"] = z_num + params["simulation"]["z_targets"] = z_num + else: + params["z_num"] = z_num + params["z_targets"] = z_num + save_toml(new_path, params) + def merge_same_simulations(path: str, delete=True): logger = get_logger(__name__) @@ -443,7 +447,7 @@ def merge_same_simulations(path: str, delete=True): base_folders.add(base_folder) sim_num, param_num = utils.count_variations(config) - pbar = utils.PBars(tqdm(total=sim_num * z_num, desc="merging data", ncols=100)) + pbar = utils.PBars(tqdm(total=sim_num * z_num, desc="Merging data", ncols=100)) spectra = [] for z_id in range(z_num): @@ -462,7 +466,7 @@ def merge_same_simulations(path: str, delete=True): if repeat_id == max_repeat_id: out_path = os.path.join( path, - utils.format_variable_list(variable_and_ind[:-1]) + PARAM_SEPARATOR + "merged", + utils.format_variable_list(variable_and_ind[1:-1]) + PARAM_SEPARATOR + "merged", ) out_path = ensure_folder(out_path, prevent_overwrite=False) spectra = np.array(spectra).reshape(repeat, len(spectra[0])) diff --git a/src/scgenerator/math.py b/src/scgenerator/math.py index a966d28..e7276fa 100644 --- a/src/scgenerator/math.py +++ b/src/scgenerator/math.py @@ -71,7 +71,7 @@ def _power_fact_array(x, n): @jit(nopython=True) -def abs2(z): +def abs2(z: np.ndarray) -> np.ndarray: return z.real ** 2 + z.imag ** 2 diff --git a/src/scgenerator/physics/pulse.py b/src/scgenerator/physics/pulse.py index 9cb843a..0662f35 100644 --- a/src/scgenerator/physics/pulse.py +++ b/src/scgenerator/physics/pulse.py @@ -44,7 +44,7 @@ P0T0_to_E0_fac = dict( """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): +def initial_field(t, shape, t0, peak_power): """returns the initial field Parameters @@ -56,7 +56,7 @@ def initial_field(t, shape, t0, power): t0 : float time parameters. Can be obtained by dividing the FWHM by `scgenerator.physics.pulse.fwhm_to_T0_fac[shape]` - power : float + peak_power : float peak power Returns @@ -70,20 +70,26 @@ def initial_field(t, shape, t0, power): raised when shape is not recognized """ if shape == "gaussian": - return gauss_pulse(t, t0, power) + return gauss_pulse(t, t0, peak_power) elif shape == "sech": - return sech_pulse(t, t0, power) + return sech_pulse(t, t0, peak_power) else: raise ValueError(f"shape '{shape}' not understood") def modify_field_ratio( - field: np.ndarray, target_power: float = None, intensity_noise: float = None + t: np.ndarray, + field: np.ndarray, + target_power: float = None, + target_energy: float = None, + intensity_noise: float = None, ) -> float: """multiply a field by this number to get the desired effects Parameters ---------- + t : np.ndarray + time (only used when target_energy is not None) field : np.ndarray initial field target_power : float, optional @@ -97,8 +103,11 @@ def modify_field_ratio( ratio (multiply field by this number) """ ratio = 1 - if target_power is not None: + if target_energy is not None: + ratio *= np.sqrt(target_energy / np.trapz(abs2(field), t)) + elif target_power is not None: ratio *= np.sqrt(target_power / abs2(field).max()) + if intensity_noise is not None: d_int, _ = technical_noise(intensity_noise) ratio *= np.sqrt(d_int) @@ -109,7 +118,7 @@ def conform_pulse_params( shape, width=None, t0=None, - power=None, + peak_power=None, energy=None, soliton_num=None, gamma=None, @@ -125,7 +134,7 @@ def conform_pulse_params( 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 : float, optional peak power, by default None energy : float, optional total energy of the pulse, by default None @@ -140,17 +149,17 @@ def conform_pulse_params( 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) + 1 of (width, t0), 1 of (peak_power, energy), gamma and beta2 together optional (not one without the other) + soliton_num, gamma, 1 of (width, peak_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 + specify width, peak_power and energy -> t0 and energy will be computed + specify soliton_num, gamma, peak_power, t0 -> width, t0 and energy will be computed Returns ------- - width, t0, power, energy + width, t0, peak_power, energy when no gamma is specified - width, t0, power, energy, soliton_num + width, t0, peak_power, energy, soliton_num when gamma is specified Raises @@ -167,14 +176,14 @@ def conform_pulse_params( 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)) + peak_power = soliton_num ** 2 * abs(beta2) / (gamma * t0 ** 2) + elif peak_power is not None: + t0 = np.sqrt(soliton_num ** 2 * abs(beta2) / (peak_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) + peak_power = soliton_num ** 2 * abs(beta2) / (gamma * t0 ** 2) else: raise TypeError("not enough parameters to determine pulse") @@ -183,26 +192,26 @@ def conform_pulse_params( else: width = t0 / fwhm_to_T0_fac[shape] - if power is not None: - energy = P0_to_E0(power, t0, shape) + if peak_power is not None: + energy = P0_to_E0(peak_power, t0, shape) else: - power = E0_to_P0(energy, t0, shape) + peak_power = E0_to_P0(energy, t0, shape) if gamma is None: - return width, t0, power, energy + return width, t0, peak_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 + soliton_num = np.sqrt(peak_power * gamma * t0 ** 2 / abs(beta2)) + return width, t0, peak_power, energy, soliton_num def E0_to_P0(E0, t0, shape="gaussian"): - """convert an initial total pulse energy to a pulse peak power""" + """convert an initial total pulse energy to a pulse peak 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""" + """converts initial peak peak_power to pulse energy""" return P0 * t0 * P0T0_to_E0_fac[shape] @@ -234,7 +243,7 @@ def technical_noise(rms_noise, relative_factor=0.4): rms_noise : float RMS amplitude noise of the laser relative factor : float - magnitude of the anticorrelation between power and pulse width noise + magnitude of the anticorrelation between peak_power and pulse width noise Returns ---------- delta_int : float @@ -825,9 +834,10 @@ def measure_properties(spectra, t, compress=True, debug=""): def measure_field(t: np.ndarray, field: np.ndarray) -> Tuple[float, float, float]: + """returns fwhm, peak_power, energy""" intensity = abs2(field) _, fwhm_lim, _, _ = find_lobe_limits(t, intensity) fwhm = length(fwhm_lim) - power = intensity.max() + peak_power = intensity.max() energy = np.trapz(intensity, t) - return fwhm, power, energy + return fwhm, peak_power, energy diff --git a/src/scgenerator/physics/simulate.py b/src/scgenerator/physics/simulate.py index 7e01fbd..c6f5284 100644 --- a/src/scgenerator/physics/simulate.py +++ b/src/scgenerator/physics/simulate.py @@ -8,7 +8,7 @@ import numpy as np from numba import jit from tqdm import tqdm -from .. import initialize, io, utils +from .. import initialize, io, utils, const from ..errors import IncompleteDataFolderError from ..logger import get_logger from . import pulse @@ -36,7 +36,7 @@ class RK4IP: w0 : float central angular frequency of the pulse w_power_fact : numpy.ndarray - precomputed factorial/power operations on w_c (scgenerator.math.power_fact) + precomputed factorial/peak_power operations on w_c (scgenerator.math.power_fact) spec_0 : numpy.ndarray initial spectral envelope as function of w_c z_targets : list @@ -304,6 +304,31 @@ class RK4IP: pass +class SequentialRK4IP(RK4IP): + def __init__( + self, + sim_params, + overall_pbar: tqdm, + save_data=False, + job_identifier="", + task_id=0, + n_percent=10, + ): + self.overall_pbar = overall_pbar + self.pbar = tqdm(**const.pbar_format(1)) + super().__init__( + sim_params, + save_data=save_data, + job_identifier=job_identifier, + task_id=task_id, + n_percent=n_percent, + ) + + def step_saved(self): + self.overall_pbar.update() + self.pbar.update(self.z / self.z_final - self.pbar.n) + + class MutliProcRK4IP(RK4IP): def __init__( self, @@ -360,20 +385,25 @@ class Simulations: New Simulations child classes can be written and must implement the following """ - _available_simulation_methods = [] - _available_simulation_methods_dict: Dict[str, Type["Simulations"]] = dict() + simulation_methods: List[Tuple[Type["Simulations"], int]] = [] + simulation_methods_dict: Dict[str, Type["Simulations"]] = dict() - 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_dict[cls.__name__] = cls - Simulations._available_simulation_methods.sort(key=lambda el: el[1]) + def __init_subclass__(cls, priority=0, **kwargs): + cls._available = cls.is_available() + Simulations.simulation_methods.append((cls, priority)) + Simulations.simulation_methods_dict[cls.__name__] = cls + Simulations.simulation_methods.sort(key=lambda el: el[1], reverse=True) super().__init_subclass__(**kwargs) @classmethod def get_best_method(cls): - return Simulations._available_simulation_methods[-1][0] + for method, _ in Simulations.simulation_methods: + if method.is_available(): + return method + + @classmethod + def is_available(cls) -> bool: + return False def __init__(self, param_seq: initialize.ParamSequence, task_id=0): """ @@ -454,11 +484,23 @@ class Simulations: raise NotImplementedError() -class SequencialSimulations(Simulations, available=True, priority=0): +class SequencialSimulations(Simulations, priority=0): + @classmethod + def is_available(cls): + return True + + def __init__(self, param_seq: initialize.ParamSequence, task_id): + super().__init__(param_seq, task_id=task_id) + self.overall_pbar = tqdm( + total=self.param_seq.num_steps, desc="Simulating", unit="step", **const.pbar_format(0) + ) + def new_sim(self, variable_list: List[tuple], params: Dict[str, Any]): v_list_str = utils.format_variable_list(variable_list) self.logger.info(f"launching simulation with {v_list_str}") - RK4IP(params, save_data=True, job_identifier=v_list_str, task_id=self.id).run() + SequentialRK4IP( + params, self.overall_pbar, save_data=True, job_identifier=v_list_str, task_id=self.id + ).run() def stop(self): pass @@ -467,7 +509,11 @@ class SequencialSimulations(Simulations, available=True, priority=0): pass -class MultiProcSimulations(Simulations, available=True, priority=10): +class MultiProcSimulations(Simulations, priority=1): + @classmethod + def is_available(cls): + return True + def __init__(self, param_seq: initialize.ParamSequence, task_id): super().__init__(param_seq, task_id=task_id) self.sim_jobs_per_node = max(1, os.cpu_count() // 2) @@ -481,7 +527,7 @@ class MultiProcSimulations(Simulations, available=True, priority=10): for i in range(self.sim_jobs_per_node) ] self.p_worker = multiprocessing.Process( - target=MultiProcSimulations.progress_worker, + target=utils.progress_worker, args=(self.param_seq.num_steps, self.progress_queue), ) self.p_worker.start() @@ -530,33 +576,37 @@ class MultiProcSimulations(Simulations, available=True, priority=10): ).run() queue.task_done() - @staticmethod - def progress_worker(num_steps: int, progress_queue: multiprocessing.Queue): - pbars: Dict[int, tqdm] = {} - with tqdm(total=num_steps, desc="Simulating", unit="step", position=0) as tq: - while True: - raw = progress_queue.get() - if raw == 0: - for pbar in pbars.values(): - pbar.close() - return - i, rel_pos = raw - if i not in pbars: - pbars[i] = tqdm( - total=1, - desc=f"Worker {i}", - position=i, - bar_format="{l_bar}{bar}" - "|[{elapsed}<{remaining}, " - "{rate_fmt}{postfix}]", - ) - pbars[i].update(rel_pos - pbars[i].n) - tq.update() + # @staticmethod + # def progress_worker(num_steps: int, progress_queue: multiprocessing.Queue): + # pbars: Dict[int, tqdm] = {} + # with tqdm(total=num_steps, desc="Simulating", unit="step", position=0) as tq: + # while True: + # raw = progress_queue.get() + # if raw == 0: + # for pbar in pbars.values(): + # pbar.close() + # return + # i, rel_pos = raw + # if i not in pbars: + # pbars[i] = tqdm( + # total=1, + # desc=f"Worker {i}", + # position=i, + # bar_format="{l_bar}{bar}" + # "|[{elapsed}<{remaining}, " + # "{rate_fmt}{postfix}]", + # ) + # pbars[i].update(rel_pos - pbars[i].n) + # tq.update() -class RaySimulations(Simulations, available=using_ray, priority=2): +class RaySimulations(Simulations, priority=2): """runs simulation with the help of the ray module. ray must be initialized before creating an instance of RaySimulations""" + @classmethod + def is_available(cls): + return using_ray and ray.is_initialized() + def __init__( self, param_seq: initialize.ParamSequence, @@ -660,8 +710,17 @@ class RaySimulations(Simulations, available=using_ray, priority=2): self.p_bars.print() -def new_simulations( - config_file: str, +def run_simulation_sequence(*config_files: os.PathLike, method=None, final_name: str = None): + prev = None + for config_file in config_files: + sim = new_simulation(config_file, prev, method) + sim.run() + prev = sim.data_folder + io.append_and_merge(prev, final_name) + + +def new_simulation( + config_file: os.PathLike, prev_data_folder=None, method: Type[Simulations] = None, ) -> Simulations: @@ -697,7 +756,7 @@ def _new_simulations( ) -> Simulations: if method is not None: if isinstance(method, str): - method = Simulations._available_simulation_methods_dict[method] + method = Simulations.simulation_methods_dict[method] return method(param_seq, task_id) elif param_seq.num_sim > 1 and param_seq["simulation", "parallel"] and using_ray: return Simulations.get_best_method()(param_seq, task_id) @@ -711,4 +770,4 @@ if __name__ == "__main__": except NameError: pass config_file, *opts = sys.argv[1:] - new_simulations(config_file, *opts) + new_simulation(config_file, *opts) diff --git a/src/scgenerator/physics/units.py b/src/scgenerator/physics/units.py index c57da72..1ad5603 100644 --- a/src/scgenerator/physics/units.py +++ b/src/scgenerator/physics/units.py @@ -2,8 +2,9 @@ # 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), ... +from numba.core.types.misc import Phantom import numpy as np -from numpy import pi +from numpy import isin, pi c = 299792458.0 hbar = 1.05457148e-34 @@ -198,6 +199,30 @@ D_ps_nm_km.label = r"$D$ (ps/(nm km))" D_ps_nm_km.type = "OTHER" +units_map = dict( + nm=nm, + um=um, + m=m, + THz=THz, + PHz=PHz, + rad_s=rad_s, + Prad_s=Prad_s, + rel_freq=rel_freq, + rel_time=rel_time, + s=s, + us=us, + ns=ns, + ps=ps, + fs=fs, +) + + +def get_unit(unit): + if isinstance(unit, str): + return units_map[unit] + return unit + + def beta2_coef(beta): fac = 1e27 out = np.zeros_like(beta) @@ -217,11 +242,16 @@ def standardize_dictionary(dico): same dictionary with units converted Example ---------- - standardize_dictionary({"power": [23, "kW"], "points": [1, 2, 3]}) - {"power": 23000, "points": [1, 2, 3]}) + standardize_dictionary({"peak_power": [23, "kW"], "points": [1, 2, 3]}) + {"peak_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): + 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: @@ -263,7 +293,7 @@ def sort_axis(axis, plt_range): """ r = np.array(plt_range[:2], dtype="float") - func = plt_range[2] + func = get_unit(plt_range[2]) indices = np.arange(len(axis))[(axis <= np.max(func(r))) & (axis >= np.min(func(r)))] cropped = axis[indices] diff --git a/src/scgenerator/plotting.py b/src/scgenerator/plotting.py index a91abb5..c7de0e1 100644 --- a/src/scgenerator/plotting.py +++ b/src/scgenerator/plotting.py @@ -528,12 +528,7 @@ def plot_results_2D( 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() + is_spectrum, x_axis, plt_range = _prep_plot(values, plt_range, params) # crop and convert x_axis, ind, ext = units.sort_axis(x_axis[::skip], plt_range) @@ -648,12 +643,7 @@ def plot_results_1D( 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() + is_spectrum, x_axis, plt_range = _prep_plot(values, plt_range, params) # crop and convert x_axis, ind, ext = units.sort_axis(x_axis, plt_range) @@ -716,6 +706,16 @@ def plot_results_1D( return fig, ax +def _prep_plot(values, plt_range, params): + is_spectrum = values.dtype == "complex" + plt_range = (*plt_range[:2], units.get_unit(plt_range[2])) + if plt_range[2].type in ["WL", "FREQ", "AFREQ"]: + x_axis = params["w"].copy() + else: + x_axis = params["t"].copy() + return is_spectrum, x_axis, plt_range + + def plot_avg( values, plt_range, @@ -795,12 +795,7 @@ def plot_avg( 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() + is_spectrum, x_axis, plt_range = _prep_plot(values, plt_range, params) # crop and convert x_axis, ind, ext = units.sort_axis(x_axis, plt_range) diff --git a/src/scgenerator/scripts/slurm_submit.py b/src/scgenerator/scripts/slurm_submit.py index a9a799a..d8d76fa 100644 --- a/src/scgenerator/scripts/slurm_submit.py +++ b/src/scgenerator/scripts/slurm_submit.py @@ -5,9 +5,8 @@ import shutil import subprocess from datetime import datetime, timedelta -from scgenerator.initialize import validate -from scgenerator.io import Paths, load_toml -from scgenerator.utils import count_variations +from ..initialize import validate_config_sequence +from ..utils import count_variations def format_time(t): @@ -20,7 +19,7 @@ def format_time(t): def create_parser(): parser = argparse.ArgumentParser(description="submit a job to a slurm cluster") - parser.add_argument("config", help="path to the toml configuration file") + parser.add_argument("configs", nargs="+", help="path to the toml configuration file") parser.add_argument( "-t", "--time", required=True, type=str, help="time required for the job in hh:mm:ss" ) @@ -59,19 +58,23 @@ def main(): "time format must be an integer number of minute or must match the pattern hh:mm:ss" ) - config = load_toml(args.config) - config = validate(config) + config_paths = args.configs + final_config = validate_config_sequence(*config_paths) - sim_num, _ = count_variations(config) + sim_num, _ = count_variations(final_config) - file_name = "submit " + config["name"] + "-" + format(datetime.now(), "%Y%m%d%H%M") + ".sh" - job_name = f"supercontinuum {config['name']}" - submit_sh = template.format(job_name=job_name, **vars(args)) + file_name = ( + "submit " + final_config["name"] + "-" + format(datetime.now(), "%Y%m%d%H%M") + ".sh" + ) + job_name = f"supercontinuum {final_config['name']}" + submit_sh = template.format( + job_name=job_name, configs_list=" ".join(args.configs), **vars(args) + ) with open(file_name, "w") as file: file.write(submit_sh) subprocess.run(["sbatch", "--test-only", file_name]) submit = input( - f"Propagate {sim_num} pulses from config {args.config} with {args.cpus_per_node} cpus" + f"Propagate {sim_num} pulses from configs {args.configs} with {args.cpus_per_node} cpus" + f" per node on {args.nodes} nodes for {format_time(args.time)} ? (y/[n])\n" ) if submit.lower() in ["y", "yes"]: diff --git a/src/scgenerator/spectra.py b/src/scgenerator/spectra.py index 04bb231..ed4613e 100644 --- a/src/scgenerator/spectra.py +++ b/src/scgenerator/spectra.py @@ -30,9 +30,10 @@ class Spectrum(np.ndarray): class Pulse(Sequence): - def __init__(self, path: str): + def __init__(self, path: str, ensure_2d=True): self.logger = get_logger(__name__) self.path = path + self.__ensure_2d = ensure_2d if not os.path.isdir(self.path): raise FileNotFoundError(f"Folder {self.path} does not exist") @@ -175,17 +176,18 @@ class Pulse(Sequence): # Load the spectra spectra = [] for i in ind: - spectra.append(io.load_single_spectrum(self.path, i)) + spectra.append(self._load1(i)) spectra = np.array(spectra) self.logger.debug(f"all spectra from {self.path} successfully loaded") - return spectra.squeeze() + return spectra def _load1(self, i: int): - return Spectrum( - np.atleast_2d(io.load_single_spectrum(self.path, i)), self.wl, self.params["frep"] - ) + spec = io.load_single_spectrum(self.path, i) + if self.__ensure_2d: + spec = np.atleast_2d(spec) + return Spectrum(spec, self.wl, self.params["frep"]) class SpectraCollection(Mapping, Sequence): diff --git a/src/scgenerator/utils.py b/src/scgenerator/utils.py index cf84a3c..6c10314 100644 --- a/src/scgenerator/utils.py +++ b/src/scgenerator/utils.py @@ -9,6 +9,7 @@ import collections import datetime as dt import itertools import logging +import multiprocessing import re import socket from typing import Any, Callable, Dict, Iterator, List, Mapping, Tuple, Union @@ -20,7 +21,7 @@ from copy import deepcopy from tqdm import tqdm -from .const import PARAM_SEPARATOR, PREFIX_KEY_BASE, valid_variable +from .const import PARAM_SEPARATOR, PREFIX_KEY_BASE, valid_variable, pbar_format from .logger import get_logger from .math import * @@ -162,6 +163,33 @@ class ProgressBarActor: return self.counters +def progress_worker(num_steps: int, progress_queue: multiprocessing.Queue): + """keeps track of progress on a separate thread + + Parameters + ---------- + num_steps : int + total number of steps, used for the main progress bar (position 0) + progress_queue : multiprocessing.Queue + values are either + Literal[0] : stop the worker and close the progress bars + Tuple[int, float] : worker id and relative progress between 0 and 1 + """ + pbars: Dict[int, tqdm] = {} + with tqdm(total=num_steps, desc="Simulating", unit="step", position=0) as tq: + while True: + raw = progress_queue.get() + if raw == 0: + for pbar in pbars.values(): + pbar.close() + return + i, rel_pos = raw + if i not in pbars: + pbars[i] = tqdm(**pbar_format(i)) + pbars[i].update(rel_pos - pbars[i].n) + tq.update() + + def count_variations(config: dict) -> Tuple[int, int]: """returns (sim_num, variable_params_num) where sim_num is the total number of simulations required and variable_params_num is the number of distinct parameters that will vary.""" @@ -347,7 +375,10 @@ def deep_update(d: Mapping, u: Mapping): return d -def override_config(old: Dict[str, Any], new: Dict[str, Any]) -> Dict[str, Any]: +def override_config(new: Dict[str, Any], old: Dict[str, Any] = None) -> Dict[str, Any]: + """makes sure all the parameters set in new are there, leaves untouched parameters in old""" + if old is None: + return new out = deepcopy(old) for section_name, section in new.items(): if isinstance(section, Mapping): diff --git a/testing/configs/compute_init_parameters/good.toml b/testing/configs/compute_init_parameters/good.toml index ebe0e03..6f6527d 100644 --- a/testing/configs/compute_init_parameters/good.toml +++ b/testing/configs/compute_init_parameters/good.toml @@ -9,7 +9,7 @@ pitch_ratio = 0.37 [pulse] intensity_noise = 0.05e-2 -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/count_variations/120sim_3vary.toml b/testing/configs/count_variations/120sim_3vary.toml index 8824349..b05a694 100644 --- a/testing/configs/count_variations/120sim_3vary.toml +++ b/testing/configs/count_variations/120sim_3vary.toml @@ -7,7 +7,7 @@ model = "marcatili" gas_name = ["air", "helium"] [pulse] -power = 100e3 +peak_power = 100e3 wavelength = 800e-9 [pulse.variable] diff --git a/testing/configs/count_variations/1sim_0vary.toml b/testing/configs/count_variations/1sim_0vary.toml index 86fc327..20339d3 100644 --- a/testing/configs/count_variations/1sim_0vary.toml +++ b/testing/configs/count_variations/1sim_0vary.toml @@ -7,7 +7,7 @@ model = "marcatili" gas_name = "air" [pulse] -power = 100e3 +peak_power = 100e3 wavelength = 800e-9 width = 250e-15 diff --git a/testing/configs/count_variations/1sim_1vary.toml b/testing/configs/count_variations/1sim_1vary.toml index c9053f8..1df17c1 100644 --- a/testing/configs/count_variations/1sim_1vary.toml +++ b/testing/configs/count_variations/1sim_1vary.toml @@ -7,7 +7,7 @@ model = "marcatili" gas_name = "air" [pulse] -power = 100e3 +peak_power = 100e3 wavelength = 800e-9 [pulse.variable] diff --git a/testing/configs/count_variations/2sim_0vary.toml b/testing/configs/count_variations/2sim_0vary.toml index 41f7229..e22cf46 100644 --- a/testing/configs/count_variations/2sim_0vary.toml +++ b/testing/configs/count_variations/2sim_0vary.toml @@ -7,7 +7,7 @@ model = "marcatili" gas_name = "air" [pulse] -power = 100e3 +peak_power = 100e3 wavelength = 800e-9 width = 250e-15 diff --git a/testing/configs/ensure_consistency/bad1.toml b/testing/configs/ensure_consistency/bad1.toml index 6a6b025..1fb03f5 100644 --- a/testing/configs/ensure_consistency/bad1.toml +++ b/testing/configs/ensure_consistency/bad1.toml @@ -10,7 +10,7 @@ pitch = 1.5e-6 pitch_ratio = 0.37 [pulse] -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/ensure_consistency/bad2.toml b/testing/configs/ensure_consistency/bad2.toml index b7be419..c35d65f 100644 --- a/testing/configs/ensure_consistency/bad2.toml +++ b/testing/configs/ensure_consistency/bad2.toml @@ -1,4 +1,4 @@ -#t0, width, power or energy missing +#t0, width, peak_power or energy missing name = "test config" diff --git a/testing/configs/ensure_consistency/bad3.toml b/testing/configs/ensure_consistency/bad3.toml index b96982c..8e271a5 100644 --- a/testing/configs/ensure_consistency/bad3.toml +++ b/testing/configs/ensure_consistency/bad3.toml @@ -10,7 +10,7 @@ pitch = 1.5e-6 pitch_ratio = 0.37 [pulse] -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/ensure_consistency/bad4.toml b/testing/configs/ensure_consistency/bad4.toml index a5fa972..898c310 100644 --- a/testing/configs/ensure_consistency/bad4.toml +++ b/testing/configs/ensure_consistency/bad4.toml @@ -10,7 +10,7 @@ pitch = 1.5e-6 pitch_ratio = 0.37 [pulse] -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/ensure_consistency/bad5.toml b/testing/configs/ensure_consistency/bad5.toml index c7aa6f3..6b13fb9 100644 --- a/testing/configs/ensure_consistency/bad5.toml +++ b/testing/configs/ensure_consistency/bad5.toml @@ -12,7 +12,7 @@ length = 1 model = "hasan" [pulse] -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/ensure_consistency/bad6.toml b/testing/configs/ensure_consistency/bad6.toml index b7f2690..b7deceb 100644 --- a/testing/configs/ensure_consistency/bad6.toml +++ b/testing/configs/ensure_consistency/bad6.toml @@ -11,7 +11,7 @@ length = 1 model = "hasan" [pulse] -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/ensure_consistency/good1.toml b/testing/configs/ensure_consistency/good1.toml index 6deb910..194f6b0 100644 --- a/testing/configs/ensure_consistency/good1.toml +++ b/testing/configs/ensure_consistency/good1.toml @@ -9,7 +9,7 @@ pitch = 1.5e-6 pitch_ratio = 0.37 [pulse] -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/ensure_consistency/good2.toml b/testing/configs/ensure_consistency/good2.toml index 3f3ad07..f44baf2 100644 --- a/testing/configs/ensure_consistency/good2.toml +++ b/testing/configs/ensure_consistency/good2.toml @@ -10,7 +10,7 @@ pitch = 1.5e-6 pitch_ratio = 0.37 [pulse] -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/ensure_consistency/good3.toml b/testing/configs/ensure_consistency/good3.toml index 8a7d0a1..b871685 100644 --- a/testing/configs/ensure_consistency/good3.toml +++ b/testing/configs/ensure_consistency/good3.toml @@ -8,7 +8,7 @@ pitch = 1.5e-6 pitch_ratio = 0.37 [pulse] -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/ensure_consistency/good4.toml b/testing/configs/ensure_consistency/good4.toml index bd857e0..7d68bc5 100644 --- a/testing/configs/ensure_consistency/good4.toml +++ b/testing/configs/ensure_consistency/good4.toml @@ -18,7 +18,7 @@ gas_name = "helium" temperature = [300, 350, 400] [pulse] -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/ensure_consistency/good5.toml b/testing/configs/ensure_consistency/good5.toml index c0e724d..27c607d 100644 --- a/testing/configs/ensure_consistency/good5.toml +++ b/testing/configs/ensure_consistency/good5.toml @@ -9,7 +9,7 @@ length = 1 model = "marcatili" [pulse] -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/ensure_consistency/good6.toml b/testing/configs/ensure_consistency/good6.toml index 1a591bd..0c12c7b 100644 --- a/testing/configs/ensure_consistency/good6.toml +++ b/testing/configs/ensure_consistency/good6.toml @@ -11,7 +11,7 @@ pitch = 1.5e-6 pitch_ratio = 0.37 [pulse] -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/override/initial_config.toml b/testing/configs/override/initial_config.toml index 6ced2f9..707022e 100644 --- a/testing/configs/override/initial_config.toml +++ b/testing/configs/override/initial_config.toml @@ -1,32 +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,] +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 +input_transmission = 1.0 length = 0.02 model = "custom" -input_transmission = 1.0 [pulse] -power = 10000 -t0 = 2.84e-14 -shape = "gaussian" -quantum_noise = false intensity_noise = 0 +peak_power = 10000 +quantum_noise = false +shape = "gaussian" +t0 = 2.84e-14 [simulation] +behaviors = ["spm", "ss"] dt = 1e-15 +frep = 80000000.0 +ideal_gas = false +lower_wavelength_interp_limit = 3e-7 parallel = true raman_type = "measured" repeat = 3 t_num = 16384 tolerated_error = 1e-9 -z_num = 64 -behaviors = [ "spm", "ss",] -frep = 80000000.0 -lower_wavelength_interp_limit = 3e-7 upper_wavelength_interp_limit = 1.9e-6 -ideal_gas = false +z_num = 64 [pulse.variable] -wavelength = [ 8.35e-7, 8.3375e-7,] +wavelength = [8.35e-7, 8.3375e-7] diff --git a/testing/configs/param_sequence/almost_equal.toml b/testing/configs/param_sequence/almost_equal.toml index 28f4ae2..696cba4 100644 --- a/testing/configs/param_sequence/almost_equal.toml +++ b/testing/configs/param_sequence/almost_equal.toml @@ -10,7 +10,7 @@ pitch = 1.5e-6 pitch_ratio = 0.37 [pulse] -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/param_sequence/equal.toml b/testing/configs/param_sequence/equal.toml index 9e09297..5892468 100644 --- a/testing/configs/param_sequence/equal.toml +++ b/testing/configs/param_sequence/equal.toml @@ -10,7 +10,7 @@ pitch = 1.5e-6 pitch_ratio = 0.37 [pulse] -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/param_sequence/no_variations.toml b/testing/configs/param_sequence/no_variations.toml index 3a88879..2acdf2a 100644 --- a/testing/configs/param_sequence/no_variations.toml +++ b/testing/configs/param_sequence/no_variations.toml @@ -11,7 +11,7 @@ pitch_ratio = 0.37 [pulse] intensity_noise = 0.1e-2 -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/run_simulations/full_anomalous.toml b/testing/configs/run_simulations/full_anomalous.toml index 4aac3b1..6ea65d8 100644 --- a/testing/configs/run_simulations/full_anomalous.toml +++ b/testing/configs/run_simulations/full_anomalous.toml @@ -16,7 +16,7 @@ gamma = 0.11 length = 0.02 [pulse] -power = 10000 +peak_power = 10000 t0 = 2.84e-14 [pulse.variable] diff --git a/testing/configs/validate_types/bad1.toml b/testing/configs/validate_types/bad1.toml index 47fd4cf..ce628c5 100644 --- a/testing/configs/validate_types/bad1.toml +++ b/testing/configs/validate_types/bad1.toml @@ -9,8 +9,8 @@ model = "pcf" pitch_ratio = 0.37 [pulse] +peak_power = 100e3 pitch = 1.5e-6 -power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/validate_types/bad2.toml b/testing/configs/validate_types/bad2.toml index 120451c..7016eec 100644 --- a/testing/configs/validate_types/bad2.toml +++ b/testing/configs/validate_types/bad2.toml @@ -10,7 +10,7 @@ pitch = 1.5e-6 pitch_ratio = 0.37 [pulse] -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/validate_types/bad3.toml b/testing/configs/validate_types/bad3.toml index 68bc9f8..02875f1 100644 --- a/testing/configs/validate_types/bad3.toml +++ b/testing/configs/validate_types/bad3.toml @@ -10,7 +10,7 @@ pitch = 1.5e-6 pitch_ratio = 0.37 [pulse] -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/validate_types/bad4.toml b/testing/configs/validate_types/bad4.toml index 20cf1fc..935dc1d 100644 --- a/testing/configs/validate_types/bad4.toml +++ b/testing/configs/validate_types/bad4.toml @@ -10,7 +10,7 @@ pitch = 1.5e-6 pitch_ratio = 0.37 [pulse] -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/validate_types/bad5.toml b/testing/configs/validate_types/bad5.toml index b1448da..6cbcb19 100644 --- a/testing/configs/validate_types/bad5.toml +++ b/testing/configs/validate_types/bad5.toml @@ -10,7 +10,7 @@ pitch = 1.5e-6 pitch_ratio = 0.37 [pulse] -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/validate_types/bad6.toml b/testing/configs/validate_types/bad6.toml index 9cc9ced..e45989c 100644 --- a/testing/configs/validate_types/bad6.toml +++ b/testing/configs/validate_types/bad6.toml @@ -11,7 +11,7 @@ pitch_ratio = 0.37 [pulse] intensity_noise = 0.05e-2 -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/validate_types/bad7.toml b/testing/configs/validate_types/bad7.toml index 0b87595..73fb985 100644 --- a/testing/configs/validate_types/bad7.toml +++ b/testing/configs/validate_types/bad7.toml @@ -8,7 +8,7 @@ gamma = 0.018 length = 1 [pulse] -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/configs/validate_types/good.toml b/testing/configs/validate_types/good.toml index 7c3876e..bd5196f 100644 --- a/testing/configs/validate_types/good.toml +++ b/testing/configs/validate_types/good.toml @@ -8,7 +8,7 @@ pitch = 1.5e-6 pitch_ratio = 0.37 [pulse] -power = 100e3 +peak_power = 100e3 quantum_noise = true shape = "gaussian" wavelength = 1050e-9 diff --git a/testing/test_initialize.py b/testing/test_initialize.py index 1ba270d..05d5cd2 100644 --- a/testing/test_initialize.py +++ b/testing/test_initialize.py @@ -92,7 +92,7 @@ class TestInitializeMethods(unittest.TestCase): with self.assertRaisesRegex( MissingParameterError, - r"1 of '\['power', 'energy', 'width', 't0'\]' is required when 'soliton_num' is specified and no defaults have been set", + r"1 of '\['peak_power', 'energy', 'width', 't0'\]' is required when 'soliton_num' is specified and no defaults have been set", ): init._ensure_consistency(conf("bad2")) diff --git a/testing/test_pulse.py b/testing/test_pulse.py index f41e556..6a632d4 100644 --- a/testing/test_pulse.py +++ b/testing/test_pulse.py @@ -6,13 +6,13 @@ 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.assertNotIn(None, conform_pulse_params("gaussian", t0=5, peak_power=6)) + self.assertNotIn(None, conform_pulse_params("gaussian", width=5, peak_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))) + self.assertEqual(4, len(conform_pulse_params("gaussian", t0=5, peak_power=6))) + self.assertEqual(4, len(conform_pulse_params("gaussian", width=5, peak_power=6))) with self.assertRaisesRegex( TypeError, "when soliton number is desired, both gamma and beta2 must be specified" @@ -30,10 +30,10 @@ class TestPulseMethods(unittest.TestCase): 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)) + 5, len(conform_pulse_params("gaussian", t0=5, peak_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)) + 5, len(conform_pulse_params("gaussian", width=5, peak_power=6, gamma=0.01, beta2=2e-6)) ) diff --git a/testing/test_utils.py b/testing/test_utils.py index e16e833..20429d7 100644 --- a/testing/test_utils.py +++ b/testing/test_utils.py @@ -58,7 +58,7 @@ class TestUtilsMethods(unittest.TestCase): old = conf("initial_config") new = conf("fiber2") - over = utils.override_config(old, new) + over = utils.override_config(new, old) self.assertIn("input_transmission", over["fiber"]["variable"]) self.assertNotIn("input_transmission", over["fiber"])