├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── cnlse ├── __init__.py ├── cnlse.py ├── dispersion.py ├── envelopes.py └── raman_response.py ├── data ├── 191119_polarisation_21m_lambda_1560nm_power_41mW.png ├── 191119_polarisation_21m_lambda_1560nm_power_41mW_angle_1deg_exp.mat └── 191119_polarisation_21m_lambda_1560nm_power_41mW_angle_89deg_exp.mat ├── draw_soliton_traping.py ├── requirements.txt ├── run_modulation_instability.py └── run_soliton_traping.py /.gitattributes: -------------------------------------------------------------------------------- 1 | *.mat filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.mat 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | pip-wheel-metadata/ 25 | share/python-wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | MANIFEST 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .nox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | *.py,cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | .python-version 87 | 88 | # pipenv 89 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 90 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 91 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 92 | # install all needed dependencies. 93 | #Pipfile.lock 94 | 95 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 96 | __pypackages__/ 97 | 98 | # Celery stuff 99 | celerybeat-schedule 100 | celerybeat.pid 101 | 102 | # SageMath parsed files 103 | *.sage.py 104 | 105 | # Environments 106 | .env 107 | .venv 108 | env/ 109 | venv/ 110 | ENV/ 111 | env.bak/ 112 | venv.bak/ 113 | 114 | # Spyder project settings 115 | .spyderproject 116 | .spyproject 117 | 118 | # Rope project settings 119 | .ropeproject 120 | 121 | # mkdocs documentation 122 | /site 123 | 124 | # mypy 125 | .mypy_cache/ 126 | .dmypy.json 127 | dmypy.json 128 | 129 | # Pyre type checker 130 | .pyre/ 131 | 132 | # Built Sphinx documentation 133 | docs/_build/ 134 | 135 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 WUST-FOG 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Visits Badge](https://badges.pufler.dev/visits/WUST-FOG/cgnlse-python)[![DOI](https://zenodo.org/badge/477591470.svg)](https://zenodo.org/badge/latestdoi/477591470) 2 | 3 | # Nonlinear phenomena in birefringent microstructured fibers 4 | 5 | We implemented model based on two coupled nonlinear Schrödinger equations that include both the Raman and the Kerr nonlinearities. We used it to study evolution of nonlinear phenomena in the temporal and spectral domains in optical fibers exhibiting high and low birefringence. 6 | 7 | ![soliton_traping](./data/191119_polarisation_21m_lambda_1560nm_power_41mW.png) 8 | 9 | ## Usage 10 | 11 | ### Installation 12 | 13 | 1. Create a virtual environment with `python -m venv cgnlse` or using `conda` by `conda create -n cgnlse python=3.8`. 14 | 2. Activate it with `source cgnlse/bin/activate` or `conda activate cgnlse`. 15 | 3. Install `gnlse` package `pip install gnlse==2.0.0` 16 | 3. Clone this repository `git clone https://github.com/WUST-FOG/cgnlse-python.git` 17 | 18 | ```bash 19 | python -m venv cgnlse 20 | source cnlse/bin/activate 21 | pip install -r requirements.txt 22 | git clone https://github.com/WUST-FOG/cgnlse-python.git 23 | cd cgnlse-python 24 | ``` 25 | 26 | ### Examples 27 | 28 | **Soliton trapping and orthogonal Raman scattering** 29 | 30 | Run test script to generate above figure and reproduce the manuscript results: 31 | 32 | ```bash 33 | python draw_soliton_traping.py 34 | ``` 35 | 36 | Note that we also provided script tu run simulations (`run_soliton_traping.py`), 37 | however used input data is not publicly available at this time, 38 | but may be obtained from the authors upon reasonable request. 39 | 40 | _Inspiration: K. Stefańska et al., Soliton trapping and orthogonal Raman scattering in a birefringent microstructured fiber_ 41 | 42 | **Modulation instability in highly birefringent fibers** 43 | 44 | To run example of vector modulation instability in highly birefringent 45 | fibers with circularly polarized modes in the normal dispersion regime 46 | type: 47 | 48 | ```bash 49 | python run_modulation_instability.py 50 | ``` 51 | 52 | Note that using also raman_polarisation and setting solver.fr to 0 53 | one can simulate the case of low-birefringent fibers. 54 | 55 | _Inspiration: K. Zołnacz et al., Vector modulation instability in highly birefringent fibers with circularly polarized eigenmodes_ 56 | 57 | ## Acknowledgement 58 | 59 | cnlse-python is a Python set of scripts for solving 60 | Coupled Nonlinear Schrodringer Equation. It is one of the WUST-FOG 61 | projects developed by [Fiber Optics Group, WUST](http://www.fog.pwr.edu.pl/). 62 | 63 | The python code based on `gnlse` package, available at 64 | [https://github.com/WUST-FOG/gnlse-python](https://github.com/WUST-FOG/gnlse-python). 65 | 66 | ## Citation 67 | If you find this code useful in your research, please consider citing: 68 | 69 | [Soliton trapping and orthogonal Raman scattering in a birefringent photonic crystal fiber](https://arxiv.org/abs/2204.13773v1): 70 | 71 | ``` 72 | @article{Stefanska:22, 73 | author = {Karolina Stefa\'{n}ska and Sylwia Majchrowska and Karolina Gemza 74 | and Grzegorz Sobo\'{n} and Jaros{\l}aw Sotor and Pawe{\l} Mergo 75 | and Karol Tarnowski and Tadeusz Martynkien}, 76 | journal = {Opt. Lett.}, 77 | number = {16}, 78 | pages = {4183--4186}, 79 | publisher = {Optica Publishing Group}, 80 | title = {Soliton trapping and orthogonal Raman scattering 81 | in a birefringent photonic crystal fiber}, 82 | volume = {47}, 83 | month = {Aug}, 84 | year = {2022}, 85 | url = {http://opg.optica.org/ol/abstract.cfm?URI=ol-47-16-4183}, 86 | doi = {10.1364/OL.463643} 87 | } 88 | ``` 89 | 90 | [gnlse-python: Open Source Software to Simulate Nonlinear Light Propagation In Optical Fibers](https://arxiv.org/abs/2110.00298): 91 | 92 | ``` 93 | @misc{redman2021gnlsepython, 94 | title={gnlse-python: Open Source Software to Simulate 95 | Nonlinear Light Propagation In Optical Fibers}, 96 | author={Pawel Redman and Magdalena Zatorska and Adam Pawlowski 97 | and Daniel Szulc and Sylwia Majchrowska and Karol Tarnowski}, 98 | year={2021}, 99 | eprint={2110.00298}, 100 | archivePrefix={arXiv}, 101 | primaryClass={physics.optics} 102 | } 103 | ``` 104 | 105 | ## License 106 | [MIT](https://choosealicense.com/licenses/mit/) 107 | -------------------------------------------------------------------------------- /cnlse/__init__.py: -------------------------------------------------------------------------------- 1 | from cnlse.dispersion import (DubbleDispersionFiberFromOPD, 2 | DubbleDispersionFiberFromTaylor) 3 | from cnlse.envelopes import DoubleSechEnvelope 4 | from cnlse.cnlse import CNLSE 5 | from cnlse.raman_response import raman_polarisation 6 | 7 | __all__ = [ 8 | 'DubbleDispersionFiberFromOPD', 'DubbleDispersionFiberFromTaylor', 9 | 'CNLSE', 'raman_polarisation', 'DoubleSechEnvelope' 10 | ] 11 | -------------------------------------------------------------------------------- /cnlse/cnlse.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import scipy.integrate 3 | from scipy.fftpack import fft, ifft, fftshift 4 | import tqdm 5 | 6 | from gnlse import GNLSESetup, Solution 7 | from gnlse.common import c 8 | 9 | 10 | class CNLSE: 11 | """ 12 | Model propagation of an optical pulse in a fiber by integrating 13 | the two coupled generalized non-linear Schrödinger equations. 14 | 15 | Attributes 16 | ---------- 17 | setup : GNLSESetup 18 | Model inputs in the form of a ``GNLSESetup`` object. 19 | """ 20 | def __init__(self, setup): 21 | if not isinstance(setup, GNLSESetup): 22 | raise TypeError("setup is not an instance of GNLSESetup") 23 | 24 | if setup.resolution is None: 25 | raise ValueError("'resolution' not set") 26 | if setup.time_window is None: 27 | raise ValueError("'time_window' not set") 28 | if setup.wavelength is None: 29 | raise ValueError("'wavelength' not set") 30 | if setup.fiber_length is None: 31 | raise ValueError("'fiber_length' not set") 32 | if setup.pulse_model is None: 33 | raise ValueError("'pulse_model' not set") 34 | 35 | # simulation parameters 36 | self.fiber_length = setup.fiber_length 37 | self.z_saves = setup.z_saves 38 | self.rtol = setup.rtol 39 | self.atol = setup.atol 40 | self.method = setup.method 41 | self.N = setup.resolution 42 | self.report = False 43 | 44 | # Time domain grid 45 | self.t = np.linspace(-setup.time_window / 2, 46 | setup.time_window / 2, 47 | self.N) 48 | 49 | # Relative angular frequency grid 50 | self.V = 2 * np.pi * np.arange(-self.N / 2, 51 | self.N / 2 52 | ) / (self.N * (self.t[1] - self.t[0])) 53 | # Central angular frequency [10^12 rad] 54 | w_0 = (2.0 * np.pi * c) / setup.wavelength 55 | self.Omega = self.V + w_0 56 | 57 | # Absolute angular frequency grid 58 | self.W = fftshift(self.Omega) 59 | 60 | # Raman scattering 61 | self.h1W = None 62 | if setup.raman_model: 63 | self.fr, h1T, h2T, h3T = setup.raman_model(self.t) 64 | self.h1W = self.N * ifft( 65 | fftshift(h1T)) 66 | self.h2W = self.N * ifft( 67 | fftshift(h2T)) 68 | self.h3W = self.N * ifft( 69 | fftshift(h3T)) 70 | 71 | # Input pulse 72 | if hasattr(setup.pulse_model, 'A'): 73 | self.A = setup.pulse_model.A(self.t) 74 | else: 75 | self.A = setup.pulse_model 76 | 77 | # Dispersion operator 78 | self.D, self.deltaB = setup.dispersion_model.D(self.V) 79 | 80 | # Nonlinearity 81 | if hasattr(setup.nonlinearity[0], 'gamma'): 82 | # in case in of frequency dependent nonlinearity 83 | gamma, scale = setup.nonlinearity[0].gamma(self.V) 84 | self.gammax = fftshift(gamma / w_0) 85 | self.scale = fftshift(scale) 86 | gamma, _ = setup.nonlinearity[1].gamma(self.V) 87 | self.gammay = fftshift(gamma / w_0) 88 | else: 89 | # in case in of direct introduced value 90 | self.gammax = setup.nonlinearity[0] / w_0 91 | self.gammay = setup.nonlinearity[1] / w_0 92 | self.scale = 1 93 | 94 | def run(self): 95 | """ 96 | Solve two mode CNLSE equation. 97 | 98 | Returns 99 | ------- 100 | setup : Solution 101 | Simulation results in the form of a ``Solution`` object. 102 | """ 103 | if self.A.size != self.N * 2: 104 | raise ValueError("'pulse_model' has not enougth values") 105 | 106 | if self.D.size != self.N * 2: 107 | raise ValueError("'dispersion' has not enougth values") 108 | 109 | self.Dx = fftshift(self.D[:self.N]) 110 | self.Dy = fftshift(self.D[self.N:]) 111 | dt = self.t[1] - self.t[0] 112 | Z = np.linspace(0, self.fiber_length, self.z_saves) 113 | 114 | if self.report: 115 | progress_bar = tqdm.tqdm(total=self.fiber_length, unit='m') 116 | 117 | def rhs(z, AW): 118 | """ 119 | The right hand side of the differential equation to integrate. 120 | """ 121 | if self.report: 122 | progress_bar.n = round(z, 3) 123 | progress_bar.update(0) 124 | 125 | CxW = AW[:self.N] 126 | CyW = AW[self.N:] 127 | Atx = fft(CxW * np.exp(self.Dx * z)) 128 | Aty = fft(CyW * np.exp(self.Dy * z)) 129 | ITx = np.abs(Atx)**2 130 | ITy = np.abs(Aty)**2 131 | 132 | if self.h1W is not None: 133 | exp_m2i_deltaB_z = np.exp(-2j * self.deltaB * z) 134 | exp_m1i_deltaB_z = np.exp(-1j * self.deltaB * z) 135 | exp_p1i_deltaB_z = np.exp(+1j * self.deltaB * z) 136 | exp_p2i_deltaB_z = np.exp(+2j * self.deltaB * z) 137 | 138 | Mx = ifft((1 - self.fr) * ((ITx + 2 / 3 * ITy) 139 | * Atx + 1 / 3 * Aty**2 * np.conj(Atx) * exp_m2i_deltaB_z) 140 | + self.fr * dt * (Atx * (fft(self.h1W * ifft(ITx)) + 141 | fft(self.h2W * ifft(ITy)) 142 | ) + Aty * (fft(self.h3W * ifft( 143 | Atx * np.conj(Aty) * exp_p1i_deltaB_z 144 | + Aty * np.conj(Atx) * exp_m1i_deltaB_z)) 145 | ) * exp_m1i_deltaB_z)) 146 | 147 | My = ifft((1 - self.fr) 148 | * (ITy * Aty + 2 / 3 * ITx * Aty 149 | + 1 / 3 * Atx**2 * np.conj(Aty) * exp_p2i_deltaB_z) 150 | + self.fr * dt * ( 151 | Aty * ( 152 | fft(self.h1W * ifft(ITy)) + fft(self.h2W * ifft(ITx)) 153 | ) + Atx * ( 154 | fft( 155 | self.h3W * ifft( 156 | Atx * np.conj(Aty) * exp_p1i_deltaB_z 157 | + Aty * np.conj(Atx) * exp_m1i_deltaB_z)) 158 | ) * exp_p1i_deltaB_z)) 159 | else: 160 | Mx = ifft(Atx * (ITx + 2/3 * ITy)) 161 | My = ifft(Aty * (ITy + 2/3 * ITx)) 162 | 163 | rx = 1j * self.gammax * self.W * Mx * np.exp(-self.Dx * z) 164 | ry = 1j * self.gammay * self.W * My * np.exp(-self.Dy * z) 165 | 166 | return np.concatenate((rx, ry)) 167 | 168 | Ax = self.A[:self.N] 169 | Ay = self.A[self.N:] 170 | solution = scipy.integrate.solve_ivp( 171 | rhs, 172 | t_span=(0, self.fiber_length), 173 | y0=np.concatenate( 174 | (ifft(Ax) * self.scale, 175 | ifft(Ay) * self.scale)), 176 | t_eval=Z, 177 | rtol=self.rtol, 178 | atol=self.atol, 179 | method=self.method, 180 | dense_output=True) 181 | 182 | if self.report: 183 | progress_bar.close() 184 | 185 | AWx = solution.y.T[:, :self.N] 186 | AWy = solution.y.T[:, self.N:] 187 | 188 | # Transform the results into the time domain 189 | Atx = np.zeros(AWx.shape, dtype=AWx.dtype) 190 | Aty = np.zeros(AWy.shape, dtype=AWy.dtype) 191 | for i in range(len(AWx[:, 0])): 192 | AWx[i, :] *= np.exp(np.transpose( 193 | self.Dx) * Z[i]) / self.scale 194 | Atx[i, :] = fft(AWx[i, :]) 195 | AWx[i, :] = fftshift(AWx[i, :]) * self.N * dt 196 | AWy[i, :] *= np.exp(np.transpose( 197 | self.Dy) * Z[i]) / self.scale 198 | Aty[i, :] = fft(AWy[i, :]) 199 | AWy[i, :] = fftshift(AWy[i, :]) * self.N * dt 200 | 201 | At = np.concatenate((Atx, Aty)) 202 | AW = np.concatenate((AWx, AWy)) 203 | 204 | return Solution(self.t, self.Omega, Z, At, AW) 205 | -------------------------------------------------------------------------------- /cnlse/dispersion.py: -------------------------------------------------------------------------------- 1 | """Dispersion operator in optical fibres. 2 | 3 | Based on delivered frequency vector and damping indexes 4 | script calculates linear dispersion operator in 5 | frequency domain. 6 | 7 | """ 8 | import numpy as np 9 | from scipy import interpolate 10 | 11 | from gnlse.common import c 12 | from gnlse.dispersion import Dispersion 13 | 14 | 15 | class DubbleDispersionFiberFromTaylor(Dispersion): 16 | """Calculates the dispersion in frequency domain 17 | using second Beta derivative 18 | 19 | Attributes 20 | ---------- 21 | loss : float 22 | Loss factor [dB/m] 23 | betas : ndarray (2, 1) 24 | Derivatives of constant propagations at pump wavelength 25 | [ps^n/km] 26 | deltaN : float 27 | Difference between the effective indices seen by 28 | the x- and y-polarized modes 29 | deltaB1 : float 30 | Difference between first derivatives of constant propagations 31 | at pump wavelength [ps/m] 32 | w0 : float 33 | Central frequency of the input pulse [THz] 34 | """ 35 | 36 | def __init__(self, loss, betas, deltaN, deltaB1, w0): 37 | self.loss = loss 38 | self.betas = betas 39 | self.deltaB = deltaN * w0 / c * 1e9 40 | self.deltaB1 = deltaB1 41 | 42 | def D(self, V): 43 | # Damping 44 | self.calc_loss() 45 | # Taylor series for subsequent derivatives 46 | # of constant propagation 47 | Lx = -1j * self.deltaB1/2 * V + 1j * self.betas[0, 0] / 2 * V**2 - self.alpha / 2 48 | Ly = 1j * self.deltaB1/2 * V + 1j * self.betas[1, 0] / 2 * V**2 - self.alpha / 2 49 | return np.concatenate((Lx, Ly)), self.deltaB 50 | 51 | 52 | class DubbleDispersionFiberFromOPD(Dispersion): 53 | """Calculates the dispersion in frequency domain 54 | using measured optical path delay (OPD) 55 | 56 | Attributes 57 | ---------- 58 | lambdasOPD1 : ndarray 59 | Wavelength grid for measured optical path delay (OPD) 60 | for x-polarized mode [um] 61 | lambdasOPD2 : ndarray 62 | Wavelength grid for measured optical path delay (OPD) 63 | for y-polarized mode [um] 64 | deltaOPD1 : ndarray 65 | Measured optical path delay (OPD) 66 | for x-polarized mode [mm] 67 | deltaOPD2 : ndarray 68 | Measured optical path delay (OPD) 69 | for y-polarized mode [mm] 70 | L : float 71 | Measured length [m] 72 | deltaN : float 73 | Difference between the effective indices seen by 74 | the x- and y-polarized modes 75 | w0 : float 76 | Central frequency of the input pulse [THz] 77 | loss : tuple, None, int 78 | (Wavelengths [um], Loss factor [dB/m]) 79 | doping : float 80 | Germanium doping level 81 | maxloss : float 82 | Value of loss to extrapolate above measured range [1/m] 83 | """ 84 | 85 | def __init__(self, lambdasOPD1, 86 | lambdasOPD2, deltaOPD1, 87 | deltaOPD2, L, deltaN, w0, 88 | loss=None, doping=0.18, 89 | maxloss=1e2): 90 | self.lambdasOPD1 = lambdasOPD1 91 | self.lambdasOPD2 = lambdasOPD2 92 | self.deltaOPD1 = deltaOPD1 93 | self.deltaOPD2 = deltaOPD2 94 | self.L = L 95 | self.w0 = w0 96 | self.deltaB = deltaN * self.w0 / c * 1e9 97 | if loss is None: 98 | self.doping = doping 99 | self.maxloss = maxloss 100 | self.lambda_loss = None 101 | self.fiber_loss = None 102 | elif loss == 0: 103 | self.doping = None 104 | self.maxloss = None 105 | self.lambda_loss = None 106 | self.fiber_loss = 0. 107 | else: 108 | self.doping = None 109 | self.maxloss = None 110 | self.lambda_loss = loss[0] 111 | self.fiber_loss = loss[1] 112 | 113 | def D(self, V): 114 | omegas1 = 2 * np.pi * c / (self.lambdasOPD1 * 1e3) # rad*THz 115 | omegas2 = 2 * np.pi * c / (self.lambdasOPD2 * 1e3) # rad*THz 116 | 117 | Neff1 = -self.deltaOPD1 * 1e-3 / self.L 118 | Neff2 = -self.deltaOPD2 * 1e-3 / self.L 119 | 120 | beta11 = Neff1 / c 121 | beta12 = Neff2 / c 122 | 123 | mu = np.mean(omegas1 - self.w0) 124 | std = np.std(omegas1 - self.w0, ddof=1) 125 | p11 = np.polyfit((omegas1 - self.w0 - mu)/std, beta11 * 1e9, 6) 126 | p11scaled = np.poly1d(p11) 127 | p1w01 = p11scaled(-mu/std) 128 | p01 = np.array(np.polyint(p11scaled)) 129 | fp01 = np.poly1d(p01) 130 | p01[-1] = p01[-1] - fp01(-mu/std) 131 | beta01 = fp01((V - mu)/std) * std 132 | 133 | mu = np.mean(omegas2 - self.w0) 134 | std = np.std(omegas2 - self.w0, ddof=1) 135 | p12 = np.polyfit((omegas2 - self.w0 - mu)/std, beta12 * 1e9, 6) 136 | p12scaled = np.poly1d(p12) 137 | p1w02 = p12scaled(-mu/std) 138 | p02 = np.array(np.polyint(p12scaled)) 139 | fp02 = np.poly1d(p02) 140 | p02[-1] = p02[-1] - fp02(-mu/std) 141 | beta02 = fp02((V - mu)/std) * std 142 | 143 | Bx = beta01 - (p1w01 + p1w02) / 2 * V 144 | By = beta02 - (p1w01 + p1w02) / 2 * V 145 | 146 | # Damping 147 | if self.fiber_loss is None: 148 | self.calc_loss(2 * np.pi * c / (V + self.w0) / 1e3) 149 | elif ( 150 | isinstance(self.fiber_loss, int) or isinstance(self.fiber_loss, float) 151 | ) and self.fiber_loss == 0: 152 | self.alpha = 0 153 | else: 154 | WL = 2 * np.pi * c / (V + self.w0) # nm 155 | # Extrapolate loss for a lambda vector 156 | loss_interp = interpolate.interp1d(self.lambda_loss, 157 | self.fiber_loss, 158 | kind='cubic', 159 | fill_value="extrapolate") 160 | loss = loss_interp(WL) 161 | loss[WL > self.lambda_loss[-1]] = np.max(self.fiber_loss) 162 | self.alpha = loss / (10 / np.log(10)) 163 | 164 | # Linear dispersion operator 165 | Lx = 1j * Bx - self.alpha / 2 166 | Ly = 1j * By - self.alpha / 2 167 | 168 | return np.concatenate((Lx, Ly)), self.deltaB 169 | 170 | def calc_loss(self, lambdas): 171 | """Calculates the total loss in [1/m] 172 | 173 | Attributes 174 | ---------- 175 | lambdas : ndarray 176 | Wavelength grid for loss calculation 177 | """ 178 | # Rayleigh scattering [dB/km*um^4] 179 | # value 0.74 for SiO2 from Appl. Phys. Lett. 83, 5175 (2003) 180 | # value 2.33 for GeO2 from Appl. Optics 36(27) (1997) 181 | R = .74 + (2.33 - .74) * self.doping 182 | alphaR = R * lambdas**(-4) * 1e-3 183 | # measured fiber water peak 184 | alphaoh_1_38 = 2.43 185 | sigma_lambda = 0.030 186 | # Journal of Non-Crystalline Solids Volume 203 (1996) 187 | alphaoh = 0.00012 / 62.7 * alphaoh_1_38 * np.exp(-.5 * ((lambdas - 0.444) / (sigma_lambda))**2) + \ 188 | 0.00050 / 62.7 * alphaoh_1_38 * np.exp(-.5 * ((lambdas - 0.506) / (sigma_lambda))**2) + \ 189 | 0.00030 / 62.7 * alphaoh_1_38 * np.exp(-.5 * ((lambdas - 0.566) / (sigma_lambda))**2) + \ 190 | 0.00640 / 62.7 * alphaoh_1_38 * np.exp(-.5 * ((lambdas - 0.593) / (sigma_lambda))**2) + \ 191 | 0.00028 / 62.7 * alphaoh_1_38 * np.exp(-.5 * ((lambdas - 0.651) / (sigma_lambda))**2) + \ 192 | 0.00440 / 62.7 * alphaoh_1_38 * np.exp(-.5 * ((lambdas - 0.685) / (sigma_lambda))**2) + \ 193 | 0.07800 / 62.7 * alphaoh_1_38 * np.exp(-.5 * ((lambdas - 0.724) / (sigma_lambda))**2) + \ 194 | 0.00380 / 62.7 * alphaoh_1_38 * np.exp(-.5 * ((lambdas - 0.825) / (sigma_lambda))**2) + \ 195 | 0.08000 / 62.7 * alphaoh_1_38 * np.exp(-.5 * ((lambdas - 0.878) / (sigma_lambda))**2) + \ 196 | 1.6 / 62.7 * alphaoh_1_38 * np.exp(-.5 * ((lambdas - 0.943) / (sigma_lambda))**2) + \ 197 | 0.07 / 62.7 * alphaoh_1_38 * np.exp(-.5 * ((lambdas - 1.139) / (sigma_lambda))**2) + \ 198 | 2.7 / 62.7 * alphaoh_1_38 * np.exp(-.5 * ((lambdas - 1.246) / (sigma_lambda))**2) + \ 199 | alphaoh_1_38 * np.exp(-.5 * ((lambdas - 1.383) / (sigma_lambda))**2) + \ 200 | 0.84 / 62.7 * alphaoh_1_38 * np.exp(-.5 * ((lambdas - 1.894) / (sigma_lambda))**2) + \ 201 | 201 / 62.7 * alphaoh_1_38 * np.exp(-.5 * ((lambdas - 2.212) / (sigma_lambda))**2) + \ 202 | 10000 / 62.7 * alphaoh_1_38 * np.exp(-.5 * ((lambdas - 2.722) / (sigma_lambda))**2) 203 | # Hiroshi Murata, Handbook of optical fibers and cables (1996) 204 | alphaIR = 4.2e8 * np.exp(-47.5 / lambdas) 205 | a = (alphaoh + alphaR + alphaIR) / (10 / np.log(10)) 206 | a[a > self.maxloss] = self.maxloss 207 | self.alpha = a 208 | -------------------------------------------------------------------------------- /cnlse/envelopes.py: -------------------------------------------------------------------------------- 1 | """Amplitude envelopes of different pulses 2 | 3 | This module contains functions drawing envelopes of various pulses: 4 | hyperbolic secant, gaussian and lorentzian. 5 | 6 | """ 7 | 8 | import numpy as np 9 | from scipy.fftpack import fft, ifft 10 | 11 | from gnlse.common import c, hbar 12 | from gnlse.envelopes import SechEnvelope, Envelope 13 | 14 | 15 | class DoubleSechEnvelope(Envelope): 16 | """Amplitude envelope of hyperbolic secant pulse. 17 | 18 | Attributes 19 | ---------- 20 | input_energy : float 21 | Input pulse energy [nJ] 22 | FWHM : float 23 | Pulse duration Full-Width Half-Maximum [ps] 24 | theta : float 25 | Azimuth angle [rad] 26 | w0 : float 27 | Central frequency of the input pulse [THz] 28 | """ 29 | 30 | def __init__(self, input_energy, FWHM, theta, w0): 31 | self.name = 'Double Hyperbolic secant envelope' 32 | self.energy = input_energy 33 | self.FWHM = FWHM 34 | self.theta = theta 35 | self.w0 = w0 36 | 37 | def A(self, T): 38 | """ 39 | 40 | Parameters 41 | ---------- 42 | T : ndarray, (n, ) 43 | Time vector 44 | 45 | Returns 46 | ------- 47 | ndarray, (n, ) 48 | Amplitude envelope of double hyperbolic secant pulse in time. 49 | """ 50 | N = len(T) 51 | dT = T[1] - T[0] 52 | V = 2 * np.pi * np.array(np.arange(-N/2, N/2)) / (N * dT) 53 | ATx = np.cos(self.theta) * SechEnvelope(1, self.FWHM).A(T) 54 | ATy = np.sin(self.theta) * SechEnvelope(1, self.FWHM).A(T) 55 | energy_x = np.trapz(-T, abs(ATx)**2) / 1e3 # nJ 56 | energy_y = np.trapz(-T, abs(ATy)**2) / 1e3 # nJ 57 | energy = energy_x + energy_y 58 | ATx = ATx * np.sqrt(self.energy / energy) 59 | ATy = ATy * np.sqrt(self.energy / energy) 60 | AWx = ifft(ATx) * dT * N 61 | AWy = ifft(ATy) * dT * N 62 | photon_energy = hbar * ( 63 | V + (2.0 * np.pi * c) / self.w0) # [J.s.THz = TJ] 64 | photon_energy = photon_energy * 1e24 # [pJ] 65 | noise = np.sqrt(photon_energy * dT * N) # [sqrt(pJ.ps)] 66 | AWx_noise = noise * np.exp(2j * np.pi * np.random.rand(1, N)) 67 | AWy_noise = noise * np.exp(2j * np.pi * np.random.rand(1, N)) 68 | AWx = AWx + AWx_noise[0, :] 69 | AWy = AWy + AWy_noise[0, :] 70 | ATx = fft(AWx / dT / N) 71 | ATy = fft(AWy / dT / N) 72 | return np.concatenate((ATx, ATy)) 73 | -------------------------------------------------------------------------------- /cnlse/raman_response.py: -------------------------------------------------------------------------------- 1 | """ 2 | Calculates Raman responses 3 | """ 4 | 5 | import numpy as np 6 | 7 | 8 | def raman_polarisation(T): 9 | """Raman scattering function for silica optical fibers, based on 10 | Prannay Balla and Govind P. Agrawal 11 | Phys. Rev. A 98, 023822 (2018) 12 | (J. Opt. Soc. Am. B 6, 1159–1166 (1989), 13 | J. Opt. Soc. Am. B 9, 1061–1082 (1992)) 14 | 15 | Parameters 16 | ---------- 17 | T : float 18 | Time vector. 19 | 20 | Returns 21 | ------- 22 | fr : float 23 | Share of Raman response. 24 | h1T : ndarray 25 | Vector representing h1 temporal Raman response. 26 | h2T : ndarray 27 | Vector representing h2 temporal Raman response. 28 | h3T : ndarray 29 | Vector representing h3 temporal Raman response. 30 | 31 | """ 32 | 33 | # Raman response [arbitrary units] 34 | fr = 0.245 35 | # Adjustable parameters used to fit the actual Raman gain spectrum [ps] 36 | tau1 = 0.0122 37 | tau2 = 0.032 38 | taub = 0.096 39 | # Fractional contribution of the anisotropic reponse to the total Raman 40 | # response 41 | fb = 0.21 42 | fc = 0.04 43 | # Fractional contribution of the isotropic reponse to the total Raman 44 | # response 45 | fa = 1 - fb - fc 46 | # Anisotropic Raman response 47 | ha = (tau1**2 + tau2**2) / tau1 / (tau2**2) * np.exp(-T / tau2) * np.sin( 48 | T / tau1) 49 | # Izotropic Raman respons 50 | hb = (2 * taub - T) / (taub**2) * np.exp(-T / taub) 51 | 52 | h1T = (fa + fc) * ha + fb * hb 53 | h2T = fa * ha 54 | h3T = (fc * ha + fb * hb) / 2 55 | 56 | h1T[T < 0] = 0 57 | h2T[T < 0] = 0 58 | h3T[T < 0] = 0 59 | 60 | return fr, h1T, h2T, h3T 61 | -------------------------------------------------------------------------------- /data/191119_polarisation_21m_lambda_1560nm_power_41mW.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WUST-FOG/cgnlse-python/28f0b9c10699fcdf1df5663b069b3fcb3a1841a8/data/191119_polarisation_21m_lambda_1560nm_power_41mW.png -------------------------------------------------------------------------------- /data/191119_polarisation_21m_lambda_1560nm_power_41mW_angle_1deg_exp.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:4458f2ece095cb50e20af8b17a4dac2015c73724bcbb8858724e92234d4fb20d 3 | size 489395768 4 | -------------------------------------------------------------------------------- /data/191119_polarisation_21m_lambda_1560nm_power_41mW_angle_89deg_exp.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2cacb0dcc88f7985066b1629e6f28412fd25284dcc5693b23e9aed7ab51d1574 3 | size 486263161 4 | -------------------------------------------------------------------------------- /draw_soliton_traping.py: -------------------------------------------------------------------------------- 1 | import os 2 | import matplotlib.pyplot as plt 3 | import matplotlib 4 | import numpy as np 5 | 6 | import gnlse 7 | from gnlse.common import c 8 | 9 | matplotlib.rc('font', size=15) 10 | matplotlib.rc('axes', titlesize=18) 11 | plt.rc('legend', fontsize=15) 12 | 13 | 14 | if __name__ == '__main__': 15 | setup = gnlse.GNLSESetup() 16 | 17 | # Physical parameters 18 | wavelength = 1560 # nm 19 | total_fiber_length = 21 # m -> total fiber length 20 | 21 | # Input pulse parameters for both modes 22 | FWHM = 0.023 # ps 23 | average_power = 0.041 # W 24 | repetition_rate = 80e6 # Hz 25 | input_energy = average_power / repetition_rate * 1e9 # nJ 26 | 27 | # Visualization 28 | ########################################################################### 29 | theta_list = [1, 89] 30 | count = len(theta_list) 31 | 32 | plt.figure(figsize=(14, 8), facecolor='w', edgecolor='k') 33 | 34 | for i, theta in enumerate(theta_list): 35 | # load result 36 | solution = gnlse.Solution() 37 | solution.from_file(os.path.join('data', 38 | f'191119_polarisation' 39 | f'_{total_fiber_length}m' 40 | f'_lambda_{wavelength}nm' 41 | f'_power_{int(average_power * 1000)}mW' 42 | f'_angle_{int(theta)}deg_exp.mat')) 43 | 44 | # prepare initial vectors 45 | Z = solution.Z 46 | Nz = solution.At.shape[0] // 2 47 | Atx = solution.At[:Nz, :] 48 | Aty = solution.At[Nz:, :] 49 | AWx = solution.AW[:Nz, :] 50 | AWy = solution.AW[Nz:, :] 51 | 52 | # visualization 53 | WL_range = [1500, 2200] 54 | decybl = [-40, 30] 55 | plt.subplot(3, count, i + 1) 56 | if i == 0: 57 | plt.title("(a) Slow axis excitation") 58 | else: 59 | plt.title("(b) Fast axis excitation") 60 | WL = 2 * np.pi * c / solution.W # wavelength grid 61 | plt.plot(WL, 10 * np.log10(np.abs(AWx[99, :])**2, 62 | where=(np.abs(AWx[99, :])**2 > 0)), '--r', 63 | label="2m") 64 | plt.plot(WL, 10 * np.log10(np.abs(AWx[393, :])**2, 65 | where=(np.abs(AWx[393, :])**2 > 0)), '-.r', 66 | label="8m") 67 | plt.plot(WL, 10 * np.log10(np.abs(AWx[981, :])**2, 68 | where=(np.abs(AWx[981, :])**2 > 0)), 'r', 69 | label="20m") 70 | plt.plot(WL, 10 * np.log10(np.abs(AWy[99, :])**2, 71 | where=(np.abs(AWy[99, :])**2 > 0)), '--g') 72 | plt.plot(WL, 10 * np.log10(np.abs(AWy[393, :])**2, 73 | where=(np.abs(AWy[393, :])**2 > 0)), '-.g') 74 | plt.plot(WL, 10 * np.log10(np.abs(AWy[981, :])**2, 75 | where=(np.abs(AWy[981, :])**2 > 0)), 'g') 76 | 77 | if i == 1: 78 | plt.legend(fancybox=True, shadow=True) 79 | else: 80 | plt.ylabel('Spectral density [dB]') 81 | plt.xlim(WL_range) 82 | plt.ylim(decybl) 83 | 84 | plt.subplot(3, count, i + 1 + count) 85 | solution.AW = AWx 86 | plt.title("Slow axis") 87 | gnlse.plot_wavelength_vs_distance(solution, WL_range=WL_range, 88 | norm=1) 89 | if i == 1: 90 | plt.ylabel('') 91 | if ((i + 1 + count) == 3) or ((i + 1 + count) == 4): 92 | plt.xlabel('') 93 | 94 | plt.subplot(3, count, i + 3 + count) 95 | solution.AW = AWy 96 | plt.title("Fast axis") 97 | gnlse.plot_wavelength_vs_distance(solution, WL_range=WL_range, 98 | norm=1) 99 | if i == 1: 100 | plt.ylabel('') 101 | 102 | plt.tight_layout() 103 | plt.savefig(f'191119_polarisation_{total_fiber_length}m' 104 | f'_lambda_{wavelength}nm' 105 | f'_power_{int(average_power * 1000)}mW.png') 106 | plt.show() 107 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | gnlse==2.0.0 -------------------------------------------------------------------------------- /run_modulation_instability.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example of modulation instability in highly birefringent 3 | fibers with circularly polarized eigenmodes [1]. 4 | 5 | [1] Zołnacz, K., Tarnowski, K. L., Napiórkowski, M., 6 | Poturaj, K., Mergo, P., Urbanczyk, W. 7 | Vector modulation instability in highly birefringent 8 | fibers with circularly polarized eigenmodes. 9 | IEEE Photonics Journal, 13(1):7100616, 2021. 10 | """ 11 | 12 | import numpy as np 13 | import matplotlib.pyplot as plt 14 | 15 | import gnlse 16 | 17 | from cnlse import CNLSE, DubbleDispersionFiberFromTaylor 18 | 19 | if __name__ == '__main__': 20 | setup = gnlse.GNLSESetup() 21 | 22 | # Numerical parameters 23 | setup.resolution = 2**11 24 | setup.time_window = 20 # ps 25 | setup.z_saves = 200 26 | 27 | # Physical parameters 28 | setup.wavelength = 1064 # nm 29 | w0 = 2 * np.pi * gnlse.common.c / setup.wavelength # 1/ps = THz 30 | setup.fiber_length = 2 # m 31 | n2 = 2.6e-20 # m^2/W 32 | Aeff0 = 54e-12 # 1/m^2 33 | gamma = n2 * 2 * np.pi / setup.wavelength * 1e9 / Aeff0 # 1/W/m 34 | setup.nonlinearity = (gamma, gamma) 35 | 36 | # The dispersion model is built from a Taylor expansion 37 | # with coefficients given below. 38 | loss = 0 39 | D = -40 # ps/km/nm 40 | beta2 = -setup.wavelength**2 * D / 2 / np.pi / gnlse.common.c * 1e-3 41 | betas = np.array([[beta2], [beta2]]) 42 | G = 5e-4 43 | Dbeta1 = G / (gnlse.common.c / 1e9) # ps/m 44 | dn = 1e-4 45 | setup.dispersion_model = DubbleDispersionFiberFromTaylor( 46 | loss, betas, dn, Dbeta1, w0) 47 | 48 | # Input pulse parameters 49 | peak_power = 2000 # W 50 | peak_noise = 1e-7 # W 51 | # Time domain grid 52 | t = np.linspace(-setup.time_window / 2, 53 | setup.time_window / 2, 54 | setup.resolution) 55 | At = gnlse.CWEnvelope( 56 | peak_power / 2, peak_noise / 2).A(np.concatenate((t, t))) 57 | setup.pulse_model = At 58 | 59 | # Nonlinear Simulation 60 | ########################################################################### 61 | solver = CNLSE(setup) 62 | solver.report = True 63 | solution = solver.run() 64 | 65 | # Visualization 66 | ########################################################################### 67 | # prepare initial vectors 68 | Z = solution.Z 69 | Atx = solution.At[:setup.z_saves, :] 70 | Aty = solution.At[setup.z_saves:, :] 71 | AWx = solution.AW[:setup.z_saves, :] 72 | AWy = solution.AW[setup.z_saves:, :] 73 | 74 | lambdas = 2 * np.pi * gnlse.common.c / solution.W 75 | V = solution.W - w0 76 | gx = np.log(abs(AWx[-1, :] 77 | )**2 / abs(AWx[0, :])**2) / setup.fiber_length 78 | gy = np.log(abs(AWy[-1, :] 79 | )**2 / abs(AWy[0, :])**2) / setup.fiber_length 80 | 81 | plt.figure(figsize=(10, 10)) 82 | 83 | plt.subplot(3, 1, 1) 84 | plt.title("Results for modulation instability") 85 | plt.plot(solution.t, abs(Atx[0, :])**2) 86 | plt.plot(solution.t, abs(Aty[0, :])**2) 87 | plt.xlabel('t [ps]') 88 | plt.ylabel(r'$|A|^2$ [W]') 89 | 90 | plt.subplot(3, 1, 2) 91 | plt.semilogy(lambdas, abs(AWx[-1, :])**2) 92 | plt.semilogy(lambdas, abs(AWy[-1, :])**2) 93 | plt.xlim([950, 1200]) 94 | plt.xlabel(r'$\lambda$ [nm]') 95 | plt.ylabel('Intensity [a.u.]') 96 | 97 | plt.subplot(3, 1, 3) 98 | plt.plot(V, gx) 99 | plt.plot(V, gy) 100 | plt.xlabel(r'$\Omega$ [THz]') 101 | plt.ylabel('Gain [1/m]') 102 | 103 | plt.show() 104 | -------------------------------------------------------------------------------- /run_soliton_traping.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example of trapped pulse generation in birefringent 3 | microstructured optical fiber in 4 | anomalous dispersion regime [1]. 5 | 6 | [1] Stefanska, K., Majchrowska, S., Gemza, K., Sobon, G., 7 | Sotor, J., Mergo, P., Tarnowski, K. L., Martynkien, T. 8 | Soliton trapping and orthogonal Raman scattering in 9 | a birefringent microstructured fiber. 10 | """ 11 | 12 | import os 13 | import numpy as np 14 | import tqdm 15 | 16 | import gnlse 17 | 18 | from cnlse import (CNLSE, DubbleDispersionFiberFromOPD, 19 | DoubleSechEnvelope, raman_polarisation) 20 | 21 | 22 | if __name__ == '__main__': 23 | setup = gnlse.GNLSESetup() 24 | 25 | # Numerical parameters 26 | setup.resolution = 2**13 27 | setup.time_window = 45 # ps 28 | setup.z_saves = 50 29 | setup.rtol = 1e-4 30 | setup.atol = 1e-6 31 | setup.method = 'RK45' 32 | 33 | # Physical parameters 34 | setup.wavelength = 1560 # nm 35 | w0 = (2.0 * np.pi * gnlse.common.c) / setup.wavelength # THz 36 | setup.fiber_length = 1 # m -> fiber length to filter temporal noise 37 | total_fiber_length = 21 # m -> total fiber length 38 | setup.raman_model = raman_polarisation 39 | 40 | # Input pulse parameters for both modes 41 | FWHM = 0.023 # ps 42 | average_power = 0.041 # W 43 | repetition_rate = 80e6 # Hz 44 | input_energy = average_power / repetition_rate * 1e9 # nJ 45 | 46 | # Read input files for calculating the nonlinearity 47 | # read mat file for slow mode 48 | mat_path = os.path.join(os.path.dirname(__file__), 49 | 'data', '191119_Eslow_KG.mat') 50 | mat = gnlse.read_mat(mat_path) 51 | # neffs 52 | neffx = mat['neff_r'][:, 0] 53 | # wavelengths in nm 54 | lambdasx = mat['lams'][:, 0] * 1e3 55 | # efective mode area in m^2 56 | Aeffx = mat['Aeff'][:, 0] * 1e-12 57 | 58 | # read mat file for fast mode 59 | mat_path = os.path.join(os.path.dirname(__file__), 60 | 'data', '191119_Efast_KG.mat') 61 | mat = gnlse.read_mat(mat_path) 62 | # neffs 63 | neffy = mat['neff_r'][:, 0] 64 | # wavelengths in nm 65 | lambdasy = mat['lams'][:, 0] * 1e3 66 | # efective mode area in m^2 67 | Aeffy = mat['Aeff'][:, 0] * 1e-12 68 | # nonlinear index of refraction [m^2/W] 69 | n2 = 2.6e-20 70 | fun_gammax = gnlse.NonlinearityFromEffectiveArea(neffx, 71 | Aeffx, 72 | lambdasx, 73 | setup.wavelength, 74 | n2, 75 | neff_max=10) 76 | fun_gammay = gnlse.NonlinearityFromEffectiveArea(neffy, 77 | Aeffx, 78 | lambdasx, 79 | setup.wavelength, 80 | n2, 81 | neff_max=10) 82 | setup.nonlinearity = [fun_gammax, fun_gammay] 83 | 84 | # The dispersion model is built from a OPD 85 | # read mat file for dispersion 86 | mat_path = os.path.join(os.path.dirname(__file__), 87 | 'data', 'laurent.mat') 88 | mat = gnlse.read_mat(mat_path) 89 | expL = mat['L'][0, 0] 90 | deltaOPD1 = mat['deltaOPD1'][:, 0] 91 | lambdasOPD1 = mat['lambdas1'][:, 0] 92 | OPD_lambda_um1 = mat['OPD_lambda_um1'][:, 0] 93 | deltaOPD2 = mat['deltaOPD2'][:, 0] 94 | lambdasOPD2 = mat['lambdas2'][:, 0] 95 | OPD_lambda_um2 = mat['OPD_lambda_um2'][:, 0] 96 | 97 | deltaN = 0.000336960439368341 98 | mat_path = os.path.join(os.path.dirname(__file__), 99 | 'data', 'loss_fited.mat') 100 | mat = gnlse.read_mat(mat_path) 101 | fiber_loss = mat['a'][0] # dB/m 102 | lambdas_loss = mat['l'][0] * 1e3 # nm 103 | 104 | setup.dispersion_model = DubbleDispersionFiberFromOPD( 105 | lambdasOPD1, lambdasOPD2, deltaOPD1, 106 | deltaOPD2, expL, deltaN, w0, (lambdas_loss, fiber_loss)) 107 | 108 | # Nonlinear Simulation 109 | ########################################################################### 110 | 111 | theta_list = [1, 89] 112 | count = len(theta_list) 113 | 114 | for i, th in enumerate(theta_list): 115 | theta = th / 180 * np.pi # rad 116 | setup.pulse_model = DoubleSechEnvelope( 117 | input_energy, FWHM, theta, w0) 118 | 119 | solver = CNLSE(setup) 120 | solution = solver.run() 121 | 122 | # prepare initial vectors 123 | Z = solution.Z 124 | Atx = solution.At[:setup.z_saves, :] 125 | Aty = solution.At[setup.z_saves:, :] 126 | AWx = solution.AW[:setup.z_saves, :] 127 | AWy = solution.AW[setup.z_saves:, :] 128 | 129 | # filter noise & simulate futher distance 130 | if total_fiber_length > setup.fiber_length: 131 | lITx = 10 * np.log10(np.abs(Atx[-1, :]) ** 2, 132 | where=(np.abs(Atx[-1, :]) ** 2 > 0)) 133 | lITy = 10 * np.log10(np.abs(Aty[-1, :]) ** 2, 134 | where=(np.abs(Aty[-1, :]) ** 2 > 0)) 135 | lIT = lITx + lITy 136 | time_filter = solution.t[np.argmax(lIT)] 137 | iis = np.logical_or( 138 | solution.t < (time_filter - .5), 139 | solution.t > (time_filter + .5)) 140 | Atx[-1, iis] = 0 141 | Aty[-1, iis] = 0 142 | 143 | # simulate futher distance 144 | for j in tqdm.tqdm(range(2, total_fiber_length + 1)): 145 | setup.pulse_model = np.concatenate( 146 | (Atx[-1, :], Aty[-1, :])) 147 | solver = CNLSE(setup) 148 | solution = solver.run() 149 | Z = np.concatenate((Z, solution.Z[1:] + j - 1), axis=None) 150 | Atx = np.concatenate((Atx, solution.At[1:setup.z_saves, :])) 151 | Aty = np.concatenate((Aty, solution.At[setup.z_saves + 1:, :])) 152 | AWx = np.concatenate((AWx, solution.AW[1:setup.z_saves, :])) 153 | AWy = np.concatenate((AWy, solution.AW[setup.z_saves + 1:, :])) 154 | 155 | # update results 156 | solution.Z = Z 157 | solution.At = np.concatenate((Atx, Aty)) 158 | solution.AW = np.concatenate((AWx, AWy)) 159 | 160 | # save results 161 | solution.to_file( 162 | f'191119_polarisation_{total_fiber_length}m' 163 | f'_lambda_{setup.wavelength}nm' 164 | f'_power_{int(average_power * 1000)}mW' 165 | f'_angle_{int(theta / np.pi * 180)}deg_exp.mat') 166 | --------------------------------------------------------------------------------