photonics-helper 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- photonics_helper-0.1.0/PKG-INFO +710 -0
- photonics_helper-0.1.0/README.md +661 -0
- photonics_helper-0.1.0/photonics_helper/__init__.py +255 -0
- photonics_helper-0.1.0/photonics_helper/_fftw.py +499 -0
- photonics_helper-0.1.0/photonics_helper/base.py +1128 -0
- photonics_helper-0.1.0/photonics_helper/breathers.py +268 -0
- photonics_helper-0.1.0/photonics_helper/chi2.py +486 -0
- photonics_helper-0.1.0/photonics_helper/dashboard.py +285 -0
- photonics_helper-0.1.0/photonics_helper/dbr.py +541 -0
- photonics_helper-0.1.0/photonics_helper/extras.py +35 -0
- photonics_helper-0.1.0/photonics_helper/fiber.py +798 -0
- photonics_helper-0.1.0/photonics_helper/gnlse.py +2958 -0
- photonics_helper-0.1.0/photonics_helper/looks.py +15 -0
- photonics_helper-0.1.0/photonics_helper/materials.db +0 -0
- photonics_helper-0.1.0/photonics_helper/materials.py +515 -0
- photonics_helper-0.1.0/photonics_helper/noise.py +343 -0
- photonics_helper-0.1.0/photonics_helper/phase_matching.py +1818 -0
- photonics_helper-0.1.0/photonics_helper/phonon.py +834 -0
- photonics_helper-0.1.0/photonics_helper/pulse.py +1889 -0
- photonics_helper-0.1.0/photonics_helper/py.typed +0 -0
- photonics_helper-0.1.0/photonics_helper/raman/__init__.py +44 -0
- photonics_helper-0.1.0/photonics_helper/raman/dashboard.py +593 -0
- photonics_helper-0.1.0/photonics_helper/raman/db.py +806 -0
- photonics_helper-0.1.0/photonics_helper/raman/explorer.py +1526 -0
- photonics_helper-0.1.0/photonics_helper/raman/reference.py +583 -0
- photonics_helper-0.1.0/photonics_helper/raman/response.py +1096 -0
- photonics_helper-0.1.0/photonics_helper/raman/spec.py +519 -0
- photonics_helper-0.1.0/photonics_helper/soliton.py +654 -0
- photonics_helper-0.1.0/photonics_helper/structured.py +571 -0
- photonics_helper-0.1.0/photonics_helper/wave_breaking.py +248 -0
- photonics_helper-0.1.0/photonics_helper.egg-info/PKG-INFO +710 -0
- photonics_helper-0.1.0/photonics_helper.egg-info/SOURCES.txt +110 -0
- photonics_helper-0.1.0/photonics_helper.egg-info/dependency_links.txt +1 -0
- photonics_helper-0.1.0/photonics_helper.egg-info/requires.txt +40 -0
- photonics_helper-0.1.0/photonics_helper.egg-info/top_level.txt +2 -0
- photonics_helper-0.1.0/pyproject.toml +116 -0
- photonics_helper-0.1.0/reproductions/__init__.py +0 -0
- photonics_helper-0.1.0/reproductions/dudley_2006_cherenkov_dw/__init__.py +0 -0
- photonics_helper-0.1.0/reproductions/dudley_2006_cherenkov_dw/reproduce.py +170 -0
- photonics_helper-0.1.0/reproductions/dudley_2006_scg/__init__.py +6 -0
- photonics_helper-0.1.0/reproductions/dudley_2006_scg/common.py +1088 -0
- photonics_helper-0.1.0/reproductions/dudley_2006_scg/fig03_basic_scg.py +355 -0
- photonics_helper-0.1.0/reproductions/dudley_2006_scg/fig04_output_features.py +171 -0
- photonics_helper-0.1.0/reproductions/dudley_2006_scg/fig05_ideal_soliton_period.py +167 -0
- photonics_helper-0.1.0/reproductions/dudley_2006_scg/fig06_raman_fission.py +167 -0
- photonics_helper-0.1.0/reproductions/dudley_2006_scg/fig07_fission_detail.py +177 -0
- photonics_helper-0.1.0/reproductions/dudley_2006_scg/fig08_dispersive_wave.py +185 -0
- photonics_helper-0.1.0/reproductions/dudley_2006_scg/fig09_dw_energy.py +135 -0
- photonics_helper-0.1.0/reproductions/dudley_2006_scg/fig10_spectrogram.py +184 -0
- photonics_helper-0.1.0/reproductions/dudley_2006_scg/fig19_coherence.py +114 -0
- photonics_helper-0.1.0/reproductions/dudley_2006_scg/fig23_mi_gain.py +199 -0
- photonics_helper-0.1.0/reproductions/gordon_1986_ssfs/__init__.py +0 -0
- photonics_helper-0.1.0/reproductions/gordon_1986_ssfs/reproduce.py +151 -0
- photonics_helper-0.1.0/reproductions/kuznetsov_ma_2012_breather/__init__.py +1 -0
- photonics_helper-0.1.0/reproductions/kuznetsov_ma_2012_breather/reproduce.py +478 -0
- photonics_helper-0.1.0/reproductions/macleod_quarter_wave_dbr/__init__.py +0 -0
- photonics_helper-0.1.0/reproductions/macleod_quarter_wave_dbr/reproduce.py +172 -0
- photonics_helper-0.1.0/reproductions/narhi_2016_mi_breathers/__init__.py +1 -0
- photonics_helper-0.1.0/reproductions/narhi_2016_mi_breathers/reproduce.py +596 -0
- photonics_helper-0.1.0/reproductions/shg_textbook/__init__.py +0 -0
- photonics_helper-0.1.0/reproductions/shg_textbook/reproduce.py +185 -0
- photonics_helper-0.1.0/reproductions/stolen_lin_1978_spm/__init__.py +0 -0
- photonics_helper-0.1.0/reproductions/stolen_lin_1978_spm/reproduce.py +158 -0
- photonics_helper-0.1.0/reproductions/tomlinson_1985_wave_breaking/__init__.py +1 -0
- photonics_helper-0.1.0/reproductions/tomlinson_1985_wave_breaking/reproduce.py +372 -0
- photonics_helper-0.1.0/setup.cfg +4 -0
- photonics_helper-0.1.0/setup.py +11 -0
- photonics_helper-0.1.0/tests/test_base.py +526 -0
- photonics_helper-0.1.0/tests/test_breathers.py +152 -0
- photonics_helper-0.1.0/tests/test_chi2.py +212 -0
- photonics_helper-0.1.0/tests/test_dashboard.py +104 -0
- photonics_helper-0.1.0/tests/test_dbr.py +352 -0
- photonics_helper-0.1.0/tests/test_extras.py +29 -0
- photonics_helper-0.1.0/tests/test_fftw_backend.py +302 -0
- photonics_helper-0.1.0/tests/test_fiber.py +183 -0
- photonics_helper-0.1.0/tests/test_frog.py +431 -0
- photonics_helper-0.1.0/tests/test_gnlse_beta_units.py +202 -0
- photonics_helper-0.1.0/tests/test_gnlse_effects.py +86 -0
- photonics_helper-0.1.0/tests/test_gnlse_engine.py +132 -0
- photonics_helper-0.1.0/tests/test_gnlse_fiber.py +40 -0
- photonics_helper-0.1.0/tests/test_gnlse_regression.py +540 -0
- photonics_helper-0.1.0/tests/test_gnlse_shock_energy.py +162 -0
- photonics_helper-0.1.0/tests/test_gnlse_solver.py +96 -0
- photonics_helper-0.1.0/tests/test_gnlse_visualize.py +248 -0
- photonics_helper-0.1.0/tests/test_helper.py +108 -0
- photonics_helper-0.1.0/tests/test_looks.py +21 -0
- photonics_helper-0.1.0/tests/test_material_data_integrity.py +62 -0
- photonics_helper-0.1.0/tests/test_materials.py +168 -0
- photonics_helper-0.1.0/tests/test_meep_units.py +95 -0
- photonics_helper-0.1.0/tests/test_nk_database.py +219 -0
- photonics_helper-0.1.0/tests/test_noise.py +133 -0
- photonics_helper-0.1.0/tests/test_phase_matching.py +1148 -0
- photonics_helper-0.1.0/tests/test_phonon.py +316 -0
- photonics_helper-0.1.0/tests/test_pulse.py +1269 -0
- photonics_helper-0.1.0/tests/test_pulse_hygiene.py +60 -0
- photonics_helper-0.1.0/tests/test_raman.py +2525 -0
- photonics_helper-0.1.0/tests/test_raman_import_surface.py +106 -0
- photonics_helper-0.1.0/tests/test_raman_noise.py +138 -0
- photonics_helper-0.1.0/tests/test_raman_ssfs.py +91 -0
- photonics_helper-0.1.0/tests/test_readiness_beta2.py +111 -0
- photonics_helper-0.1.0/tests/test_reproductions.py +273 -0
- photonics_helper-0.1.0/tests/test_self_steepening.py +104 -0
- photonics_helper-0.1.0/tests/test_soliton.py +458 -0
- photonics_helper-0.1.0/tests/test_structured.py +260 -0
- photonics_helper-0.1.0/tests/test_tapered_gnlse_engine.py +487 -0
- photonics_helper-0.1.0/tests/test_tmm.py +133 -0
- photonics_helper-0.1.0/tests/test_tmm_physics.py +290 -0
- photonics_helper-0.1.0/tests/test_typing.py +26 -0
- photonics_helper-0.1.0/tests/test_units.py +377 -0
- photonics_helper-0.1.0/tests/test_wave_breaking.py +110 -0
- photonics_helper-0.1.0/tests/test_waveguide_import.py +224 -0
- photonics_helper-0.1.0/tests/test_z_dependent_dispersion.py +282 -0
|
@@ -0,0 +1,710 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: photonics-helper
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A comprehensive helper library for photonics and optics calculations
|
|
5
|
+
Author-email: Hitaishi V <hitaishi2222@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/hitaishi2222/photonics_helper
|
|
8
|
+
Project-URL: Documentation, https://github.com/hitaishi2222/photonics_helper#readme
|
|
9
|
+
Keywords: photonics,optics,wavelength,frequency,conversion
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
16
|
+
Requires-Python: >=3.12
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: matplotlib>=3.10.3
|
|
19
|
+
Requires-Dist: numpy>=2.2.6
|
|
20
|
+
Requires-Dist: pydantic>=2.12.5
|
|
21
|
+
Requires-Dist: rich>=14.3.3
|
|
22
|
+
Requires-Dist: scipy>=1.15.3
|
|
23
|
+
Provides-Extra: plotting
|
|
24
|
+
Requires-Dist: plotly>=5.0; extra == "plotting"
|
|
25
|
+
Provides-Extra: webapp
|
|
26
|
+
Requires-Dist: dash>=2.18.0; extra == "webapp"
|
|
27
|
+
Provides-Extra: examples
|
|
28
|
+
Requires-Dist: tqdm>=4.66; extra == "examples"
|
|
29
|
+
Provides-Extra: extras
|
|
30
|
+
Requires-Dist: imageio>=2.33; extra == "extras"
|
|
31
|
+
Provides-Extra: fftw
|
|
32
|
+
Requires-Dist: pyfftw>=0.15.1; extra == "fftw"
|
|
33
|
+
Provides-Extra: mode-export
|
|
34
|
+
Requires-Dist: femwell>=0.1.12; extra == "mode-export"
|
|
35
|
+
Requires-Dist: tidy3d>=2.12; extra == "mode-export"
|
|
36
|
+
Provides-Extra: docs
|
|
37
|
+
Requires-Dist: mkdocs-material>=9.5; extra == "docs"
|
|
38
|
+
Requires-Dist: mkdocstrings[python]>=0.26; extra == "docs"
|
|
39
|
+
Provides-Extra: dev
|
|
40
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
41
|
+
Requires-Dist: ruff>=0.8; extra == "dev"
|
|
42
|
+
Requires-Dist: mypy>=1.13; extra == "dev"
|
|
43
|
+
Provides-Extra: all
|
|
44
|
+
Requires-Dist: plotly>=5.0; extra == "all"
|
|
45
|
+
Requires-Dist: dash>=2.18.0; extra == "all"
|
|
46
|
+
Requires-Dist: tqdm>=4.66; extra == "all"
|
|
47
|
+
Requires-Dist: pyfftw>=0.15.1; extra == "all"
|
|
48
|
+
Requires-Dist: imageio>=2.33; extra == "all"
|
|
49
|
+
|
|
50
|
+
# Photonics Helper
|
|
51
|
+
|
|
52
|
+
<p align="center">
|
|
53
|
+
<img src="docs/img/icon.png" width="160" alt="Photonics Helper logo" />
|
|
54
|
+
</p>
|
|
55
|
+
|
|
56
|
+
> ⚠️ **Disclaimer**: This library is under active development. APIs, interfaces, and internals may change between versions without notice. It is intended as a learning resource and research aid — not a production-grade simulation tool. If you rely on it for published results, please verify all outputs independently and cite the underlying physical models rather than this library.
|
|
57
|
+
|
|
58
|
+
A comprehensive helper library for photonics and optics calculations, providing easy-to-use tools for wavelength, frequency, and angular frequency conversions.
|
|
59
|
+
|
|
60
|
+
# Installation
|
|
61
|
+
|
|
62
|
+
The package is not yet on PyPI. Install from the repository:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install git+https://github.com/hitaishi2222/photonics_helper
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
For the **FFTW3-accelerated GNLSE / Raman solver** (recommended for large grids
|
|
69
|
+
and long propagation runs):
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install "photonics-helper[fftw] @ git+https://github.com/hitaishi2222/photonics_helper"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
When `pyfftw` is present, every FFT in `gnlse.py` and `raman.py` executes on
|
|
76
|
+
the system FFTW3 library with cached plans; otherwise the solver transparently
|
|
77
|
+
falls back to `numpy.fft`. See `photonics_helper._fftw` (env vars
|
|
78
|
+
`PHOTONICS_FFTW_PLANNER`, `PHOTONICS_FFTW_THREADS`).
|
|
79
|
+
|
|
80
|
+
For very large grids an optional **cupy (GPU)** backend can be forced with
|
|
81
|
+
`PHOTONICS_FFT_BACKEND=cupy`. It is never auto-selected and falls back to the
|
|
82
|
+
CPU chain with a warning when cupy or a GPU is unavailable. Benchmark it with
|
|
83
|
+
`python benchmarks/fft_backend_benchmark.py`.
|
|
84
|
+
|
|
85
|
+
# Key Features
|
|
86
|
+
|
|
87
|
+
- **Type Safety**: Full inline type hints (PEP 561 `py.typed`)
|
|
88
|
+
- **Unit Conversions**:
|
|
89
|
+
- Wavelength (nm, μm, m)
|
|
90
|
+
- Frequency (THz, GHz, MHz, Hz)
|
|
91
|
+
- Angular Frequency (rad/s, rad/ps)
|
|
92
|
+
- **Array Operations**: NumPy-based array operations for batch processing
|
|
93
|
+
- **Pulse Visualization**: Interactive 2D/3D pulse envelope plots
|
|
94
|
+
- Gaussian, sech, chirped, Airy, and custom pulse shapes
|
|
95
|
+
- Temporal intensity, spectral intensity, phase, and polar plots
|
|
96
|
+
- FWHM markers and pulse width (T₀) annotations
|
|
97
|
+
- Light/dark theme support
|
|
98
|
+
- Interactive Plotly HTML or static Matplotlib PNG export
|
|
99
|
+
- **DBR Simulation**: Transfer Matrix Method for multilayer stacks
|
|
100
|
+
- **FROG**: SHG-FROG trace generation and PCGPA pulse retrieval
|
|
101
|
+
- Generate FROG traces from electric fields
|
|
102
|
+
- Retrieve pulse shape, chirp, and phase from measured traces
|
|
103
|
+
- Fidelity metric for retrieval quality assessment
|
|
104
|
+
- **Raman Modeling**: Full Raman response physics for 40+ materials
|
|
105
|
+
- Time-domain response (electronic Kerr + delayed lattice oscillation)
|
|
106
|
+
- Frequency-domain gain spectrum
|
|
107
|
+
- Stokes / anti-Stokes wavelength calculation
|
|
108
|
+
- Pump-wavelength explorer and material comparison overlays
|
|
109
|
+
- SQLite material database (`materials.db`) with 44 materials
|
|
110
|
+
- Interactive Dash dashboard for comparing Raman properties
|
|
111
|
+
- **Comprehensive Documentation**: Clear documentation with examples
|
|
112
|
+
- **Easy to Use**: Intuitive API design
|
|
113
|
+
|
|
114
|
+
# Units are classes — an intentional, opinionated choice
|
|
115
|
+
|
|
116
|
+
> **TL;DR:** Passing units as bare `float`s is how silent factor-of-1000 bugs are
|
|
117
|
+
> born. This library deliberately routes every physical quantity through typed
|
|
118
|
+
> unit classes in `photonics_helper/base.py`.
|
|
119
|
+
|
|
120
|
+
This is a **design decision, not a bug**. The maintainer's stance is simple: you
|
|
121
|
+
should not have to keep unit conversions in your head (or in your comments)
|
|
122
|
+
while doing photonics. Construct a quantity once in whatever unit is natural,
|
|
123
|
+
and the library converts, stores, and returns it in the units you ask for.
|
|
124
|
+
|
|
125
|
+
Every module — dispersion (`fiber.py`), pulses (`pulse.py`), the GNLSE solver
|
|
126
|
+
(`gnlse.py`), materials (`materials.py`), phase matching (`phase_matching.py`),
|
|
127
|
+
and the DBR/TMM stack (`dbr.py`) — accepts and returns these classes at its
|
|
128
|
+
public boundaries instead of raw numbers.
|
|
129
|
+
|
|
130
|
+
| Quantity | Class (scalar / array) | Example |
|
|
131
|
+
|----------|-----------------------|---------|
|
|
132
|
+
| Wavelength | `Wavelength` / `WavelengthArray` | `Wavelength(1550, "nm")` |
|
|
133
|
+
| Frequency | `Frequency` / `FrequencyArray` | `Frequency(193.4, "THz")` |
|
|
134
|
+
| Angular frequency | `AngularFrequency` / `AngularFrequencyArray` | `AngularFrequency(1.2, "rad/ps")` |
|
|
135
|
+
| Wavenumber | `Wavenumber` / `WavenumberArray` | `Wavenumber(6450, "1/cm")` |
|
|
136
|
+
| Length | `Length` | `Length(5, "mm")` |
|
|
137
|
+
| Time | `Time` | `Time(50, "fs")` |
|
|
138
|
+
| Energy | `Energy` | `Energy(1.24, "eV")` |
|
|
139
|
+
| Power | `Power` | `Power(100, "mW")` |
|
|
140
|
+
| Area | `Area` | `Area(0.2, "um^2")` |
|
|
141
|
+
|
|
142
|
+
The pattern is always the same — construct in any unit, read out in any unit:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from photonics_helper.base import Wavelength
|
|
146
|
+
|
|
147
|
+
wl = Wavelength(1550, "nm") # constructed in nm
|
|
148
|
+
wl.as_um # 1.55 -> view in μm
|
|
149
|
+
wl.as_m # 1.55e-06 -> internally stored in SI (m)
|
|
150
|
+
wl.to_freq().as_THz # 193.41 -> convert and read out in THz
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Internally everything is normalised to SI on construction, so downstream
|
|
154
|
+
arithmetic and comparisons stay consistent; the `.as_*` properties are simply
|
|
155
|
+
views on the same value. `*Array` variants mirror the scalars for vectorised
|
|
156
|
+
work (dispersion tables, spectral grids, …), and MEEP-style conversions are
|
|
157
|
+
available through `from_meep` / `as_meep`.
|
|
158
|
+
|
|
159
|
+
**The trade-off, stated honestly:** you write `Length(5, "mm")` instead of
|
|
160
|
+
`5e-3`, and functions return `Time` objects rather than floats. In exchange,
|
|
161
|
+
unit mismatches surface as explicit conversions instead of propagating silently
|
|
162
|
+
through a simulation — which, for photonics, is almost always the better deal.
|
|
163
|
+
|
|
164
|
+
# Quick Start
|
|
165
|
+
|
|
166
|
+
To get started, import the library and use its functions:
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from photonics_helper import Wavelength
|
|
170
|
+
|
|
171
|
+
# Convert wavelength to frequency
|
|
172
|
+
wl = Wavelength(1550, "nm")
|
|
173
|
+
freq = wl.to_freq()
|
|
174
|
+
print(f"Frequency: {freq.as_THz:.2f} THz")
|
|
175
|
+
|
|
176
|
+
# Convert frequency to angular frequency
|
|
177
|
+
omega = freq.to_omega()
|
|
178
|
+
print(f"Angular frequency: {omega.as_rad_ps:.2f} rad/ps")
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
# Pulse Visualization
|
|
182
|
+
|
|
183
|
+
Visualize pulse envelopes with interactive 2D/3D plots:
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from photonics_helper.pulse import Envelope
|
|
187
|
+
|
|
188
|
+
# Create a 50 fs Gaussian pulse
|
|
189
|
+
pulse = Envelope(shape="gaussian", peak_amplitude=1.0, pulse_width=50e-15)
|
|
190
|
+
|
|
191
|
+
# Generate interactive Plotly HTML
|
|
192
|
+
fig = pulse.visualize_2d(backend="plotly", theme="light")
|
|
193
|
+
fig.write_html("pulse.html")
|
|
194
|
+
|
|
195
|
+
# Or generate static Matplotlib PNG
|
|
196
|
+
fig = pulse.visualize_2d(backend="matplotlib", figsize=(14, 10))
|
|
197
|
+
fig.savefig("pulse.png", dpi=150, bbox_inches="tight")
|
|
198
|
+
|
|
199
|
+
# 3D spectrogram
|
|
200
|
+
fig_3d = pulse.visualize_3d()
|
|
201
|
+
fig_3d.write_html("spectrogram.html")
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The visualization shows:
|
|
205
|
+
- **Temporal intensity** with FWHM and T₀ markers
|
|
206
|
+
- **Spectral intensity** (Fourier transform)
|
|
207
|
+
- **Instantaneous phase** (for chirped pulses)
|
|
208
|
+
- **Polar plot** (Re vs Im of the field)
|
|
209
|
+
- **Parameters box**: shape, T₀, FWHM, chirp
|
|
210
|
+
|
|
211
|
+
# Pulse Trains
|
|
212
|
+
|
|
213
|
+
Model mode-locked laser output with pulse trains:
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
from photonics_helper.pulse import Envelope, Wave
|
|
217
|
+
from photonics_helper.base import Wavelength, Frequency
|
|
218
|
+
|
|
219
|
+
# Single pulse
|
|
220
|
+
pulse = Envelope(shape="gaussian", peak_amplitude=1.0, pulse_width=50e-15)
|
|
221
|
+
|
|
222
|
+
# Mode-locked laser: 1 GHz rep rate, 10 pulses
|
|
223
|
+
wave = Wave.from_pulse_train(
|
|
224
|
+
envelope=pulse,
|
|
225
|
+
central_wavelength=Wavelength(1550, "nm"),
|
|
226
|
+
grid=Envelope._make_grid(pulse, N=2**14),
|
|
227
|
+
repetition_rate=Frequency(1, "GHz"),
|
|
228
|
+
n_pulses=10,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# Visualize
|
|
232
|
+
fig = wave.visualize(t_unit="ns", w_unit="THz", t_scale=1e9, w_scale=1e12)
|
|
233
|
+
fig.savefig("pulse_train.png", dpi=150, bbox_inches="tight")
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
# Physical Power & Energy
|
|
237
|
+
|
|
238
|
+
`Wave.peak_power()` and `Wave.pulse_energy()` operate on the **normalized**
|
|
239
|
+
envelope `A(t)` by default, so their values are in field-units², not watts or
|
|
240
|
+
joules. Calling them without an effective area emits a one-time `UserWarning`,
|
|
241
|
+
and the `Wave.visualize()` summary labels the value as `(normalized units)`.
|
|
242
|
+
|
|
243
|
+
To get physical units, attach an effective mode area:
|
|
244
|
+
|
|
245
|
+
```python
|
|
246
|
+
from photonics_helper import Area, Frequency, Wavelength, Time
|
|
247
|
+
from photonics_helper.pulse import Envelope, TemporalGrid, Wave
|
|
248
|
+
|
|
249
|
+
A0 = 2.0 # envelope peak amplitude (V/m)
|
|
250
|
+
T0 = 50e-15 # pulse width
|
|
251
|
+
env = Envelope(shape="gaussian", peak_amplitude=A0, pulse_width=Time(T0, "s"))
|
|
252
|
+
grid = TemporalGrid(N=2**14, Tmax=Time(20 * T0, "s"))
|
|
253
|
+
wave = Wave(
|
|
254
|
+
grid=grid,
|
|
255
|
+
envelope=env,
|
|
256
|
+
central_wavelength=Wavelength(800, "nm"),
|
|
257
|
+
refractive_index=1.44,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
wave = wave.with_effective_area(Area(80, "um^2"))
|
|
261
|
+
|
|
262
|
+
wave.peak_power() # W = ½·n·c·ε₀·A_eff·A₀²
|
|
263
|
+
wave.pulse_energy() # J = ∫ P dt
|
|
264
|
+
wave.average_power(Frequency(80, "MHz")) # W
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
For one-shot conversions without a `Wave`, use
|
|
268
|
+
`PeakPower.from_envelope(A, A_eff, n, lambda0)`:
|
|
269
|
+
|
|
270
|
+
```python
|
|
271
|
+
from photonics_helper import Area, PeakPower, Wavelength
|
|
272
|
+
|
|
273
|
+
PeakPower.from_envelope(A0, Area(80, "um^2"), n=1.44, lambda0=Wavelength(800, "nm"))
|
|
274
|
+
# → Power -> ... W
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
The conversion follows the plane-wave intensity relation
|
|
278
|
+
`I = ½·n·c·ε₀·|A|²`, evaluated at the envelope peak.
|
|
279
|
+
|
|
280
|
+
# Waveguide Mode Import (FEM)
|
|
281
|
+
|
|
282
|
+
Bring an effective-index table from an external eigenmode solver (Lumerical
|
|
283
|
+
MODE, COMSOL Wave Optics, …) into the library's dispersion objects with
|
|
284
|
+
`WaveguideMode`.
|
|
285
|
+
|
|
286
|
+
**CSV** — comma-separated, optional header, columns `wavelength_um, neff`
|
|
287
|
+
(optionally a third `ng` column):
|
|
288
|
+
|
|
289
|
+
```csv
|
|
290
|
+
wavelength_um, neff
|
|
291
|
+
1.50, 2.4310
|
|
292
|
+
1.55, 2.4205
|
|
293
|
+
1.60, 2.4102
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
**NPZ** — `np.savez` archive with `wavelength_um` (µm) and `neff`, optionally
|
|
297
|
+
`ng` and `central_wavelength_nm`:
|
|
298
|
+
|
|
299
|
+
```python
|
|
300
|
+
import numpy as np
|
|
301
|
+
np.savez("mode.npz", wavelength_um=wl_um, neff=neff, central_wavelength_nm=1550.0)
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
```python
|
|
305
|
+
from photonics_helper import WaveguideMode, Wavelength
|
|
306
|
+
|
|
307
|
+
mode = WaveguideMode.from_csv("mode.csv") # or .from_npz("mode.npz")
|
|
308
|
+
pc = mode.to_propagation_constant() # β = n_eff·ω/c
|
|
309
|
+
D = mode.to_dispersion() # D(λ) = -λ/c · d²n_eff/dλ²
|
|
310
|
+
|
|
311
|
+
mode.neff_at(Wavelength(1550, "nm")) # interpolated n_eff
|
|
312
|
+
pc.beta2(Wavelength(1550, "nm")) # d²β/dω² (s²/m)
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
The table is validated on load: a finite, positive, strictly increasing
|
|
316
|
+
wavelength grid with at least four points (the cubic-spline minimum), mirroring
|
|
317
|
+
`RefractiveIndex`.
|
|
318
|
+
|
|
319
|
+
Runnable example: [`examples/29_waveguide_mode_import.py`](examples/29_waveguide_mode_import.py)
|
|
320
|
+
(fabricates a synthetic FEM export, round-trips CSV/NPZ, and checks `beta2`
|
|
321
|
+
against the analytic derivative).
|
|
322
|
+
|
|
323
|
+
### Real solver exports (femwell + Tidy3D)
|
|
324
|
+
|
|
325
|
+
[`examples/generate_waveguide_mode_data.py`](examples/generate_waveguide_mode_data.py)
|
|
326
|
+
solves a canonical Si strip (500 × 220 nm on SiO₂) with **femwell** (FEM) and
|
|
327
|
+
**Tidy3D**'s local mode solver, and writes `n_eff(λ)` tables in the conventions
|
|
328
|
+
above to `examples/data/`. The committed exports let the consumer example run
|
|
329
|
+
without either solver installed:
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
pip install femwell tidy3d # or: pip install -e '.[mode-export]'
|
|
333
|
+
python examples/generate_waveguide_mode_data.py # regenerate examples/data/
|
|
334
|
+
python examples/31_waveguide_mode_import_femwell_tidy3d.py
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
The two independent solvers agree on `n_eff` to **0.76 %** over 1.5–1.6 µm;
|
|
338
|
+
the import example cross-checks them, builds `PropagationConstant` /
|
|
339
|
+
`Dispersion`, and feeds the result into `phase_matching`.
|
|
340
|
+
|
|
341
|
+
# χ⁽²⁾ Nonlinear Optics (SHG / SFG / DFG)
|
|
342
|
+
|
|
343
|
+
`photonics_helper.chi2` integrates scalar, long-pulse three-wave mixing in a
|
|
344
|
+
waveguide with a fourth-order Runge–Kutta step in the interaction picture
|
|
345
|
+
(RK4IP). Envelopes are normalized so `|A|²` is power in watts. At perfect
|
|
346
|
+
phase matching the pump-depleted SHG solution is exact,
|
|
347
|
+
`η = tanh²(κL)` with `κ = σ√P₀` (see `reproductions/shg_textbook/`).
|
|
348
|
+
|
|
349
|
+
```python
|
|
350
|
+
from photonics_helper import Area, Wavelength
|
|
351
|
+
from photonics_helper.chi2 import Lambda_qpm, shg_coupling, solve_shg
|
|
352
|
+
|
|
353
|
+
wl = Wavelength(1550, "nm")
|
|
354
|
+
sigma = shg_coupling(wl, d_eff=10e-12, n=2.0, A_eff=Area(1.0, "um^2"))
|
|
355
|
+
|
|
356
|
+
result = solve_shg(length=4e-3, P0=0.1, sigma=sigma, n_steps=4000)
|
|
357
|
+
result.efficiency()[-1] # η at the output
|
|
358
|
+
result.power("sh") # SH power vs z (W)
|
|
359
|
+
|
|
360
|
+
# Quasi-phase-matching: Λ = 2π/|Δk| from the phase mismatch
|
|
361
|
+
period = Lambda_qpm(delta_k)
|
|
362
|
+
result = solve_shg(length=4e-3, P0=0.1, sigma=sigma, delta_k=delta_k, qpm_period=period)
|
|
363
|
+
|
|
364
|
+
# SFG / DFG (generic ω₃ = ω₁ + ω₂)
|
|
365
|
+
from photonics_helper.chi2 import solve_sfg, solve_dfg
|
|
366
|
+
solve_sfg(length=5e-3, P1=0.1, P2=0.1, sigma=sigma)
|
|
367
|
+
solve_dfg(length=5e-3, Ppump=0.1, Psignal=1e-3, sigma=sigma)
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Scope: scalar envelopes, no group-velocity mismatch, dispersion, walk-off or
|
|
371
|
+
loss — suitable for CW / long-pulse efficiency estimates.
|
|
372
|
+
`delta_k_shg(beta_fn, omega)` computes `Δk = β(2ω) − 2β(ω)` from any
|
|
373
|
+
`phase_matching` adaptor, and `qpm_grating(z, Λ)` gives the square-wave poling
|
|
374
|
+
sign.
|
|
375
|
+
|
|
376
|
+
# FROG (Frequency-Resolved Optical Gating)
|
|
377
|
+
|
|
378
|
+
Generate FROG traces and retrieve ultrashort pulse shapes using PCGPA.
|
|
379
|
+
|
|
380
|
+
```python
|
|
381
|
+
import numpy as np
|
|
382
|
+
from photonics_helper import generate_trace, retrieve, fidelity
|
|
383
|
+
|
|
384
|
+
# Create a chirped pulse
|
|
385
|
+
T0 = 50e-15 # 50 fs
|
|
386
|
+
N = 2**10
|
|
387
|
+
dt = 10 * T0 / N
|
|
388
|
+
t = np.arange(N) * dt - N * dt / 2
|
|
389
|
+
E = np.exp(-t**2 / (2 * T0**2)) * np.exp(1j * 0.5 * 2.0 * (t / T0)**2)
|
|
390
|
+
|
|
391
|
+
# Generate FROG trace
|
|
392
|
+
trace = generate_trace(E, dt=dt)
|
|
393
|
+
|
|
394
|
+
# Retrieve pulse using PCGPA
|
|
395
|
+
result = retrieve(trace, max_iter=100)
|
|
396
|
+
f = fidelity(trace, result) # → ~1.0 for good retrieval
|
|
397
|
+
|
|
398
|
+
# Visualize trace + retrieved field
|
|
399
|
+
fig = trace.visualize(retrieved=result)
|
|
400
|
+
fig.savefig("frog.png", dpi=150, bbox_inches="tight")
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
# Raman Material Database
|
|
404
|
+
|
|
405
|
+
Load Raman material parameters from the built-in SQLite database (44 materials):
|
|
406
|
+
|
|
407
|
+
```python
|
|
408
|
+
from photonics_helper.raman import RamanSpec
|
|
409
|
+
|
|
410
|
+
# Load from bundled materials.db (falls back to hardcoded dict)
|
|
411
|
+
silica = RamanSpec.from_database("Silica")
|
|
412
|
+
print(silica.summary())
|
|
413
|
+
# Material: Silica
|
|
414
|
+
# Raman shift: 440.0 cm⁻¹ = 13.19 THz
|
|
415
|
+
# Linewidth: 45.0 cm⁻¹ = 1.35 THz
|
|
416
|
+
# fR: 0.18
|
|
417
|
+
# n₂: 3.2e-20 m²/W
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
All 44 available materials:
|
|
421
|
+
|
|
422
|
+
| Category | Materials |
|
|
423
|
+
|----------|-----------|
|
|
424
|
+
| Glasses | Silica, GeO₂, As₂S₃, As₂Se₃, ZBLAN |
|
|
425
|
+
| Semiconductors | Si, Ge, GaAs, GaN, AlN, InP, InGaAs, AlGaAs, SiC, Si₃N₄, Si₃N₄-Ligentec |
|
|
426
|
+
| II-VI | CdS, CdTe, ZnO, ZnSe |
|
|
427
|
+
| Oxides | Ga₂O₃, Al₂O₃ (sapphire), BaTiO₃, LiNbO₃, LiTaO₃, KTP, YVO₄ |
|
|
428
|
+
| Chalcogenides | GeAsSe |
|
|
429
|
+
| Crystals & Hosts | Diamond, YAG, YLF, Zerodur |
|
|
430
|
+
| NLO Crystals | LBO, AgGaS₂, AgGaSe₂ |
|
|
431
|
+
| Optical substrates | BaF₂, CaF₂, MgF₂, KBr, F₂, N-BK7, N-F2, N-SF11, PMMA |
|
|
432
|
+
|
|
433
|
+
```python
|
|
434
|
+
# Stokes / anti-Stokes for a given pump
|
|
435
|
+
from photonics_helper.base import Wavelength
|
|
436
|
+
|
|
437
|
+
pump = Wavelength(800, "nm")
|
|
438
|
+
stokes = silica.stokes_wavelength(pump)
|
|
439
|
+
anti = silica.anti_stokes_wavelength(pump)
|
|
440
|
+
print(f"Stokes: {stokes.as_nm:.1f} nm, Anti-Stokes: {anti.as_nm:.1f} nm")
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
# Soliton Analysis
|
|
444
|
+
|
|
445
|
+
Analyze soliton dynamics from GNLSE simulation results:
|
|
446
|
+
|
|
447
|
+
```python
|
|
448
|
+
from photonics_helper.gnlse import GNLSESolver, FiberProfile
|
|
449
|
+
from photonics_helper.pulse import Wave, Envelope, TemporalGrid
|
|
450
|
+
from photonics_helper.soliton import SolitonAnalyzer, plot_soliton_trajectories
|
|
451
|
+
from photonics_helper.base import Wavelength, Time, Area, Length
|
|
452
|
+
import numpy as np
|
|
453
|
+
|
|
454
|
+
# Create pulse and fiber (waveguide with confinement factor)
|
|
455
|
+
grid = TemporalGrid(N=256, Tmax=Time(20e-12, "s"))
|
|
456
|
+
pulse = Wave(
|
|
457
|
+
grid=grid,
|
|
458
|
+
envelope=Envelope(shape="sech", peak_amplitude=1.0, pulse_width=Time(100e-15, "s")),
|
|
459
|
+
central_wavelength=Wavelength(1550, "nm"),
|
|
460
|
+
)
|
|
461
|
+
fiber = FiberProfile(
|
|
462
|
+
n2=6.0e-18, # GeAsSe
|
|
463
|
+
alpha=0.0,
|
|
464
|
+
A_eff=Area(0.2e-12, "m^2"),
|
|
465
|
+
length=Length(5e-3, "m"),
|
|
466
|
+
confinement_factor=0.8, # waveguide
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
# Run GNLSE
|
|
470
|
+
solver = GNLSESolver(
|
|
471
|
+
pulse, fiber,
|
|
472
|
+
betas=np.array([-0.2, 0.001]), # β₂=-0.2 ps²/m, β₃=0.001 ps³/m
|
|
473
|
+
include_raman=True,
|
|
474
|
+
)
|
|
475
|
+
solver.propagate(num_steps=200)
|
|
476
|
+
|
|
477
|
+
# Analyze soliton dynamics
|
|
478
|
+
analyzer = SolitonAnalyzer(
|
|
479
|
+
pulse, fiber, solver.betas, solver.z_array, solver.spectra_vs_z
|
|
480
|
+
)
|
|
481
|
+
print(f"Soliton order: {analyzer.soliton_order():.2f}")
|
|
482
|
+
print(f"Fission length: {analyzer.fission_length():.2f} mm")
|
|
483
|
+
print(f"DW wavelength: {analyzer.dispersive_wave_wavelength()*1e9:.1f} nm")
|
|
484
|
+
|
|
485
|
+
# Plot results
|
|
486
|
+
fig = plot_soliton_trajectories(solver)
|
|
487
|
+
fig.savefig("trajectories.png", dpi=150)
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
# Breathers, Noise and Wave Breaking
|
|
491
|
+
|
|
492
|
+
Three modules cover physics that deterministic pulse solvers cannot express on
|
|
493
|
+
their own: exact NLSE breather solutions, stochastic seeding, and the
|
|
494
|
+
normal-dispersion wave-breaking regime.
|
|
495
|
+
|
|
496
|
+
## Analytic breathers (`breathers.py`)
|
|
497
|
+
|
|
498
|
+
Exact soliton-on-finite-background solutions of the focusing NLSE — the
|
|
499
|
+
Akhmediev breather, the Peregrine soliton and the Kuznetsov–Ma soliton — plus a
|
|
500
|
+
physical-parameter mapping that turns fibre parameters into an exact initial
|
|
501
|
+
field:
|
|
502
|
+
|
|
503
|
+
```python
|
|
504
|
+
from photonics_helper.base import Time
|
|
505
|
+
from photonics_helper.breathers import (
|
|
506
|
+
SolitonOnBackground,
|
|
507
|
+
peregrine_soliton,
|
|
508
|
+
akhmediev_breather,
|
|
509
|
+
general_sfb,
|
|
510
|
+
)
|
|
511
|
+
from photonics_helper.pulse import TemporalGrid
|
|
512
|
+
|
|
513
|
+
# SMF-28 at 1550 nm, 0.7 W background (Kibler et al. 2012 parameters)
|
|
514
|
+
sob = SolitonOnBackground(beta2=-21.8e-27, gamma=1.3e-3, P0=0.7)
|
|
515
|
+
print(f"L_NL = {sob.L_NL:.1f} m, T0 = {sob.T0*1e12:.3f} ps")
|
|
516
|
+
print(f"KM period = {sob.spatial_period_m(0.66)/1e3:.3f} km")
|
|
517
|
+
print(f"KM peak = {sob.peak_power(0.66):.3f} W")
|
|
518
|
+
|
|
519
|
+
grid = TemporalGrid(N=4096, Tmax=Time(80e-12, "s"))
|
|
520
|
+
wave = sob.initial_wave(grid, a=0.66) # exact Kuznetsov-Ma field
|
|
521
|
+
|
|
522
|
+
# Or use the dimensionless solutions directly:
|
|
523
|
+
psi = peregrine_soliton(xi=0.0, tau=grid.t / sob.T0) # peak |psi|^2 = 9
|
|
524
|
+
psi_ab = akhmediev_breather(xi=0.0, tau=grid.t / sob.T0, a=0.25)
|
|
525
|
+
psi_any = general_sfb(xi=0.0, tau=grid.t / sob.T0, a=0.66)
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
## Stochastic noise (`noise.py`)
|
|
529
|
+
|
|
530
|
+
Reproducible (seedable) noise sources, needed for spontaneous MI and
|
|
531
|
+
supercontinuum coherence studies:
|
|
532
|
+
|
|
533
|
+
```python
|
|
534
|
+
from photonics_helper.noise import add_noise, add_ase_noise, complex_gaussian_noise
|
|
535
|
+
|
|
536
|
+
noisy = add_noise(cw_wave, rms_relative=0.01, seed=1) # ~1 % amplitude noise
|
|
537
|
+
seeded = add_ase_noise(cw_wave, level_dB=-50.0, seed=1) # -50 dB ASE background
|
|
538
|
+
n = complex_gaussian_noise(grid, rms=1e-3, seed=0) # raw time-domain noise
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
`add_ase_noise` handles the `TemporalGrid.fft`/`ifft` `dt` scaling internally, so
|
|
542
|
+
the requested dB level is exact.
|
|
543
|
+
|
|
544
|
+
## Wave breaking (`wave_breaking.py`)
|
|
545
|
+
|
|
546
|
+
Analytic wave-breaking distance and diagnostics for normal dispersion:
|
|
547
|
+
|
|
548
|
+
```python
|
|
549
|
+
from photonics_helper.wave_breaking import WaveBreaking, wave_breaking_distance
|
|
550
|
+
|
|
551
|
+
wb = WaveBreaking(beta2=20e-27, gamma=1.5e-3, P0=10.0, T0=10e-12)
|
|
552
|
+
print(f"z_WB = {wb.z_WB:.1f} m, sqrt(L_D L_NL) = {wb.sqrt_LD_LNL:.1f} m")
|
|
553
|
+
# after propagating a Gaussian:
|
|
554
|
+
# result = wb.analyze(solver.z_array, [w.envelope_field for w in solver.evolution], grid.t)
|
|
555
|
+
# result["z_onset_m"], result["z_oscillation_m"], result["peak_steepness"]
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
# Structured light — Laguerre-Gaussian / OAM (`structured.py`)
|
|
559
|
+
|
|
560
|
+
Analytic Laguerre-Gaussian transverse modes carrying orbital angular momentum
|
|
561
|
+
(OAM), Gaussian-beam propagation helpers and discrete overlap integrals. Modes
|
|
562
|
+
are normalized to unit power, so ``overlap`` gives the modal overlap directly:
|
|
563
|
+
|
|
564
|
+
```python
|
|
565
|
+
from photonics_helper.structured import (
|
|
566
|
+
LaguerreGaussianMode,
|
|
567
|
+
overlap,
|
|
568
|
+
rayleigh_range,
|
|
569
|
+
beam_waist,
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
w0, lam = 1e-3, 1064e-9 # 1 mm waist at 1064 nm
|
|
573
|
+
lg01 = LaguerreGaussianMode(p=0, l=1, w0=w0, wavelength=lam) # OAM = hbar
|
|
574
|
+
lg02 = LaguerreGaussianMode(p=0, l=2, w0=w0, wavelength=lam)
|
|
575
|
+
|
|
576
|
+
print(overlap(lg01, lg01)) # 1.0 (normalized)
|
|
577
|
+
print(overlap(lg01, lg02)) # ~ 0 (orthogonal OAM)
|
|
578
|
+
|
|
579
|
+
z_r = rayleigh_range(w0, lam) # pi w0^2 / lambda
|
|
580
|
+
print(beam_waist(w0, z_r, lam)) # sqrt(2) * w0
|
|
581
|
+
|
|
582
|
+
field = lg01.structured() # StructuredField on (x, y)
|
|
583
|
+
print(field.power, field.second_moment_radius())
|
|
584
|
+
fig = field.plot() # intensity + phase panels
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
``plot_transverse_profile`` mirrors the pulse backend pattern
|
|
588
|
+
(``backend="matplotlib"`` default, ``backend="plotly"`` when plotly is
|
|
589
|
+
installed). The ``OAM`` winding of a mode is ``2 pi l`` around any loop that
|
|
590
|
+
encloses the optical axis; see `examples/28_structured_light.py`.
|
|
591
|
+
|
|
592
|
+
# Reproductions
|
|
593
|
+
|
|
594
|
+
The `reproductions/` directory validates the library against published results,
|
|
595
|
+
not against other software. Each folder has a `parameters.json`, a script that
|
|
596
|
+
asserts its result against an analytic/closed-form reference, a figure, and a
|
|
597
|
+
README with the DOI, findings and open issues.
|
|
598
|
+
|
|
599
|
+
| Reproduction | Reference (DOI) |
|
|
600
|
+
|---|---|
|
|
601
|
+
| `stolen_lin_1978_spm` | Stolen & Lin, *Phys. Rev. A* **17**, 1448 (1978) · [10.1103/PhysRevA.17.1448](https://doi.org/10.1103/PhysRevA.17.1448) |
|
|
602
|
+
| `macleod_quarter_wave_dbr` | Macleod, *Thin-Film Optical Filters* (textbook) |
|
|
603
|
+
| `gordon_1986_ssfs` | Gordon, *Opt. Lett.* **11**, 662 (1986) · [10.1364/OL.11.000662](https://doi.org/10.1364/OL.11.000662) |
|
|
604
|
+
| `dudley_2006_cherenkov_dw` | Akhmediev & Karlsson, *Phys. Rev. A* **51**, 2602 (1995) · [10.1103/PhysRevA.51.2602](https://doi.org/10.1103/PhysRevA.51.2602) |
|
|
605
|
+
| `dudley_2006_scg` | Dudley, Genty & Coen, *Rev. Mod. Phys.* **78**, 1135 (2006) · [10.1103/RevModPhys.78.1135](https://doi.org/10.1103/RevModPhys.78.1135) |
|
|
606
|
+
| `kuznetsov_ma_2012_breather` | Kibler et al., *Sci. Rep.* **2**, 463 (2012) · [10.1038/srep00463](https://doi.org/10.1038/srep00463) |
|
|
607
|
+
| `narhi_2016_mi_breathers` | Närhi et al., *Nat. Commun.* **7**, 13675 (2016) · [10.1038/ncomms13675](https://doi.org/10.1038/ncomms13675) |
|
|
608
|
+
| `tomlinson_1985_wave_breaking` | Tomlinson, Stolen & Johnson, *Opt. Lett.* **10**, 457 (1985) · [10.1364/OL.10.000457](https://doi.org/10.1364/OL.10.000457) |
|
|
609
|
+
|
|
610
|
+
See [`reproductions/README.md`](reproductions/README.md) for the full status,
|
|
611
|
+
cross-cutting findings and issues, and
|
|
612
|
+
[`reproductions/dudley_2006_scg/PAPER_ANALYSIS.md`](reproductions/dudley_2006_scg/PAPER_ANALYSIS.md)
|
|
613
|
+
for the figure-by-figure reproducibility map of the Dudley review.
|
|
614
|
+
|
|
615
|
+
```sh
|
|
616
|
+
python -m pytest tests/test_reproductions.py # all reproduction regressions
|
|
617
|
+
python reproductions/dudley_2006_scg/fig05_ideal_soliton_period.py # one figure
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
# Dashboard & API docs
|
|
621
|
+
|
|
622
|
+
One Dash app hosts both the Raman explorer and a GNLSE result viewer:
|
|
623
|
+
|
|
624
|
+
```python
|
|
625
|
+
from photonics_helper.dashboard import app
|
|
626
|
+
|
|
627
|
+
app().run(debug=False, port=8050) # Raman Explorer | GNLSE Viewer
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
Requires the `webapp` (Dash) and `plotting` (Plotly) extras; see
|
|
631
|
+
`examples/32_unified_dashboard.py`. The standalone Raman app remains available
|
|
632
|
+
as `photonics_helper.raman.app()`.
|
|
633
|
+
|
|
634
|
+
API documentation is built with mkdocs + mkdocstrings from the docstrings:
|
|
635
|
+
|
|
636
|
+
```bash
|
|
637
|
+
pip install -e ".[docs]"
|
|
638
|
+
mkdocs serve # http://127.0.0.1:8000
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
`.github/workflows/` also provides the CI matrix, the mkdocs build/deploy, and
|
|
642
|
+
the PyPI Trusted Publishing release (cut a `v*` tag to publish).
|
|
643
|
+
|
|
644
|
+
# Development
|
|
645
|
+
|
|
646
|
+
To install for development:
|
|
647
|
+
|
|
648
|
+
```sh
|
|
649
|
+
git clone https://github.com/hitaishi2222/photonics_helper
|
|
650
|
+
cd photonics_helper
|
|
651
|
+
python -m venv .venv
|
|
652
|
+
source .venv/bin/activate # On Linux/Mac
|
|
653
|
+
pip install -e .
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
# Roadmap
|
|
657
|
+
|
|
658
|
+
- ~~Add methods to convert wavelengths to energy (in eV)~~
|
|
659
|
+
- ~~Add functionality for dispersion calculations~~
|
|
660
|
+
- ~~Modeling Envelopes~~
|
|
661
|
+
- ~~Modeling Pulse~~
|
|
662
|
+
- **Transfer Matrix Method (TMM)** — available (see `dbr.py`)
|
|
663
|
+
- DBR multilayer stack simulation
|
|
664
|
+
- Characteristic-matrix (Macleod) formalism with correct layer ordering
|
|
665
|
+
- Spectral response (R, T) including absorbing and oblique stacks, plus `TMM.plot_spectrum`
|
|
666
|
+
- Electric-field profiling via `TMM.field_profile`
|
|
667
|
+
- `Pattern` layer stack
|
|
668
|
+
- **FROG** ✅
|
|
669
|
+
- SHG-FROG trace generation
|
|
670
|
+
- PCGPA pulse retrieval
|
|
671
|
+
- Fidelity metric
|
|
672
|
+
- **Raman Modeling** ✅
|
|
673
|
+
- Time-domain Raman response h_R(t)
|
|
674
|
+
- Frequency-domain gain spectrum H(Ω)
|
|
675
|
+
- Raman pulse interaction (R(t) ⊗ |E|²)
|
|
676
|
+
- Material comparison overlays with 6 panel types
|
|
677
|
+
- Pump wavelength explorer (Stokes/anti-Stokes)
|
|
678
|
+
- SQLite material database (44 entries)
|
|
679
|
+
- Interactive Dash dashboard
|
|
680
|
+
- Catalog explorer example (`examples/11_raman_material_catalog.py`)
|
|
681
|
+
- **GNLSE** ✅
|
|
682
|
+
- Dispersion (arbitrary-order β_k)
|
|
683
|
+
- Kerr effect
|
|
684
|
+
- Raman scattering (delayed response)
|
|
685
|
+
- Self-steepening (optional; default off — set `include_self_steepening=True` to match laserfun's `shock=True`)
|
|
686
|
+
- Two-photon absorption (TPA)
|
|
687
|
+
- Adaptive step-size (SSFM)
|
|
688
|
+
- Soliton propagation, fission, supercontinuum
|
|
689
|
+
- **Soliton Analysis** ✅
|
|
690
|
+
- Soliton order, dispersion/nonlinear/fission lengths
|
|
691
|
+
- Dispersive wave (Cherenkov) wavelength
|
|
692
|
+
- Soliton trajectory extraction and RSFS rate
|
|
693
|
+
- Soliton counting via peak detection
|
|
694
|
+
- Publication-ready visualization (trajectories, fission dynamics, DW spectrum)
|
|
695
|
+
- **Waveguide Support** ✅
|
|
696
|
+
- Confinement factor Γ in γ formula: `γ = n₂·ω₀·Γ/(c·A_eff)`
|
|
697
|
+
- Backward compatible (Γ=1.0 recovers fiber behavior)
|
|
698
|
+
- **Waveguide mode import (FEM)** ✅ — `WaveguideMode.from_csv/from_npz` → `PropagationConstant`/`Dispersion`, `PropagationConstant.beta2`
|
|
699
|
+
- **χ⁽²⁾ nonlinear optics** ✅ — `chi2` SHG/SFG/DFG RK4IP solver, QPM grating, `Lambda_qpm`, textbook `tanh²(κL)` reproduction
|
|
700
|
+
- **GPU FFT backend** ✅ — opt-in cupy path (`PHOTONICS_FFT_BACKEND=cupy`) with transparent CPU fallback and a benchmark
|
|
701
|
+
- **Unified dashboard** ✅ — `photonics_helper.dashboard.app()` combines the Raman Explorer and an interactive GNLSE result viewer
|
|
702
|
+
- **Release engineering** ✅ — PyPI Trusted Publishing, mkdocs API docs, and a Python 3.12/3.13 × ±pyfftw CI matrix (ruff + mypy gates)
|
|
703
|
+
- **Chalcogenide Materials** ✅
|
|
704
|
+
- GeAsSe added (n₂=6e-18 m²/W, 44 materials total)
|
|
705
|
+
- Suitable for soliton fission in chalcogenide waveguides
|
|
706
|
+
- **Structured Light** ✅ — Laguerre–Gaussian / OAM modes, Gaussian-beam propagation helpers, modal overlap integrals and transverse-profile plotting (`structured.py`)
|
|
707
|
+
- ~~Add methods for bandwidth calculations~~
|
|
708
|
+
- ~~Add methods for power/intensity conversions~~ (physical scaling via `Wave.with_effective_area` / `PeakPower.from_envelope`)
|
|
709
|
+
|
|
710
|
+
> ⚠️ **Note**: The TMM / DBR module (`dbr.py`) is still under active development. The API and internals may change.
|