added refractive index plot
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
.conda-env
|
||||
dispersion_config.toml
|
||||
pyrightconfig.json
|
||||
.DS_Store
|
||||
|
||||
@@ -9,7 +9,7 @@ conda activate app-env
|
||||
|
||||
3. The prompt should now read '(app-env)' on the left. The app is not published on Github or anywhere else. The link below points to my own personnal home server (I didn't find any way of getting a direct download link You are now ready to install everything with this command:
|
||||
|
||||
pip install "git+file://<path to the git repository>"
|
||||
pip install http://130.92.113.172/dispersionapp_v0.1.0.zip
|
||||
|
||||
# Usage
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
[project]
|
||||
name = "dispersionapp"
|
||||
version = "0.1.0"
|
||||
version = "0.1.3"
|
||||
description = "Model hollow capillary and revolver fiber interactively"
|
||||
authors = [{ name = "Benoît Sierro", email = "benoit.sierro@unibe.ch" }]
|
||||
|
||||
dependencies = [
|
||||
"scgenerator @ git+https://github.com/bsierro/scgenerator.git",
|
||||
"click",
|
||||
"pydantic",
|
||||
"pydantic < 2",
|
||||
"tomli",
|
||||
"tomli_w",
|
||||
"PySide6 >= 6.4.0",
|
||||
|
||||
@@ -17,6 +17,7 @@ class CurrentState(BaseModel):
|
||||
core_diameter_um: float
|
||||
pressure_mbar: float
|
||||
wall_thickness_um: float
|
||||
num_resonances: conint(ge=6, le=40) = 6
|
||||
n_tubes: int
|
||||
gap_um: float
|
||||
t_fwhm_fs: float
|
||||
@@ -27,7 +28,6 @@ class Config(BaseModel):
|
||||
wl_max: confloat(ge=500, le=6000) = 1600
|
||||
wl_pump: confloat(ge=200, le=6000) = 800
|
||||
rep_rate: confloat(gt=0) = 8e3
|
||||
num_resonances: conint(ge=6, le=20) = 6
|
||||
gas: str = "argon"
|
||||
safety_factor: float = 10.0
|
||||
current_state: CurrentState | None = None
|
||||
@@ -45,7 +45,7 @@ class Config(BaseModel):
|
||||
try:
|
||||
out = cls(**d)
|
||||
except ValidationError as e:
|
||||
s = f"invalid input in config file {config_file}:\n{e}"
|
||||
s = f"invalid input in config file {config_file}:\n{e}\ncreating new config"
|
||||
print(s)
|
||||
sys.exit(1)
|
||||
out._file_name = Path(config_file)
|
||||
@@ -58,12 +58,20 @@ class Config(BaseModel):
|
||||
tmp.replace(self._file_name)
|
||||
|
||||
def update_current(
|
||||
self, core_diameter_um, pressure_mbar, wall_thickness_um, n_tubes, gap_um, t_fwhm_fs
|
||||
self,
|
||||
core_diameter_um: float,
|
||||
pressure_mbar: float,
|
||||
wall_thickness_um: float,
|
||||
num_resonances: int,
|
||||
n_tubes: int,
|
||||
gap_um: float,
|
||||
t_fwhm_fs: float,
|
||||
):
|
||||
self.current_state = CurrentState(
|
||||
core_diameter_um=core_diameter_um,
|
||||
pressure_mbar=pressure_mbar,
|
||||
wall_thickness_um=wall_thickness_um,
|
||||
num_resonances=num_resonances,
|
||||
n_tubes=n_tubes,
|
||||
gap_um=gap_um,
|
||||
t_fwhm_fs=t_fwhm_fs,
|
||||
|
||||
@@ -7,8 +7,9 @@ from functools import cache
|
||||
import numpy as np
|
||||
import scgenerator as sc
|
||||
|
||||
from dispersionapp.core import Config, LimitValues, N_ion_max, N_sf_max, b2, energy
|
||||
from dispersionapp.plotapp import PlotApp
|
||||
from dispersionapp.core import (Config, LimitValues, N_ion_max, N_sf_max, b2,
|
||||
energy)
|
||||
from dispersionapp.plotapp import PlotApp, QtWidgets
|
||||
|
||||
|
||||
def app(config_file: os.PathLike | None = None):
|
||||
@@ -19,27 +20,34 @@ def app(config_file: os.PathLike | None = None):
|
||||
wl_ind = sc.math.argclosest(wl, config.wl_pump * 1e-9)
|
||||
w0 = w[wl_ind]
|
||||
|
||||
|
||||
gas = sc.materials.Gas(config.gas)
|
||||
|
||||
with PlotApp(
|
||||
f"Dispersion design with {config.gas.title()}",
|
||||
core_diameter_um=np.arange(50, 301, dtype=float),
|
||||
pressure_mbar=np.geomspace(1, 2000),
|
||||
wall_thickness_um=np.geomspace(0.01, 10),
|
||||
wall_thickness_um=np.geomspace(0.01, 10, 201),
|
||||
num_resonances=np.arange(6, 41),
|
||||
n_tubes=np.arange(6, 16),
|
||||
gap_um=np.arange(1, 15.5, 0.5),
|
||||
t_fwhm_fs=np.arange(10, 201, dtype=float),
|
||||
) as app:
|
||||
# initial setup
|
||||
ax = app["Dispersion plot"]
|
||||
ax.horizontal_line("reference", 0, color="gray")
|
||||
ax.set_xlabel("wavelength (nm)")
|
||||
ax.set_ylabel("beta2 (fs^2/cm)")
|
||||
beta_ax = app["Dispersion plot"]
|
||||
beta_ax.horizontal_line("reference", 0, color="gray")
|
||||
beta_ax.set_xlabel("wavelength (nm)")
|
||||
beta_ax.set_ylabel("beta2 (fs^2/cm)")
|
||||
|
||||
n_ax = app["Refractive index (real)"]
|
||||
n_ax.link_x(beta_ax)
|
||||
n_ax.set_xlabel("wavelength (nm)")
|
||||
n_ax.set_ylabel("n")
|
||||
|
||||
if config.current_state is not None:
|
||||
app.params["core_diameter_um"].value = config.current_state.core_diameter_um
|
||||
app.params["pressure_mbar"].value = config.current_state.pressure_mbar
|
||||
app.params["wall_thickness_um"].value = config.current_state.wall_thickness_um
|
||||
app.params["num_resonances"].value = config.current_state.num_resonances
|
||||
app.params["n_tubes"].value = config.current_state.n_tubes
|
||||
app.params["gap_um"].value = config.current_state.gap_um
|
||||
app.params["t_fwhm_fs"].value = config.current_state.t_fwhm_fs
|
||||
@@ -47,10 +55,19 @@ def app(config_file: os.PathLike | None = None):
|
||||
app.params["core_diameter_um"].value = 100
|
||||
app.params["pressure_mbar"].value = 500
|
||||
app.params["wall_thickness_um"].value = 1
|
||||
app.params["num_resonances"].value = 6
|
||||
app.params["n_tubes"].value = 7
|
||||
app.params["gap_um"].value = 5
|
||||
app.params["t_fwhm_fs"].value = 100
|
||||
ax.set_lim(ylim=(-4, 2))
|
||||
|
||||
def reset_view():
|
||||
# reseting beta_ax resets n_ax as well since they're linked
|
||||
beta_ax.set_lim(ylim=(-4, 2))
|
||||
|
||||
reset_button = QtWidgets.QPushButton("Reset axes")
|
||||
reset_button.clicked.connect(reset_view)
|
||||
app.params_layout.addWidget(reset_button)
|
||||
reset_view()
|
||||
|
||||
app.update(config.update_current)
|
||||
|
||||
@@ -86,13 +103,14 @@ def app(config_file: os.PathLike | None = None):
|
||||
)
|
||||
|
||||
@cache
|
||||
def compute_vincetti(
|
||||
def compute_n_eff_vincetti(
|
||||
wall_thickness_um: float,
|
||||
core_diameter_um: float,
|
||||
pressure_mbar: float,
|
||||
num_resonances: int,
|
||||
n_tubes: int,
|
||||
gap_um: float,
|
||||
) -> np.ndarray:
|
||||
):
|
||||
core_diameter = core_diameter_um * 1e-6
|
||||
wall_thickness = wall_thickness_um * 1e-6
|
||||
gap = gap_um * 1e-6
|
||||
@@ -100,15 +118,21 @@ def app(config_file: os.PathLike | None = None):
|
||||
|
||||
tr = sc.fiber.tube_radius_from_gap(core_diameter / 2, gap, n_tubes)
|
||||
n_gas_2 = gas.sellmeier.n_gas_2(wl, None, pressure)
|
||||
n_eff_vinc = sc.fiber.n_eff_vincetti(
|
||||
wl,
|
||||
800e-9,
|
||||
n_gas_2,
|
||||
wall_thickness,
|
||||
tr,
|
||||
gap,
|
||||
n_tubes,
|
||||
n_terms=config.num_resonances,
|
||||
return sc.fiber.n_eff_vincetti(
|
||||
wl, 800e-9, n_gas_2, wall_thickness, tr, gap, n_tubes, n_terms=num_resonances
|
||||
)
|
||||
|
||||
@cache
|
||||
def compute_vincetti_beta(
|
||||
wall_thickness_um: float,
|
||||
core_diameter_um: float,
|
||||
pressure_mbar: float,
|
||||
num_resonances: int,
|
||||
n_tubes: int,
|
||||
gap_um: float,
|
||||
) -> np.ndarray:
|
||||
n_eff_vinc = compute_n_eff_vincetti(
|
||||
wall_thickness_um, core_diameter_um, pressure_mbar, num_resonances, n_tubes, gap_um
|
||||
)
|
||||
return b2(w, n_eff_vinc)
|
||||
|
||||
@@ -121,22 +145,37 @@ def app(config_file: os.PathLike | None = None):
|
||||
return b2(w, n_eff_cap)
|
||||
|
||||
@app.update
|
||||
def draw_vincetty(
|
||||
def draw_vincetty_n_eff(
|
||||
wall_thickness_um: float,
|
||||
core_diameter_um: float,
|
||||
pressure_mbar: float,
|
||||
num_resonances: int,
|
||||
n_tubes: int,
|
||||
gap_um: float,
|
||||
):
|
||||
b2 = compute_vincetti(
|
||||
wall_thickness_um, core_diameter_um, pressure_mbar, n_tubes, gap_um
|
||||
n_eff = compute_n_eff_vincetti(
|
||||
wall_thickness_um, core_diameter_um, pressure_mbar, num_resonances, n_tubes, gap_um
|
||||
)
|
||||
ax.set_line_data("Vincetti", wl * 1e9, sc.units.beta2_fs_cm_inv(b2))
|
||||
n_ax.set_line_data("Vincetti", wl * 1e9, n_eff)
|
||||
|
||||
@app.update
|
||||
def draw_vincetty_beta(
|
||||
wall_thickness_um: float,
|
||||
core_diameter_um: float,
|
||||
pressure_mbar: float,
|
||||
num_resonances: int,
|
||||
n_tubes: int,
|
||||
gap_um: float,
|
||||
):
|
||||
b2 = compute_vincetti_beta(
|
||||
wall_thickness_um, core_diameter_um, pressure_mbar, num_resonances, n_tubes, gap_um
|
||||
)
|
||||
beta_ax.set_line_data("Vincetti", wl * 1e9, sc.units.beta2_fs_cm_inv(b2))
|
||||
|
||||
@app.update
|
||||
def draw_capillary(core_diameter_um: float, pressure_mbar: float):
|
||||
b2 = compute_capillary(core_diameter_um, pressure_mbar)
|
||||
ax.set_line_data("Capillary", wl * 1e9, sc.units.beta2_fs_cm_inv(b2))
|
||||
beta_ax.set_line_data("Capillary", wl * 1e9, sc.units.beta2_fs_cm_inv(b2))
|
||||
|
||||
@app.update
|
||||
def draw_energy_limit(core_diameter_um: float, pressure_mbar: float, t_fwhm_fs: float):
|
||||
@@ -153,7 +192,7 @@ def app(config_file: os.PathLike | None = None):
|
||||
return
|
||||
|
||||
zdw = lim.wl_zero_disp * 1e9
|
||||
ax.set_line_data("zdw", [zdw, zdw], [-3, 3])
|
||||
beta_ax.set_line_data("zdw", [zdw, zdw], [-3, 3])
|
||||
|
||||
info_lines.append(f"ZDW = {zdw:.0f}nm")
|
||||
if lim.ion_lim > lim.sf_lim:
|
||||
@@ -170,6 +209,6 @@ def app(config_file: os.PathLike | None = None):
|
||||
f"N = {lim.soliton_sf_limit:.1f}",
|
||||
"limited by ionization",
|
||||
]
|
||||
app.info_label.setText("\n".join(info_lines))
|
||||
app.info_label.setText(" ".join(info_lines))
|
||||
|
||||
config.save()
|
||||
|
||||
@@ -5,7 +5,8 @@ import itertools
|
||||
from collections.abc import MutableMapping, Sequence
|
||||
from functools import cache
|
||||
from types import MethodType
|
||||
from typing import Any, Callable, Iterable, Iterator, Optional, Type, Union, overload
|
||||
from typing import (Any, Callable, Iterable, Iterator, Optional, Type, Union,
|
||||
overload)
|
||||
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
@@ -29,30 +30,17 @@ MPL_COLORS = [
|
||||
key_type = Union[str, int]
|
||||
|
||||
|
||||
class Field(QtWidgets.QWidget):
|
||||
class SliderField(QtWidgets.QWidget):
|
||||
dtype: Type
|
||||
value: Any
|
||||
value_changed: QtCore.Signal
|
||||
timer: QtCore.QTimer
|
||||
|
||||
@property
|
||||
def value(self) -> Any:
|
||||
raise NotImplementedError()
|
||||
|
||||
def values(self) -> list[Any]:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class SliderField(Field):
|
||||
dtype: Type
|
||||
possible_values: np.ndarray
|
||||
_slider_max = 100
|
||||
_tuple_signal = QtCore.Signal(tuple)
|
||||
_int_signal = QtCore.Signal(int)
|
||||
_float_signal = QtCore.Signal(float)
|
||||
_str_signal = QtCore.Signal(str)
|
||||
|
||||
def __init__(self, name: str, values: Iterable) -> None:
|
||||
def __init__(self, name: str, values: Iterable[float] | Iterable[int]) -> None:
|
||||
super().__init__()
|
||||
self.__value = None
|
||||
self.slider = QtWidgets.QSlider(QtCore.Qt.Orientation.Horizontal)
|
||||
@@ -64,16 +52,18 @@ class SliderField(Field):
|
||||
self.field.editingFinished.connect(self.field_changed)
|
||||
|
||||
self.step_backward_button = QtWidgets.QPushButton("<")
|
||||
self.step_backward_button.setFixedWidth(30)
|
||||
self.step_backward_button.clicked.connect(self.step_backward)
|
||||
|
||||
self.step_forward_button = QtWidgets.QPushButton(">")
|
||||
self.step_forward_button.setFixedWidth(30)
|
||||
self.step_forward_button.clicked.connect(self.step_forward)
|
||||
|
||||
self._layout = QtWidgets.QHBoxLayout()
|
||||
self.setLayout(self._layout)
|
||||
self._layout.setContentsMargins(10, 0, 10, 0)
|
||||
|
||||
pretty_name = " ".join(s.title() for s in name.split("_"))
|
||||
pretty_name = " ".join(s for s in name.split("_"))
|
||||
self.name_label = QtWidgets.QLabel(pretty_name + " :")
|
||||
|
||||
self._layout.addWidget(self.name_label)
|
||||
@@ -86,8 +76,6 @@ class SliderField(Field):
|
||||
self.value_changed = {
|
||||
int: self._int_signal,
|
||||
float: self._float_signal,
|
||||
str: self._str_signal,
|
||||
tuple: self._tuple_signal,
|
||||
}[self.dtype]
|
||||
self.value_changed.emit(self.__value)
|
||||
|
||||
@@ -130,11 +118,8 @@ class SliderField(Field):
|
||||
self.value_changed.emit(new_value)
|
||||
|
||||
def field_changed(self):
|
||||
try:
|
||||
new_val = self.dtype(self.field.text())
|
||||
except (ValueError, TypeError):
|
||||
self.update_label()
|
||||
return
|
||||
new_val = min(self.value_to_slider_map, key=lambda el: abs(el - new_val))
|
||||
self.value = new_val
|
||||
|
||||
def slider_changed(self):
|
||||
@@ -168,7 +153,7 @@ class SliderField(Field):
|
||||
if self.dtype is int:
|
||||
return format(self.value)
|
||||
elif self.dtype is float:
|
||||
return format(self.value, ".3g")
|
||||
return format(self.value, ".5g")
|
||||
else:
|
||||
return format(self.value)
|
||||
|
||||
@@ -176,66 +161,6 @@ class SliderField(Field):
|
||||
return list(self.possible_values)
|
||||
|
||||
|
||||
class AnimatedSliderField(SliderField):
|
||||
def __init__(self, name: str, values: Iterable) -> None:
|
||||
super().__init__(name, values)
|
||||
self.timer = QtCore.QTimer()
|
||||
self.timer.timeout.connect(self.step_forward)
|
||||
|
||||
self.play_button = QtWidgets.QPushButton("⏯")
|
||||
self.play_button.clicked.connect(self.toggle)
|
||||
self.play_button.setMaximumWidth(30)
|
||||
self.playing = False
|
||||
|
||||
self.interval_field = QtWidgets.QLineEdit()
|
||||
self.interval_field.setMaximumWidth(60)
|
||||
# self.interval_field.setValidator(QtGui.QIntValidator(1, 5000))
|
||||
self.interval_field.editingFinished.connect(self.set_interval)
|
||||
self.interval_field.inputRejected.connect(self.set_interval)
|
||||
self.interval = 16
|
||||
self.set_interval()
|
||||
|
||||
self._layout.addWidget(self.play_button)
|
||||
self._layout.addWidget(self.interval_field)
|
||||
|
||||
def toggle(self):
|
||||
if self.playing:
|
||||
self.stop()
|
||||
self.playing = False
|
||||
else:
|
||||
self.play()
|
||||
self.playing = True
|
||||
|
||||
def play(self):
|
||||
if self.slider.value() == self._slider_max:
|
||||
self.slider.setValue(0)
|
||||
self.timer.start(self.interval)
|
||||
self.play_button.setStyleSheet("QPushButton {background-color: #DEF2DD;}")
|
||||
|
||||
def stop(self):
|
||||
self.timer.stop()
|
||||
self.play_button.setStyleSheet("QPushButton {background-color: none;}")
|
||||
|
||||
def set_interval(self):
|
||||
try:
|
||||
self.interval = max(1, int(self.interval_field.text()))
|
||||
except ValueError:
|
||||
self.interval_field.setText(str(self.interval))
|
||||
if self.interval < 16:
|
||||
self.increment = int(np.ceil(16 / self.interval))
|
||||
self.interval *= self.increment
|
||||
else:
|
||||
self.increment = 1
|
||||
|
||||
def step_forward(self):
|
||||
current = self.slider.value()
|
||||
if current + self.increment <= self._slider_max:
|
||||
self.slider.setValue(current + self.increment)
|
||||
else:
|
||||
self.slider.setValue(self._slider_max)
|
||||
self.stop()
|
||||
|
||||
|
||||
class Plot:
|
||||
name: str
|
||||
dock: Dock
|
||||
@@ -511,11 +436,12 @@ class PlotApp:
|
||||
self.params_widget = QtWidgets.QWidget()
|
||||
self.header_widget = QtWidgets.QWidget()
|
||||
self.info_label = QtWidgets.QLabel()
|
||||
# self.info_label.setMaximumWidth(120)
|
||||
self.window.setCentralWidget(self.central_widget)
|
||||
|
||||
self.central_layout = QtWidgets.QVBoxLayout()
|
||||
self.params_layout = QtWidgets.QVBoxLayout()
|
||||
self.header_layout = QtWidgets.QHBoxLayout()
|
||||
self.params_layout = QtWidgets.QGridLayout()
|
||||
self.header_layout = QtWidgets.QVBoxLayout()
|
||||
|
||||
_pl = QtWidgets.QSizePolicy.Policy.Preferred
|
||||
info_sp = QtWidgets.QSizePolicy(_pl, _pl)
|
||||
@@ -540,10 +466,10 @@ class PlotApp:
|
||||
|
||||
self.__ran = False
|
||||
self.params = {}
|
||||
for p_name, values in params.items():
|
||||
field = AnimatedSliderField(p_name, values)
|
||||
for i, (p_name, values) in enumerate(params.items()):
|
||||
field = SliderField(p_name, values)
|
||||
self.params[p_name] = field
|
||||
self.params_layout.addWidget(field)
|
||||
self.params_layout.addWidget(field, *divmod(i, 2))
|
||||
|
||||
def set_antialiasing(self, val: bool):
|
||||
pg.setConfigOptions(antialias=val)
|
||||
|
||||
Reference in New Issue
Block a user