better np hash, Fiber always array, resonance

This commit is contained in:
Benoît Sierro
2021-10-18 09:45:55 +02:00
parent c44d82fb19
commit 1132b19012
9 changed files with 164 additions and 63 deletions

View File

@@ -153,6 +153,9 @@ capillary_spacing : float, optional if d is specified
capillary_resonance_strengths : list, optional capillary_resonance_strengths : list, optional
list of resonance strengths. Default is [] list of resonance strengths. Default is []
capillary_resonance_max_order : int, optional
max order of resonance strengths to be deduced
capillary_nested : int, optional capillary_nested : int, optional
how many nested capillaries. Default is 0 how many nested capillaries. Default is 0

View File

@@ -7,28 +7,23 @@ scgenerator module but some function may be used in any python program
from __future__ import annotations from __future__ import annotations
import itertools import itertools
import multiprocessing
import os import os
import random
import re
import shutil
import threading
from collections import abc from collections import abc
from io import StringIO
from pathlib import Path from pathlib import Path
from string import printable as str_printable from string import printable as str_printable
from functools import cache from functools import cache
from typing import Any, Callable, Generator, Iterable, MutableMapping, Sequence, TypeVar, Union from typing import Any, MutableMapping, Sequence, TypeVar
import numpy as np import numpy as np
from numpy.lib.arraysetops import isin
import pkg_resources as pkg import pkg_resources as pkg
import toml import toml
from tqdm import tqdm from tqdm import tqdm
import itertools
from .pbar import PBars
from ..const import PARAM_FN, PARAM_SEPARATOR, SPEC1_FN, SPECN_FN1, Z_FN, __version__ from ..const import SPEC1_FN, __version__
from ..env import pbar_policy
from ..logger import get_logger from ..logger import get_logger
T_ = TypeVar("T_") T_ = TypeVar("T_")
@@ -95,11 +90,11 @@ def load_spectrum(file: os.PathLike) -> np.ndarray:
return np.load(file) return np.load(file)
def conform_toml_path(path: os.PathLike) -> str: def conform_toml_path(path: os.PathLike) -> Path:
path: str = str(path) path: str = str(path)
if not path.lower().endswith(".toml"): if not path.lower().endswith(".toml"):
path = path + ".toml" path = path + ".toml"
return path return Path(path)
def open_single_config(path: os.PathLike) -> dict[str, Any]: def open_single_config(path: os.PathLike) -> dict[str, Any]:
@@ -116,13 +111,20 @@ def _open_config(path: os.PathLike):
path = conform_toml_path(path) path = conform_toml_path(path)
dico = resolve_loadfile_arg(load_toml(path)) dico = resolve_loadfile_arg(load_toml(path))
dico.setdefault("variable", {}) dico = standardize_variable_dicts(dico)
for key in {"simulation", "fiber", "gas", "pulse"} & dico.keys(): if "Fiber" not in dico:
section = dico.pop(key) dico = dict(name=path.name, Fiber=[dico])
dico["variable"].update(section.pop("variable", {})) return dico
dico.update(section)
if len(dico["variable"]) == 0:
dico.pop("variable") def standardize_variable_dicts(dico: dict[str, Any]):
if "Fiber" in dico:
dico["Fiber"] = [standardize_variable_dicts(fiber) for fiber in dico["Fiber"]]
if (var := dico.get("variable")) is not None:
if isinstance(var, MutableMapping):
dico["variable"] = [var]
else:
dico["variable"] = [{}]
return dico return dico
@@ -194,14 +196,18 @@ def load_config_sequence(path: os.PathLike) -> tuple[Path, list[dict[str, Any]]]
final_path = loaded_config.get("name") final_path = loaded_config.get("name")
configs = [] configs = []
for i, params in enumerate(fiber_list): for i, params in enumerate(fiber_list):
params.setdefault("variable", {})
configs.append(loaded_config | params) configs.append(loaded_config | params)
configs[0]["variable"] = loaded_config.get("variable", {}) | configs[0]["variable"] for root_vary, first_vary in itertools.product(
configs[0]["variable"]["num"] = list(range(configs[0].get("repeat", 1))) loaded_config["variable"], configs[0]["variable"]
):
if len(common := root_vary.keys() & first_vary.keys()) != 0:
raise ValueError(f"These variable keys are specified twice : {common!r}")
configs[0] |= {k: v for k, v in loaded_config.items() if k != "variable"}
configs[0]["variable"].append(dict(num=list(range(configs[0].get("repeat", 1)))))
return Path(final_path), configs return Path(final_path), configs
@cache
def load_material_dico(name: str) -> dict[str, Any]: def load_material_dico(name: str) -> dict[str, Any]:
"""loads a material dictionary """loads a material dictionary
Parameters Parameters

View File

@@ -6,6 +6,14 @@ import numpy as np
CacheInfo = namedtuple("CacheInfo", "hits misses size") CacheInfo = namedtuple("CacheInfo", "hits misses size")
def make_arg_hashable(arg):
if isinstance(arg, np.ndarray):
return arg.tobytes()
elif isinstance(arg, list):
return tuple(make_arg_hashable(a) for a in arg)
return arg
def np_cache(func): def np_cache(func):
def new_cached_func(): def new_cached_func():
cache = {} cache = {}
@@ -14,15 +22,8 @@ def np_cache(func):
@wraps(func) @wraps(func)
def wrapped(*args, **kwargs): def wrapped(*args, **kwargs):
nonlocal cache, hits, misses nonlocal cache, hits, misses
hashable_args = tuple( hashable_args = tuple(make_arg_hashable(a) for a in args)
tuple(arg) if isinstance(arg, (np.ndarray, list)) else arg for arg in args hashable_kwargs = tuple({k: make_arg_hashable(a) for k, a in kwargs.items()}.items())
)
hashable_kwargs = tuple(
{
k: tuple(kwarg) if isinstance(kwarg, (np.ndarray, list)) else kwarg
for k, kwarg in kwargs.items()
}.items()
)
key = hash((hashable_args, hashable_kwargs)) key = hash((hashable_args, hashable_kwargs))
if key not in cache: if key not in cache:
misses += 1 misses += 1

View File

@@ -59,6 +59,7 @@ VALID_VARIABLE = {
"capillary_thickness", "capillary_thickness",
"capillary_spacing", "capillary_spacing",
"capillary_resonance_strengths", "capillary_resonance_strengths",
"capillary_resonance_max_order",
"capillary_nested", "capillary_nested",
"he_mode", "he_mode",
"fit_parameters", "fit_parameters",
@@ -377,7 +378,10 @@ class Parameters(_AbstractParameters):
capillary_radius: float = Parameter(in_range_excl(0, 1e-3)) capillary_radius: float = Parameter(in_range_excl(0, 1e-3))
capillary_thickness: float = Parameter(in_range_excl(0, 1e-3)) capillary_thickness: float = Parameter(in_range_excl(0, 1e-3))
capillary_spacing: float = Parameter(in_range_excl(0, 1e-3)) capillary_spacing: float = Parameter(in_range_excl(0, 1e-3))
capillary_resonance_strengths: Iterable[float] = Parameter(num_list, default=[]) capillary_resonance_strengths: Iterable[float] = Parameter(
validator_list(type_checker(int, float, np.ndarray))
)
capillary_resonance_max_order: int = Parameter(non_negative(int), default=0)
capillary_nested: int = Parameter(non_negative(int), default=0) capillary_nested: int = Parameter(non_negative(int), default=0)
# gas # gas
@@ -468,12 +472,12 @@ class Parameters(_AbstractParameters):
param_dict = {k: v for k, v in asdict(self).items() if v is not None} param_dict = {k: v for k, v in asdict(self).items() if v is not None}
evaluator = Evaluator.default() evaluator = Evaluator.default()
evaluator.set(**param_dict) evaluator.set(**param_dict)
for p_name in to_compute: results = [evaluator.compute(p_name) for p_name in to_compute]
evaluator.compute(p_name)
valid_fields = self.all_parameters() valid_fields = self.all_parameters()
for k, v in evaluator.params.items(): for k, v in evaluator.params.items():
if k in valid_fields: if k in valid_fields:
setattr(self, k, v) setattr(self, k, v)
return results
@classmethod @classmethod
def all_parameters(cls) -> list[str]: def all_parameters(cls) -> list[str]:
@@ -481,7 +485,7 @@ class Parameters(_AbstractParameters):
@classmethod @classmethod
def load(cls, path: os.PathLike) -> "Parameters": def load(cls, path: os.PathLike) -> "Parameters":
return cls(**utils._open_config(path)) return cls(**utils.load_toml(path))
@classmethod @classmethod
def load_and_compute(cls, path: os.PathLike) -> "Parameters": def load_and_compute(cls, path: os.PathLike) -> "Parameters":
@@ -870,8 +874,8 @@ class Configuration:
config.setdefault("name", Parameters.name.default) config.setdefault("name", Parameters.name.default)
self.z_num += config["z_num"] self.z_num += config["z_num"]
fiber_names.add(config["name"]) fiber_names.add(config["name"])
vary_dict = config.pop("variable") vary_dict_list: list[dict[str, list]] = config.pop("variable")
self.variationer.append(vary_dict) self.variationer.append(vary_dict_list)
self.fiber_paths.append( self.fiber_paths.append(
utils.ensure_folder( utils.ensure_folder(
self.final_path / fiber_folder(i, self.name, config["name"]), self.final_path / fiber_folder(i, self.name, config["name"]),
@@ -879,10 +883,13 @@ class Configuration:
prevent_overwrite=not self.overwrite, prevent_overwrite=not self.overwrite,
) )
) )
self.__validate_variable(vary_dict) self.__validate_variable(vary_dict_list)
self.num_fibers += 1 self.num_fibers += 1
Evaluator.evaluate_default( Evaluator.evaluate_default(
self.__build_base_config() | config | {k: v[0] for k, v in vary_dict.items()}, True self.__build_base_config()
| config
| {k: v[0] for vary_dict in vary_dict_list for k, v in vary_dict.items()},
True,
) )
self.num_sim = self.variationer.var_num() self.num_sim = self.variationer.var_num()
self.total_num_steps = sum( self.total_num_steps = sum(
@@ -893,17 +900,18 @@ class Configuration:
def __build_base_config(self): def __build_base_config(self):
cfg = self.master_config.copy() cfg = self.master_config.copy()
vary = cfg.pop("variable", {}) vary: list[dict[str, list]] = cfg.pop("variable")
return cfg | {k: v[0] for k, v in vary.items()} return cfg | {k: v[0] for vary_dict in vary for k, v in vary_dict.items()}
def __validate_variable(self, vary_dict: dict[str, list]): def __validate_variable(self, vary_dict_list: list[dict[str, list]]):
for k, v in vary_dict.items(): for vary_dict in vary_dict_list:
p = getattr(Parameters, k) for k, v in vary_dict.items():
validator_list(p.validator)("variable " + k, v) p = getattr(Parameters, k)
if k not in VALID_VARIABLE: validator_list(p.validator)("variable " + k, v)
raise TypeError(f"{k!r} is not a valid variable parameter") if k not in VALID_VARIABLE:
if len(v) == 0: raise TypeError(f"{k!r} is not a valid variable parameter")
raise ValueError(f"variable parameter {k!r} must not be empty") if len(v) == 0:
raise ValueError(f"variable parameter {k!r} must not be empty")
def __iter__(self) -> Iterator[tuple[VariationDescriptor, Parameters]]: def __iter__(self) -> Iterator[tuple[VariationDescriptor, Parameters]]:
for i in range(self.num_fibers): for i in range(self.num_fibers):
@@ -1116,6 +1124,8 @@ default_rules: list[Rule] = [
conditions=dict(model="pcf"), conditions=dict(model="pcf"),
), ),
Rule("capillary_spacing", fiber.capillary_spacing_hasan), Rule("capillary_spacing", fiber.capillary_spacing_hasan),
Rule("capillary_resonance_strengths", fiber.capillary_resonance_strengths),
Rule("capillary_resonance_strengths", lambda: [], priorities=-1),
# Fiber nonlinearity # Fiber nonlinearity
Rule("A_eff", fiber.A_eff_from_V), Rule("A_eff", fiber.A_eff_from_V),
Rule("A_eff", fiber.A_eff_from_diam), Rule("A_eff", fiber.A_eff_from_diam),

View File

@@ -76,12 +76,13 @@ class Variationer:
num_vars = [] num_vars = []
for d in var_list: for d in var_list:
values = list(d.values()) values = list(d.values())
len_to_test = len(values[0]) if len(values) > 0:
if not all(len(v) == len_to_test for v in values[1:]): len_to_test = len(values[0])
raise VariationSpecsError( if not all(len(v) == len_to_test for v in values[1:]):
f"variable items should all have the same number of parameters" raise VariationSpecsError(
) f"variable items should all have the same number of parameters"
num_vars.append(len_to_test) )
num_vars.append(len_to_test)
if len(num_vars) == 0: if len(num_vars) == 0:
num_vars = [1] num_vars = [1]
self.all_indices.append(num_vars) self.all_indices.append(num_vars)

View File

@@ -196,6 +196,60 @@ def capillary_spacing_hasan(
) )
def resonance_thickness(
wl_for_disp: np.ndarray, order: int, n_gas_2: np.ndarray, core_radius: float
) -> float:
"""computes at which wall thickness the specified wl is resonant
Parameters
----------
wl_for_disp : np.ndarray
in m
order : int
0, 1, ...
n_gas_2 : np.ndarray
as returned by materials.n_gas_2
core_radius : float
in m
Returns
-------
float
thickness in m
"""
n_si_2 = mat.n_gas_2(wl_for_disp, "silica", None, None, True)
return (
wl_for_disp
/ (4 * np.sqrt(n_si_2))
* (2 * order + 1)
* (1 - n_gas_2 / n_si_2 + wl_for_disp ** 2 / (4 * n_si_2 * core_radius ** 2)) ** -0.5
)
def resonance_strength(
wl_for_disp: np.ndarray, core_radius: float, capillary_thickness: float, order: int
) -> float:
a = 1.83 + (2.3 * capillary_thickness / core_radius)
n_si = np.sqrt(mat.n_gas_2(wl_for_disp, "silica", None, None, True))
return (
capillary_thickness
/ (n_si * core_radius) ** (2.303 * a / n_si)
* ((order + 2) / (3 * order)) ** (3.57 * a)
)
def capillary_resonance_strengths(
wl_for_disp: np.ndarray,
core_radius: float,
capillary_thickness: float,
capillary_resonance_max_order: int,
) -> list[float]:
return [
resonance_strength(wl_for_disp, core_radius, capillary_thickness, o)
for o in range(1, capillary_resonance_max_order + 1)
]
@np_cache @np_cache
def n_eff_hasan( def n_eff_hasan(
wl_for_disp: np.ndarray, wl_for_disp: np.ndarray,
@@ -238,6 +292,7 @@ def n_eff_hasan(
Hasan, Md Imran, Nail Akhmediev, and Wonkeun Chang. "Empirical formulae for dispersion and effective mode area in hollow-core antiresonant fibers." Journal of Lightwave Technology 36.18 (2018): 4060-4065. Hasan, Md Imran, Nail Akhmediev, and Wonkeun Chang. "Empirical formulae for dispersion and effective mode area in hollow-core antiresonant fibers." Journal of Lightwave Technology 36.18 (2018): 4060-4065.
""" """
u = u_nm(1, 1) u = u_nm(1, 1)
alpha = 5e-12
Rg = core_radius / capillary_spacing Rg = core_radius / capillary_spacing
@@ -257,7 +312,7 @@ def n_eff_hasan(
n_eff_2 += ( n_eff_2 += (
strength strength
* wl_for_disp ** 2 * wl_for_disp ** 2
/ (wl_for_disp ** 2 - chi_sil * (2 * capillary_thickness / (m + 1)) ** 2) / (alpha + wl_for_disp ** 2 - chi_sil * (2 * capillary_thickness / (m + 1)) ** 2)
) )
return np.sqrt(n_eff_2) return np.sqrt(n_eff_2)

View File

@@ -7,11 +7,14 @@ from ..logger import get_logger
from . import units from . import units
from .. import _utils from .. import _utils
from .units import NA, c, kB, me, e, hbar from .units import NA, c, kB, me, e, hbar
from .._utils.cache import np_cache
@np_cache
def n_gas_2( def n_gas_2(
wl_for_disp: np.ndarray, gas_name: str, pressure: float, temperature: float, ideal_gas: bool wl_for_disp: np.ndarray, gas_name: str, pressure: float, temperature: float, ideal_gas: bool
): ):
"""Returns the sqare of the index of refraction of the specified gas"""
material_dico = _utils.load_material_dico(gas_name) material_dico = _utils.load_material_dico(gas_name)
if ideal_gas: if ideal_gas:

View File

@@ -489,12 +489,12 @@ class Simulations:
self.sim_jobs_per_node = 1 self.sim_jobs_per_node = 1
def finished_and_complete(self): def finished_and_complete(self):
for sim in self.configuration.all_configs.values(): # for sim in self.configuration.all_configs.values():
if ( # if (
self.configuration.sim_status(sim.output_path)[0] # self.configuration.sim_status(sim.output_path)[0]
!= self.configuration.State.COMPLETE # != self.configuration.State.COMPLETE
): # ):
return False # return False
return True return True
def run(self): def run(self):

22
testing/test_resonance.py Normal file
View File

@@ -0,0 +1,22 @@
import numpy as np
import scgenerator as sc
import matplotlib.pyplot as plt
from pathlib import Path
def main():
capillary_thickness = 1.4e-6
wl = np.linspace(200e-9, 2000e-9, 500)
n_gas_2 = sc.materials.n_gas_2(wl, "air", 3e5, 300, True)
resonances = []
for i in range(5):
t = sc.fiber.resonance_thickness(wl, i, n_gas_2, 40e-6)
resonances += list(1e9 * sc.math.all_zeros(wl, t - capillary_thickness))
plt.plot(1e9 * wl, 1e6 * t)
plt.xlabel("nm")
plt.ylabel("μm")
plt.show()
if __name__ == "__main__":
main()