new parameters display logic

This commit is contained in:
Benoît Sierro
2023-10-13 15:23:22 +02:00
parent f9b75efd6a
commit 159433f654
5 changed files with 77 additions and 86 deletions

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "scgenerator"
version = "0.3.18"
version = "0.3.19"
description = "Simulate nonlinear pulse propagation in optical fibers"
readme = "README.md"
authors = [{ name = "Benoit Sierro", email = "benoit.sierro@iap.unibe.ch" }]

View File

@@ -27,7 +27,7 @@ SPEC1_FN_N = "spectrum_{}_{}.npy"
Z_FN = "z.npy"
PARAM_FN = "params.toml"
PARAM_SEPARATOR = " "
DECIMALS_DISPLAY = 6
MANDATORY_PARAMETERS = {
"name",

View File

@@ -12,18 +12,19 @@ from typing import Any, Callable, ClassVar, Iterable, Iterator, Set, Type, TypeV
import numpy as np
from scgenerator.const import MANDATORY_PARAMETERS, __version__
from scgenerator.const import DECIMALS_DISPLAY, MANDATORY_PARAMETERS, __version__
from scgenerator.evaluator import Evaluator, EvaluatorError
from scgenerator.io import CustomEncoder, DataFile, custom_decode_hook
from scgenerator.operators import Qualifier, SpecOperator, VariableQuantity
from scgenerator.physics.units import unit_formatter
T = TypeVar("T")
DISPLAY_INFO = {}
DISPLAY_FUNCTIONS: dict[str, Callable[[float], str]] = {}
def _format_display_info(name: str, value) -> str:
try:
return DISPLAY_INFO[name](value)
return DISPLAY_FUNCTIONS[name](value)
except KeyError:
return format(value, ".9g")
@@ -217,7 +218,7 @@ class Parameter:
validator: Callable[[str, Any], None],
converter: Callable = None,
default=None,
display_info: tuple[float, str] = None,
unit: str | None = None,
can_pickle: bool = True,
):
"""
@@ -233,15 +234,14 @@ class Parameter:
converts a valid value (for example, str.lower), by default None
default : callable, optional
factory function for a default value (for example, list), by default None
display_info : tuple[float, str], optional
a factor by which to multiply the value and a string to be appended as a suffix
when displaying the value
example : (1e-6, "MW") will mean the value 1.12e6 is displayed as '1.12MW'
display_info : tuple[str, int], optional
a unit name (without prefix) and a max number of decimals for display purposes
example : ("W", 3) will mean the value 1.120045e6 is displayed as '1.12MW'
"""
self._validator = validator
self.converter = converter
self.default = default
self.display_info = display_info
self.unit = unit
self.can_pickle = can_pickle
def __set_name__(self, owner: Type[Parameters], name):
@@ -252,7 +252,8 @@ class Parameter:
pass
if self.default is not None:
Evaluator.register_default_param(self.name, self.default)
DISPLAY_INFO[self.name] = self.display
if self.unit is not None:
DISPLAY_FUNCTIONS[self.name] = unit_formatter(self.unit, DECIMALS_DISPLAY)
def __get__(self, instance: Parameters, owner):
if instance is None:
@@ -275,16 +276,6 @@ class Parameter:
else:
self.__delete__(instance)
def display(self, num: float) -> str:
if self.display_info is None:
return str(num)
else:
fac, unit = self.display_info
num_str = format(num * fac, ".2f")
if num_str.endswith(".00"):
num_str = num_str[:-3]
return f"{num_str} {unit}"
def validate(self: Parameter, v) -> tuple[bool, Any]:
if v is None:
is_value = False
@@ -325,9 +316,9 @@ class Parameters:
effective_area: float = Parameter(non_negative(float, int))
effective_area_file: DataFile = Parameter(DataFile.validate)
numerical_aperture: float = Parameter(in_range_excl(0, 1))
pcf_pitch: float = Parameter(in_range_excl(0, 1e-3), display_info=(1e6, "μm"))
pcf_pitch: float = Parameter(in_range_excl(0, 1e-3), unit="m")
pcf_pitch_ratio: float = Parameter(in_range_excl(0, 1))
core_radius: float = Parameter(in_range_excl(0, 1e-3), display_info=(1e6, "μm"))
core_radius: float = Parameter(in_range_excl(0, 1e-3), unit="m")
he_mode: tuple[int, int] = Parameter(int_pair, default=(1, 1))
fit_parameters: tuple[int, int] = Parameter(float_pair, default=(0.08, 200e-9))
beta2_coefficients: Iterable[float] = Parameter(num_list)
@@ -336,13 +327,13 @@ class Parameters:
literal("pcf", "marcatili", "marcatili_adjusted", "hasan", "custom"),
)
zero_dispersion_wavelength: float = Parameter(
validator_list(non_negative(float, int)), display_info=(1e9, "nm")
validator_list(non_negative(float, int)), unit="m"
)
length: float = Parameter(non_negative(float, int), display_info=(1e2, "cm"))
length: float = Parameter(non_negative(float, int), unit="m")
capillary_num: int = Parameter(positive(int))
capillary_radius: float = Parameter(in_range_excl(0, 1e-3), display_info=(1e6, "μm"))
capillary_thickness: float = Parameter(in_range_excl(0, 1e-3), display_info=(1e6, "μm"))
capillary_spacing: float = Parameter(in_range_excl(0, 1e-3), display_info=(1e6, "μm"))
capillary_radius: float = Parameter(in_range_excl(0, 1e-3), unit="m")
capillary_thickness: float = Parameter(in_range_excl(0, 1e-3), unit="m")
capillary_spacing: float = Parameter(in_range_excl(0, 1e-3), unit="m")
capillary_resonance_strengths: Iterable[float] = Parameter(
validator_list(type_checker(int, float, np.ndarray))
)
@@ -351,31 +342,29 @@ class Parameters:
# gas
gas_name: str = Parameter(low_string, default="vacuum")
pressure: float = Parameter(non_negative(float, int), display_info=(1e-5, "bar"))
pressure_in: float = Parameter(non_negative(float, int), display_info=(1e-5, "bar"))
pressure_out: float = Parameter(non_negative(float, int), display_info=(1e-5, "bar"))
temperature: float = Parameter(positive(float, int), display_info=(1, "K"), default=300)
pressure: float = Parameter(non_negative(float, int), unit="bar")
pressure_in: float = Parameter(non_negative(float, int), unit="bar")
pressure_out: float = Parameter(non_negative(float, int), unit="bar")
temperature: float = Parameter(positive(float, int), unit="K", default=300)
plasma_density: float = Parameter(non_negative(float, int), default=0)
# pulse
field_file: DataFile = Parameter(DataFile.validate)
input_time: np.ndarray = Parameter(type_checker(np.ndarray))
input_field: np.ndarray = Parameter(type_checker(np.ndarray))
repetition_rate: float = Parameter(
non_negative(float, int), display_info=(1e-3, "kHz"), default=40e6
)
peak_power: float = Parameter(positive(float, int), display_info=(1e-3, "kW"))
mean_power: float = Parameter(positive(float, int), display_info=(1e3, "mW"))
energy: float = Parameter(positive(float, int), display_info=(1e6, "μJ"))
repetition_rate: float = Parameter(non_negative(float, int), unit="Hz", default=40e6)
peak_power: float = Parameter(positive(float, int), unit="W")
mean_power: float = Parameter(positive(float, int), unit="W")
energy: float = Parameter(positive(float, int), unit="J")
soliton_num: float = Parameter(non_negative(float, int))
additional_noise_factor: float = Parameter(positive(float, int), default=1)
shape: str = Parameter(literal("gaussian", "sech"), default="gaussian")
wavelength: float = Parameter(in_range_incl(100e-9, 10000e-9), display_info=(1e9, "nm"))
intensity_noise: float = Parameter(in_range_incl(0, 1), display_info=(1e2, "%"), default=0)
wavelength: float = Parameter(in_range_incl(100e-9, 10000e-9), unit="m")
intensity_noise: float = Parameter(in_range_incl(0, 1), unit="%", default=0)
noise_correlation: float = Parameter(in_range_incl(-10, 10), default=0)
width: float = Parameter(in_range_excl(0, 1e-9), display_info=(1e15, "fs"))
t0: float = Parameter(in_range_excl(0, 1e-9), display_info=(1e15, "fs"))
delay: float = Parameter(type_checker(float, int), display_info=(1e15, "fs"))
width: float = Parameter(in_range_excl(0, 1e-9), unit="s")
t0: float = Parameter(in_range_excl(0, 1e-9), unit="s")
delay: float = Parameter(type_checker(float, int), unit="s")
# Behaviors to include
quantum_noise: bool = Parameter(boolean, default=False)
@@ -394,13 +383,12 @@ class Parameters:
repeat: int = Parameter(positive(int), default=1)
t_num: int = Parameter(positive(int), default=4096)
z_num: int = Parameter(positive(int), default=128)
time_window: float = Parameter(positive(float, int), display_info=(1e12, "ps"))
dt: float = Parameter(in_range_excl(0, 10e-15), display_info=(1e15, "fs"))
time_window: float = Parameter(positive(float, int), unit="s")
dt: float = Parameter(in_range_excl(0, 10e-15), unit="s")
tolerated_error: float = Parameter(in_range_excl(1e-15, 1e-3), default=1e-11)
step_size: float = Parameter(non_negative(float, int), default=0)
wavelength_window: tuple[float, float] = Parameter(
validator_and(float_pair, validator_list(in_range_incl(100e-9, 10000e-9))),
display_info=(1e9, "nm"),
validator_and(float_pair, validator_list(in_range_incl(100e-9, 10000e-9))), unit="m"
)
interpolation_degree: int = Parameter(validator_and(type_checker(int), in_range_incl(2, 18)))
prev_sim_dir: str = Parameter(string)
@@ -428,9 +416,9 @@ class Parameters:
w_c: np.ndarray = Parameter(type_checker(np.ndarray))
w0: float = Parameter(positive(float))
t: np.ndarray = Parameter(type_checker(np.ndarray))
dispersion_length: float = Parameter(non_negative(float, int), display_info=(1e2, "cm"))
nonlinear_length: float = Parameter(non_negative(float, int), display_info=(1e2, "cm"))
soliton_length: float = Parameter(non_negative(float, int), display_info=(1e2, "cm"))
dispersion_length: float = Parameter(non_negative(float, int), unit="m")
nonlinear_length: float = Parameter(non_negative(float, int), unit="m")
soliton_length: float = Parameter(non_negative(float, int), unit="m")
adapt_step_size: bool = Parameter(boolean)
hr_w: np.ndarray = Parameter(type_checker(np.ndarray))
z_targets: np.ndarray = Parameter(type_checker(np.ndarray))
@@ -621,9 +609,10 @@ class Parameters:
if isinstance(exclude, str):
exclude = [exclude]
p_pairs = [(k, format_value(k, getattr(self, k))) for k in params if k not in exclude]
p_pairs.sort()
max_left = max(len(el[0]) for el in p_pairs)
max_right = max(len(el[1]) for el in p_pairs if "\n" not in el[1])
return "\n".join("{:>{l}} = {:{r}}".format(*p, l=max_left, r=max_right) for p in p_pairs)
return "\n".join("{:<{l}} = {:{r}}".format(*p, l=max_left, r=max_right) for p in p_pairs)
def strip_params_dict(self, copy=False) -> dict[str, Any]:
"""

View File

@@ -93,6 +93,40 @@ def W_to_Vm(n0: float, effective_area: float) -> float:
return 1.0 / np.sqrt(effective_area * 0.5 * epsilon0 * c * n0)
def unit_formatter(
unit: str, decimals: int = 1, vmin: float | None = 1e-28
) -> Callable[[float | int], str]:
if not unit:
def formatter(val):
return f"{val:.{decimals}g}"
elif unit == "%":
def formatter(val):
return f"{100*val:.{decimals}g}%"
else:
def formatter(val):
if vmin is not None and abs(val) < vmin:
return f"0{unit}"
base, true_exp = format(val, f".{3+decimals}e").split("e")
true_exp = int(true_exp)
exp, mult = divmod(true_exp, 3)
if exp < -8:
mult -= -3 * (8 + exp)
exp = -8
elif exp > 8:
mult += 3 * (exp - 8)
exp = 8
prefix = "yzafpnµm kMGTPEZY"[8 + exp] if exp else ""
return f"{float(base)*10**mult:.{decimals}g}{prefix}{unit}"
return formatter
class unit:
inv = None

View File

@@ -5,6 +5,8 @@ from typing import Callable, Generic, Iterator, ParamSpec, Sequence, TypeVar
import numpy as np
from scgenerator.physics.units import unit_formatter
T = TypeVar("T")
P = ParamSpec("P")
@@ -75,40 +77,6 @@ def int_linear(id, d_min, d_max, d_num) -> int:
return d_min + id * ((d_max - d_min) // (d_num - 1))
def unit_formatter(
unit: str, decimals: int = 1, vmin: float | None = 1e-28
) -> Callable[[float | int], str]:
if not unit:
def formatter(val):
return f"{val:.{decimals}g}"
elif unit == "%":
def formatter(val):
return f"{100*val:.{decimals}g}%"
else:
def formatter(val):
if vmin is not None and abs(val) < vmin:
return f"0{unit}"
base, true_exp = format(val, f".{3+decimals}e").split("e")
true_exp = int(true_exp)
exp, mult = divmod(true_exp, 3)
if exp < -8:
mult -= -3 * (8 + exp)
exp = -8
elif exp > 8:
mult += 3 * (exp - 8)
exp = 8
prefix = "yzafpnµm kMGTPEZY"[8 + exp] if exp else ""
return f"{float(base)*10**mult:.{decimals}g}{prefix}{unit}"
return formatter
def sequence_from_specs(
start: float | int, stop: float | int, num: int, kind: str = "linear"
) -> np.ndarray: