├── .github └── workflows │ └── main.yml ├── .gitignore ├── .readthedocs.yml ├── .zenndo.json ├── LICENSE ├── MANIFEST.in ├── README.md ├── data ├── neff_pcf.mat └── supercontinuum_3pulses.png ├── docs ├── Makefile ├── _static │ ├── input_pulse.png │ ├── plot_raman.png │ └── supercontinuum_3pulses.png ├── code_documentation.rst ├── conf.py ├── dispersion.rst ├── envelopes.rst ├── examples │ ├── _static │ │ ├── 3rd_order_soliton.png │ │ ├── gvd.png │ │ ├── spm.png │ │ ├── spm_gvd.png │ │ ├── supercontinuum_3pulses.png │ │ ├── test_dispersion.png │ │ ├── test_nonlinearity.png │ │ └── test_raman.png │ ├── example_dispersive_wave.rst │ ├── example_effective_mode_area.rst │ ├── example_soliton.rst │ ├── example_supercontinuum.rst │ └── index.rst ├── gnlse.rst ├── gnlse_intro.rst ├── import_export.rst ├── index.rst ├── make.bat ├── nonlinearity.rst ├── raman_response.rst ├── references.rst ├── requirements.txt └── visualisation.rst ├── examples ├── plot_Raman_response.py ├── plot_input_pulse.py ├── test_3rd_order_soliton.py ├── test_Dudley.py ├── test_dispersion.py ├── test_gvd.py ├── test_import_export.py ├── test_nonlinearity.py ├── test_raman.py ├── test_spm+gvd.py ├── test_spm.py └── test_wl_del_freq.py ├── gnlse ├── __init__.py ├── common.py ├── dispersion.py ├── envelopes.py ├── gnlse.py ├── import_export.py ├── nonlinearity.py ├── raman_response.py └── visualization.py ├── requirements.txt ├── setup.py ├── test-requirements.txt └── tox.ini /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | tests: 7 | runs-on: ubuntu-latest 8 | container: 9 | image: python:3.9 10 | steps: 11 | - name: Packages 12 | run: pip install 'tox<4' 13 | - name: Checkout 14 | uses: actions/checkout@v3 15 | - name: Tox 16 | run: | 17 | git config --global --add safe.directory "${GITHUB_WORKSPACE}" 18 | tox -v 19 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | sphinx: 4 | configuration: docs/conf.py 5 | 6 | python: 7 | version: 3.7 8 | install: 9 | - requirements: docs/requirements.txt 10 | -------------------------------------------------------------------------------- /.zenndo.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "

The propagation of pulses in optical fibers is described by the generalized nonlinear Schrodinger equation (GNLSE), which takes into account the fiber losses, nonlinear effects, and higher-order chromatic dispersion. The GNLSE is a partial differential equation, whose order depends on the accounted nonlinear and dispersion effects. We present gnlse-python, an nonlinear optics modeling toolbox that contains a rich set of components and modules to solve GNLSE using the split-step Fourier transform method (SSFM). The numerical solver is freely available, implemented in Python language and includes a number of optical fiber analysis tools.

", 3 | "license": "MIT", 4 | "title": "WUST-FOG/gnlse-python: v2.0.0 bulit on 26 April 2021", 5 | "version": "2.0.0", 6 | "upload_type": "software", 7 | "publication_date": "2022-04-26", 8 | "creators": [ 9 | { 10 | "affiliation": "Wroc\u0142aw University of Science and Technology", 11 | "name": "Sylwia Majchrowska" 12 | }, 13 | { 14 | "affiliation": "Wroc\u0142aw University of Science and Technology", 15 | "name": "Pawe\u0142 Redman" 16 | }, 17 | { 18 | "affiliation": "Wroc\u0142aw University of Science and Technology", 19 | "name": "Magdalena Zatorska" 20 | }, 21 | { 22 | "affiliation": "Wroc\u0142aw University of Science and Technology", 23 | "name": "Daniel Szulc" 24 | }, 25 | { 26 | "affiliation": "Wroc\u0142aw University of Science and Technology", 27 | "name": "Adam Paw\u0142owski" 28 | }, 29 | { 30 | "affiliation": "Wroc\u0142aw University of Science and Technology", 31 | "name": "Karol Tarnowski" 32 | } 33 | 34 | ], 35 | "access_right": "open", 36 | "related_identifiers": [ 37 | { 38 | "scheme": "url", 39 | "identifier": "https://github.com/WUST-FOG/gnlse-python/tree/2.0.0", 40 | "relation": "isSupplementTo" 41 | }, 42 | { 43 | "scheme": "doi", 44 | "identifier": "10.5281/zenodo.6495720", 45 | "relation": "isVersionOf" 46 | } 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include gnlse *.py 2 | include README 3 | include LICENSE 4 | include requirements.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![CI workflow badge](https://github.com/WUST-FOG/gnlse-python/workflows/CI/badge.svg) 2 | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.6495720.svg)](https://doi.org/10.5281/zenodo.6495720) 3 | ![Visits Badge](https://badges.pufler.dev/visits/WUST-FOG/gnlse-python) 4 | 5 | # GNLSE: Nonlinear optics modeling tool for optical fibers 6 | 7 | `gnlse` is a Python set of scripts for solving 8 | Generalized Nonlinear Schrodringer Equation. It is one of the WUST-FOG students 9 | projects developed by [Fiber Optics Group, WUST](http://www.fog.pwr.edu.pl/). 10 | 11 | Complete documentation is available at 12 | [https://gnlse.readthedocs.io](https://gnlse.readthedocs.io). 13 | 14 | ## Installation 15 | 16 | ### Using pip 17 | 18 | ``` 19 | pip install gnlse 20 | ``` 21 | 22 | ### From scratch 23 | 24 | 1. Create a virtual environment with `python -m venv gnlse` or using `conda`. 25 | 2. Activate it with `. gnlse/bin/activate`. 26 | 3. Clone this repository `git clone https://github.com/WUST-FOG/gnlse-python.git` 27 | 4. Install gnlse package `pip install .` (or `pip install -v -e .` for develop mode) or set `PYTHONPATH` enviroment variable 28 | 29 | ```bash 30 | python -m venv gnlse 31 | . gnlse/bin/activate 32 | git clone https://github.com/WUST-FOG/gnlse-python.git 33 | cd gnlse-python 34 | pip install . 35 | ``` 36 | 37 | ## Usage 38 | 39 | We provided some examples in `examples` subdirectory. They can be run by typing 40 | name of the script without any arguments. 41 | 42 | Example: 43 | 44 | ```bash 45 | cd gnlse-python/examples 46 | 47 | python test_Dudley.py 48 | ``` 49 | 50 | And you expect to visualise supercontinuum generation process in use of 3 types 51 | of pulses (simulation similar to Fig.3 of Dudley et. al, RMP 78 1135 (2006)): 52 | 53 | ![supercontinuum](https://raw.githubusercontent.com/WUST-FOG/gnlse-python/main/data/supercontinuum_3pulses.png) 54 | 55 | ### Major features 56 | 57 | - **Modular Design** 58 | 59 | Main core of gnlse module is derived from the RK4IP matlab script 60 | written by J.C.Travers, H. Frosz and J.M. Dudley 61 | that is provided in "Supercontinuum Generation in Optical Fibers", 62 | edited by J. M. Dudley and J. R. Taylor (Cambridge 2010). 63 | The toolbox prepares integration using SCIPYs ode solvers (adaptive step size). 64 | We decompose the solver framework into different components 65 | and one can easily construct a customized simulations 66 | by accounting different physical phenomena, ie. self stepening, Raman response. 67 | 68 | - **Raman response models** 69 | 70 | We implement three different raman response functions: 71 | - 'blowwood': Blow and D. Wood, IEEE J. of Quant. Elec., vol. 25, no. 12, pp. 2665–2673, Dec. 1989, 72 | - 'linagrawal': Lin and Agrawal, Opt. Lett., vol. 31, no. 21, pp. 3086–3088, Nov. 2006, 73 | - 'hollenbeck': Hollenbeck and Cantrell, J. Opt. Soc. Am. B, vol. 19, no. 12, Dec. 2002. 74 | 75 | - **Nonlinearity** 76 | 77 | We implement the possibility to account effective mode area's dependence on frequency: 78 | - provide float value for gamma (effective nonlinear coefficient) 79 | - 'NonlinearityFromEffectiveArea': introduce effective mode area's dependence on frequency (J. Laegsgaard, Opt. Express, vol. 15, no. 24, pp. 16110-16123, Nov. 2007). 80 | 81 | - **Dispersion operator** 82 | 83 | We implement two version of dispersion operator: 84 | - dispersion calculated from Taylor expansion, 85 | - dispersion calculated from effective refractive indicies. 86 | 87 | - **Available demos** 88 | 89 | We prepare few examples in `examples` subdirectory: 90 | - plot_input_pulse.py: plots envelope of different pulse shapes, 91 | - plot_Raman_response.py: plots different Raman in temporal domain, 92 | - test_3rd_order_soliton.py: evolution of the spectral and temporal characteristics of the 3rd order soliton, 93 | - test_dispersion.py: example of supercontinuum generation using different dispersion operators, 94 | - test_nonlinearity.py: example of supercontinuum generation using different GNLSE and M-GNLSE (take into account mode profile dispersion), 95 | - test_Dudley.py: example of supercontinuum generation with three types of input pulse, 96 | - test_gvd.py: example of pulse broadening due to group velocity dispersion, 97 | - test_import_export.py: example of saving file with `.mat` extension, 98 | - test_raman.py: example of soliton fision for diffrent raman response functions, 99 | - test_spm.py: example of self phase modulation, 100 | - test_spm+gvd.py: example of generation of 1st order soliton. 101 | 102 | For more advanced examples with Coupled Generalized Nonlinear Schrodringer Equation with two modes please refer to [cgnlse-python](https://github.com/WUST-FOG/cgnlse-python). 103 | 104 | ## Release History 105 | 106 | v2.0.1 was released in 08/01/2023. 107 | The main branch works with **python 3.9**. 108 | 109 | * **2.0.0 -> Apr 26th, 2022** 110 | * CHANGE: Code refactor - rename envelopes module 111 | * FIX: Fixed extrapolation for nonlinear coefficient 112 | * **1.1.3 -> Feb 13th, 2022** 113 | * FIX: Shift scalling data for interpolated dispersion 114 | * **1.1.2 -> Aug 30th, 2021** 115 | * ADD: Continious wave envelope 116 | * FIX: Shift scalling data for nonlinear coefficient 117 | * **1.1.1 -> Aug 28th, 2021** 118 | * CHANGE: Minor bug fix with scaling 119 | * CHANGE: Few minor changes in the documentation 120 | * **1.1.0 -> Aug 21st, 2021** 121 | * Modified-GNLSE extension 122 | * CHANGE: Code refactor - relocate GNLSE's attribiutes setting into constructor 123 | * ADD: Possibility to take into account the effective mode area's dependence on frequency 124 | * **1.0.0 -> Aug 13th, 2020** 125 | * The first proper release 126 | * CHANGE: Complete documentation and code 127 | 128 | ## Authors 129 | 130 | - [Pawel Redman](https://redman.xyz/) 131 | - [Magda Zatorska](https://github.com/magdazatorska) 132 | - [Adam Pawlowski](https://github.com/adampawl) 133 | - [Daniel Szulc](http://szulc.xyz/) 134 | - [Sylwia Majchrowska](https://majsylw.netlify.app/) 135 | - [Karol Tarnowski](http://www.tarnowski.wppt.pwr.edu.pl/) 136 | 137 | ## Acknowledgement 138 | 139 | *gnlse-python* is an open source project that is contributed by researchers, 140 | engineers, and students from Wroclaw University of Science and Technology 141 | as a part of Fiber Optics Group's nonlinear simulations projects. 142 | The python code based on MATLAB code published in 143 | 'Supercontinuum Generation in Optical Fibers' 144 | by J. M. Dudley and J. R. Taylor, available at 145 | [http://scgbook.info/](http://scgbook.info/). 146 | 147 | ## Citation 148 | 149 | If you find this code useful in your research, please consider [citing](https://arxiv.org/abs/2110.00298): 150 | 151 | ``` 152 | @misc{redman2021gnlsepython, 153 | title={gnlse-python: Open Source Software to Simulate 154 | Nonlinear Light Propagation In Optical Fibers}, 155 | author={Pawel Redman and Magdalena Zatorska and Adam Pawlowski 156 | and Daniel Szulc and Sylwia Majchrowska and Karol Tarnowski}, 157 | year={2021}, 158 | eprint={2110.00298}, 159 | archivePrefix={arXiv}, 160 | primaryClass={physics.optics} 161 | } 162 | ``` 163 | 164 | ## Contributing 165 | Pull requests are welcome. 166 | For major changes, please open an issue first to discuss 167 | what you would like to change. 168 | 169 | Please make sure to update example tests as appropriate. 170 | 171 | ## License 172 | [MIT](https://choosealicense.com/licenses/mit/) 173 | -------------------------------------------------------------------------------- /data/neff_pcf.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WUST-FOG/gnlse-python/801e440490ca770a363596b50a5ef5cf12813901/data/neff_pcf.mat -------------------------------------------------------------------------------- /data/supercontinuum_3pulses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WUST-FOG/gnlse-python/801e440490ca770a363596b50a5ef5cf12813901/data/supercontinuum_3pulses.png -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/_static/input_pulse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WUST-FOG/gnlse-python/801e440490ca770a363596b50a5ef5cf12813901/docs/_static/input_pulse.png -------------------------------------------------------------------------------- /docs/_static/plot_raman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WUST-FOG/gnlse-python/801e440490ca770a363596b50a5ef5cf12813901/docs/_static/plot_raman.png -------------------------------------------------------------------------------- /docs/_static/supercontinuum_3pulses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WUST-FOG/gnlse-python/801e440490ca770a363596b50a5ef5cf12813901/docs/_static/supercontinuum_3pulses.png -------------------------------------------------------------------------------- /docs/code_documentation.rst: -------------------------------------------------------------------------------- 1 | GNLSE package documentation 2 | =========================== 3 | 4 | Pulse envelopes 5 | ----------------- 6 | 7 | This module allows one to model proper input envelope as the given initial 8 | conditions to solve Generalized Nonlinear Schrodiner Equation (GNLSE). 9 | 10 | .. autosummary:: 11 | 12 | gnlse.SechEnvelope 13 | gnlse.GaussianEnvelope 14 | gnlse.LorentzianEnvelope 15 | 16 | GNLSE model 17 | ----------- 18 | 19 | This part of the module is its core, and it implements the split-step Fourier 20 | method to solve the GNLSE. 21 | 22 | .. autosummary:: 23 | 24 | gnlse.GNLSESetup 25 | gnlse.GNLSE 26 | gnlse.Solution 27 | 28 | Dispersion operators 29 | -------------------- 30 | 31 | Package supports two dispersion operators: calculated from a Taylor expansion 32 | and calculated from effective refractive indices. 33 | 34 | .. autosummary:: 35 | 36 | gnlse.DispersionFiberFromTaylor 37 | gnlse.DispersionFiberFromInterpolation 38 | 39 | Nonlinear coefficient 40 | --------------------- 41 | 42 | The guided fiber mode has a certain effective mode area, which in general 43 | depends on frequency. Package supports two possibilites to account 44 | the parameter: by giving the scalar value for gamma at central wavelength 45 | or calculated values for freqency range from effective refractive indices 46 | and effective mode area (experimantal or simulated data) 47 | Mode profile dispersion in GNLSE is accounted based on algorithm 48 | described in [J07]_. 49 | 50 | .. autosummary:: 51 | 52 | gnlse.NonlinearityFromEffectiveArea 53 | 54 | Raman responses 55 | --------------- 56 | 57 | Package supports three Raman response functions: ``blowwood`` [BW89]_, 58 | ``linagrawal`` [LA06]_ and ``hollenbeck`` [HC02]_. 59 | 60 | .. autosummary:: 61 | 62 | gnlse.raman_blowwood 63 | gnlse.raman_holltrell 64 | gnlse.raman_linagrawal 65 | 66 | Visualisation 67 | ------------- 68 | 69 | Various plotting functions for visualizing GNLSE simulations using Matplotlib 70 | library are prepared. 71 | 72 | .. autosummary:: 73 | 74 | gnlse.plot_wavelength_vs_distance 75 | gnlse.plot_delay_vs_distance 76 | gnlse.quick_plot 77 | 78 | Importing and exporting 79 | ----------------------- 80 | 81 | The following functions allow one to read and write data as \\*.mat files. 82 | 83 | .. autosummary:: 84 | 85 | gnlse.read_mat 86 | gnlse.write_mat 87 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | 5 | sys.path.insert(0, os.path.abspath('..')) 6 | 7 | project = 'gnlse-python' 8 | copyright = '2020, Developers of gnlse-python' 9 | author = 'Developers of gnlse-python' 10 | 11 | master_doc = 'index' 12 | 13 | 14 | extensions = [ 15 | 'sphinx.ext.autodoc', 16 | 'sphinx.ext.autosummary', 17 | 'numpydoc', 18 | ] 19 | 20 | autodoc_mock_imports = [ 21 | 'hdf5storage', 22 | 'matplotlib', 23 | 'matplotlib.pyplot', 24 | 'numpy', 25 | 'pyfftw', 26 | 'scipy', 27 | 'scipy.integrate', 28 | 'scipy.interpolate', 29 | 'tqdm', 30 | ] 31 | 32 | autosummary_generate = True 33 | numpydoc_show_class_members = False 34 | 35 | templates_path = ['_templates'] 36 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 37 | html_theme = 'sphinx_rtd_theme' 38 | html_static_path = ['_static'] 39 | -------------------------------------------------------------------------------- /docs/dispersion.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Dispersion operators 4 | ==================== 5 | 6 | Package supports two dispersion operators: calculated from a Taylor expansion 7 | and calculated from effective refractive indices. 8 | 9 | .. autoclass:: gnlse.DispersionFiberFromTaylor 10 | .. autoclass:: gnlse.DispersionFiberFromInterpolation 11 | -------------------------------------------------------------------------------- /docs/envelopes.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Pulse envelopes 4 | ================= 5 | 6 | In physics, the envelope of an oscillating signal is a smooth curve 7 | outlining its extremes. The given envelope function is a function of time of 8 | input pulse. The below figure illustrates a modulated waves varying between 9 | an upper and a lower envelope of four types of optical signal. 10 | 11 | .. image:: _static/input_pulse.png 12 | :alt: example_input_pulse 13 | 14 | .. autoclass:: gnlse.SechEnvelope 15 | .. autoclass:: gnlse.GaussianEnvelope 16 | .. autoclass:: gnlse.LorentzianEnvelope 17 | .. autoclass:: gnlse.CWEnvelope 18 | -------------------------------------------------------------------------------- /docs/examples/_static/3rd_order_soliton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WUST-FOG/gnlse-python/801e440490ca770a363596b50a5ef5cf12813901/docs/examples/_static/3rd_order_soliton.png -------------------------------------------------------------------------------- /docs/examples/_static/gvd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WUST-FOG/gnlse-python/801e440490ca770a363596b50a5ef5cf12813901/docs/examples/_static/gvd.png -------------------------------------------------------------------------------- /docs/examples/_static/spm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WUST-FOG/gnlse-python/801e440490ca770a363596b50a5ef5cf12813901/docs/examples/_static/spm.png -------------------------------------------------------------------------------- /docs/examples/_static/spm_gvd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WUST-FOG/gnlse-python/801e440490ca770a363596b50a5ef5cf12813901/docs/examples/_static/spm_gvd.png -------------------------------------------------------------------------------- /docs/examples/_static/supercontinuum_3pulses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WUST-FOG/gnlse-python/801e440490ca770a363596b50a5ef5cf12813901/docs/examples/_static/supercontinuum_3pulses.png -------------------------------------------------------------------------------- /docs/examples/_static/test_dispersion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WUST-FOG/gnlse-python/801e440490ca770a363596b50a5ef5cf12813901/docs/examples/_static/test_dispersion.png -------------------------------------------------------------------------------- /docs/examples/_static/test_nonlinearity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WUST-FOG/gnlse-python/801e440490ca770a363596b50a5ef5cf12813901/docs/examples/_static/test_nonlinearity.png -------------------------------------------------------------------------------- /docs/examples/_static/test_raman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WUST-FOG/gnlse-python/801e440490ca770a363596b50a5ef5cf12813901/docs/examples/_static/test_raman.png -------------------------------------------------------------------------------- /docs/examples/example_dispersive_wave.rst: -------------------------------------------------------------------------------- 1 | Dispersive wave generation in anomalous dispersion regime 2 | ========================================================= 3 | 4 | Example of dispersive wave generation in anomalus dispersion regime at 5 | a central wavelength of 835 nm in a 15 centimeter long photonic 6 | crystal fiber using three different models to model Raman response. :: 7 | 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | 11 | import gnlse 12 | 13 | import os 14 | 15 | if __name__ == '__main__': 16 | setup = gnlse.GNLSESetup() 17 | 18 | # Numerical parameters 19 | setup.resolution = 2**14 20 | setup.time_window = 12.5 # ps 21 | setup.z_saves = 200 22 | 23 | # Physical parameters 24 | setup.wavelength = 835 # nm 25 | setup.fiber_length = 0.15 # m 26 | setup.nonlinearity = 0.0 # 1/W/m 27 | setup.raman_model = gnlse.raman_blowwood 28 | setup.self_steepening = True 29 | 30 | # The dispersion model is built from a Taylor expansion with coefficients 31 | # given below. 32 | loss = 0 33 | betas = np.array([-0.024948815481502, 8.875391917212998e-05, 34 | -9.247462376518329e-08, 1.508210856829677e-10]) 35 | 36 | # Input pulse parameters 37 | power = 10000 38 | # pulse duration [ps] 39 | tfwhm = 0.05 40 | # hyperbolic secant 41 | setup.pulse_model = gnlse.SechEnvelope(power, tfwhm) 42 | 43 | # Type of dyspersion operator: build from interpolation of given neffs 44 | # read mat file for neffs 45 | mat_path = os.path.join(os.path.dirname(__file__), '..', 46 | 'data', 'neff_pcf.mat') 47 | mat = gnlse.read_mat(mat_path) 48 | # neffs 49 | neff = mat['neff'][:, 1] 50 | # wavelengths in nm 51 | lambdas = mat['neff'][:, 0] * 1e9 52 | 53 | # Visualization 54 | ########################################################################### 55 | 56 | # Set type of dispersion function 57 | simulation_type = { 58 | 'Results for Taylor expansion': gnlse.DispersionFiberFromTaylor( 59 | loss, betas), 60 | 'Results for interpolation': gnlse.DispersionFiberFromInterpolation( 61 | loss, neff, lambdas, setup.wavelength) 62 | } 63 | 64 | count = len(simulation_type) 65 | plt.figure(figsize=(15, 7), facecolor='w', edgecolor='k') 66 | for (i, (name, dispersion_model)) in enumerate(simulation_type.items()): 67 | setup.dispersion_model = dispersion_model 68 | solver = gnlse.GNLSE(setup) 69 | solution = solver.run() 70 | 71 | plt.subplot(2, count, i + 1) 72 | plt.title(name) 73 | gnlse.plot_wavelength_vs_distance(solution, WL_range=[400, 1400]) 74 | 75 | plt.subplot(2, count, i + 1 + count) 76 | gnlse.plot_delay_vs_distance(solution, time_range=[-.5, 5]) 77 | 78 | plt.tight_layout() 79 | plt.show() 80 | 81 | 82 | Output: 83 | 84 | .. image:: _static/test_raman.png 85 | :alt: example_dispersive_wave 86 | -------------------------------------------------------------------------------- /docs/examples/example_effective_mode_area.rst: -------------------------------------------------------------------------------- 1 | Consideration of mode profile dispersion 2 | ======================================== 3 | 4 | Consideration of mode profile dispersion in GNLSE account 5 | based on algorithm described in [J07]_. 6 | Simulation conducted for a central wavelength of 835 nm in 7 | a 15 centimeter long photonic crystal fiber. :: 8 | 9 | import matplotlib.pyplot as plt 10 | import numpy as np 11 | import os 12 | 13 | import gnlse 14 | 15 | if __name__ == '__main__': 16 | setup = gnlse.GNLSESetup() 17 | 18 | # Numerical parameters 19 | setup.resolution = 2**14 20 | setup.time_window = 12.5 # ps 21 | setup.z_saves = 200 22 | 23 | # Physical parameters 24 | setup.wavelength = 835 # nm 25 | w0 = (2.0 * np.pi * gnlse.common.c) / setup.wavelength # 1/ps = THz 26 | setup.fiber_length = 0.15 # m 27 | setup.raman_model = gnlse.raman_blowwood 28 | setup.self_steepening = True 29 | 30 | # Input pulse parameters 31 | power = 1000 32 | # pulse duration [ps] 33 | tfwhm = 0.05 34 | # hyperbolic secant 35 | setup.pulse_model = gnlse.SechEnvelope(power, tfwhm) 36 | 37 | # The dispersion model is built from a Taylor expansion with coefficients 38 | # given below. 39 | loss = 0 40 | betas = np.array([-0.024948815481502, 8.875391917212998e-05, 41 | -9.247462376518329e-08, 1.508210856829677e-10]) 42 | setup.dispersion_model = gnlse.DispersionFiberFromTaylor(loss, betas) 43 | # parameters for calculating the nonlinearity 44 | n2 = 2.7e-20 # m^2/W 45 | Aeff0 = 1.78e-12 # 1/m^2 46 | gamma = n2 * w0 / gnlse.common.c / 1e-9 / Aeff0 # 1/W/m 47 | 48 | # read mat file for neffs to cover interpolation example 49 | mat_path = os.path.join(os.path.dirname(__file__), '..', 50 | 'data', 'neff_pcf.mat') 51 | mat = gnlse.read_mat(mat_path) 52 | 53 | # neffs 54 | neff = mat['neff'][:, 1] 55 | # wavelengths in nm 56 | lambdas = mat['neff'][:, 0] * 1e9 57 | # efective mode area in m^2 58 | Aeff = mat['neff'][:, 2] * 1e-12 59 | 60 | # This example extends the original code with additional simulations for 61 | nonlinearity_setups = [ 62 | ["Scalar $\\gamma$", 63 | gnlse.DispersionFiberFromTaylor(loss, betas), 64 | gamma], 65 | ["Frequency dependent $\\gamma$", 66 | gnlse.DispersionFiberFromTaylor(loss, betas), 67 | gnlse.NonlinearityFromEffectiveArea( 68 | neff, Aeff, lambdas, setup.wavelength, 69 | n2=n2, neff_max=10)] 70 | ] 71 | 72 | count = len(nonlinearity_setups) 73 | plt.figure(figsize=(10, 8), facecolor='w', edgecolor='k') 74 | for i, model in enumerate(nonlinearity_setups): 75 | setup.dispersion = model[1] 76 | setup.nonlinearity = model[2] 77 | solver = gnlse.GNLSE(setup) 78 | solution = solver.run() 79 | 80 | plt.subplot(2, count, i + 1) 81 | plt.title(model[0]) 82 | gnlse.plot_wavelength_vs_distance(solution, WL_range=[700, 1000]) 83 | plt.subplot(2, count, i + 1 + count) 84 | gnlse.plot_delay_vs_distance(solution, time_range=[-.5, .5]) 85 | 86 | plt.tight_layout() 87 | plt.show() 88 | 89 | 90 | Output: 91 | 92 | .. image:: _static/test_nonlinearity.png 93 | :alt: example_effective_mode_area 94 | -------------------------------------------------------------------------------- /docs/examples/example_soliton.rst: -------------------------------------------------------------------------------- 1 | Propagation of higher-order soliton 2 | =================================== 3 | 4 | Evolution of the spectral and temporal characteristics of the 5 | higher-order N = 3 soliton in three cases: 6 | - propagation without self steppening and Raman response; 7 | - soliton fission with self steppening, but no Raman response accounted; 8 | - soliton fission with self steppening, and Raman response accounted. :: 9 | 10 | import numpy as np 11 | import matplotlib.pyplot as plt 12 | 13 | import gnlse 14 | 15 | if __name__ == '__main__': 16 | setup = gnlse.gnlse.GNLSESetup() 17 | 18 | # Numerical parameters 19 | # number of grid time points 20 | setup.resolution = 2**13 21 | # time window [ps] 22 | setup.time_window = 12.5 23 | # number of distance points to save 24 | setup.z_saves = 200 25 | # relative tolerance for ode solver 26 | setup.rtol = 1e-6 27 | # absoulte tolerance for ode solver 28 | setup.atol = 1e-6 29 | 30 | # Physical parameters 31 | # Central wavelength [nm] 32 | setup.wavelength = 835 33 | # Nonlinear coefficient [1/W/m] 34 | setup.nonlinearity = 0.11 35 | # Dispersion: derivatives of propagation constant at central wavelength 36 | # n derivatives of betas are in [ps^n/m] 37 | betas = np.array([-11.830e-3]) 38 | # Input pulse: pulse duration [ps] 39 | tFWHM = 0.050 40 | # for dispersive length calculation 41 | t0 = tFWHM / 2 / np.log(1 + np.sqrt(2)) 42 | 43 | # 3rd order soliton conditions 44 | ########################################################################### 45 | # Dispersive length 46 | LD = t0 ** 2 / np.abs(betas[0]) 47 | # Non-linear length for 3rd order soliton 48 | LNL = LD / (3 ** 2) 49 | # Input pulse: peak power [W] 50 | power = 1 / (LNL * setup.nonlinearity) 51 | # Length of soliton, in which it break dispersive characteristic 52 | Z0 = np.pi * LD / 2 53 | # Fiber length [m] 54 | setup.fiber_length = .5 55 | # Type of pulse: hyperbolic secant 56 | setup.pulse_model = gnlse.SechEnvelope(power, 0.050) 57 | # Loss coefficient [dB/m] 58 | loss = 0 59 | # Type of dyspersion operator: build from Taylor expansion 60 | setup.dispersion_model = gnlse.DispersionFiberFromTaylor(loss, betas) 61 | 62 | # Set type of Ramman scattering function and selftepening 63 | simulation_type = { 64 | '3rd order soliton': (False, None), 65 | '3rd order soliton\nwith self-steepening': (True, None), 66 | 'Raman induced fission\nof 3rd order soliton': (True, 67 | gnlse.raman_blowwood) 68 | } 69 | 70 | count = len(simulation_type) 71 | plt.figure(figsize=(15, 7), facecolor='w', edgecolor='k') 72 | for (i, (name, 73 | (self_steepening, 74 | raman_model))) in enumerate(simulation_type.items()): 75 | setup.raman_model = raman_model 76 | setup.self_steepening = self_steepening 77 | solver = gnlse.GNLSE(setup) 78 | solution = solver.run() 79 | 80 | plt.subplot(2, count, i + 1) 81 | plt.title(name) 82 | gnlse.plot_wavelength_vs_distance(solution, WL_range=[400, 1400]) 83 | 84 | plt.subplot(2, count, i + 1 + count) 85 | gnlse.plot_delay_vs_distance(solution, time_range=[-.25, .25]) 86 | 87 | plt.tight_layout() 88 | plt.show() 89 | 90 | 91 | Output: 92 | 93 | .. image:: _static/3rd_order_soliton.png 94 | :alt: example_soliton 95 | -------------------------------------------------------------------------------- /docs/examples/example_supercontinuum.rst: -------------------------------------------------------------------------------- 1 | Supercontinuum generation in anomalous dispersion regime 2 | ======================================================== 3 | 4 | Example of supercontinuum generation in anomalous dispersion regime at 5 | a central wavelength of 835 nm in a 15 centimeter long fiber using three 6 | different input shape of input pulse's envelopes. :: 7 | 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | 11 | import gnlse 12 | 13 | 14 | if __name__ == '__main__': 15 | setup = gnlse.GNLSESetup() 16 | 17 | # Numerical parameters 18 | setup.resolution = 2**14 19 | setup.time_window = 12.5 # ps 20 | setup.z_saves = 200 21 | 22 | # Physical parameters 23 | setup.wavelength = 835 # nm 24 | setup.fiber_length = 0.15 # m 25 | setup.nonlinearity = 0.11 # 1/W/m 26 | setup.raman_model = gnlse.raman_blowwood 27 | setup.self_steepening = True 28 | 29 | # The dispersion model is built from a Taylor expansion with coefficients 30 | # given below. 31 | loss = 0 32 | betas = np.array([ 33 | -11.830e-3, 8.1038e-5, -9.5205e-8, 2.0737e-10, -5.3943e-13, 1.3486e-15, 34 | -2.5495e-18, 3.0524e-21, -1.7140e-24 35 | ]) 36 | setup.dispersion_model = gnlse.DispersionFiberFromTaylor(loss, betas) 37 | 38 | # Input pulse parameters 39 | peak_power = 10000 # W 40 | duration = 0.050 # ps 41 | 42 | # This example extends the original code with additional simulations for 43 | pulse_models = [ 44 | gnlse.SechEnvelope(peak_power, duration), 45 | gnlse.GaussianEnvelope(peak_power, duration), 46 | gnlse.LorentzianEnvelope(peak_power, duration) 47 | ] 48 | 49 | count = len(pulse_models) 50 | plt.figure(figsize=(14, 8), facecolor='w', edgecolor='k') 51 | for i, pulse_model in enumerate(pulse_models): 52 | print('%s...' % pulse_model.name) 53 | 54 | setup.pulse_model = pulse_model 55 | solver = gnlse.GNLSE(setup) 56 | solution = solver.run() 57 | 58 | plt.subplot(2, count, i + 1) 59 | plt.title(pulse_model.name) 60 | gnlse.plot_wavelength_vs_distance(solution, WL_range=[400, 1400]) 61 | 62 | plt.subplot(2, count, i + 1 + count) 63 | gnlse.plot_delay_vs_distance(solution, time_range=[-0.5, 5]) 64 | 65 | plt.tight_layout() 66 | plt.show() 67 | 68 | 69 | Output: 70 | 71 | .. image:: _static/supercontinuum_3pulses.png 72 | :alt: example_supercontinuum 73 | -------------------------------------------------------------------------------- /docs/examples/index.rst: -------------------------------------------------------------------------------- 1 | Examples 2 | ======== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | example_soliton 8 | example_supercontinuum 9 | example_dispersive_wave 10 | example_effective_mode_area 11 | -------------------------------------------------------------------------------- /docs/gnlse.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Integration 4 | =========== 5 | 6 | This part of the module is its core, and it implements the split-step Fourier 7 | method to solve the Generalized Nonlinear Schrodiner Equation (GNLSE), 8 | which in time domain can be written as 9 | 10 | .. math:: 11 | 12 | \frac{\partial A(z,T)}{\partial z} = & -\frac{\alpha}{2}A(z,T) - 13 | \sum_{k\geq 2} \left(\frac{i^{k-1}}{k!}\beta_k 14 | \frac{\partial^k A(z,T)}{\partial T^k} \right) + \\ 15 | & + i\gamma\left(1 + \frac{i}{\omega_c} \frac{\partial}{\partial T} \right) 16 | \left( A(z,T) \int_{-\infty}^{+\infty} R(T') |A(z,T-T')|^2 dT' \right), 17 | 18 | where :math:`\alpha` is the attenuation constant, and :math:`\beta_k` are the 19 | higher order dispersion coefficients obtained by a Taylor series expansion of 20 | the propagation constant :math:`\beta(\omega)` around the center frequency 21 | :math:`\omega_c`. The term in second line describes the nonlinear effects - 22 | temporal derivative in this term is responsible for self-steepening 23 | and optical shock formation, whereas the convolution integral describes 24 | the delayed Raman response :math:`R(T')` [H07]_. This form of the GNLSE is 25 | commonly employed for numerical simulations of propagation of pulses in 26 | a nonlinear medium such as optical fiber. 27 | 28 | This solver is efficient, thanks to an adaptive-step-size implementation of 29 | the fourth-order Runge-Kutta in the Interaction Picture method (RK4IP), 30 | adapted from [H07]_. Our Python code is partially based on MATLAB code, 31 | published in [DT10]_, available at http://scgbook.info/. The toolbox prepares 32 | integration using SCIPYs ode solver, while the transitions between time and 33 | frequency domains are accomplished using the FFT and iFFT from pyfftw library. 34 | 35 | The solver is divided into three parts: specific model setup 36 | (``gnlse.GNLSESetup``), the framework for preparing split-step Fourier 37 | alghoritm (``gnlse.GNLSE``), and class for managing the solution 38 | (``gnlse.Solution``). 39 | 40 | .. autoclass:: gnlse.GNLSESetup 41 | .. autoclass:: gnlse.GNLSE 42 | .. autoclass:: gnlse.Solution 43 | -------------------------------------------------------------------------------- /docs/gnlse_intro.rst: -------------------------------------------------------------------------------- 1 | GNLSE: Nonlinear optics modeling tool for optical fibers 2 | ======================================================== 3 | 4 | gnlse-python is a Python set of scripts for solving Generalized Nonlinear 5 | Schrodinger Equation. It is one of the WUST-FOG students projects developed by 6 | `Fiber Optics Group, WUST `_. 7 | 8 | Complete documentation is available at `https://gnlse.readthedocs.io `_. 9 | 10 | Installation 11 | ------------ 12 | 13 | Using pip 14 | ********* 15 | 16 | Instal the latest version 17 | 18 | ``pip install gnlse`` 19 | 20 | or pick appropriate version as v2.0.0 21 | 22 | ``pip install gnlse==2.0.0`` 23 | 24 | From scratch 25 | ************ 26 | 27 | 1. Create a virtual environment using ``python -m venv gnlse`` or ``conda``. 28 | 2. Activate it: 29 | 30 | ``. gnlse/bin/activate`` 31 | 32 | 3. Clone the GitHub repository: 33 | 34 | ``git clone https://github.com/WUST-FOG/gnlse-python.git`` 35 | 36 | 4. Install the package with all requirements: 37 | 38 | ``pip install .`` 39 | 40 | or add the relevant path to your ``PYTHONPATH`` enviroment variable. 41 | 42 | Dependencies 43 | ************ 44 | 45 | The following Python packages are required to install gnlse module. During 46 | instalation of package the latest version of each of listed below package will 47 | automatically be installed if missing: 48 | 49 | :: 50 | 51 | matplotlib>=2.2.2 52 | numpy>=1.14.3 53 | scipy>=1.1.0 54 | pyfftw>=0.10.0 55 | hdf5storage>=0.1.15 56 | tqdm>=4.11.2 57 | 58 | Usage 59 | ----- 60 | 61 | We provide some example Python scripts in the ``examples`` subdirectory. For 62 | instance, running ``test_Dudley.py`` demonstrates supercontinuum generation in 63 | an optical fiber using three different input pulse envelopes. 64 | 65 | :: 66 | 67 | cd gnlse-python/examples 68 | python test_Dudley.py 69 | 70 | A visualisation, pictured in the screenshot below, is then displayed. 71 | 72 | .. image:: _static/supercontinuum_3pulses.png 73 | 74 | .. toctree:: 75 | :maxdepth: 2 76 | :caption: Examples: 77 | 78 | examples/index 79 | 80 | More examples can be found in the `examples subdirectory 81 | `_. 82 | 83 | Major features 84 | -------------- 85 | 86 | * **A modular design**: the solver framework was decomposed into different 87 | components and one can easily construct a customized simulations by 88 | accounting different physical phenomena, ie. self stepening, 89 | Raman response. 90 | 91 | The main core of the module is derived from the RK4IP MATLAB script 92 | written by J. C. Travers, H. Frosz and J. M. Dudley, published [DT10]_. 93 | 94 | * Support for three Raman response functions: ``blowwood`` [BW89]_, 95 | ``linagrawal`` [LA06]_ and ``hollenbeck`` [HC02]_. 96 | * Support for two dispersion operators: calculated from a Taylor expansion 97 | and calculated from effective refractive indices. 98 | * A number of example scripts in the ``examples`` subdirectory: 99 | 100 | * ``plot_input_pulse.py``, plotting various input pulse envelopes, 101 | * ``plot_Raman_response.py``, plotting supported Raman response functions 102 | in the time domain, 103 | * ``test_3rd_order_soliton.py``, demonstrating the evolution of spectral 104 | and temporal characteristics of a third-order soliton, 105 | * ``test_dispersion.py``, an example of supercontinuum generation using 106 | different dispersion operators, 107 | * ``test_nonlinearity.py``, an example of soliton fission using 108 | different GNLSE and Modified GNLSE accounting mode profile dispersion, 109 | (take into account mode profile dispersion), 110 | * ``test_Dudley.py``, an example of supercontinuum generation using 111 | different input pulse envelopes, 112 | * ``test_gvd.py``, showing pulse broadening due to group velocity 113 | dispersion, 114 | * ``test_import_export.py``, an example of saving and loading simulation 115 | results to and from a \*.mat file, 116 | * ``test_raman.py``, showing solition fission in case for different Raman 117 | responses, 118 | * ``test_spm.py``, an example of self-phase modulation, 119 | * ``test_spm+gvd.py``, demonstrating generation of a first-order soliton. 120 | 121 | 122 | Release information 123 | ------------------- 124 | 125 | v2.0.0 was released on April 26, 2022. The main branch works with 126 | **Python 3.7.** 127 | 128 | ======= ================= ==================================================== 129 | Version Date Notes 130 | ======= ================= ==================================================== 131 | 2.0.0 April 26, 2022 * CHANGE: Code refactor - rename modules 132 | * FIX: Fixed extrapolation for nonlinear coefficient 133 | 1.1.3 February 13, 2022 * FIX: Fix scaling for interpolated dispersion 134 | 1.1.2 August 30, 2021 * ADD: Continious wave envelope 135 | * FIX: Shift scalling data for nonlinear coefficient 136 | 1.1.1 August 28, 2021 * CHANGE: Minor bug fix with scaling 137 | * CHANGE: Few minor changes in the documentation 138 | 1.1.0 August 21, 2021 * Modified-GNLSE extension 139 | * CHANGE: Code refactor - relocate attribiutes 140 | 1.0.0 August 13, 2020 * The first proper release 141 | * CHANGE: Complete documentation and code. 142 | ======= ================= ==================================================== 143 | 144 | Authors 145 | ******* 146 | 147 | * `Adam Pawłowski `_ 148 | * `Paweł Redman `_ 149 | * `Daniel Szulc `_ 150 | * `Magda Zatorska `_ 151 | * `Sylwia Majchrowska `_ 152 | * `Karol Tarnowski `_ 153 | 154 | Citation 155 | ******** 156 | 157 | If you use a code please `cite `_ us: 158 | 159 | Paragraph ... :: 160 | 161 | @misc{redman2021gnlsepython, 162 | title={gnlse-python: Open Source Software to Simulate Nonlinear Light Propagation In Optical Fibers}, 163 | author={Paweł Redman and Magdalena Zatorska and Adam Pawłowski and Daniel Szulc and Sylwia Majchrowska and Karol Tarnowski}, 164 | year={2021}, 165 | eprint={2110.00298}, 166 | archivePrefix={arXiv}, 167 | primaryClass={physics.optics} 168 | } 169 | 170 | Contributing 171 | ************ 172 | 173 | Pull requests are welcome. For major changes, please open an issue first to 174 | discuss what you would like to change. 175 | 176 | Please make sure to update examples and tests as appropriate. 177 | 178 | Acknowledgements 179 | **************** 180 | 181 | **gnlse-python** is an open-source project, contributed to by researchers, 182 | engineers and students of the Wrocław University of Science and Technology, as 183 | part of Fiber Optic Group's nonlinear simulation projects. The Python code was 184 | partially based on MATLAB code, published in [DT10]_, available at 185 | http://scgbook.info/. 186 | 187 | License 188 | ******* 189 | 190 | This project is licensed under the terms of the `MIT License 191 | `_. 192 | -------------------------------------------------------------------------------- /docs/import_export.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Importing and exporting 4 | ======================= 5 | 6 | The following functions allow one to read and write data as `.mat` files. 7 | 8 | .. autofunction:: gnlse.read_mat 9 | .. autofunction:: gnlse.write_mat 10 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to gnlse-python's documentation! 2 | ======================================== 3 | 4 | Contents: 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | gnlse_intro 10 | code_documentation 11 | examples/index 12 | references 13 | 14 | 15 | Indices and tables 16 | ================== 17 | 18 | * :ref:`genindex` 19 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/nonlinearity.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Nonlinear coefficient 4 | ===================== 5 | 6 | The time derivative term inside GNLSE models the dispersion of 7 | 8 | the nonlinearity. This is usually associated with effects such 9 | as self-steepening and optical shock formation, characterized by 10 | a timescale :math:`\tau_0 = 1/\omega_0`. In the context of fibre 11 | propagation, an additional dispersion of the nonlinearity arises 12 | due to the frequency dependence of the effective mode area. 13 | The last effect can be accounted in :math:`\tau_0` 14 | coefficient in an approximate manner. 15 | 16 | A better - still approximate - approach to include the dispersion 17 | of the effective mode area is to 18 | describe it directly in the frequency domain [J07]_. 19 | In this case, we can derive a GNLSE for the pulse evolution using 20 | :math:`\gamma(\omega)` defined as 21 | 22 | .. math:: 23 | 24 | \overline{\gamma}(\omega) = 25 | \frac{n_2n_{\mathrm{eff}}(\omega_0)\omega_0} 26 | {\mathrm{c}n_\mathrm{eff}(\omega)\sqrt{A_{\mathrm{eff}}(\omega)A_{\mathrm{eff}}(\omega_0)}}. 27 | 28 | 29 | This approach is more rigorous than the approximation 30 | of (:math:`\gamma = \gamma(\omega_0)`) 31 | and requires the definition of a pseudo-envelope :math:`C(z, \omega)` as 32 | 33 | 34 | .. math:: 35 | 36 | C(z, \omega) = \frac{A_{eff}^{1/4}(\omega_0 )}{A_{eff}^{1/4}(\omega )} A(z, \omega). 37 | 38 | .. autoclass:: gnlse.NonlinearityFromEffectiveArea 39 | -------------------------------------------------------------------------------- /docs/raman_response.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Raman responses 4 | =============== 5 | 6 | Calculates different Raman responses :math:`R(T)`, based on the chosen Raman 7 | model, the response is calculated. There are three available Raman reponse 8 | models for silica optical fibers: 9 | based on K. J. Blow and D. Wood model [BW89]_, 10 | based on Dawn Hollenbeck and Cyrus D. Cantrell model [HC02]_, 11 | and based on Q. Lin and Govind P. Agrawal model [LA06]_. 12 | 13 | The below figure illustrates a various Raman response functions in time 14 | domain. 15 | 16 | .. image:: _static/plot_raman.png 17 | :alt: example_input_raman 18 | 19 | .. autofunction:: gnlse.raman_blowwood 20 | .. autofunction:: gnlse.raman_holltrell 21 | .. autofunction:: gnlse.raman_linagrawal 22 | -------------------------------------------------------------------------------- /docs/references.rst: -------------------------------------------------------------------------------- 1 | Bibliography 2 | ============ 3 | 4 | .. [BW89] Blow, K. J., & Wood, D. (1989). Theoretical description of transient 5 | stimulated Raman scattering in optical fibers. IEEE Journal of Quantum 6 | Electronics, 25(12), 2665–2673. https://doi.org/10.1109/3.40655 7 | .. [DT10] Dudley, J., & Taylor, J. (Eds.). (2010). Supercontinuum Generation 8 | in Optical Fibers. Cambridge: Cambridge University Press. 9 | doi:10.1017/CBO9780511750465 10 | .. [HC02] Hollenbeck, D., & Cantrell, C. D. (2002). Multiple-vibrational-mode 11 | model for fiber-optic Raman gain spectrum and response function. Journal of 12 | the Optical Society of America B, 19(12), 2886. 13 | https://doi.org/10.1364/josab.19.002886 14 | .. [H07] Hult, J. (2010). A Fourth-Order Runge–Kutta 15 | in the Interaction Picture Method for Simulating Supercontinuum Generation 16 | in Optical Fibers. Journal of Lightwave Technology, 25(12), 3770-3775. 17 | https://doi.org/10.1109/JLT.2007.909373 18 | .. [LA06] Lin, Q., & Agrawal, G. P. (2006). Raman response function for silica 19 | fibers. Optics Letters, 31(21), 3086. https://doi.org/10.1364/ol.31.003086 20 | .. [J07] J. Laegsgaard, (2007). Mode profile dispersion in the generalized 21 | nonlinear Schrödinger equation. Optics Express 15(24), 16110-16123. 22 | https://doi.org/10.1364/OE.15.016110 23 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | numpydoc 2 | sphinx_rtd_theme 3 | rinohtype 4 | pygments>=2.5.1 5 | -------------------------------------------------------------------------------- /docs/visualisation.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Visualisation 4 | ============= 5 | 6 | Various plotting functions for visualizing GNLSE simulations using Matplotlib 7 | library are prepared. 8 | 9 | .. autofunction:: gnlse.plot_wavelength_vs_distance 10 | .. autofunction:: gnlse.plot_delay_vs_distance 11 | .. autofunction:: gnlse.quick_plot 12 | -------------------------------------------------------------------------------- /examples/plot_Raman_response.py: -------------------------------------------------------------------------------- 1 | """Calculates different Raman responses and plots them. 2 | Based on the chosen Raman model, the response is 3 | calculated and shown on the graph. There are three 4 | available Raman reponse models. 5 | """ 6 | 7 | import numpy as np 8 | import matplotlib.pyplot as plt 9 | import gnlse 10 | 11 | 12 | if __name__ == '__main__': 13 | # Initial paremeters 14 | ########################################################################## 15 | # Number of grid points 16 | n = 2 ** 13 17 | # Time window width [ps] 18 | time_window = 12.5 19 | # The speed of light [nm/ps] 20 | c = 299792458 * 1e-3 21 | # Central wavelength [nm] 22 | wavelength = 835 23 | # Central frequency [THz] 24 | w0 = (2 * np.pi * c) / wavelength 25 | # Time grid [ps] 26 | T = np.linspace(-time_window / 2, time_window / 2, n) 27 | 28 | # K. J. Blow and D. Wood Raman response 29 | fr, RT1 = gnlse.raman_blowwood(T) 30 | RT1 = RT1 / np.max(RT1) 31 | # Q. Lin and Govind P. Agrawal Raman response 32 | fr2, RT2 = gnlse.raman_linagrawal(T) 33 | RT2 = RT2 / np.max(RT2) 34 | # D. Hollenbeck and C. D. Cantrell Raman response 35 | fr3, RT3 = gnlse.raman_holltrell(T) 36 | RT3 = RT3 / np.max(RT3) 37 | 38 | plt.plot(T, RT1, label="Blow-Wood") 39 | plt.plot(T, RT2, label="Lin-Agrawal") 40 | plt.plot(T, RT3, label="Hollenbeck-Cantrell") 41 | 42 | plt.xlim(-0.025, 1) 43 | plt.ylabel("Raman response [a.u.]") 44 | plt.xlabel("Time [ps]") 45 | 46 | plt.legend() 47 | 48 | plt.show() 49 | -------------------------------------------------------------------------------- /examples/plot_input_pulse.py: -------------------------------------------------------------------------------- 1 | """Calculates envelopes of various pulses and plots them. 2 | Based on the chosen envelopes pulse, the aplitude envelope is 3 | calculated and shown on the graph. There are three 4 | available envelopes pulses models: 5 | hyperbolic secant, gaussian and lorentzian. 6 | """ 7 | 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | 11 | import gnlse 12 | 13 | if __name__ == '__main__': 14 | # time full with half maximum of pulse 15 | FWHM = 2 16 | # Time grid [ps] 17 | T = np.linspace(-2 * FWHM, 2 * FWHM, 1000 * FWHM) 18 | # peak power [W] 19 | Pmax = 100 20 | 21 | # Amplitude envelope of gaussina pulse 22 | A1 = gnlse.GaussianEnvelope(Pmax, FWHM).A(T) 23 | # Amplitude envelope of hiperbolic secans pulse 24 | A2 = gnlse.SechEnvelope(Pmax, FWHM).A(T) 25 | # Amplitude envelope of lorentzian pulse 26 | A3 = gnlse.LorentzianEnvelope(Pmax, FWHM).A(T) 27 | # Amplitude envelope of continious wave 28 | A4 = gnlse.CWEnvelope(Pmax).A(T) 29 | 30 | plt.figure(figsize=(12, 8)) 31 | plt.subplot(1, 2, 1) 32 | plt.plot(T, A1, label='gauss') 33 | plt.plot(T, A2, label='sech') 34 | plt.plot(T, A3, label='lorentz') 35 | plt.plot(T, A4, label='cw') 36 | plt.xlabel("Time [ps]") 37 | plt.ylabel("Amplitude [sqrt(W)]") 38 | plt.legend() 39 | 40 | plt.subplot(1, 2, 2) 41 | plt.plot(T, A1**2, label='gauss') 42 | plt.plot(T, A2**2, label='sech') 43 | plt.plot(T, A3**2, label='lorentz') 44 | plt.plot(T, A4**2, label='cw') 45 | plt.xlabel("Time [ps]") 46 | plt.ylabel("Power [W]") 47 | plt.legend() 48 | 49 | plt.show() 50 | -------------------------------------------------------------------------------- /examples/test_3rd_order_soliton.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example: Evolution of the spectral and temporal characteristics of the 3 | higher-order N = 3 soliton in three cases: 4 | - propagation without self steppening and Raman response; 5 | - soliton fission with self steppening, but no Raman response accounted; 6 | - soliton fission with self steppening, and Raman response accounted. 7 | """ 8 | 9 | import numpy as np 10 | import matplotlib.pyplot as plt 11 | 12 | import gnlse 13 | 14 | if __name__ == '__main__': 15 | setup = gnlse.gnlse.GNLSESetup() 16 | 17 | # Numerical parameters 18 | # number of grid time points 19 | setup.resolution = 2**13 20 | # time window [ps] 21 | setup.time_window = 12.5 22 | # number of distance points to save 23 | setup.z_saves = 200 24 | # relative tolerance for ode solver 25 | setup.rtol = 1e-6 26 | # absoulte tolerance for ode solver 27 | setup.atol = 1e-6 28 | 29 | # Physical parameters 30 | # Central wavelength [nm] 31 | setup.wavelength = 835 32 | # Nonlinear coefficient [1/W/m] 33 | setup.nonlinearity = 0.11 34 | # Dispersion: derivatives of propagation constant at central wavelength 35 | # n derivatives of betas are in [ps^n/m] 36 | betas = np.array([-11.830e-3]) 37 | # Input pulse: pulse duration [ps] 38 | tFWHM = 0.050 39 | # for dispersive length calculation 40 | t0 = tFWHM / 2 / np.log(1 + np.sqrt(2)) 41 | 42 | # 3rd order soliton conditions 43 | ########################################################################### 44 | # Dispersive length 45 | LD = t0 ** 2 / np.abs(betas[0]) 46 | # Non-linear length for 3rd order soliton 47 | LNL = LD / (3 ** 2) 48 | # Input pulse: peak power [W] 49 | power = 1 / (LNL * setup.nonlinearity) 50 | # Length of soliton, in which it break dispersive characteristic 51 | Z0 = np.pi * LD / 2 52 | # Fiber length [m] 53 | setup.fiber_length = .5 54 | # Type of pulse: hyperbolic secant 55 | setup.pulse_model = gnlse.SechEnvelope(power, 0.050) 56 | # Loss coefficient [dB/m] 57 | loss = 0 58 | # Type of dyspersion operator: build from Taylor expansion 59 | setup.dispersion_model = gnlse.DispersionFiberFromTaylor(loss, betas) 60 | 61 | # Set type of Ramman scattering function and selftepening 62 | simulation_type = { 63 | '3rd order soliton': (False, None), 64 | '3rd order soliton\nwith self-steepening': (True, None), 65 | 'Raman induced fission\nof 3rd order soliton': (True, 66 | gnlse.raman_blowwood) 67 | } 68 | 69 | count = len(simulation_type) 70 | plt.figure(figsize=(15, 7), facecolor='w', edgecolor='k') 71 | for (i, (name, 72 | (self_steepening, 73 | raman_model))) in enumerate(simulation_type.items()): 74 | setup.raman_model = raman_model 75 | setup.self_steepening = self_steepening 76 | solver = gnlse.GNLSE(setup) 77 | solution = solver.run() 78 | 79 | plt.subplot(2, count, i + 1) 80 | plt.title(name) 81 | gnlse.plot_wavelength_vs_distance(solution, WL_range=[400, 1400]) 82 | 83 | plt.subplot(2, count, i + 1 + count) 84 | gnlse.plot_delay_vs_distance(solution, time_range=[-.25, .25]) 85 | 86 | plt.tight_layout() 87 | plt.show() 88 | -------------------------------------------------------------------------------- /examples/test_Dudley.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example of supercontinuum generation in anomalous dispersion regime at 3 | a central wavelength of 835 nm in a 15 centimeter long fiber. 4 | Data from J. M. Dudley, G. Genty, and S. Coen, Rev. Mod. Phys., vol. 78, no. 4, 5 | pp. 1135–1184, 2006. 6 | 7 | The python code based on MATLAB code published in 8 | 'Supercontinuum Generation in Optical Fibers' 9 | by J. M. Dudley and J. R. Taylor, available at http://scgbook.info/. 10 | """ 11 | 12 | import numpy as np 13 | import matplotlib.pyplot as plt 14 | 15 | import gnlse 16 | 17 | 18 | if __name__ == '__main__': 19 | setup = gnlse.GNLSESetup() 20 | 21 | # Numerical parameters 22 | setup.resolution = 2**14 23 | setup.time_window = 12.5 # ps 24 | setup.z_saves = 200 25 | 26 | # Physical parameters 27 | setup.wavelength = 835 # nm 28 | setup.fiber_length = 0.15 # m 29 | setup.nonlinearity = 0.11 # 1/W/m 30 | setup.raman_model = gnlse.raman_blowwood 31 | setup.self_steepening = True 32 | 33 | # The dispersion model is built from a Taylor expansion with coefficients 34 | # given below. 35 | loss = 0 36 | betas = np.array([ 37 | -11.830e-3, 8.1038e-5, -9.5205e-8, 2.0737e-10, -5.3943e-13, 1.3486e-15, 38 | -2.5495e-18, 3.0524e-21, -1.7140e-24 39 | ]) 40 | setup.dispersion_model = gnlse.DispersionFiberFromTaylor(loss, betas) 41 | 42 | # Input pulse parameters 43 | peak_power = 10000 # W 44 | duration = 0.050 # ps 45 | 46 | # This example extends the original code with additional simulations for 47 | pulse_models = [ 48 | gnlse.SechEnvelope(peak_power, duration), 49 | gnlse.GaussianEnvelope(peak_power, duration), 50 | gnlse.LorentzianEnvelope(peak_power, duration) 51 | ] 52 | 53 | count = len(pulse_models) 54 | plt.figure(figsize=(14, 8), facecolor='w', edgecolor='k') 55 | for i, pulse_model in enumerate(pulse_models): 56 | print('%s...' % pulse_model.name) 57 | 58 | setup.pulse_model = pulse_model 59 | solver = gnlse.GNLSE(setup) 60 | solution = solver.run() 61 | 62 | plt.subplot(2, count, i + 1) 63 | plt.title(pulse_model.name) 64 | gnlse.plot_wavelength_vs_distance(solution, WL_range=[400, 1400]) 65 | 66 | plt.subplot(2, count, i + 1 + count) 67 | gnlse.plot_delay_vs_distance(solution, time_range=[-0.5, 5]) 68 | 69 | plt.tight_layout() 70 | plt.show() 71 | -------------------------------------------------------------------------------- /examples/test_dispersion.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example: Example of supercontinuum generation in anomalous dispersion regime 3 | at a central wavelength of 835 nm in a 15 centimeter long fiber. 4 | Comparision of results obtained with two dispersion input: 5 | 1. dispersion calculated from Taylor expansion 6 | 2. dispersion calculated from effective refractive indicies 7 | 8 | The python code based on MATLAB code published in 9 | 'Supercontinuum Generation in Optical Fibers' 10 | by J. M. Dudley and J. R. Taylor, available at http://scgbook.info/. 11 | """ 12 | 13 | import numpy as np 14 | import matplotlib.pyplot as plt 15 | 16 | import gnlse 17 | 18 | import os 19 | 20 | if __name__ == '__main__': 21 | setup = gnlse.GNLSESetup() 22 | 23 | # Numerical parameters 24 | setup.resolution = 2**14 25 | setup.time_window = 12.5 # ps 26 | setup.z_saves = 200 27 | 28 | # Physical parameters 29 | setup.wavelength = 835 # nm 30 | setup.fiber_length = 0.15 # m 31 | setup.nonlinearity = 0.0 # 1/W/m 32 | setup.raman_model = gnlse.raman_blowwood 33 | setup.self_steepening = True 34 | 35 | # The dispersion model is built from a Taylor expansion with coefficients 36 | # given below. 37 | loss = 0 38 | betas = np.array([-0.024948815481502, 8.875391917212998e-05, 39 | -9.247462376518329e-08, 1.508210856829677e-10]) 40 | 41 | # Input pulse parameters 42 | power = 10000 43 | # pulse duration [ps] 44 | tfwhm = 0.05 45 | # hyperbolic secant 46 | setup.pulse_model = gnlse.SechEnvelope(power, tfwhm) 47 | 48 | # Type of dyspersion operator: build from interpolation of given neffs 49 | # read mat file for neffs 50 | mat_path = os.path.join(os.path.dirname(__file__), '..', 51 | 'data', 'neff_pcf.mat') 52 | mat = gnlse.read_mat(mat_path) 53 | # neffs 54 | neff = mat['neff'][:, 1] 55 | # wavelengths in nm 56 | lambdas = mat['neff'][:, 0] * 1e9 57 | 58 | # Visualization 59 | ########################################################################### 60 | 61 | # Set type of dispersion function 62 | simulation_type = { 63 | 'Results for Taylor expansion': gnlse.DispersionFiberFromTaylor( 64 | loss, betas), 65 | 'Results for interpolation': gnlse.DispersionFiberFromInterpolation( 66 | loss, neff, lambdas, setup.wavelength) 67 | } 68 | 69 | count = len(simulation_type) 70 | plt.figure(figsize=(15, 7), facecolor='w', edgecolor='k') 71 | for (i, (name, dispersion_model)) in enumerate(simulation_type.items()): 72 | setup.dispersion_model = dispersion_model 73 | solver = gnlse.GNLSE(setup) 74 | solution = solver.run() 75 | 76 | plt.subplot(2, count, i + 1) 77 | plt.title(name) 78 | gnlse.plot_wavelength_vs_distance(solution, WL_range=[400, 1400]) 79 | 80 | plt.subplot(2, count, i + 1 + count) 81 | gnlse.plot_delay_vs_distance(solution, time_range=[-.5, 5]) 82 | 83 | plt.tight_layout() 84 | plt.show() 85 | -------------------------------------------------------------------------------- /examples/test_gvd.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example: group velocity dispersion on distance of 4 dispersion length 3 | for gaussian pulse as in Chapter 3 of Nonlinear Fiber Optics. 4 | 5 | Interface based on MATLAB code published in 6 | 'Supercontinuum Generation in Optical Fibers' 7 | by J. M. Dudley and J. R. Taylor, available at http://scgbook.info/. 8 | """ 9 | 10 | import numpy as np 11 | import gnlse 12 | import matplotlib.pyplot as plt 13 | 14 | 15 | if __name__ == '__main__': 16 | setup = gnlse.gnlse.GNLSESetup() 17 | 18 | # Numerical parameters 19 | ########################################################################### 20 | # number of grid time points 21 | setup.resolution = 2**13 22 | # time window [ps] 23 | setup.time_window = 12.5 24 | # number of distance points to save 25 | setup.z_saves = 200 26 | # relative tolerance for ode solver 27 | setup.rtol = 1e-6 28 | # absoulte tolerance for ode solver 29 | setup.atol = 1e-6 30 | 31 | # Physical parameters 32 | ########################################################################### 33 | # Central wavelength [nm] 34 | setup.wavelength = 835 35 | # Nonlinear coefficient [1/W/m] 36 | setup.nonlinearity = 0. 37 | # Dispersion: derivatives of propagation constant at central wavelength 38 | # n derivatives of betas are in [ps^n/m] 39 | betas = np.array([-11.830e-3]) 40 | # Input pulse: pulse duration [ps] 41 | tFWHM = 0.050 42 | t0 = tFWHM / 2 / np.sqrt(np.log(2)) # for dispersive length calculations 43 | 44 | ########################################################################### 45 | # Dispersive length 46 | LD = t0 ** 2 / np.abs(betas[0]) 47 | # Input pulse: peak power [W] 48 | power = 1 # value can be choosen arbitrarily 49 | # Fiber length [m] 50 | setup.fiber_length = 4 * LD 51 | # Type of pulse: gaussian 52 | setup.pulse_model = gnlse.GaussianEnvelope(power, tFWHM) 53 | # Loss coefficient [dB/m] 54 | loss = 0 55 | # Type of dyspersion operator: build from Taylor expansion 56 | setup.dispersion_model = gnlse.DispersionFiberFromTaylor(loss, betas) 57 | 58 | # Type of Ramman scattering function: None (default) 59 | # Selftepening: not accounted 60 | setup.self_steepening = False 61 | 62 | # Simulation 63 | ########################################################################### 64 | solver = gnlse.gnlse.GNLSE(setup) 65 | solution = solver.run() 66 | 67 | # Visualization 68 | ########################################################################### 69 | 70 | plt.subplot(1, 2, 1) 71 | gnlse.plot_wavelength_vs_distance(solution, WL_range=[780, 900]) 72 | plt.subplot(1, 2, 2) 73 | gnlse.plot_delay_vs_distance(solution, time_range=[-.5, .5]) 74 | 75 | plt.show() 76 | -------------------------------------------------------------------------------- /examples/test_import_export.py: -------------------------------------------------------------------------------- 1 | """ 2 | Runs a simple simulation, saves it to disk and loads it back for plotting. 3 | """ 4 | 5 | import os 6 | import gnlse 7 | 8 | if __name__ == '__main__': 9 | setup = gnlse.GNLSESetup() 10 | setup.resolution = 2**13 11 | setup.time_window = 12.5 # ps 12 | setup.z_saves = 200 13 | setup.fiber_length = 0.15 # m 14 | setup.wavelength = 835 # nm 15 | setup.pulse_model = gnlse.GaussianEnvelope(1, 0.1) 16 | 17 | solver = gnlse.GNLSE(setup) 18 | solution = solver.run() 19 | 20 | path = 'test.mat' 21 | 22 | solution.to_file(path) 23 | solution = gnlse.Solution() 24 | solution.from_file(path) 25 | 26 | gnlse.quick_plot(solution) 27 | 28 | os.remove(path) 29 | -------------------------------------------------------------------------------- /examples/test_nonlinearity.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example: Example of soliton fission for a 3 | central wavelength of 835 nm in a 15 centimeter long fiber. 4 | Comparision of results obtained with two simulation input: 5 | 1. dispersion calculated from Taylor expansion, 6 | and gamma value procided only for pump wavelength 7 | 2. dispersion calculated from Taylor expansion, 8 | and gamma calculated from effective mode areas 9 | with modification proposed by J. Lægsgaard [2]. 10 | 11 | 12 | [1] "Supercontinuum Generation in Optical Fibers," 13 | J. M. Dudley and J. R. Taylor, 2010. 14 | [2] "Mode profile dispersion in the generalized 15 | nonlinear Schrödinger equation," J. Lægsgaard, 16 | Opt. Express 15, 16110-16123, 2007. 17 | """ 18 | 19 | import matplotlib.pyplot as plt 20 | import numpy as np 21 | import os 22 | 23 | import gnlse 24 | 25 | if __name__ == '__main__': 26 | setup = gnlse.GNLSESetup() 27 | 28 | # Numerical parameters 29 | setup.resolution = 2**14 30 | setup.time_window = 12.5 # ps 31 | setup.z_saves = 200 32 | 33 | # Physical parameters 34 | setup.wavelength = 835 # nm 35 | w0 = (2.0 * np.pi * gnlse.common.c) / setup.wavelength # 1/ps = THz 36 | setup.fiber_length = 0.15 # m 37 | setup.raman_model = gnlse.raman_blowwood 38 | setup.self_steepening = True 39 | 40 | # Input pulse parameters 41 | power = 1000 42 | # pulse duration [ps] 43 | tfwhm = 0.05 44 | # hyperbolic secant 45 | setup.pulse_model = gnlse.SechEnvelope(power, tfwhm) 46 | 47 | # The dispersion model is built from a Taylor expansion with coefficients 48 | # given below. 49 | loss = 0 50 | betas = np.array([-0.024948815481502, 8.875391917212998e-05, 51 | -9.247462376518329e-08, 1.508210856829677e-10]) 52 | setup.dispersion_model = gnlse.DispersionFiberFromTaylor(loss, betas) 53 | # parameters for calculating the nonlinearity 54 | n2 = 2.7e-20 # m^2/W 55 | Aeff0 = 1.78e-12 # 1/m^2 56 | gamma = n2 * w0 / gnlse.common.c / 1e-9 / Aeff0 # 1/W/m 57 | 58 | # read mat file for neffs to cover interpolation example 59 | mat_path = os.path.join(os.path.dirname(__file__), '..', 60 | 'data', 'neff_pcf.mat') 61 | mat = gnlse.read_mat(mat_path) 62 | 63 | # neffs 64 | neff = mat['neff'][:, 1] 65 | # wavelengths in nm 66 | lambdas = mat['neff'][:, 0] * 1e9 67 | # efective mode area in m^2 68 | Aeff = mat['neff'][:, 2] * 1e-12 69 | 70 | # This example extends the original code with additional simulations for 71 | nonlinearity_setups = [ 72 | ["Scalar $\\gamma$", 73 | gnlse.DispersionFiberFromTaylor(loss, betas), 74 | gamma], 75 | ["Frequency dependent $\\gamma$", 76 | gnlse.DispersionFiberFromTaylor(loss, betas), 77 | gnlse.NonlinearityFromEffectiveArea( 78 | neff, Aeff, lambdas, setup.wavelength, 79 | n2=n2, neff_max=10)] 80 | ] 81 | 82 | count = len(nonlinearity_setups) 83 | plt.figure(figsize=(10, 8), facecolor='w', edgecolor='k') 84 | for i, model in enumerate(nonlinearity_setups): 85 | setup.dispersion = model[1] 86 | setup.nonlinearity = model[2] 87 | solver = gnlse.GNLSE(setup) 88 | solution = solver.run() 89 | 90 | plt.subplot(2, count, i + 1) 91 | plt.title(model[0]) 92 | gnlse.plot_wavelength_vs_distance(solution, WL_range=[700, 1000]) 93 | plt.subplot(2, count, i + 1 + count) 94 | gnlse.plot_delay_vs_distance(solution, time_range=[-.5, .5]) 95 | 96 | plt.tight_layout() 97 | plt.show() 98 | -------------------------------------------------------------------------------- /examples/test_raman.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example of dispersive wave generation in anomalus dispersion regime at 3 | a central wavelength of 835 nm in a 15 centimeter long photonic crystal fiber. 4 | 5 | Interface based on MATLAB code published in 6 | 'Supercontinuum Generation in Optical Fibers' 7 | by J. M. Dudley and J. R. Taylor, available at http://scgbook.info/. 8 | """ 9 | 10 | import numpy as np 11 | import matplotlib.pyplot as plt 12 | 13 | import gnlse 14 | 15 | 16 | if __name__ == '__main__': 17 | setup = gnlse.GNLSESetup() 18 | 19 | # Numerical parameters 20 | setup.resolution = 2**14 21 | setup.time_window = 12.5 # ps 22 | setup.z_saves = 400 23 | 24 | # Input pulse parameters 25 | peak_power = 10000 # W 26 | duration = 0.050284 # ps 27 | 28 | # Physical parameters 29 | setup.wavelength = 835 # nm 30 | setup.fiber_length = 0.15 # m 31 | setup.nonlinearity = 0.11 # 1/W/m 32 | setup.pulse_model = gnlse.SechEnvelope(peak_power, duration) 33 | setup.self_steepening = True 34 | 35 | # The dispersion model is built from a Taylor expansion with coefficients 36 | # given below. 37 | loss = 0 38 | betas = np.array([ 39 | -11.830e-3, 8.1038e-5, -9.5205e-8, 2.0737e-10, -5.3943e-13, 1.3486e-15, 40 | -2.5495e-18, 3.0524e-21, -1.7140e-24 41 | ]) 42 | setup.dispersion_model = gnlse.DispersionFiberFromTaylor(loss, betas) 43 | 44 | # This example extends the original code with additional simulations for 45 | # three types of models of Raman response and no raman scattering case 46 | raman_models = { 47 | 'Blow-Wood': gnlse.raman_blowwood, 48 | 'Hollenbeck-Cantrell': gnlse.raman_holltrell, 49 | 'Lin-Agrawal': gnlse.raman_linagrawal, 50 | 'No scattering': None 51 | } 52 | 53 | count = len(raman_models) 54 | plt.figure(figsize=(20, 10), facecolor='w', edgecolor='k') 55 | for (i, (name, raman_model)) in enumerate(raman_models.items()): 56 | setup.raman_model = raman_model 57 | solver = gnlse.GNLSE(setup) 58 | solution = solver.run() 59 | 60 | plt.subplot(2, count, i + 1) 61 | plt.title(name) 62 | gnlse.plot_wavelength_vs_distance(solution, WL_range=[500, 1250]) 63 | 64 | plt.subplot(2, count, i + 1 + count) 65 | gnlse.plot_delay_vs_distance(solution, time_range=[-0.5, 4]) 66 | plt.tight_layout() 67 | plt.show() 68 | -------------------------------------------------------------------------------- /examples/test_spm+gvd.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example: Forming of 1st order soliton pulse (sech) from gaussian input 3 | by interaction of group velocity dispersion and self-phase modulation. 4 | 5 | Interface based on MATLAB code published in 6 | 'Supercontinuum Generation in Optical Fibers' 7 | by J. M. Dudley and J. R. Taylor, available at http://scgbook.info/. 8 | """ 9 | 10 | import numpy as np 11 | import gnlse 12 | import matplotlib.pyplot as plt 13 | 14 | 15 | if __name__ == '__main__': 16 | setup = gnlse.gnlse.GNLSESetup() 17 | 18 | # Numerical parameters 19 | ########################################################################### 20 | # number of grid time points 21 | setup.resolution = 2**13 22 | # time window [ps] 23 | setup.time_window = 12.5 24 | # number of distance points to save 25 | setup.z_saves = 200 26 | # relative tolerance for ode solver 27 | setup.rtol = 1e-6 28 | # absoulte tolerance for ode solver 29 | setup.atol = 1e-6 30 | 31 | # Physical parameters 32 | ########################################################################### 33 | # Central wavelength [nm] 34 | setup.wavelength = 835 35 | # Nonlinear coefficient [1/W/m] 36 | setup.nonlinearity = 0.11 37 | # Dispersion: derivatives of propagation constant at central wavelength 38 | # n derivatives of betas are in [ps^n/m] 39 | betas = np.array([-11.830e-3]) 40 | # Input pulse: pulse duration [ps] 41 | tFWHM = 0.050 42 | t0 = tFWHM / 2 / np.sqrt(np.log(2)) # for dispersive length calculation 43 | 44 | # 3rd order soliton conditions 45 | ########################################################################### 46 | # Dispersive length 47 | LD = t0 ** 2 / np.abs(betas[0]) 48 | # Non-linear length for 1st order soliton 49 | LNL = LD / (1 ** 2) 50 | # Input pulse: peak power [W] 51 | power = 1 / (LNL * setup.nonlinearity) 52 | # Fiber length [m] 53 | setup.fiber_length = 10 * LD 54 | # Type of pulse: gaussian 55 | setup.pulse_model = gnlse.GaussianEnvelope(power, tFWHM) 56 | # Loss coefficient [dB/m] 57 | loss = 0 58 | # Type of dyspersion operator: build from Taylor expansion 59 | setup.dispersion_model = gnlse.DispersionFiberFromTaylor(loss, betas) 60 | 61 | # Type of Ramman scattering function: None (default) 62 | # Selftepening: not accounted 63 | setup.self_steepening = False 64 | 65 | # Simulation 66 | ########################################################################### 67 | solver = gnlse.gnlse.GNLSE(setup) 68 | solution = solver.run() 69 | 70 | # Visualization 71 | ########################################################################### 72 | plt.figure(figsize=(15, 7)) 73 | 74 | plt.subplot(1, 2, 1) 75 | gnlse.plot_wavelength_vs_distance(solution, WL_range=[780, 900]) 76 | plt.subplot(1, 2, 2) 77 | gnlse.plot_delay_vs_distance(solution, time_range=[-.5, .5]) 78 | 79 | plt.show() 80 | -------------------------------------------------------------------------------- /examples/test_spm.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example: self-phase modulation on 3.5*pi nonlinear length 3 | for gaussian pulse as in Chapter 4 of Nonlinear Fiber Optics. 4 | 5 | Interface based on MATLAB code published in 6 | 'Supercontinuum Generation in Optical Fibers' 7 | by J. M. Dudley and J. R. Taylor, available at http://scgbook.info/. 8 | """ 9 | 10 | import numpy as np 11 | import gnlse 12 | import matplotlib.pyplot as plt 13 | 14 | 15 | if __name__ == '__main__': 16 | setup = gnlse.gnlse.GNLSESetup() 17 | 18 | # Numerical parameters 19 | ########################################################################### 20 | # number of grid time points 21 | setup.resolution = 2**13 22 | # time window [ps] 23 | setup.time_window = 12.5 24 | # number of distance points to save 25 | setup.z_saves = 200 26 | # relative tolerance for ode solver 27 | setup.rtol = 1e-6 28 | # absoulte tolerance for ode solver 29 | setup.atol = 1e-6 30 | 31 | # Physical parameters 32 | ########################################################################### 33 | # Central wavelength [nm] 34 | setup.wavelength = 835 35 | # Nonlinear coefficient [1/W/m] 36 | setup.nonlinearity = 0.11 37 | # Dispersion: derivatives of propagation constant at central wavelength 38 | # n derivatives of betas are in [ps^n/m] 39 | betas = np.array([0]) 40 | # Input pulse: pulse duration [ps] 41 | tFWHM = 0.050 42 | 43 | ########################################################################### 44 | # Input pulse: peak power [W] 45 | power = 1 # value can be choosen arbitrarily 46 | # Non-linear length for given nonlinearity and power 47 | LNL = 1 / (power * setup.nonlinearity) 48 | # Fiber length [m] 49 | setup.fiber_length = 3.5 * np.pi * LNL 50 | # Type of pulse: gaussian 51 | setup.pulse_model = gnlse.GaussianEnvelope(power, tFWHM) 52 | # Loss coefficient [dB/m] 53 | loss = 0 54 | # Type of dyspersion operator: build from Taylor expansion 55 | setup.dispersion_model = gnlse.DispersionFiberFromTaylor(loss, betas) 56 | 57 | # Type of Ramman scattering function: None (default) 58 | # Selftepening: not accounted 59 | setup.self_steepening = False 60 | 61 | # Simulation 62 | ########################################################################### 63 | solver = gnlse.gnlse.GNLSE(setup) 64 | solution = solver.run() 65 | 66 | # Visualization 67 | ########################################################################### 68 | plt.subplot(1, 2, 1) 69 | gnlse.plot_wavelength_vs_distance(solution, WL_range=[400, 1400]) 70 | plt.subplot(1, 2, 2) 71 | gnlse.plot_delay_vs_distance(solution, time_range=[-.25, .25]) 72 | 73 | plt.show() 74 | -------------------------------------------------------------------------------- /examples/test_wl_del_freq.py: -------------------------------------------------------------------------------- 1 | """ 2 | This example presents potential of new plot functions. 3 | Especially: 4 | -different scales(linear and logarithmic) 5 | -different x arguments(delay, frequency, wavelength) 6 | -color map as argument of plot function 7 | -slice plot for chosen z(propagation) distances 8 | or z=0 and z=end if no specific z were chosen. 9 | Data used in this example are taken from test_Dudley.py file from examples. 10 | """ 11 | 12 | import numpy as np 13 | import matplotlib.pyplot as plt 14 | 15 | import gnlse 16 | 17 | if __name__ == '__main__': 18 | setup = gnlse.GNLSESetup() 19 | 20 | # Numerical parameters 21 | setup.resolution = 2**14 22 | setup.time_window = 12.5 # ps 23 | setup.z_saves = 200 24 | 25 | # Physical parameters 26 | setup.wavelength = 835 # nm 27 | setup.fiber_length = 0.15 # m 28 | setup.nonlinearity = 0.11 # 1/W/m 29 | setup.raman_model = gnlse.raman_blowwood 30 | setup.self_steepening = True 31 | 32 | # The dispersion model is built from a Taylor expansion with coefficients 33 | # given below. 34 | loss = 0 35 | betas = np.array([ 36 | -11.830e-3, 8.1038e-5, -9.5205e-8, 2.0737e-10, -5.3943e-13, 1.3486e-15, 37 | -2.5495e-18, 3.0524e-21, -1.7140e-24 38 | ]) 39 | setup.dispersion_model = gnlse.DispersionFiberFromTaylor(loss, betas) 40 | 41 | # Input pulse parameters 42 | power = 10000 43 | # pulse duration [ps] 44 | tfwhm = 0.05 45 | # hyperbolic secant 46 | setup.pulse_model = gnlse.SechEnvelope(power, tfwhm) 47 | solver = gnlse.GNLSE(setup) 48 | solution = solver.run() 49 | 50 | plt.figure(figsize=(14, 8), facecolor='w', edgecolor='k') 51 | 52 | plt.subplot(4, 3, 1) 53 | gnlse.plot_delay_vs_distance(solution, time_range=[-.5, 5], cmap="jet") 54 | 55 | plt.subplot(4, 3, 2) 56 | gnlse.plot_frequency_vs_distance(solution, frequency_range=[-300, 200], 57 | cmap="plasma") 58 | 59 | plt.subplot(4, 3, 3) 60 | gnlse.plot_wavelength_vs_distance(solution, WL_range=[400, 1400]) 61 | 62 | plt.subplot(4, 3, 4) 63 | gnlse.plot_delay_vs_distance_logarithmic(solution, time_range=[-.5, 5], 64 | cmap="jet") 65 | 66 | plt.subplot(4, 3, 5) 67 | gnlse.plot_frequency_vs_distance_logarithmic(solution, 68 | frequency_range=[-300, 200], 69 | cmap="plasma") 70 | 71 | plt.subplot(4, 3, 6) 72 | gnlse.plot_wavelength_vs_distance_logarithmic(solution, 73 | WL_range=[400, 1400]) 74 | 75 | plt.subplot(4, 3, 7) 76 | gnlse.plot_delay_for_distance_slice(solution, time_range=[-.5, 5]) 77 | 78 | plt.subplot(4, 3, 8) 79 | gnlse.plot_frequency_for_distance_slice(solution, 80 | frequency_range=[-300, 200]) 81 | 82 | plt.subplot(4, 3, 9) 83 | gnlse.plot_wavelength_for_distance_slice(solution, WL_range=[400, 1400]) 84 | 85 | plt.subplot(4, 3, 10) 86 | gnlse.plot_delay_for_distance_slice_logarithmic( 87 | solution, time_range=[-.5, 5]) 88 | 89 | plt.subplot(4, 3, 11) 90 | gnlse.plot_frequency_for_distance_slice_logarithmic( 91 | solution, frequency_range=[-300, 200]) 92 | 93 | plt.subplot(4, 3, 12) 94 | gnlse.plot_wavelength_for_distance_slice_logarithmic(solution, 95 | WL_range=[400, 1400]) 96 | 97 | plt.tight_layout() 98 | plt.show() 99 | -------------------------------------------------------------------------------- /gnlse/__init__.py: -------------------------------------------------------------------------------- 1 | from gnlse.dispersion import (DispersionFiberFromTaylor, 2 | DispersionFiberFromInterpolation) 3 | from gnlse.envelopes import (SechEnvelope, GaussianEnvelope, 4 | LorentzianEnvelope, CWEnvelope) 5 | from gnlse.gnlse import GNLSESetup, Solution, GNLSE 6 | from gnlse.import_export import read_mat, write_mat 7 | from gnlse.nonlinearity import NonlinearityFromEffectiveArea 8 | from gnlse.raman_response import (raman_blowwood, raman_holltrell, 9 | raman_linagrawal) 10 | from gnlse.visualization import ( 11 | plot_delay_vs_distance, 12 | plot_delay_vs_distance_logarithmic, 13 | plot_delay_for_distance_slice, 14 | plot_delay_for_distance_slice_logarithmic, 15 | plot_frequency_vs_distance, 16 | plot_frequency_vs_distance_logarithmic, 17 | plot_frequency_for_distance_slice, 18 | plot_frequency_for_distance_slice_logarithmic, 19 | plot_wavelength_vs_distance, 20 | plot_wavelength_vs_distance_logarithmic, 21 | plot_wavelength_for_distance_slice, 22 | plot_wavelength_for_distance_slice_logarithmic, 23 | quick_plot) 24 | 25 | __all__ = [ 26 | 'DispersionFiberFromTaylor', 'DispersionFiberFromInterpolation', 27 | 'SechEnvelope', 'GaussianEnvelope', 'LorentzianEnvelope', 'GNLSESetup', 28 | 'GNLSE', 'Solution', 'read_mat', 'write_mat', 'raman_blowwood', 29 | 'raman_holltrell', 'raman_linagrawal', 30 | 'plot_delay_vs_distance', 31 | 'plot_delay_vs_distance_logarithmic', 32 | 'plot_delay_for_distance_slice', 33 | 'plot_delay_for_distance_slice_logarithmic', 34 | 'plot_frequency_vs_distance', 35 | 'plot_frequency_vs_distance_logarithmic', 36 | 'plot_frequency_for_distance_slice', 37 | 'plot_frequency_for_distance_slice_logarithmic', 38 | 'plot_wavelength_vs_distance', 39 | 'plot_wavelength_vs_distance_logarithmic', 40 | 'plot_wavelength_for_distance_slice', 41 | 'plot_wavelength_for_distance_slice_logarithmic', 42 | 'quick_plot', 'NonlinearityFromEffectiveArea', 'CWEnvelope' 43 | ] 44 | -------------------------------------------------------------------------------- /gnlse/common.py: -------------------------------------------------------------------------------- 1 | pi = 3.1415926535897932384626433 2 | 3 | c = 299792.458 # Speed of light in vacuum [nm/ps] 4 | hplanck = 6.62607015e-34 # Planck constant [J.s] 5 | hbar = hplanck / 2 / pi # Reduced Planck constant [J.s] 6 | -------------------------------------------------------------------------------- /gnlse/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 | 13 | 14 | class Dispersion(object): 15 | """ 16 | Attributes 17 | ----------- 18 | loss : float 19 | Loss factor [dB/m] 20 | """ 21 | 22 | def __init__(self, loss): 23 | self.loss = loss 24 | 25 | def D(V): 26 | """Calculate linear dispersion operator 27 | for given frequency grid created during simulation 28 | 29 | Parameters 30 | ---------- 31 | V : ndarray, (N) 32 | Frequency vector 33 | 34 | Returns 35 | ------- 36 | ndarray, (N) 37 | Linear dispersion operator in frequency domain 38 | """ 39 | 40 | raise NotImplementedError('Dispersion not implemented') 41 | 42 | def calc_loss(self): 43 | """Calculate damping 44 | for given frequency grid created during simulation 45 | """ 46 | self.alpha = np.log(10**(self.loss / 10)) 47 | 48 | 49 | class DispersionFiberFromTaylor(Dispersion): 50 | """Calculates the dispersion in frequency domain 51 | 52 | Attributes 53 | ---------- 54 | loss : float 55 | Loss factor [dB/m] 56 | betas : ndarray (N) 57 | Derivatives of constant propagations at pump wavelength 58 | [ps^2/m, ..., ps^n/m] 59 | """ 60 | 61 | def __init__(self, loss, betas): 62 | self.loss = loss 63 | self.betas = betas 64 | 65 | def D(self, V): 66 | # Damping 67 | self.calc_loss() 68 | # Taylor series for subsequent derivatives 69 | # of constant propagation 70 | B = sum(beta / np.math.factorial(i + 2) * V**(i + 2) 71 | for i, beta in enumerate(self.betas)) 72 | L = 1j * B - self.alpha / 2 73 | return L 74 | 75 | 76 | class DispersionFiberFromInterpolation(Dispersion): 77 | """Calculates the propagation function in frequency domain, using 78 | the extrapolation method based on delivered refractive indexes 79 | and corresponding wavelengths. The returned value is a vector 80 | of dispersion operator. 81 | 82 | Attributes 83 | ----------- 84 | loss : float 85 | Loss factor [dB/m] 86 | neff : ndarray (N) 87 | Effective refractive index 88 | lambdas : ndarray (N) 89 | Wavelength corresponding to refractive index 90 | central_wavelength : float 91 | Wavelength corresponding to pump wavelength in nm 92 | """ 93 | 94 | def __init__(self, loss, neff, lambdas, central_wavelength): 95 | # Loss factor in dB/m 96 | self.loss = loss 97 | # refractive indices 98 | self.neff = neff 99 | # wavelengths for neffs in [nm] 100 | self.lambdas = lambdas 101 | # Central frequency in [1/ps = THz] 102 | self.w0 = (2.0 * np.pi * c) / central_wavelength 103 | 104 | def D(self, V): 105 | # Central frequency [1/ps = THz] 106 | omega = 2 * np.pi * c / self.lambdas 107 | dOmega = V[1] - V[0] 108 | Bet = self.neff * omega / c * 1e9 # [1/m] 109 | 110 | # Extrapolate betas for a frequency vector 111 | fun_interpolation = interpolate.interp1d(omega, 112 | Bet, 113 | kind='cubic', 114 | fill_value="extrapolate") 115 | 116 | B = fun_interpolation(V + self.w0) 117 | # Propagation constant at central frequency [1/m] 118 | B0 = fun_interpolation(self.w0) 119 | # Value of propagation at a lower end of interval [1/m] 120 | B0plus = fun_interpolation(self.w0 + dOmega) 121 | # Value of propagation at a higher end of interval [1/m] 122 | B0minus = fun_interpolation(self.w0 - dOmega) 123 | 124 | # Difference quotient, approximation of 125 | # derivative of a function at a point [ps/m] 126 | B1 = (B0plus - B0minus) / (2 * dOmega) 127 | 128 | # Damping 129 | self.calc_loss() 130 | 131 | # Linear dispersion operator 132 | L = 1j * (B - (B0 + B1 * V)) - self.alpha / 2 133 | return L 134 | -------------------------------------------------------------------------------- /gnlse/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 | 10 | 11 | class Envelope(object): 12 | def A(T): 13 | raise NotImplementedError() 14 | 15 | 16 | class SechEnvelope(Envelope): 17 | """Amplitude envelope of hyperbolic secant pulse. 18 | 19 | Attributes 20 | ---------- 21 | Pmax : float 22 | Peak power, [W]. 23 | FWHM : float 24 | Pulse duration Full-Width Half-Maximum. 25 | """ 26 | 27 | def __init__(self, Pmax, FWHM): 28 | self.name = 'Hyperbolic secant envelope' 29 | self.Pmax = Pmax 30 | self.FWHM = FWHM 31 | 32 | def A(self, T): 33 | """ 34 | 35 | Parameters 36 | ---------- 37 | T : ndarray, (n, ) 38 | Time vector 39 | 40 | Returns 41 | ------- 42 | ndarray, (n, ) 43 | Amplitude envelope of hyperbolic secant pulse in time. 44 | """ 45 | m = 2 * np.log(1 + np.sqrt(2)) 46 | return np.sqrt(self.Pmax) * 2 / (np.exp(m * T / self.FWHM) + 47 | np.exp(-m * T / self.FWHM)) 48 | 49 | 50 | class GaussianEnvelope(Envelope): 51 | """Amplitude envelope of gaussian pulse. 52 | 53 | Attributes 54 | ---------- 55 | Pmax : float 56 | Peak power [W]. 57 | FWHM : float 58 | Pulse duration Full-Width Half-Maximum. 59 | """ 60 | 61 | def __init__(self, Pmax, FWHM): 62 | self.name = 'Gaussian envelope' 63 | self.Pmax = Pmax 64 | self.FWHM = FWHM 65 | 66 | def A(self, T): 67 | """ 68 | 69 | Parameters 70 | ---------- 71 | T : ndarray, (n, ) 72 | Time vector. 73 | 74 | Returns 75 | ------- 76 | ndarray, (n, ) 77 | Amplitude envelope of gaussian pulse in time. 78 | """ 79 | m = 4 * np.log(2) 80 | return np.sqrt(self.Pmax) * np.exp(-m * .5 * T**2 / self.FWHM**2) 81 | 82 | 83 | class LorentzianEnvelope(Envelope): 84 | """Amplitude envelope of lorentzian pulse. 85 | 86 | Attributes 87 | ---------- 88 | Pmax : float 89 | Peak power [W]. 90 | FWHM : float 91 | Pulse duration Full-Width Half-Maximum. 92 | """ 93 | 94 | def __init__(self, Pmax, FWHM): 95 | self.name = 'Lorentzian envelope' 96 | self.Pmax = Pmax 97 | self.FWHM = FWHM 98 | 99 | def A(self, T): 100 | """ 101 | 102 | Parameters 103 | ---------- 104 | T : ndarray, (n, ) 105 | Time vector. 106 | 107 | Returns 108 | ------- 109 | ndarray, (n, ) 110 | Amplitude envelope of lorentzian pulse in time. 111 | """ 112 | m = 2 * np.sqrt(np.sqrt(2) - 1) 113 | return np.sqrt(self.Pmax) / (1 + (m * T / self.FWHM)**2) 114 | 115 | 116 | class CWEnvelope(Envelope): 117 | """Amplitude envelope of continious wave 118 | with or without some temporal noise. 119 | 120 | Attributes 121 | ---------- 122 | Pmax : float 123 | Peak power [W]. 124 | Pn : float, optional 125 | Peak power for noise [W]. 126 | """ 127 | 128 | def __init__(self, Pmax, Pn=0): 129 | self.name = 'Continious Wave' 130 | self.Pmax = Pmax 131 | self.Pn = Pn 132 | 133 | def A(self, T): 134 | """ 135 | 136 | Parameters 137 | ---------- 138 | T : ndarray, (n, ) 139 | Time vector. 140 | 141 | Returns 142 | ------- 143 | ndarray, (n, ) 144 | Amplitude envelope of continious wave in time. 145 | """ 146 | cw = np.fft.ifft(np.sqrt(self.Pmax) * np.ones(np.size(T))) 147 | noise = 0 148 | if self.Pn: 149 | noise = np.sqrt(self.Pn 150 | ) * np.exp( 151 | 1j * 2 * np.pi * np.random.rand(np.size(T))) 152 | return np.fft.fft(cw + noise) 153 | -------------------------------------------------------------------------------- /gnlse/gnlse.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import scipy.integrate 3 | import pyfftw 4 | import tqdm 5 | 6 | from gnlse.common import c 7 | from gnlse.import_export import write_mat, read_mat 8 | 9 | 10 | class GNLSESetup: 11 | """ 12 | Model inputs for the ``GNLSE`` class. 13 | 14 | Attributes 15 | ---------- 16 | resolution : int 17 | Number of points on the computational grid. Determines time resolution 18 | and bandwidth. Avoid numbers with large prime factors. 19 | time_window : float [ps] 20 | Width of the time window. 21 | wavelength : float [nm] 22 | Central wavelength of the input pulse. 23 | fiber_length : float [m] 24 | Length of the simulated optical fiber. 25 | z_saves : int 26 | Number of snapshots to save along the fiber. Larger numbers require 27 | more memory to store the result. 28 | nonlinearity : float [1/W/m] 29 | Effective nonlinearity. 30 | pulse_model : Envelope 31 | Input pulse envelope model. 32 | dispersion_model : Dispersion, optional 33 | Fiber dispersion model or ``None`` to model a dispersionless fiber. 34 | raman_model : function, optional 35 | Raman scattering model or ``None`` if the effect is to be neglected. 36 | self_steepning : bool, optional 37 | Whether to include the effect of self-steepening. Disabled by default. 38 | rtol : float, optional 39 | Relative tolerance passed to the ODE solver. 40 | atol : float, optional 41 | Absolute tolerance passed to the ODE solver. 42 | method : str, optional 43 | Integration method passed to the ODE solver. 44 | """ 45 | 46 | def __init__(self): 47 | self.resolution = None 48 | self.time_window = None 49 | self.wavelength = None 50 | self.fiber_length = None 51 | 52 | self.z_saves = 200 53 | self.nonlinearity = 0 54 | self.pulse_model = None 55 | self.dispersion_model = None 56 | self.raman_model = None 57 | self.self_steepening = False 58 | 59 | self.rtol = 1e-3 60 | self.atol = 1e-4 61 | self.method = 'RK45' 62 | 63 | 64 | class Solution: 65 | """ 66 | Represents a solution to a GNLSE problem. 67 | 68 | Attributes 69 | ---------- 70 | t : ndarray, (n,) 71 | Time domain grid. 72 | W : ndarray, (n,) 73 | Absolute angular frequency grid. 74 | Z : ndarray (m,) 75 | Points at which intermediate steps were saved. 76 | At : ndarray, (n, m) 77 | Intermediate steps in the time domain. 78 | AW : ndarray, (n, m) 79 | Intermediate steps in the frequency domain. 80 | """ 81 | 82 | def __init__(self, t=None, W=None, w_0=None, Z=None, At=None, AW=None, 83 | Aty=None, AWy=None): 84 | self.t = t 85 | self.W = W 86 | self.w_0 = w_0 87 | self.Z = Z 88 | self.At = At 89 | self.AW = AW 90 | 91 | def to_file(self, path): 92 | """ 93 | Saves a solution to a file. 94 | 95 | Parameters 96 | ---------- 97 | path : str 98 | Path to file. 99 | """ 100 | 101 | data = {'t': self.t, 'W': self.W, 'Z': self.Z, 'At': self.At, 102 | 'AW': self.AW} 103 | write_mat(data, path) 104 | 105 | def from_file(self, path): 106 | """ 107 | Load a solution from file. 108 | 109 | Parameters 110 | ---------- 111 | path : str 112 | Path to file. 113 | """ 114 | 115 | data = read_mat(path) 116 | self.t = data['t'] 117 | self.W = data['W'] 118 | self.Z = data['Z'] 119 | self.At = data['At'] 120 | self.AW = data['AW'] 121 | 122 | 123 | class GNLSE: 124 | """ 125 | Models propagation of an optical pulse in a fiber by integrating 126 | the generalized non-linear Schrödinger equation. 127 | 128 | Attributes 129 | ---------- 130 | setup : GNLSESetup 131 | Model inputs in the form of a ``GNLSESetup`` object. 132 | """ 133 | 134 | def __init__(self, setup): 135 | if not isinstance(setup, GNLSESetup): 136 | raise TypeError("setup is not an instance of GNLSESetup") 137 | 138 | if setup.resolution is None: 139 | raise ValueError("'resolution' not set") 140 | if setup.time_window is None: 141 | raise ValueError("'time_window' not set") 142 | if setup.wavelength is None: 143 | raise ValueError("'wavelength' not set") 144 | if setup.fiber_length is None: 145 | raise ValueError("'fiber_length' not set") 146 | if setup.pulse_model is None: 147 | raise ValueError("'pulse_model' not set") 148 | 149 | # simulation parameters 150 | self.fiber_length = setup.fiber_length 151 | self.z_saves = setup.z_saves 152 | self.rtol = setup.rtol 153 | self.atol = setup.atol 154 | self.method = setup.method 155 | self.N = setup.resolution 156 | 157 | # Time domain grid 158 | self.t = np.linspace(-setup.time_window / 2, 159 | setup.time_window / 2, 160 | self.N) 161 | 162 | # Relative angular frequency grid 163 | self.V = 2 * np.pi * np.arange(-self.N / 2, 164 | self.N / 2 165 | ) / (self.N * (self.t[1] - self.t[0])) 166 | # Central angular frequency [10^12 rad] 167 | self.w_0 = (2.0 * np.pi * c) / setup.wavelength 168 | self.Omega = self.V + self.w_0 169 | 170 | # Absolute angular frequency grid 171 | if setup.self_steepening and np.abs(self.w_0) > np.finfo(float).eps: 172 | W = self.V + self.w_0 173 | else: 174 | W = np.full(self.V.shape, self.w_0) 175 | self.W = np.fft.fftshift(W) 176 | 177 | # Nonlinearity 178 | if hasattr(setup.nonlinearity, 'gamma'): 179 | # in case in of frequency dependent nonlinearity 180 | gamma, self.scale = setup.nonlinearity.gamma(self.V) 181 | self.gamma = gamma / self.w_0 182 | self.gamma = np.fft.fftshift(self.gamma) 183 | self.scale = np.fft.fftshift(self.scale) 184 | else: 185 | # in case in of direct introduced value 186 | self.gamma = setup.nonlinearity / self.w_0 187 | self.scale = 1 188 | 189 | # Raman scattering 190 | self.RW = None 191 | if setup.raman_model: 192 | self.fr, RT = setup.raman_model(self.t) 193 | if np.abs(self.fr) < np.finfo(float).eps: 194 | self.RW = None 195 | else: 196 | self.RW = self.N * np.fft.ifft( 197 | np.fft.fftshift(np.transpose(RT))) 198 | 199 | # Dispersion operator 200 | if setup.dispersion_model: 201 | self.D = setup.dispersion_model.D(self.V) 202 | else: 203 | self.D = np.zeros(self.V.shape) 204 | 205 | # Input pulse 206 | if hasattr(setup.pulse_model, 'A'): 207 | self.A = setup.pulse_model.A(self.t) 208 | else: 209 | self.A = setup.pulse_model 210 | 211 | def run(self): 212 | """ 213 | Solve one mode GNLSE equation described by the given 214 | ``GNLSESetup`` object. 215 | 216 | Returns 217 | ------- 218 | setup : Solution 219 | Simulation results in the form of a ``Solution`` object. 220 | """ 221 | dt = self.t[1] - self.t[0] 222 | self.D = np.fft.fftshift(self.D) 223 | x = pyfftw.empty_aligned(self.N, dtype="complex128") 224 | X = pyfftw.empty_aligned(self.N, dtype="complex128") 225 | plan_forward = pyfftw.FFTW(x, X) 226 | plan_inverse = pyfftw.FFTW(X, x, direction="FFTW_BACKWARD") 227 | 228 | progress_bar = tqdm.tqdm(total=self.fiber_length, unit='m') 229 | 230 | def rhs(z, AW): 231 | """ 232 | The right hand side of the differential equation to integrate. 233 | """ 234 | 235 | progress_bar.n = round(z, 3) 236 | progress_bar.update(0) 237 | 238 | x[:] = AW * np.exp(self.D * z) 239 | At = plan_forward().copy() 240 | IT = np.abs(At)**2 241 | 242 | if self.RW is not None: 243 | X[:] = IT 244 | plan_inverse() 245 | x[:] *= self.RW 246 | plan_forward() 247 | RS = dt * self.fr * X 248 | X[:] = At * ((1 - self.fr) * IT + RS) 249 | M = plan_inverse() 250 | else: 251 | X[:] = At * IT 252 | M = plan_inverse() 253 | 254 | rv = 1j * self.gamma * self.W * M * np.exp( 255 | -self.D * z) 256 | 257 | return rv 258 | 259 | Z = np.linspace(0, self.fiber_length, self.z_saves) 260 | solution = scipy.integrate.solve_ivp( 261 | rhs, 262 | t_span=(0, self.fiber_length), 263 | y0=np.fft.ifft(self.A) * self.scale, 264 | t_eval=Z, 265 | rtol=self.rtol, 266 | atol=self.atol, 267 | method=self.method) 268 | AW = solution.y.T 269 | 270 | progress_bar.close() 271 | 272 | # Transform the results into the time domain 273 | At = np.zeros(AW.shape, dtype=AW.dtype) 274 | for i in range(len(AW[:, 0])): 275 | AW[i, :] *= np.exp(np.transpose( 276 | self.D) * Z[i]) / self.scale 277 | At[i, :] = np.fft.fft(AW[i, :]) 278 | AW[i, :] = np.fft.fftshift(AW[i, :]) * self.N * dt 279 | 280 | return Solution(self.t, self.Omega, self.w_0, Z, At, AW) 281 | -------------------------------------------------------------------------------- /gnlse/import_export.py: -------------------------------------------------------------------------------- 1 | """Import and export \\*.mat files. 2 | 3 | This module contains functions that enable to read matlab files (\\*.mat) 4 | in python as dictionary, and to export dictionary to \\*.mat. 5 | 6 | """ 7 | 8 | import hdf5storage as hdf 9 | 10 | 11 | def read_mat(filename): 12 | """Imports \\*.mat file as dictionary. 13 | 14 | Parameters 15 | ---------- 16 | filename : string 17 | Name of \\*.mat file ('example.mat'). 18 | 19 | Returns 20 | ------- 21 | mat : dict 22 | dictionary of variables in imported file 23 | keys - names of variables 24 | values - variables' values 25 | 26 | """ 27 | mat = hdf.loadmat(filename, appendmat=True) 28 | return mat 29 | 30 | 31 | def write_mat(dictionary, filename): 32 | """Exports dictionary to \\*.mat file. 33 | 34 | Parameters 35 | ---------- 36 | dictionary : dict 37 | A list of variables. 38 | filename : string 39 | Name of \\*.mat file ('example.mat'). 40 | """ 41 | 42 | hdf.savemat(filename, 43 | dictionary, 44 | appendmat=True, 45 | store_python_metadata=True, 46 | action_for_matlab_incompatible='ignore') 47 | -------------------------------------------------------------------------------- /gnlse/nonlinearity.py: -------------------------------------------------------------------------------- 1 | """Nonlinearity coefficient in optical fibres. 2 | 3 | Based on delivered frequency vector and effictive mode areas 4 | script calculates nonlinear coefficient in 5 | frequency domain. 6 | 7 | """ 8 | import numpy as np 9 | from scipy import interpolate 10 | 11 | from gnlse.common import c 12 | 13 | 14 | class Nonlinearity(object): 15 | def gamma(V): 16 | """Calculate nonlinear coefficient 17 | for given frequency grid created during simulation 18 | 19 | Parameters 20 | ---------- 21 | V : ndarray, (N) 22 | Frequency vector 23 | 24 | Returns 25 | ------- 26 | ndarray, (N) 27 | Nonlinear coefficient in frequency domain 28 | """ 29 | 30 | raise NotImplementedError('Nonlinearity not implemented') 31 | 32 | 33 | class NonlinearityFromEffectiveArea(Nonlinearity): 34 | """Calculate the nonlinearity coefficient in 35 | frequency domain based on modified gnlse example form 36 | J. Lægsgaard, "Mode profile dispersion in the generalized 37 | nonlinear Schrödinger equation," 38 | Opt. Express 15, 16110-16123 (2007). 39 | 40 | Attributes 41 | ---------- 42 | neff : ndarray (N) 43 | Effective refractive index 44 | Aeff : ndarray (N) 45 | Effective mode area 46 | lambdas : ndarray (N) 47 | Wavelength corresponding to refractive index 48 | central_wavelength : float 49 | Wavelength corresponding to pump wavelength in nm 50 | n2 : float 51 | Nonlinear index of refraction in m^2/W 52 | """ 53 | 54 | def __init__(self, neff, Aeff, lambdas, central_wavelength, 55 | n2=2.7e-20, neff_max=None): 56 | # refractive indices 57 | self.neff = neff 58 | # efective mode area in m^-2 59 | self.Aeff = Aeff 60 | # wavelengths for neffs in nm 61 | self.lambdas = lambdas 62 | # central frequency in 1/ps [THz] 63 | self.w0 = (2.0 * np.pi * c) / central_wavelength 64 | # nonlinear index of refraction in m^2/W 65 | self.n2 = n2 66 | # maximum (artificial) value of neff 67 | self.neff_max = neff_max 68 | 69 | def gamma(self, V): 70 | # Central frequency [1/ps = THz] 71 | omega = 2 * np.pi * c / self.lambdas 72 | Omega = V + self.w0 73 | 74 | # Extrapolate effective mode area for a frequency vector 75 | Aeff_interp = interpolate.interp1d(omega, 76 | self.Aeff, 77 | kind='cubic', 78 | fill_value="extrapolate") 79 | # Extrapolate effective mode area for a frequency vector 80 | neff_interp = interpolate.interp1d(omega, 81 | self.neff, 82 | kind='cubic', 83 | fill_value="extrapolate") 84 | 85 | # Refractive index 86 | neff = neff_interp(Omega) 87 | if self.neff_max is not None: 88 | neff[Omega < omega[-1]] = self.neff_max 89 | # and at central frequency 90 | n0 = neff_interp(self.w0) 91 | # Efective mode area 92 | Aeff = Aeff_interp(Omega) 93 | if self.neff_max is not None: 94 | Aeff[Omega < omega[-1]] = max(self.Aeff) 95 | # and at central frequency [1/m^2] 96 | Aeff0 = Aeff_interp(self.w0) 97 | 98 | gamma = self.n2 * self.w0 \ 99 | * n0 / c / 1e-9 / neff / np.sqrt(Aeff * Aeff0) 100 | return gamma, np.power(Aeff0 / Aeff, 1. / 4) 101 | -------------------------------------------------------------------------------- /gnlse/raman_response.py: -------------------------------------------------------------------------------- 1 | """Calculates different Raman responses , based on the 2 | chosen Raman model, the response is calculated. 3 | There are three available Raman reponse models for 4 | silica optical fibers: 5 | - based on K. J. Blow and D. Wood model, 6 | - based on Dawn Hollenbeck and Cyrus D. Cantrell model, 7 | - based on Q. Lin and Govind P. Agrawal model. 8 | 9 | Example 10 | ------- 11 | By applying different index of raman response for 12 | silica fibers, different model is chosen. 13 | Function takes the time vector and calculates the 14 | Raman response based on the chosen model. 15 | 16 | """ 17 | 18 | import numpy as np 19 | 20 | 21 | def raman_blowwood(T): 22 | """Raman scattering function for silica optical fibers, based on K. J. Blow 23 | and D. Wood model. 24 | 25 | Parameters 26 | ---------- 27 | T : float 28 | Time vector. 29 | 30 | Returns 31 | ------- 32 | fr : float 33 | Share of Raman response. 34 | RT : ndarray 35 | Vector representing Raman response. 36 | 37 | """ 38 | 39 | # Raman response [arbitrary units] 40 | fr = 0.18 41 | # Adjustable parameters used to fit the actual Raman gain spectrum [ps] 42 | tau1 = 0.0122 43 | tau2 = 0.032 44 | # Raman response function 45 | ha = (tau1**2 + tau2**2) / tau1 / (tau2**2) * np.exp(-T / tau2) * np.sin( 46 | T / tau1) 47 | RT = ha 48 | 49 | RT[T < 0] = 0 50 | 51 | return fr, RT 52 | 53 | 54 | def raman_holltrell(T): 55 | """Raman scattering function for silica optical fibers, based on Dawn 56 | Hollenbeck and Cyrus D. Cantrell model. 57 | 58 | Parameters 59 | ---------- 60 | T : float 61 | Time vector. 62 | 63 | Returns 64 | ------- 65 | fr : float 66 | Share of Raman response. 67 | RT : ndarray 68 | Vector representing Raman response. 69 | 70 | """ 71 | 72 | # Raman response [arbitrary units] 73 | fr = 0.2 74 | # The speed of light [nm/ps] 75 | c = 299792458 * 1e-3 76 | 77 | # Component position [1/cm] 78 | CP = np.array([ 79 | 56.25, 100.0, 231.25, 362.5, 463.0, 497.0, 611.5, 691.67, 793.67, 80 | 835.5, 930.0, 1080.0, 1215.0 81 | ]) 82 | # Peak intensity (amplitude) 83 | A = np.array([ 84 | 1.0, 11.40, 36.67, 67.67, 74.0, 4.5, 6.8, 4.6, 4.2, 4.5, 2.7, 3.1, 3.0 85 | ]) 86 | # Gaussian FWHM [1/cm] 87 | Gauss = np.array([ 88 | 52.10, 110.42, 175.00, 162.50, 135.33, 24.5, 41.5, 155.00, 59.5, 64.3, 89 | 150.0, 91.0, 160.0 90 | ]) 91 | # Lorentzian FWHM [1/cm] 92 | Lorentz = np.array([ 93 | 17.37, 38.81, 58.33, 54.17, 45.11, 8.17, 13.83, 51.67, 19.83, 21.43, 94 | 50.00, 30.33, 53.33 95 | ]) 96 | 97 | w = 1e-7 * 2 * np.pi * c * CP 98 | L = 1e-7 * np.pi * c * Gauss 99 | gamma = 1e-7 * np.pi * c * Lorentz 100 | 101 | # RT = A * np.exp(-gamma * T) * np.exp((-L ** 2 * T ** 2 )/ 4) * np.sin( 102 | # w * T) # rozszerzenie pośrednie 103 | # RT = A * np.exp((-(L ** 2) * T ** 2) / 4) * np.sin(w * T) # nonuniform 104 | # RT = A * np.sin(w * T) * np.exp(-gamma * T) # unform 105 | 106 | RT = np.zeros_like(T) 107 | 108 | for i in range(len(A)): 109 | RT += A[i] * np.exp(-gamma[i] * T) * np.exp( 110 | (-L[i]**2 * T**2) / 4) * np.sin(w[i] * T) 111 | 112 | RT[T < 0] = 0 113 | dt = T[1] - T[0] 114 | RT = RT / (np.sum(RT) * dt) 115 | 116 | return fr, RT 117 | 118 | 119 | def raman_linagrawal(T): 120 | """Raman scattering function for silica optical fibers, based on Q. Lin 121 | and Govind P. Agrawal model. 122 | 123 | Parameters 124 | ---------- 125 | T : float 126 | Time vector. 127 | 128 | Returns 129 | ------- 130 | fr : float 131 | Share of Raman response. 132 | RT : ndarray 133 | Vector representing Raman response. 134 | 135 | """ 136 | 137 | # Raman response [arbitrary units] 138 | fr = 0.245 139 | # Adjustable parameters used to fit the actual Raman gain spectrum [ps] 140 | tau1 = 0.0122 141 | tau2 = 0.032 142 | taub = 0.096 143 | # Fractional contribution of the anisotropic reponse to the total Raman 144 | # response 145 | fb = 0.21 146 | fc = 0.04 147 | # Fractional contribution of the isotropic reponse to the total Raman 148 | # response 149 | fa = 1 - fb - fc 150 | # Anisotropic Raman response 151 | ha = (tau1**2 + tau2**2) / tau1 / (tau2**2) * np.exp(-T / tau2) * np.sin( 152 | T / tau1) 153 | # Izotropic Raman respons 154 | hb = (2 * taub - T) / (taub**2) * np.exp(-T / taub) 155 | # Total Raman response 156 | RT = (fa + fc) * ha + fb * hb 157 | 158 | RT[T < 0] = 0 159 | 160 | return fr, RT 161 | 162 | 163 | if __name__ == '__main__': 164 | """Calculates different Raman responses and plots them. 165 | Based on the chosen Raman model, the response is 166 | calculated and shown on the graph. There are three 167 | available Raman reponse models. 168 | """ 169 | import matplotlib.pyplot as plt 170 | # Initial paremeters 171 | ########################################################################### 172 | # Number of grid points 173 | n = 2**13 174 | # Time window width [ps] 175 | twidth = 12.5 176 | # The speed of light [nm/ps] 177 | c = 299792458 * 1e-3 178 | # Central wavelength [nm] 179 | wavelength = 835 180 | # Central frequency [THz] 181 | w0 = (2 * np.pi * c) / wavelength 182 | # Time grid [ps] 183 | T = np.linspace(-twidth / 2, twidth / 2, n) 184 | 185 | # Blowwood Raman response 186 | fr, RT1 = raman_blowwood(T) 187 | RT1 = RT1 / np.max(RT1) 188 | # Linagrawal Raman response 189 | fr2, RT2 = raman_linagrawal(T) 190 | RT2 = RT2 / np.max(RT2) 191 | # Holltrell Raman response 192 | fr3, RT3 = raman_holltrell(T) 193 | RT3 = RT3 / np.max(RT3) 194 | 195 | plt.plot(T, RT1, label="Blowwod") 196 | plt.plot(T, RT2, label="Linagrawal") 197 | plt.plot(T, RT3, label="Holltrell") 198 | 199 | plt.xlim(-0.025, 1) 200 | plt.ylabel("Raman Response [AU]") 201 | plt.xlabel("Time Grid [ps]") 202 | 203 | plt.legend() 204 | 205 | plt.show() 206 | -------------------------------------------------------------------------------- /gnlse/visualization.py: -------------------------------------------------------------------------------- 1 | """ 2 | Various plotting functions for visualizing GNLSE simulations using Matplotlib. 3 | """ 4 | 5 | import numpy as np 6 | import matplotlib.pyplot as plt 7 | 8 | from scipy.interpolate import interp2d 9 | 10 | from gnlse.common import c 11 | 12 | 13 | def plot_frequency_vs_distance_logarithmic(solver, ax=None, norm=None, 14 | frequency_range=None, cmap="magma"): 15 | """Plotting results in logarithmic scale in frequency domain. 16 | 17 | Parameters 18 | ---------- 19 | solver : Solution 20 | Model outputs in the form of a ``Solution`` object. 21 | frequency_range : list, (2, ) 22 | frequency range. Set [-150, 150] as default. 23 | ax : :class:`~matplotlib.axes.Axes` 24 | :class:`~matplotlib.axes.Axes` instance for plotting 25 | norm : float 26 | Normalization factor for output spectrum. As default maximum of 27 | square absolute of ``solver.AW`` variable is taken. 28 | 29 | Returns 30 | ------- 31 | ax : :class:`~matplotlib.axes.Axes` 32 | Used :class:`~matplotlib.axes.Axes` instance. 33 | """ 34 | 35 | if ax is None: 36 | ax = plt.gca() 37 | 38 | if norm is None: 39 | norm = np.max(np.abs(solver.AW)**2) 40 | 41 | lIW = np.fliplr( 42 | 10 * np.log10(np.abs(solver.AW)**2 / norm, 43 | where=(np.abs(solver.AW)**2 > 0))) 44 | frequency = (solver.W - solver.w_0) / 2 / np.pi # frequency grid 45 | 46 | if frequency_range is not None: 47 | iis = np.logical_and(frequency >= frequency_range[0], 48 | frequency <= frequency_range[1]) 49 | # indices of interest 50 | 51 | frequency = frequency[iis] 52 | lIW = lIW[:, iis] 53 | 54 | ax.imshow(lIW, origin='lower', aspect='auto', cmap=cmap, 55 | extent=[np.min(frequency), np.max(frequency), 56 | 0, np.max(solver.Z)], vmin=-40) 57 | ax.set_xlabel("Frequency [THz]") 58 | ax.set_ylabel("Distance [m]") 59 | return ax 60 | 61 | 62 | def plot_frequency_vs_distance(solver, frequency_range=None, 63 | ax=None, norm=None, cmap="magma"): 64 | """Plotting results in frequency domain. Linear scale. 65 | 66 | Parameters 67 | ---------- 68 | solver : Solution 69 | Model outputs in the form of a ``Solution`` object. 70 | frequency_range : list, (2, ) 71 | frequency range. Set [-150, 150] as default. 72 | ax : :class:`~matplotlib.axes.Axes` 73 | :class:`~matplotlib.axes.Axes` instance for plotting 74 | norm : float 75 | Normalization factor for output spectrum. As default maximum of 76 | square absolute of ``solver.AW`` variable is taken. 77 | 78 | Returns 79 | ------- 80 | ax : :class:`~matplotlib.axes.Axes` 81 | Used :class:`~matplotlib.axes.Axes` instance. 82 | """ 83 | 84 | if ax is None: 85 | ax = plt.gca() 86 | 87 | if norm is None: 88 | norm = np.max(np.abs(solver.AW)**2) 89 | 90 | IW = np.fliplr( 91 | np.abs(solver.AW)**2 / norm) 92 | frequency = (solver.W - solver.w_0) / 2 / np.pi # frequency grid 93 | 94 | if frequency_range is not None: 95 | iis = np.logical_and(frequency >= frequency_range[0], 96 | frequency <= frequency_range[1]) 97 | # indices of interest 98 | 99 | frequency = frequency[iis] 100 | IW = IW[:, iis] 101 | 102 | ax.imshow(IW, origin='lower', aspect='auto', cmap=cmap, 103 | extent=[np.min(frequency), np.max(frequency), 104 | 0, np.max(solver.Z)], vmin=0) 105 | ax.set_xlabel("Frequency [THz]") 106 | ax.set_ylabel("Distance [m]") 107 | return ax 108 | 109 | 110 | def plot_delay_for_distance_slice(solver, time_range=None, ax=None, 111 | z_slice=None, norm=None): 112 | """Plotting intensity in linear scale in time domain. 113 | 114 | Parameters 115 | ---------- 116 | solver : Solution 117 | Model outputs in the form of a ``Solution`` object. 118 | time_range : list, (2, ) 119 | Time range. Set [min(``solver.t``), max(``solver.t``)] as default. 120 | ax : :class:`~matplotlib.axes.Axes` 121 | :class:`~matplotlib.axes.Axes` instance for plotting. 122 | norm : float 123 | Normalization factor for output spectrum. As default maximum of 124 | square absolute of ``solver.At`` variable is taken. 125 | 126 | Returns 127 | ------- 128 | ax : :class:`~matplotlib.axes.Axes` 129 | Used :class:`~matplotlib.axes.Axes` instance. 130 | """ 131 | 132 | if ax is None: 133 | ax = plt.gca() 134 | 135 | if time_range is None: 136 | time_range = [np.min(solver.t), np.max(solver.t)] 137 | 138 | if norm is None: 139 | norm = np.max(np.abs(solver.At[0][:])**2) 140 | 141 | It = np.abs(solver.At)**2 / norm 142 | 143 | # indices of interest if no z_slice positions were given 144 | if z_slice is None: 145 | iis = [0, -1] 146 | # indices of interest nearest to given z_slice positions 147 | else: 148 | iis = [np.nonzero( 149 | np.min(np.abs(solver.Z - z)) == np.abs(solver.Z - z) 150 | )[0][0] for z in z_slice] 151 | 152 | for i in iis: 153 | label_i = "z = " + str(solver.Z[i]) + "m" 154 | ax.plot(solver.t, It[i][:], label=label_i) 155 | 156 | ax.set_xlim(time_range) 157 | ax.set_xlabel("Delay [ps]") 158 | ax.set_ylabel("Normalized Power") 159 | ax.legend() 160 | return ax 161 | 162 | 163 | def plot_wavelength_for_distance_slice(solver, WL_range=None, ax=None, 164 | z_slice=None, norm=None): 165 | """Plotting chosen slices of intensity 166 | in linear scale in wavelength domain. 167 | 168 | Parameters 169 | ---------- 170 | solver : Solution 171 | Model outputs in the form of a ``Solution`` object. 172 | WL_range : list, (2, ) 173 | Wavelength range. Set [400, 1350] as default. 174 | ax : :class:`~matplotlib.axes.Axes` 175 | :class:`~matplotlib.axes.Axes` instance for plotting 176 | norm : float 177 | Normalization factor for output spectrum. As default maximum of 178 | square absolute of ``solver.AW`` variable is taken. 179 | 180 | Returns 181 | ------- 182 | ax : :class:`~matplotlib.axes.Axes` 183 | Used :class:`~matplotlib.axes.Axes` instance. 184 | """ 185 | 186 | if ax is None: 187 | ax = plt.gca() 188 | 189 | if WL_range is None: 190 | WL_range = [np.min(c / (solver.W / 2 / np.pi)), 191 | np.max(c / (solver.W / 2 / np.pi))] 192 | 193 | if norm is None: 194 | norm = np.max(np.abs(solver.AW)**2) 195 | 196 | IW = np.fliplr( 197 | np.abs(solver.AW)**2 / norm) 198 | WL = 2 * np.pi * c / solver.W # wavelength grid 199 | WL_asc = np.flip(WL, ) # ascending order for interpolation 200 | iio = np.logical_and(WL_asc > WL_range[0], 201 | WL_asc < WL_range[1]) # indices in order 202 | 203 | WL_asc = WL_asc[iio] 204 | IW = IW[:, iio] 205 | 206 | # indices of interest if no z_slice positions were given 207 | if z_slice is None: 208 | iis = [0, -1] 209 | # indices of interest nearest to given z_slice positions 210 | else: 211 | iis = [np.nonzero( 212 | np.min(np.abs(solver.Z - z)) == np.abs(solver.Z - z) 213 | )[0][0] for z in z_slice] 214 | 215 | for i in iis: 216 | label_i = "z = " + str(solver.Z[i]) + "m" 217 | ax.plot(WL_asc, IW[i][:], label=label_i) 218 | 219 | ax.set_xlim([np.min(WL_asc), np.max(WL_asc)]) 220 | ax.set_xlabel("Wavelength [nm]") 221 | ax.set_ylabel("Normalized Spectral Density") 222 | ax.legend() 223 | return ax 224 | 225 | 226 | def plot_wavelength_for_distance_slice_logarithmic(solver, WL_range=None, 227 | ax=None, 228 | z_slice=None, norm=None): 229 | """Plotting chosen slices of intensity 230 | in linear scale in wavelength domain. 231 | 232 | Parameters 233 | ---------- 234 | solver : Solution 235 | Model outputs in the form of a ``Solution`` object. 236 | WL_range : list, (2, ) 237 | Wavelength range. Set [400, 1350] as default. 238 | ax : :class:`~matplotlib.axes.Axes` 239 | :class:`~matplotlib.axes.Axes` instance for plotting 240 | norm : float 241 | Normalization factor for output spectrum. As default maximum of 242 | square absolute of ``solver.AW`` variable is taken. 243 | 244 | Returns 245 | ------- 246 | ax : :class:`~matplotlib.axes.Axes` 247 | Used :class:`~matplotlib.axes.Axes` instance. 248 | """ 249 | 250 | if ax is None: 251 | ax = plt.gca() 252 | 253 | if WL_range is None: 254 | WL_range = [np.min(c / (solver.W / 2 / np.pi)), 255 | np.max(c / (solver.W / 2 / np.pi))] 256 | 257 | if norm is None: 258 | norm = np.max(np.abs(solver.AW)**2) 259 | 260 | lIW = np.fliplr( 261 | 10 * np.log10(np.abs(solver.AW)**2 / norm, 262 | where=(np.abs(solver.AW)**2 > 0))) 263 | WL = 2 * np.pi * c / solver.W # wavelength grid 264 | WL_asc = np.flip(WL, ) # ascending order for interpolation 265 | iio = np.logical_and(WL_asc > WL_range[0], 266 | WL_asc < WL_range[1]) # indices in order 267 | 268 | WL_asc = WL_asc[iio] 269 | lIW = lIW[:, iio] 270 | 271 | # indices of interest if no z_slice positions were given 272 | if z_slice is None: 273 | iis = [0, -1] 274 | # indices of interest nearest to given z_slice positions 275 | else: 276 | iis = [np.nonzero( 277 | np.min(np.abs(solver.Z - z)) == np.abs(solver.Z - z) 278 | )[0][0] for z in z_slice] 279 | 280 | for i in iis: 281 | label_i = "z = " + str(solver.Z[i]) + "m" 282 | ax.plot(WL_asc, lIW[i][:], label=label_i) 283 | 284 | ax.set_xlim([np.min(WL_asc), np.max(WL_asc)]) 285 | ax.set_ylim(-40) 286 | ax.set_xlabel("Wavelength [nm]") 287 | ax.set_ylabel("Normalized Spectral Density") 288 | ax.legend() 289 | return ax 290 | 291 | 292 | def plot_delay_for_distance_slice_logarithmic(solver, time_range=None, ax=None, 293 | z_slice=None, norm=None): 294 | """Plotting chosen slices of intensity in linear scale in time domain. 295 | 296 | Parameters 297 | ---------- 298 | solver : Solution 299 | Model outputs in the form of a ``Solution`` object. 300 | time_range : list, (2, ) 301 | Time range. Set [min(``solver.t``), max(``solver.t``)] as default. 302 | ax : :class:`~matplotlib.axes.Axes` 303 | :class:`~matplotlib.axes.Axes` instance for plotting. 304 | norm : float 305 | Normalization factor for output spectrum. As default maximum of 306 | square absolute of ``solver.At`` variable is taken. 307 | 308 | Returns 309 | ------- 310 | ax : :class:`~matplotlib.axes.Axes` 311 | Used :class:`~matplotlib.axes.Axes` instance. 312 | """ 313 | 314 | if ax is None: 315 | ax = plt.gca() 316 | 317 | if time_range is None: 318 | time_range = [np.min(solver.t), np.max(solver.t)] 319 | 320 | if norm is None: 321 | norm = np.max(np.abs(solver.At)**2) 322 | 323 | lIt = 10 * np.log10(np.abs(solver.At)**2 / norm, 324 | where=(np.abs(solver.At)**2 > 0)) 325 | 326 | # indices of interest if no z_slice positions were given 327 | if z_slice is None: 328 | iis = [0, -1] 329 | # indices of interest nearest to given z_slice positions 330 | else: 331 | iis = [np.nonzero( 332 | np.min(np.abs(solver.Z - z)) == np.abs(solver.Z - z) 333 | )[0][0] for z in z_slice] 334 | 335 | for i in iis: 336 | label_i = "z = " + str(solver.Z[i]) + "m" 337 | ax.plot(solver.t, lIt[i][:], label=label_i) 338 | 339 | ax.set_xlim(time_range) 340 | ax.set_ylim(-40) 341 | ax.set_xlabel("Delay [ps]") 342 | ax.set_ylabel("Normalized Power") 343 | ax.legend() 344 | return ax 345 | 346 | 347 | def plot_frequency_for_distance_slice(solver, frequency_range=None, ax=None, 348 | z_slice=None, norm=None): 349 | """Plotting chosen slices of intensity in linear scale in frequency domain. 350 | 351 | Parameters 352 | ---------- 353 | solver : Solution 354 | Model outputs in the form of a ``Solution`` object. 355 | frequency_range : list, (2, ) 356 | frequency range. Set [-150, 150] as default. 357 | ax : :class:`~matplotlib.axes.Axes` 358 | :class:`~matplotlib.axes.Axes` instance for plotting 359 | norm : float 360 | Normalization factor for output spectrum. As default maximum of 361 | square absolute of ``solver.AW`` variable is taken. 362 | 363 | Returns 364 | ------- 365 | ax : :class:`~matplotlib.axes.Axes` 366 | Used :class:`~matplotlib.axes.Axes` instance. 367 | """ 368 | 369 | if ax is None: 370 | ax = plt.gca() 371 | 372 | if frequency_range is None: 373 | frequency_range = [np.min((solver.W - solver.w_0) / 2 / np.pi), 374 | np.max((solver.W - solver.w_0) / 2 / np.pi)] 375 | 376 | if norm is None: 377 | norm = np.max(np.abs(solver.AW)**2) 378 | 379 | IW = np.fliplr( 380 | np.abs(solver.AW)**2 / norm) 381 | 382 | # indices of interest if no z_slice positions were given 383 | if z_slice is None: 384 | iis = [0, -1] # beginning, end 385 | # indices of interest nearest to given z_slice positions 386 | else: 387 | iis = [np.nonzero( 388 | np.min(np.abs(solver.Z - z)) == np.abs(solver.Z - z) 389 | )[0][0] for z in z_slice] 390 | 391 | for i in iis: 392 | label_i = "z = " + str(solver.Z[i]) + "m" 393 | ax.plot((solver.W - solver.w_0) / 2 / np.pi, IW[i][:], label=label_i) 394 | 395 | ax.set_xlim(frequency_range) 396 | ax.set_xlabel("Frequency [Thz]") 397 | ax.set_ylabel("Normalized Spectral Density") 398 | ax.legend() 399 | return ax 400 | 401 | 402 | def plot_frequency_for_distance_slice_logarithmic(solver, frequency_range=None, 403 | ax=None, z_slice=None, 404 | norm=None): 405 | """Plotting chosen slices of intensity 406 | in logarithmic scale in frequency domain. 407 | 408 | Parameters 409 | ---------- 410 | solver : Solution 411 | Model outputs in the form of a ``Solution`` object. 412 | frequency_range : list, (2, ) 413 | frequency range. Set [-150, 150] as default. 414 | ax : :class:`~matplotlib.axes.Axes` 415 | :class:`~matplotlib.axes.Axes` instance for plotting 416 | norm : float 417 | Normalization factor for output spectrum. As default maximum of 418 | square absolute of ``solver.AW`` variable is taken. 419 | 420 | Returns 421 | ------- 422 | ax : :class:`~matplotlib.axes.Axes` 423 | Used :class:`~matplotlib.axes.Axes` instance. 424 | """ 425 | 426 | if ax is None: 427 | ax = plt.gca() 428 | 429 | if frequency_range is None: 430 | frequency_range = [np.min((solver.W - solver.w_0) / 2 / np.pi), 431 | np.max((solver.W - solver.w_0) / 2 / np.pi)] 432 | 433 | if norm is None: 434 | norm = np.max(np.abs(solver.AW)**2) 435 | 436 | lIW = np.fliplr( 437 | 10 * np.log10(np.abs(solver.AW)**2 / norm, 438 | where=(np.abs(solver.AW)**2 > 0))) 439 | 440 | # indices of interest if no z_slice positions were given 441 | if z_slice is None: 442 | iis = [0, -1] # beginning, end 443 | 444 | # indices of interest nearest to given z_slice positions 445 | else: 446 | iis = [np.nonzero( 447 | np.min(np.abs(solver.Z - z)) == np.abs(solver.Z - z) 448 | )[0][0] for z in z_slice] 449 | 450 | for i in iis: 451 | label_i = "z = " + str(solver.Z[i]) + "m" 452 | ax.plot((solver.W - solver.w_0) / 2 / np.pi, lIW[i][:], label=label_i) 453 | 454 | ax.set_xlim(frequency_range) 455 | ax.set_ylim(-40) 456 | ax.set_xlabel("Frequency [Thz]") 457 | ax.set_ylabel("Normalized Spectral Density") 458 | ax.legend() 459 | return ax 460 | 461 | 462 | def plot_delay_vs_distance_logarithmic(solver, time_range=None, ax=None, 463 | norm=None, cmap="magma"): 464 | """Plotting intensity in logarithmic scale in time domain. 465 | 466 | Parameters 467 | ---------- 468 | solver : Solution 469 | Model outputs in the form of a ``Solution`` object. 470 | time_range : list, (2, ) 471 | Time range. Set [min(``solver.t``), max(``solver.t``)] as default. 472 | ax : :class:`~matplotlib.axes.Axes` 473 | :class:`~matplotlib.axes.Axes` instance for plotting. 474 | norm : float 475 | Normalization factor for output spectrum. As default maximum of 476 | square absolute of ``solver.At`` variable is taken. 477 | 478 | Returns 479 | ------- 480 | ax : :class:`~matplotlib.axes.Axes` 481 | Used :class:`~matplotlib.axes.Axes` instance. 482 | """ 483 | if ax is None: 484 | ax = plt.gca() 485 | 486 | if time_range is None: 487 | time_range = [np.min(solver.t), np.max(solver.t)] 488 | 489 | if norm is None: 490 | norm = np.max(np.abs(solver.At)**2) 491 | 492 | lIT = 10 * np.log10(np.abs(solver.At)**2 / norm, 493 | where=(np.abs(solver.At)**2 > 0)) 494 | 495 | ax.pcolormesh(solver.t, solver.Z, lIT, shading="auto", vmin=-40, 496 | cmap=cmap) 497 | ax.set_xlim(time_range) 498 | ax.set_xlabel("Delay [ps]") 499 | ax.set_ylabel("Distance [m]") 500 | return ax 501 | 502 | 503 | def plot_delay_vs_distance(solver, time_range=None, ax=None, norm=None, 504 | cmap="magma"): 505 | """Plotting normalized intensity in linear scale in time domain. 506 | 507 | Parameters 508 | ---------- 509 | solver : Solution 510 | Model outputs in the form of a ``Solution`` object. 511 | time_range : list, (2, ) 512 | Time range. Set [min(``solver.t``), max(``solver.t``)] as default. 513 | ax : :class:`~matplotlib.axes.Axes` 514 | :class:`~matplotlib.axes.Axes` instance for plotting. 515 | norm : float 516 | Normalization factor for output spectrum. As default maximum of 517 | square absolute of ``solver.At`` variable is taken. 518 | 519 | Returns 520 | ------- 521 | ax : :class:`~matplotlib.axes.Axes` 522 | Used :class:`~matplotlib.axes.Axes` instance. 523 | """ 524 | if ax is None: 525 | ax = plt.gca() 526 | 527 | if time_range is None: 528 | time_range = [np.min(solver.t), np.max(solver.t)] 529 | 530 | if norm is None: 531 | norm = np.max(np.abs(solver.At)**2) 532 | 533 | lIT = np.abs(solver.At)**2 / norm 534 | 535 | ax.pcolormesh(solver.t, solver.Z, lIT, shading="auto", vmin=0, 536 | cmap=cmap) 537 | ax.set_xlim(time_range) 538 | ax.set_xlabel("Delay [ps]") 539 | ax.set_ylabel("Distance [m]") 540 | return ax 541 | 542 | 543 | def plot_wavelength_vs_distance(solver, WL_range=None, ax=None, 544 | norm=None, cmap="magma"): 545 | """Plotting results in linear scale in wavelength domain. 546 | 547 | Parameters 548 | ---------- 549 | solver : Solution 550 | Model outputs in the form of a ``Solution`` object. 551 | WL_range : list, (2, ) 552 | Wavelength range. Set [400, 1350] as default. 553 | ax : :class:`~matplotlib.axes.Axes` 554 | :class:`~matplotlib.axes.Axes` instance for plotting 555 | norm : float 556 | Normalization factor for output spectrum. As default maximum of 557 | square absolute of ``solver.AW`` variable is taken. 558 | 559 | Returns 560 | ------- 561 | ax : :class:`~matplotlib.axes.Axes` 562 | Used :class:`~matplotlib.axes.Axes` instance. 563 | """ 564 | 565 | if ax is None: 566 | ax = plt.gca() 567 | 568 | if WL_range is None: 569 | WL_range = [np.min(c / (solver.W / 2 / np.pi)), 570 | np.max(c / (solver.W / 2 / np.pi))] 571 | 572 | if norm is None: 573 | norm = np.max(np.abs(solver.AW)**2) 574 | 575 | IW = np.fliplr( 576 | np.abs(solver.AW)**2 / norm) 577 | WL = 2 * np.pi * c / solver.W # wavelength grid 578 | WL_asc = np.flip(WL, ) # ascending order for interpolation 579 | iis = np.logical_and(WL_asc > WL_range[0], 580 | WL_asc < WL_range[1]) # indices of interest 581 | 582 | WL_asc = WL_asc[iis] 583 | IW = IW[:, iis] 584 | 585 | interpolator = interp2d(WL_asc, solver.Z, IW) 586 | newWL = np.linspace(np.min(WL_asc), np.max(WL_asc), IW.shape[1]) 587 | toshow = interpolator(newWL, solver.Z) 588 | 589 | ax.imshow(toshow, origin='lower', aspect='auto', cmap=cmap, 590 | extent=[np.min(WL_asc), np.max(WL_asc), 0, np.max(solver.Z)], 591 | vmin=0) 592 | ax.set_xlabel("Wavelength [nm]") 593 | ax.set_ylabel("Distance [m]") 594 | return ax 595 | 596 | 597 | def plot_wavelength_vs_distance_logarithmic(solver, WL_range=None, 598 | ax=None, norm=None, cmap="magma"): 599 | """Plotting results in logarithmic scale in wavelength domain. 600 | 601 | Parameters 602 | ---------- 603 | solver : Solution 604 | Model outputs in the form of a ``Solution`` object. 605 | WL_range : list, (2, ) 606 | Wavelength range. Set [400, 1350] as default. 607 | ax : :class:`~matplotlib.axes.Axes` 608 | :class:`~matplotlib.axes.Axes` instance for plotting 609 | norm : float 610 | Normalization factor for output spectrum. As default maximum of 611 | square absolute of ``solver.AW`` variable is taken. 612 | 613 | Returns 614 | ------- 615 | ax : :class:`~matplotlib.axes.Axes` 616 | Used :class:`~matplotlib.axes.Axes` instance. 617 | """ 618 | 619 | if ax is None: 620 | ax = plt.gca() 621 | 622 | if WL_range is None: 623 | WL_range = [np.min(c / (solver.W / 2 / np.pi)), 624 | np.max(c / (solver.W / 2 / np.pi))] 625 | 626 | if norm is None: 627 | norm = np.max(np.abs(solver.AW)**2) 628 | 629 | lIW = np.fliplr( 630 | 10 * np.log10(np.abs(solver.AW)**2 / norm, 631 | where=(np.abs(solver.AW)**2 > 0))) 632 | WL = 2 * np.pi * c / solver.W # wavelength grid 633 | WL_asc = np.flip(WL, ) # ascending order for interpolation 634 | iis = np.logical_and(WL_asc > WL_range[0], 635 | WL_asc < WL_range[1]) # indices of interest 636 | 637 | WL_asc = WL_asc[iis] 638 | lIW = lIW[:, iis] 639 | 640 | interpolator = interp2d(WL_asc, solver.Z, lIW) 641 | newWL = np.linspace(np.min(WL_asc), np.max(WL_asc), lIW.shape[1]) 642 | toshow = interpolator(newWL, solver.Z) 643 | 644 | ax.imshow(toshow, origin='lower', aspect='auto', cmap=cmap, 645 | extent=[np.min(WL_asc), np.max(WL_asc), 0, np.max(solver.Z)], 646 | vmin=-40) 647 | ax.set_xlabel("Wavelength [nm]") 648 | ax.set_ylabel("Distance [m]") 649 | return ax 650 | 651 | 652 | def quick_plot(solution): 653 | """Plotting results in time and frequency domain for default value 654 | of parameters. 655 | 656 | Parameters 657 | ---------- 658 | solver : Solution 659 | Model outputs in the form of a ``Solution`` object. 660 | """ 661 | plt.suptitle('GNLSE solution') 662 | 663 | plt.subplot(1, 2, 1) 664 | plot_wavelength_vs_distance(solution) 665 | 666 | plt.subplot(1, 2, 2) 667 | plot_delay_vs_distance(solution) 668 | 669 | plt.show() 670 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | matplotlib>=2.2.2 2 | numpy>=1.14.3 3 | scipy>=1.1.0 4 | pyfftw>=0.10.0 5 | hdf5storage>=0.1.15 6 | tqdm>=4.11.2 7 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import pkg_resources 2 | 3 | from setuptools import find_packages 4 | from setuptools import setup 5 | 6 | with open('requirements.txt', 'r') as fh: 7 | reqs = [str(requirement) 8 | for requirement in pkg_resources.parse_requirements(fh)] 9 | 10 | with open("README.md", "r") as fh: 11 | long_description = fh.read() 12 | 13 | setup( 14 | name='gnlse', 15 | version='2.0.0', 16 | url='https://github.com/WUST-FOG/gnlse-python', 17 | author='Redman, P., Zatorska, M., Pawlowski, A., Szulc, D., ' 18 | 'Majchrowska, S., Tarnowski, K.', 19 | description='gnlse-python is a Python set of scripts for solving ' 20 | 'Generalized Nonlinear Schrodringer Equation', 21 | long_description=long_description, 22 | long_description_content_type='text/markdown', 23 | packages=find_packages(), 24 | classifiers=[ 25 | 'Programming Language :: Python :: 3', 26 | 'License :: OSI Approved :: MIT License', 27 | 'Operating System :: OS Independent', 28 | ], 29 | python_requires='>=3.7', 30 | install_requires=reqs, 31 | ) 32 | -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | autopep8 2 | flake8 3 | doc8 4 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | minversion = 1.6 3 | skipsdist = True 4 | envlist = docs,examples,doc8,pep8,autopep8 5 | 6 | [testenv] 7 | basepython = python3 8 | usedevelop = True 9 | install_command = pip install {opts} {packages} 10 | 11 | [testenv:autopep8] 12 | description = Print autopep8 suggestions. 13 | envdir = {toxworkdir}/linters 14 | deps = -r test-requirements.txt 15 | commands = autopep8 --recursive --aggressive --diff --exit-code . 16 | 17 | [testenv:doc8] 18 | description = Style guide enforcement with doc8. 19 | envdir = {toxworkdir}/linters 20 | deps = -r test-requirements.txt 21 | commands = doc8 {posargs} 22 | 23 | [testenv:docs] 24 | description = Build project documentation. 25 | envdir = {toxworkdir}/sphinx 26 | deps = -r docs/requirements.txt 27 | allowlist_externals = rm 28 | commands = 29 | rm -rf docs/_build 30 | sphinx-build -W -b html docs docs/_build 31 | 32 | [testenv:examples] 33 | description = Run all python scripts from examples directory - physics check. 34 | envdir = {toxworkdir}/build 35 | deps = -r requirements.txt 36 | # Env variable MPLBACKEND=Agg prevents display of windows during tests 37 | setenv = MPLBACKEND=Agg 38 | allowlist_externals = bash 39 | commands = bash -ex -c 'for FILE in examples/*.py; do python $FILE; done' 40 | 41 | [testenv:pep8] 42 | description = Style guide enforcement with flake8. 43 | envdir = {toxworkdir}/linters 44 | deps = -r test-requirements.txt 45 | commands = flake8 {posargs} 46 | 47 | [doc8] 48 | # D000 - invalid rst format 49 | # D002 - no trailing whitespace 50 | # D004 - no carriage returns (use unix newlines) 51 | ignore = D000,D002,D004 52 | ignore-path = .git,.tox,.venv,docs/_build,*.egg-info 53 | 54 | [flake8] 55 | exclude = .git,.tox,.venv,build,docs,*.egg-info 56 | --------------------------------------------------------------------------------