initial commit
This commit is contained in:
162
.gitignore
vendored
Normal file
162
.gitignore
vendored
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
pyrightconfig.json
|
||||||
|
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# pdm
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||||
|
#pdm.lock
|
||||||
|
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||||
|
# in version control.
|
||||||
|
# https://pdm.fming.dev/#use-with-ide
|
||||||
|
.pdm.toml
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
7
config.toml
Normal file
7
config.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
wl_min = 160
|
||||||
|
wl_max = 1600
|
||||||
|
wl_pump = 800
|
||||||
|
|
||||||
|
rep_rate = 8e3
|
||||||
|
|
||||||
|
safety_factor = 10
|
||||||
231
dispersion_app.py
Executable file
231
dispersion_app.py
Executable file
@@ -0,0 +1,231 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import warnings
|
||||||
|
from functools import cache
|
||||||
|
from typing import Any, NamedTuple
|
||||||
|
|
||||||
|
import click
|
||||||
|
import numpy as np
|
||||||
|
import scgenerator as sc
|
||||||
|
import tomli
|
||||||
|
from customfunc.app import PlotApp
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
DEFAULT_CONFIG_FILE = "config.toml"
|
||||||
|
|
||||||
|
|
||||||
|
class Config(BaseModel):
|
||||||
|
wl_min: float
|
||||||
|
wl_max: float
|
||||||
|
wl_pump: float
|
||||||
|
rep_rate: float
|
||||||
|
gas: str
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load(cls, config_file: str | None = None) -> Config:
|
||||||
|
config_file = config_file or DEFAULT_CONFIG_FILE
|
||||||
|
with open(config_file, "rb") as file:
|
||||||
|
d = tomli.load(file)
|
||||||
|
d = cls.default() | d
|
||||||
|
return cls(**d)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default(cls) -> dict[str, Any]:
|
||||||
|
return dict(wl_min=160, wl_max=1600, wl_pump=800, rep_rate=8e3, gas="argon")
|
||||||
|
|
||||||
|
|
||||||
|
class LimitValues(NamedTuple):
|
||||||
|
wl_zero_disp: float
|
||||||
|
ion_lim: float
|
||||||
|
sf_lim: float
|
||||||
|
|
||||||
|
|
||||||
|
def b2(w, n_eff):
|
||||||
|
dw = w[1] - w[0]
|
||||||
|
beta = sc.fiber.beta(w, n_eff)
|
||||||
|
return sc.math.differentiate_arr(beta, 2, 4, dw)
|
||||||
|
|
||||||
|
|
||||||
|
def N_sf_max(
|
||||||
|
wl: np.ndarray, t0: float, wl_zero_disp: float, gas: sc.materials.Gas, safety: float = 10.0
|
||||||
|
) -> np.ndarray:
|
||||||
|
"""
|
||||||
|
maximum soliton number according to self focusing
|
||||||
|
|
||||||
|
eq. S15 in Travers2019
|
||||||
|
"""
|
||||||
|
delta_gas = gas.sellmeier.delta(wl, wl_zero_disp)
|
||||||
|
return t0 * np.sqrt(wl / (safety * np.abs(delta_gas)))
|
||||||
|
|
||||||
|
|
||||||
|
def N_ion_max(
|
||||||
|
wl: np.ndarray, t0: float, wl_zero_disp: float, gas: sc.materials.Gas, safety: float = 10.0
|
||||||
|
) -> np.ndarray:
|
||||||
|
"""
|
||||||
|
eq. S16 in Travers2019
|
||||||
|
"""
|
||||||
|
ind = sc.math.argclosest(wl, wl_zero_disp)
|
||||||
|
f = np.gradient(np.gradient(gas.sellmeier.chi(wl), wl), wl)
|
||||||
|
factor = (sc.math.u_nm(1, 1) / sc.units.c) ** 2 * (0.5 * wl / np.pi) ** 3
|
||||||
|
delta = factor * (f / f[ind] - 1)
|
||||||
|
denom = safety * np.pi * wl * np.abs(delta) * f[ind]
|
||||||
|
return t0 * sc.math.u_nm(1, 1) * np.sqrt(gas.n2() * gas.barrier_suppression / denom)
|
||||||
|
|
||||||
|
|
||||||
|
def solition_num(
|
||||||
|
t0: float, w0: float, beta2: float, n2: float, core_radius: float, peak_power: float
|
||||||
|
) -> float:
|
||||||
|
gamma = sc.fiber.gamma_parameter(n2, w0, sc.fiber.A_eff_marcatili(core_radius))
|
||||||
|
ld = sc.pulse.L_D(t0, beta2)
|
||||||
|
return np.sqrt(gamma * ld * peak_power)
|
||||||
|
|
||||||
|
|
||||||
|
def energy(
|
||||||
|
t0: float, w0: float, beta2: float, n2: float, core_radius: float, solition_num: float
|
||||||
|
) -> float:
|
||||||
|
gamma = sc.fiber.gamma_parameter(n2, w0, sc.fiber.A_eff_marcatili(core_radius))
|
||||||
|
peak_power = solition_num**2 * abs(beta2) / (t0**2 * gamma)
|
||||||
|
return sc.pulse.P0_to_E0(peak_power, t0, "sech")
|
||||||
|
|
||||||
|
|
||||||
|
def app(config_file: os.PathLike | None = None):
|
||||||
|
config = Config.load(config_file)
|
||||||
|
# grid stuff
|
||||||
|
w = sc.w_from_wl(config.wl_min, config.wl_max, 2048)
|
||||||
|
wl = sc.units.m.inv(w)
|
||||||
|
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()}",
|
||||||
|
wall_thickness_um=np.geomspace(0.01, 10),
|
||||||
|
core_diameter_um=np.linspace(50, 300),
|
||||||
|
pressure_mbar=np.geomspace(1, 2000),
|
||||||
|
n_tubes=np.arange(6, 16),
|
||||||
|
gap_um=np.linspace(1, 15),
|
||||||
|
t_fwhm_fs=np.linspace(10, 200),
|
||||||
|
) as app:
|
||||||
|
# initial setup
|
||||||
|
app[0].horizontal_line("reference", 0, color="gray")
|
||||||
|
app[0].set_xlabel("wavelength (nm)")
|
||||||
|
app[0].set_ylabel("beta2 (fs^2/cm)")
|
||||||
|
app.params["wall_thickness_um"].value = 1
|
||||||
|
app.params["core_diameter_um"].value = 100
|
||||||
|
app.params["pressure_mbar"].value = 500
|
||||||
|
app.params["n_tubes"].value = 7
|
||||||
|
app.params["gap_um"].value = 5
|
||||||
|
app.params["t_fwhm_fs"].value = 100
|
||||||
|
app[0].set_lim(ylim=(-4, 2))
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def compute_max_energy(
|
||||||
|
core_diameter_um: float, pressure_mbar: float, t_fwhm_fs: float
|
||||||
|
) -> LimitValues:
|
||||||
|
t_fwhm = 1e-15 * t_fwhm_fs
|
||||||
|
pressure = 1e2 * pressure_mbar
|
||||||
|
core_radius = 0.5e-6 * core_diameter_um
|
||||||
|
t0 = sc.pulse.fwhm_to_T0_fac["sech"] * t_fwhm
|
||||||
|
|
||||||
|
disp = compute_capillary(core_diameter_um, pressure_mbar)
|
||||||
|
wl_zero_disp = sc.math.all_zeros(wl, disp)
|
||||||
|
if len(wl_zero_disp) == 0:
|
||||||
|
return
|
||||||
|
wl_zero_disp = wl_zero_disp[0]
|
||||||
|
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
ion_limit = N_ion_max(wl, t0, wl_zero_disp, gas)[wl_ind]
|
||||||
|
sf_limit = N_sf_max(wl, t0, wl_zero_disp, gas)[wl_ind]
|
||||||
|
|
||||||
|
beta2 = disp[wl_ind]
|
||||||
|
n2 = gas.n2(pressure=pressure)
|
||||||
|
|
||||||
|
return LimitValues(
|
||||||
|
wl_zero_disp,
|
||||||
|
energy(t0, w0, beta2, n2, core_radius, ion_limit),
|
||||||
|
energy(t0, w0, beta2, n2, core_radius, sf_limit),
|
||||||
|
)
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def compute_vincetti(
|
||||||
|
wall_thickness_um: float,
|
||||||
|
core_diameter_um: float,
|
||||||
|
pressure_mbar: float,
|
||||||
|
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
|
||||||
|
pressure = pressure_mbar * 1e2
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
return b2(w, n_eff_vinc)
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def compute_capillary(core_diameter_um: float, pressure_mbar: float) -> np.ndarray:
|
||||||
|
pressure = pressure_mbar * 1e2
|
||||||
|
core_diameter = core_diameter_um * 1e-6
|
||||||
|
n_gas_2 = gas.sellmeier.n_gas_2(wl, None, pressure)
|
||||||
|
n_eff_cap = sc.fiber.n_eff_marcatili(wl, n_gas_2, core_diameter / 2)
|
||||||
|
return b2(w, n_eff_cap)
|
||||||
|
|
||||||
|
@app.update
|
||||||
|
def draw_vincetty(
|
||||||
|
wall_thickness_um: float,
|
||||||
|
core_diameter_um: float,
|
||||||
|
pressure_mbar: float,
|
||||||
|
n_tubes: int,
|
||||||
|
gap_um: float,
|
||||||
|
):
|
||||||
|
b2 = compute_vincetti(
|
||||||
|
wall_thickness_um, core_diameter_um, pressure_mbar, n_tubes, gap_um
|
||||||
|
)
|
||||||
|
app[0].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)
|
||||||
|
app[0].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):
|
||||||
|
lim = compute_max_energy(core_diameter_um, pressure_mbar, t_fwhm_fs)
|
||||||
|
if lim.ion_lim > lim.sf_lim:
|
||||||
|
power = lim.sf_lim * 1e3 * config.rep_rate
|
||||||
|
app[0].set_line_name(
|
||||||
|
"Capillary", f"Capillary, max energy = {power:.0f}mW (self-focusing)"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
power = lim.ion_lim * 1e3 * config.rep_rate
|
||||||
|
app[0].set_line_name(
|
||||||
|
"Capillary", f"Capillary, max energy = {power:.0f}mW (ionization)"
|
||||||
|
)
|
||||||
|
|
||||||
|
zdw = lim.wl_zero_disp * 1e9
|
||||||
|
app[0].set_line_data("zdw", [zdw, zdw], [0, 1])
|
||||||
|
app[0].set_line_name("zdw", f"ZDW = {zdw:.0f}nm")
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.option(
|
||||||
|
"-c",
|
||||||
|
"--config",
|
||||||
|
default=DEFAULT_CONFIG_FILE,
|
||||||
|
help="configuration file in TOML format",
|
||||||
|
type=click.Path(exists=True, file_okay=True, dir_okay=False, resolve_path=True),
|
||||||
|
)
|
||||||
|
def main(config: os.PathLike):
|
||||||
|
app(config)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__" and not hasattr(sys, "ps1"):
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user