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
list of resonance strengths. Default is []
capillary_resonance_max_order : int, optional
max order of resonance strengths to be deduced
capillary_nested : int, optional
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
import itertools
import multiprocessing
import os
import random
import re
import shutil
import threading
from collections import abc
from io import StringIO
from pathlib import Path
from string import printable as str_printable
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
from numpy.lib.arraysetops import isin
import pkg_resources as pkg
import toml
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 ..env import pbar_policy
from ..const import SPEC1_FN, __version__
from ..logger import get_logger
T_ = TypeVar("T_")
@@ -95,11 +90,11 @@ def load_spectrum(file: os.PathLike) -> np.ndarray:
return np.load(file)
def conform_toml_path(path: os.PathLike) -> str:
def conform_toml_path(path: os.PathLike) -> Path:
path: str = str(path)
if not path.lower().endswith(".toml"):
path = path + ".toml"
return path
return Path(path)
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)
dico = resolve_loadfile_arg(load_toml(path))
dico.setdefault("variable", {})
for key in {"simulation", "fiber", "gas", "pulse"} & dico.keys():
section = dico.pop(key)
dico["variable"].update(section.pop("variable", {}))
dico.update(section)
if len(dico["variable"]) == 0:
dico.pop("variable")
dico = standardize_variable_dicts(dico)
if "Fiber" not in dico:
dico = dict(name=path.name, Fiber=[dico])
return dico
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
@@ -194,14 +196,18 @@ def load_config_sequence(path: os.PathLike) -> tuple[Path, list[dict[str, Any]]]
final_path = loaded_config.get("name")
configs = []
for i, params in enumerate(fiber_list):
params.setdefault("variable", {})
configs.append(loaded_config | params)
configs[0]["variable"] = loaded_config.get("variable", {}) | configs[0]["variable"]
configs[0]["variable"]["num"] = list(range(configs[0].get("repeat", 1)))
for root_vary, first_vary in itertools.product(
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
@cache
def load_material_dico(name: str) -> dict[str, Any]:
"""loads a material dictionary
Parameters

View File

@@ -6,6 +6,14 @@ import numpy as np
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 new_cached_func():
cache = {}
@@ -14,15 +22,8 @@ def np_cache(func):
@wraps(func)
def wrapped(*args, **kwargs):
nonlocal cache, hits, misses
hashable_args = tuple(
tuple(arg) if isinstance(arg, (np.ndarray, list)) else arg for arg in args
)
hashable_kwargs = tuple(
{
k: tuple(kwarg) if isinstance(kwarg, (np.ndarray, list)) else kwarg
for k, kwarg in kwargs.items()
}.items()
)
hashable_args = tuple(make_arg_hashable(a) for a in args)
hashable_kwargs = tuple({k: make_arg_hashable(a) for k, a in kwargs.items()}.items())
key = hash((hashable_args, hashable_kwargs))
if key not in cache:
misses += 1

View File

@@ -59,6 +59,7 @@ VALID_VARIABLE = {
"capillary_thickness",
"capillary_spacing",
"capillary_resonance_strengths",
"capillary_resonance_max_order",
"capillary_nested",
"he_mode",
"fit_parameters",
@@ -377,7 +378,10 @@ class Parameters(_AbstractParameters):
capillary_radius: 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_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)
# gas
@@ -468,12 +472,12 @@ class Parameters(_AbstractParameters):
param_dict = {k: v for k, v in asdict(self).items() if v is not None}
evaluator = Evaluator.default()
evaluator.set(**param_dict)
for p_name in to_compute:
evaluator.compute(p_name)
results = [evaluator.compute(p_name) for p_name in to_compute]
valid_fields = self.all_parameters()
for k, v in evaluator.params.items():
if k in valid_fields:
setattr(self, k, v)
return results
@classmethod
def all_parameters(cls) -> list[str]:
@@ -481,7 +485,7 @@ class Parameters(_AbstractParameters):
@classmethod
def load(cls, path: os.PathLike) -> "Parameters":
return cls(**utils._open_config(path))
return cls(**utils.load_toml(path))
@classmethod
def load_and_compute(cls, path: os.PathLike) -> "Parameters":
@@ -870,8 +874,8 @@ class Configuration:
config.setdefault("name", Parameters.name.default)
self.z_num += config["z_num"]
fiber_names.add(config["name"])
vary_dict = config.pop("variable")
self.variationer.append(vary_dict)
vary_dict_list: list[dict[str, list]] = config.pop("variable")
self.variationer.append(vary_dict_list)
self.fiber_paths.append(
utils.ensure_folder(
self.final_path / fiber_folder(i, self.name, config["name"]),
@@ -879,10 +883,13 @@ class Configuration:
prevent_overwrite=not self.overwrite,
)
)
self.__validate_variable(vary_dict)
self.__validate_variable(vary_dict_list)
self.num_fibers += 1
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.total_num_steps = sum(
@@ -893,10 +900,11 @@ class Configuration:
def __build_base_config(self):
cfg = self.master_config.copy()
vary = cfg.pop("variable", {})
return cfg | {k: v[0] for k, v in vary.items()}
vary: list[dict[str, list]] = cfg.pop("variable")
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 vary_dict in vary_dict_list:
for k, v in vary_dict.items():
p = getattr(Parameters, k)
validator_list(p.validator)("variable " + k, v)
@@ -1116,6 +1124,8 @@ default_rules: list[Rule] = [
conditions=dict(model="pcf"),
),
Rule("capillary_spacing", fiber.capillary_spacing_hasan),
Rule("capillary_resonance_strengths", fiber.capillary_resonance_strengths),
Rule("capillary_resonance_strengths", lambda: [], priorities=-1),
# Fiber nonlinearity
Rule("A_eff", fiber.A_eff_from_V),
Rule("A_eff", fiber.A_eff_from_diam),

View File

@@ -76,6 +76,7 @@ class Variationer:
num_vars = []
for d in var_list:
values = list(d.values())
if len(values) > 0:
len_to_test = len(values[0])
if not all(len(v) == len_to_test for v in values[1:]):
raise VariationSpecsError(

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
def n_eff_hasan(
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.
"""
u = u_nm(1, 1)
alpha = 5e-12
Rg = core_radius / capillary_spacing
@@ -257,7 +312,7 @@ def n_eff_hasan(
n_eff_2 += (
strength
* 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)

View File

@@ -7,11 +7,14 @@ from ..logger import get_logger
from . import units
from .. import _utils
from .units import NA, c, kB, me, e, hbar
from .._utils.cache import np_cache
@np_cache
def n_gas_2(
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)
if ideal_gas:

View File

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