From e985f053accd8399c79476f07ab10388a37b3ddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Sierro?= Date: Tue, 1 Jun 2021 10:47:57 +0200 Subject: [PATCH] better submit, more tests --- src/scgenerator/initialize.py | 38 +++++++---- src/scgenerator/physics/pulse.py | 18 +++--- src/scgenerator/scripts/slurm_submit.py | 59 ++++++++++++++++- src/scgenerator/utils.py | 34 +--------- testing/configs/custom_field/init_field.npz | Bin 0 -> 49656 bytes testing/configs/custom_field/mean_power.toml | 8 +++ testing/configs/custom_field/no_change.toml | 6 ++ testing/configs/custom_field/peak_power.toml | 7 ++ testing/configs/custom_field/recover1.toml | 7 ++ testing/configs/custom_field/recover2.toml | 7 ++ testing/configs/ensure_consistency/good1.toml | 1 + testing/test_initialize.py | 60 ++++++++++++++---- 12 files changed, 176 insertions(+), 69 deletions(-) create mode 100644 testing/configs/custom_field/init_field.npz create mode 100644 testing/configs/custom_field/mean_power.toml create mode 100644 testing/configs/custom_field/no_change.toml create mode 100644 testing/configs/custom_field/peak_power.toml create mode 100644 testing/configs/custom_field/recover1.toml create mode 100644 testing/configs/custom_field/recover2.toml diff --git a/src/scgenerator/initialize.py b/src/scgenerator/initialize.py index 7741bcb..1c91c2d 100644 --- a/src/scgenerator/initialize.py +++ b/src/scgenerator/initialize.py @@ -675,23 +675,35 @@ def compute_init_parameters(config: Dict[str, Any]) -> Dict[str, Any]: def setup_custom_field(params: Dict[str, Any]) -> bool: + """sets up a custom field function if necessary and returns + True if it did so, False otherwise + + Parameters + ---------- + params : Dict[str, Any] + params dictionary + + Returns + ------- + bool + True if the field has been modified + """ logger = get_logger(__name__) - custom_field = True if "prev_data_dir" in params: spec = io.load_last_spectrum(Path(params["prev_data_dir"]))[1] - params["field_0"] = np.fft.ifft(spec) * params["input_transmission"] - elif "field_file" in params: - field_data = np.load(params["field_file"]) - field_interp = interp1d( - field_data["time"], field_data["field"], bounds_error=False, fill_value=(0, 0) - ) - params["field_0"] = field_interp(params["t"]) - elif "field_0" in params: - params = _evalutate_custom_field_equation(params) + params["field_0"] = np.fft.ifft(spec) * np.sqrt(params["input_transmission"]) else: - custom_field = False + if "field_file" in params: + field_data = np.load(params["field_file"]) + field_interp = interp1d( + field_data["time"], field_data["field"], bounds_error=False, fill_value=(0, 0) + ) + params["field_0"] = field_interp(params["t"]) + elif "field_0" in params: + params = _evalutate_custom_field_equation(params) + else: + return False - if custom_field: params["field_0"] = params["field_0"] * pulse.modify_field_ratio( params["t"], params["field_0"], @@ -706,7 +718,7 @@ def setup_custom_field(params: Dict[str, Any]) -> bool: logger.debug(f"had to adjust w by {delta_w}") params["wavelength"] = units.m.inv(units.m(params["wavelength"]) - delta_w) _update_frequency_domain(params) - return custom_field + return True def _update_pulse_parameters(params): diff --git a/src/scgenerator/physics/pulse.py b/src/scgenerator/physics/pulse.py index 0662f35..00cb831 100644 --- a/src/scgenerator/physics/pulse.py +++ b/src/scgenerator/physics/pulse.py @@ -11,7 +11,7 @@ n is the number of spectra at the same z position and nt is the size of the time import itertools import os -from typing import Tuple +from typing import Literal, Tuple import matplotlib.pyplot as plt import numpy as np @@ -115,14 +115,14 @@ def modify_field_ratio( def conform_pulse_params( - shape, - width=None, - t0=None, - peak_power=None, - energy=None, - soliton_num=None, - gamma=None, - beta2=None, + shape: Literal["gaussian", "sech"], + width: float = None, + t0: float = None, + peak_power: float = None, + energy: float = None, + soliton_num: float = None, + gamma: float = None, + beta2: float = None, ): """makes sure all parameters of the pulse are set and consistent diff --git a/src/scgenerator/scripts/slurm_submit.py b/src/scgenerator/scripts/slurm_submit.py index 3dbcbb5..1d46265 100644 --- a/src/scgenerator/scripts/slurm_submit.py +++ b/src/scgenerator/scripts/slurm_submit.py @@ -4,12 +4,65 @@ import re import shutil import subprocess from datetime import datetime, timedelta +from typing import Tuple +import numpy as np from ..initialize import validate_config_sequence from ..io import Paths from ..utils import count_variations +def primes(n): + primfac = [] + d = 2 + while d * d <= n: + while (n % d) == 0: + primfac.append(d) # supposing you want multiple factors repeated + n //= d + d += 1 + if n > 1: + primfac.append(n) + return primfac + + +def balance(n, lim=(32, 32)): + factors = primes(n) + if len(factors) == 1: + factors = primes(n + 1) + a, b, x, y = 1, 1, 1, 1 + while len(factors) > 0 and x <= lim[0] and y <= lim[1]: + a = x + b = y + if y >= x: + x *= factors.pop(0) + else: + y *= factors.pop() + return a, b + + +def distribute( + num: int, nodes: int = None, cpus_per_node: int = None, lim=(16, 32) +) -> Tuple[int, int]: + if nodes is None and cpus_per_node is None: + balanced = balance(num, lim) + if num > max(lim): + while np.product(balanced) < min(lim): + num += 1 + balanced = balance(num, lim) + nodes = min(balanced) + cpus_per_node = max(balanced) + + elif nodes is None: + nodes = num // cpus_per_node + while nodes > lim[0]: + nodes //= 2 + elif cpus_per_node is None: + cpus_per_node = num // nodes + while cpus_per_node > lim[1]: + cpus_per_node //= 2 + return nodes, cpus_per_node + + def format_time(t): try: t = float(t) @@ -25,9 +78,9 @@ def create_parser(): "-t", "--time", required=True, type=str, help="time required for the job in hh:mm:ss" ) parser.add_argument( - "-c", "--cpus-per-node", required=True, type=int, help="number of cpus required per node" + "-c", "--cpus-per-node", default=None, type=int, help="number of cpus required per node" ) - parser.add_argument("-n", "--nodes", required=True, type=int, help="number of nodes required") + parser.add_argument("-n", "--nodes", default=None, type=int, help="number of nodes required") parser.add_argument( "--environment-setup", required=False, @@ -70,6 +123,8 @@ def main(): sim_num, _ = count_variations(final_config) + args.nodes, args.cpus_per_nodes = distribute(sim_num, args.nodes, args.cpus_per_nodes) + file_name = ( "submit " + final_config["name"] + "-" + format(datetime.now(), "%Y%m%d%H%M") + ".sh" ) diff --git a/src/scgenerator/utils.py b/src/scgenerator/utils.py index cc66c54..1f31c35 100644 --- a/src/scgenerator/utils.py +++ b/src/scgenerator/utils.py @@ -25,10 +25,6 @@ from .const import PARAM_SEPARATOR, PREFIX_KEY_BASE, valid_variable, HUSH_PROGRE from .logger import get_logger from .math import * -# XXX ############################################ -# XXX ############### Pure Python ################ -# XXX ############################################ - class PBars: @classmethod @@ -245,34 +241,6 @@ def format_value(value): return str(value) -# def variable_list_from_path(s: str) -> List[tuple]: -# s = s.replace("/", "") -# str_list = s.split(PARAM_SEPARATOR) -# 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 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 variable_iterator(config) -> Iterator[Tuple[List[Tuple[str, Any]], dict]]: """given a config with "variable" parameters, iterates through every possible combination, yielding a a list of (parameter_name, value) tuples and a full config dictionary. @@ -385,7 +353,7 @@ def parallelize(func, arg_iter, sim_jobs=4, progress_tracker_kwargs=None, const_ return np.array(results) -def deep_update(d: Mapping, u: Mapping): +def deep_update(d: Mapping, u: Mapping) -> dict: for k, v in u.items(): if isinstance(v, collections.abc.Mapping): d[k] = deep_update(d.get(k, {}), v) diff --git a/testing/configs/custom_field/init_field.npz b/testing/configs/custom_field/init_field.npz new file mode 100644 index 0000000000000000000000000000000000000000..e5687ede4877b824bb07085f80f612862c76698a GIT binary patch literal 49656 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 P9q|Yv_K{EiefR$W+Bd-G literal 0 HcmV?d00001 diff --git a/testing/configs/custom_field/mean_power.toml b/testing/configs/custom_field/mean_power.toml new file mode 100644 index 0000000..953ec5c --- /dev/null +++ b/testing/configs/custom_field/mean_power.toml @@ -0,0 +1,8 @@ +dt = 1e-15 +field_file = "testing/configs/custom_field/init_field.npz" +length = 1 +mean_power = 220e-3 +repetition_rate = 40e6 +t_num = 2048 +wavelength = 1000e-9 +z_num = 32 diff --git a/testing/configs/custom_field/no_change.toml b/testing/configs/custom_field/no_change.toml new file mode 100644 index 0000000..288b1fb --- /dev/null +++ b/testing/configs/custom_field/no_change.toml @@ -0,0 +1,6 @@ +dt = 1e-15 +field_file = "testing/configs/custom_field/init_field.npz" +length = 1 +t_num = 2048 +wavelength = 1000e-9 +z_num = 32 diff --git a/testing/configs/custom_field/peak_power.toml b/testing/configs/custom_field/peak_power.toml new file mode 100644 index 0000000..e658d0a --- /dev/null +++ b/testing/configs/custom_field/peak_power.toml @@ -0,0 +1,7 @@ +dt = 1e-15 +field_file = "testing/configs/custom_field/init_field.npz" +length = 1 +peak_power = 20000 +t_num = 2048 +wavelength = 1000e-9 +z_num = 32 diff --git a/testing/configs/custom_field/recover1.toml b/testing/configs/custom_field/recover1.toml new file mode 100644 index 0000000..52400f3 --- /dev/null +++ b/testing/configs/custom_field/recover1.toml @@ -0,0 +1,7 @@ +dt = 1e-15 +input_transmission = 1 +length = 1 +prev_data_dir = "testing/configs/custom_field/recover_data" +t_num = 2048 +wavelength = 1000e-9 +z_num = 32 diff --git a/testing/configs/custom_field/recover2.toml b/testing/configs/custom_field/recover2.toml new file mode 100644 index 0000000..853b8ea --- /dev/null +++ b/testing/configs/custom_field/recover2.toml @@ -0,0 +1,7 @@ +dt = 1e-15 +input_transmission = 0.9 +length = 1 +prev_data_dir = "testing/configs/custom_field/recover_data" +t_num = 2048 +wavelength = 1000e-9 +z_num = 32 diff --git a/testing/configs/ensure_consistency/good1.toml b/testing/configs/ensure_consistency/good1.toml index 194f6b0..c1b778d 100644 --- a/testing/configs/ensure_consistency/good1.toml +++ b/testing/configs/ensure_consistency/good1.toml @@ -5,6 +5,7 @@ name = "test config" [fiber] gamma = 0.018 length = 1 +model = "pcf" pitch = 1.5e-6 pitch_ratio = 0.37 diff --git a/testing/test_initialize.py b/testing/test_initialize.py index 05d5cd2..f438a61 100644 --- a/testing/test_initialize.py +++ b/testing/test_initialize.py @@ -2,8 +2,9 @@ import unittest from copy import deepcopy import scgenerator.initialize as init +import numpy as np import toml -from scgenerator import utils +from scgenerator import defaults, utils, math from scgenerator.errors import * @@ -30,7 +31,7 @@ class TestParamSequence(unittest.TestCase): for param_seq in self.iterconf(["almost_equal", "equal", "no_variations"]): l = [] s = [] - for vary_list, _ in param_seq.iterate_without_computing(): + for vary_list, _ in utils.required_simulations(param_seq.config): self.assertNotIn(vary_list, l) self.assertNotIn(utils.format_variable_list(vary_list), s) l.append(vary_list) @@ -39,12 +40,12 @@ class TestParamSequence(unittest.TestCase): def test_init_config_not_affected_by_iteration(self): for param_seq in self.iterconf(["almost_equal", "equal", "no_variations"]): config = deepcopy(param_seq.config) - for _ in param_seq.iterate_without_computing(): + for _ in utils.required_simulations(param_seq.config): self.assertEqual(config.items(), param_seq.config.items()) def test_no_variations_yields_only_num_and_id(self): for param_seq in self.iterconf(["no_variations"]): - for vary_list, _ in param_seq.iterate_without_computing(): + for vary_list, _ in utils.required_simulations(param_seq.config): self.assertEqual(vary_list[1][0], "num") self.assertEqual(vary_list[0][0], "id") self.assertEqual(2, len(vary_list)) @@ -65,18 +66,18 @@ class TestInitializeMethods(unittest.TestCase): 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"): + with self.assertRaisesRegex(TypeError, "Variable parameters should be specified in a list"): init._validate_types(conf("bad5")) with self.assertRaisesRegex( TypeError, - "value '0' of type for key 'repeat' is not valid, must be a strictly positive integer", + "value '0' of type .*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", + r"Variable parameters lists should contain at least 1 element", ): init._ensure_consistency(conf("bad7")) @@ -92,7 +93,7 @@ class TestInitializeMethods(unittest.TestCase): with self.assertRaisesRegex( MissingParameterError, - r"1 of '\['peak_power', 'energy', 'width', 't0'\]' is required when 'soliton_num' is specified and no defaults have been set", + r"1 of '\['peak_power', 'mean_power', 'energy', 'width', 't0'\]' is required when 'soliton_num' is specified and no defaults have been set", ): init._ensure_consistency(conf("bad2")) @@ -156,14 +157,49 @@ class TestInitializeMethods(unittest.TestCase): dict( t_num=16384, time_window=37e-12, - lower_wavelength_interp_limit=0, - upper_wavelength_interp_limit=1900e-9, + lower_wavelength_interp_limit=defaults.default_parameters[ + "lower_wavelength_interp_limit" + ], + upper_wavelength_interp_limit=defaults.default_parameters[ + "upper_wavelength_interp_limit" + ], ).items(), init._ensure_consistency(conf("good6"))["simulation"].items(), ) - # def test_compute_init_parameters(self): - # conf = lambda s: load_conf("compute_init_parameters/" + s) + def test_setup_custom_field(self): + d = np.load("testing/configs/custom_field/init_field.npz") + t = d["time"] + field = d["field"] + conf = load_conf("custom_field/no_change") + conf = init._generate_sim_grid(conf) + result = init.setup_custom_field(conf) + self.assertAlmostEqual(conf["field_0"].real.max(), field.real.max(), 4) + self.assertTrue(result) + + conf = load_conf("custom_field/peak_power") + conf = init._generate_sim_grid(conf) + result = init.setup_custom_field(conf) + self.assertAlmostEqual(math.abs2(conf["field_0"]).max(), 20000, 4) + self.assertTrue(result) + + conf = load_conf("custom_field/mean_power") + conf = init._generate_sim_grid(conf) + result = init.setup_custom_field(conf) + self.assertAlmostEqual(np.trapz(math.abs2(conf["field_0"]), conf["t"]), 0.22 / 40e6, 4) + self.assertTrue(result) + + conf = load_conf("custom_field/recover1") + conf = init._generate_sim_grid(conf) + result = init.setup_custom_field(conf) + self.assertAlmostEqual(math.abs2(conf["field_0"] - field).sum(), 0) + self.assertTrue(result) + + conf = load_conf("custom_field/recover2") + conf = init._generate_sim_grid(conf) + result = init.setup_custom_field(conf) + self.assertAlmostEqual((math.abs2(conf["field_0"]) / 0.9 - math.abs2(field)).sum(), 0) + self.assertTrue(result) if __name__ == "__main__":