better np hash, Fiber always array, resonance
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,17 +900,18 @@ 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]):
|
||||
for k, v in vary_dict.items():
|
||||
p = getattr(Parameters, k)
|
||||
validator_list(p.validator)("variable " + k, v)
|
||||
if k not in VALID_VARIABLE:
|
||||
raise TypeError(f"{k!r} is not a valid variable parameter")
|
||||
if len(v) == 0:
|
||||
raise ValueError(f"variable parameter {k!r} must not be empty")
|
||||
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)
|
||||
if k not in VALID_VARIABLE:
|
||||
raise TypeError(f"{k!r} is not a valid variable parameter")
|
||||
if len(v) == 0:
|
||||
raise ValueError(f"variable parameter {k!r} must not be empty")
|
||||
|
||||
def __iter__(self) -> Iterator[tuple[VariationDescriptor, Parameters]]:
|
||||
for i in range(self.num_fibers):
|
||||
@@ -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),
|
||||
|
||||
@@ -76,12 +76,13 @@ class Variationer:
|
||||
num_vars = []
|
||||
for d in var_list:
|
||||
values = list(d.values())
|
||||
len_to_test = len(values[0])
|
||||
if not all(len(v) == len_to_test for v in values[1:]):
|
||||
raise VariationSpecsError(
|
||||
f"variable items should all have the same number of parameters"
|
||||
)
|
||||
num_vars.append(len_to_test)
|
||||
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(
|
||||
f"variable items should all have the same number of parameters"
|
||||
)
|
||||
num_vars.append(len_to_test)
|
||||
if len(num_vars) == 0:
|
||||
num_vars = [1]
|
||||
self.all_indices.append(num_vars)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
22
testing/test_resonance.py
Normal 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()
|
||||
Reference in New Issue
Block a user