├── .gitignore
├── .readthedocs.yaml
├── CITATION.cff
├── LICENSE
├── README.rst
├── docs
├── Makefile
├── auto_source
│ ├── pyLaserPulse.abstract_bases.rst
│ ├── pyLaserPulse.base_components.rst
│ ├── pyLaserPulse.bessel_mode_solver.rst
│ ├── pyLaserPulse.catalogue_components.active_fibres.rst
│ ├── pyLaserPulse.catalogue_components.bulk_components.rst
│ ├── pyLaserPulse.catalogue_components.fibre_components.rst
│ ├── pyLaserPulse.catalogue_components.passive_fibres.rst
│ ├── pyLaserPulse.coupling.rst
│ ├── pyLaserPulse.data.paths.rst
│ ├── pyLaserPulse.exceptions.rst
│ ├── pyLaserPulse.grid.rst
│ ├── pyLaserPulse.optical_assemblies.rst
│ ├── pyLaserPulse.pulse.rst
│ ├── pyLaserPulse.pump.rst
│ ├── pyLaserPulse.single_plot_window.matplotlib_gallery.rst
│ ├── pyLaserPulse.sys_info.rst
│ └── pyLaserPulse.utils.rst
├── conf.py
├── docs
│ ├── images
│ │ └── ANDi_SCG_grating_compression.png
│ └── videos
│ │ └── simulation_gallery.gif
├── images
│ └── ANDi_SCG_grating_compression.png
├── index.rst
├── make.bat
├── readme_link.rst
└── videos
│ └── simulation_gallery.gif
├── examples
├── fibre_amplifiers
│ ├── Yb_fibre_CPA_system.py
│ ├── Yb_fibre_amplifier_multiple_pumps.py
│ ├── nonlinear_Yb_fibre_amplifier.py
│ ├── nonlinear_Yb_fibre_amplifier_cladding_pumping.py
│ ├── nonlinear_Yb_fibre_amplifier_with_coherence.py
│ └── nonlinear_Yb_fibre_amplifier_with_tap_couplers.py
├── multimode_fibres
│ └── step_index_modal_dispersion.py
├── saving_and_recalling_data
│ ├── amp 1
│ │ ├── grid.npz
│ │ ├── optical_assembly.npz
│ │ └── pulse.npz
│ ├── amplifier_1.py
│ └── amplifier_2.py
└── supercontinuum
│ ├── 1040_nm_ZDW.py
│ ├── ANDi_SCG_grating_compression.py
│ └── coherence_using_multiprocessing.py
├── pyLaserPulse
├── __init__.py
├── abstract_bases.py
├── base_components.py
├── bessel_mode_solver.py
├── catalogue_components
│ ├── __init__.py
│ ├── active_fibres.py
│ ├── bulk_components.py
│ ├── fibre_components.py
│ └── passive_fibres.py
├── coupling.py
├── data
│ ├── __init__.py
│ ├── components
│ │ └── loss_spectra
│ │ │ ├── Andover_155FS10_25_bandpass.dat
│ │ │ ├── Opneti_95_5_PM_fibre_tap_fast_axis_blocked.dat
│ │ │ ├── Opneti_high_power_isolator_HPMIS_1030_1_250_5.dat
│ │ │ ├── Opneti_microoptic_976_1030_wdm.dat
│ │ │ ├── Opneti_microoptic_isolator_PMIS_S_P_1030_F.dat
│ │ │ ├── Thorlabs_IO_J_1030.dat
│ │ │ └── fused_WDM_976_1030.dat
│ ├── fibres
│ │ └── cross_sections
│ │ │ ├── Er_silica.dat
│ │ │ ├── Tm_silica.dat
│ │ │ ├── Yb_Al_silica.dat
│ │ │ └── Yb_Nufern_PM_YSF_HI_HP.dat
│ ├── materials
│ │ ├── Raman_profiles
│ │ │ └── silica.dat
│ │ ├── Sellmeier_coefficients
│ │ │ └── silica.dat
│ │ ├── loss_spectra
│ │ │ └── silica.dat
│ │ └── reflectivity_spectra
│ │ │ ├── aluminium.dat
│ │ │ ├── gold.dat
│ │ │ └── silver.dat
│ └── paths.py
├── exceptions.py
├── grid.py
├── optical_assemblies.py
├── pulse.py
├── pump.py
├── single_plot_window
│ ├── __init__.py
│ ├── icon.png
│ ├── icon.svg
│ ├── matplotlib_gallery.py
│ ├── mplwidget.py
│ ├── plotGallery.ui
│ ├── plotWidget.ui
│ ├── plot_gallery.py
│ ├── plot_widget.py
│ └── single_matplotlib_window.py
├── sys_info.py
└── utils.py
├── requirements.txt
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # These are some examples of commonly ignored file patterns.
2 | # You should customize this list as applicable to your project.
3 | # Learn more about .gitignore:
4 | # https://www.atlassian.com/git/tutorials/saving-changes/gitignore
5 |
6 | # Compiled Python bytecode
7 | __pycache__/
8 | *.py[cod]
9 | *$py.class
10 |
11 | # Log files
12 | *.log
13 |
14 | # Distribution / packaging
15 | .Python
16 | build/
17 | dist/
18 | pyLaserPulse.egg-info/
19 |
20 | # junk files
21 | test.py
22 | change_details.sh
23 |
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | # Read the Docs configuration file for Sphinx projects
2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
3 |
4 | # Required
5 | version: 2
6 |
7 | # Set the OS, Python version and other tools you might need
8 | build:
9 | os: ubuntu-22.04
10 | tools:
11 | python: "3.11"
12 | # You can also specify other tool versions:
13 | # nodejs: "19"
14 | # rust: "1.64"
15 | # golang: "1.19"
16 |
17 | # Build documentation in the "docs/" directory with Sphinx
18 | sphinx:
19 | configuration: docs/conf.py
20 |
21 | # Optionally build your docs in additional formats such as PDF and ePub
22 | # formats:
23 | # - pdf
24 | # - epub
25 |
26 | # Optional but recommended, declare the Python requirements required
27 | # to build your documentation
28 | # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
29 | # python:
30 | # install:
31 | # - requirements: docs/requirements.txt
32 |
--------------------------------------------------------------------------------
/CITATION.cff:
--------------------------------------------------------------------------------
1 | cff-version: 1.2.0
2 | message: "If you use this software, please cite it as below."
3 | authors:
4 | - family-names: "Feehan"
5 | given-names: "James S."
6 | title: "pyLaserPulse"
7 | version: 0.0.0
8 | doi:
9 | date-released: 2023-06-23
10 | url: "https://github.com/jsfeehan/pyLaserPulse"
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | pyLaserPulse: Simulation toolbox for modelling pulsed optical fibre systems.
2 | Copyright (C) 2022 James S. Feehan
3 |
4 | This program is free software; you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation; either version 3, or (at your option)
7 | any later version.
8 |
9 | This program is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this program; if not, see .
16 |
--------------------------------------------------------------------------------
/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/auto_source/pyLaserPulse.abstract_bases.rst:
--------------------------------------------------------------------------------
1 | pyLaserPulse.abstract\_bases
2 | ============================
3 |
4 | .. automodule:: pyLaserPulse.abstract_bases
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | .. rubric:: Classes
17 |
18 | .. autosummary::
19 |
20 | active_fibre_base
21 | component_base
22 | coupling_transmission_base
23 | fibre_base
24 | loss_spectrum_base
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/docs/auto_source/pyLaserPulse.base_components.rst:
--------------------------------------------------------------------------------
1 | pyLaserPulse.base\_components
2 | =============================
3 |
4 | .. automodule:: pyLaserPulse.base_components
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | .. rubric:: Classes
17 |
18 | .. autosummary::
19 |
20 | component
21 | fibre_component
22 | fibre_pulse_picker
23 | grating_compressor
24 | photonic_crystal_active_fibre
25 | photonic_crystal_passive_fibre
26 | pulse_picker
27 | rotated_splice
28 | step_index_active_fibre
29 | step_index_fibre_compressor
30 | step_index_passive_fibre
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/docs/auto_source/pyLaserPulse.bessel_mode_solver.rst:
--------------------------------------------------------------------------------
1 | pyLaserPulse.bessel\_mode\_solver
2 | =================================
3 |
4 | .. automodule:: pyLaserPulse.bessel_mode_solver
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | .. rubric:: Classes
17 |
18 | .. autosummary::
19 |
20 | bessel_mode_solver
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/docs/auto_source/pyLaserPulse.catalogue_components.active_fibres.rst:
--------------------------------------------------------------------------------
1 | pyLaserPulse.catalogue\_components.active\_fibres
2 | =================================================
3 |
4 | .. automodule:: pyLaserPulse.catalogue_components.active_fibres
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | .. rubric:: Classes
17 |
18 | .. autosummary::
19 |
20 | CorActive_SCF_YB550_4_125_19
21 | NKT_DC_200_40_PZ_YB
22 | Nufern_EDFC_980_HP
23 | Nufern_FUD_4288_LMA_YDF_48_400E
24 | Nufern_PLMA_30_400
25 | Nufern_PLMA_YDF_10_125_M
26 | Nufern_PLMA_YDF_25_250
27 | Nufern_PM_YDF_5_130_VIII
28 | Nufern_PM_YSF_HI_HP
29 | OFS_R37003
30 | ORC_HD406_YDF
31 | Thorlabs_Liekki_M5_980_125
32 | Thorlabs_Liekki_Yb1200_6_125_DC
33 | nLight_Yb1200_4_125
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/docs/auto_source/pyLaserPulse.catalogue_components.bulk_components.rst:
--------------------------------------------------------------------------------
1 | pyLaserPulse.catalogue\_components.bulk\_components
2 | ===================================================
3 |
4 | .. automodule:: pyLaserPulse.catalogue_components.bulk_components
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | .. rubric:: Classes
17 |
18 | .. autosummary::
19 |
20 | Andover_155FS10_25_bandpass
21 | Thorlabs_Laserline_bandpass
22 | Thorlabs_broadband_PBS
23 | half_wave_plate
24 | quarter_wave_plate
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/docs/auto_source/pyLaserPulse.catalogue_components.fibre_components.rst:
--------------------------------------------------------------------------------
1 | pyLaserPulse.catalogue\_components.fibre\_components
2 | ====================================================
3 |
4 | .. automodule:: pyLaserPulse.catalogue_components.fibre_components
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | .. rubric:: Classes
17 |
18 | .. autosummary::
19 |
20 | AFR_fast_axis_blocking_isolator_PMI_03_1_P_N_B_F
21 | JDSU_fused_976_1030_WDM
22 | Opneti_1x2_PM_95_5_filter_coupler_500mW_fast_axis_blocked
23 | Opneti_1x2_PM_filter_coupler_500mW
24 | Opneti_PM_isolator_WDM_hybrid
25 | Opneti_fast_axis_blocking_isolator_PMIS_S_P_1030
26 | Opneti_high_power_PM_isolator
27 | Opneti_high_power_filter_WDM_1020_1080
28 | Thorlabs_IO_J_1030
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/docs/auto_source/pyLaserPulse.catalogue_components.passive_fibres.rst:
--------------------------------------------------------------------------------
1 | pyLaserPulse.catalogue\_components.passive\_fibres
2 | ==================================================
3 |
4 | .. automodule:: pyLaserPulse.catalogue_components.passive_fibres
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | .. rubric:: Classes
17 |
18 | .. autosummary::
19 |
20 | Corning_HI1060
21 | Corning_HI1060_FLEX
22 | NKT_DC_200_40_PZ_SI
23 | NKT_NL_1050_NEG_1
24 | NKT_SC_5_1040
25 | NKT_SC_5_1040_PM
26 | NKT_femtowhite_800
27 | Nufern_FUD4258_UHNA
28 | Nufern_PLMA_GDF_10_125
29 | Nufern_PLMA_GDF_10_125_M
30 | Nufern_PLMA_GDF_25_250
31 | Nufern_PM_GDF_5_130
32 | Nufern_SM2000D
33 | OFS_980
34 | PM980_XP
35 | SMF_28
36 | SMF_28e
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/docs/auto_source/pyLaserPulse.coupling.rst:
--------------------------------------------------------------------------------
1 | pyLaserPulse.coupling
2 | =====================
3 |
4 | .. automodule:: pyLaserPulse.coupling
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | .. rubric:: Classes
17 |
18 | .. autosummary::
19 |
20 | fibreToFibreCoupling
21 | freeSpaceToFibreCoupling
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/docs/auto_source/pyLaserPulse.data.paths.rst:
--------------------------------------------------------------------------------
1 | pyLaserPulse.data.paths
2 | =======================
3 |
4 | .. automodule:: pyLaserPulse.data.paths
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/docs/auto_source/pyLaserPulse.exceptions.rst:
--------------------------------------------------------------------------------
1 | pyLaserPulse.exceptions
2 | =======================
3 |
4 | .. automodule:: pyLaserPulse.exceptions
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | .. rubric:: Exceptions
21 |
22 | .. autosummary::
23 |
24 | BoundaryConditionError
25 | NanFieldError
26 | PropagationMethodNotConvergingError
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/docs/auto_source/pyLaserPulse.grid.rst:
--------------------------------------------------------------------------------
1 | pyLaserPulse.grid
2 | =================
3 |
4 | .. automodule:: pyLaserPulse.grid
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | .. rubric:: Classes
17 |
18 | .. autosummary::
19 |
20 | grid
21 | grid_from_pyLaserPulse_simulation
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/docs/auto_source/pyLaserPulse.optical_assemblies.rst:
--------------------------------------------------------------------------------
1 | pyLaserPulse.optical\_assemblies
2 | ================================
3 |
4 | .. automodule:: pyLaserPulse.optical_assemblies
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | .. rubric:: Classes
17 |
18 | .. autosummary::
19 |
20 | assembly
21 | passive_assembly
22 | sm_fibre_amplifier
23 | sm_fibre_laser
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/docs/auto_source/pyLaserPulse.pulse.rst:
--------------------------------------------------------------------------------
1 | pyLaserPulse.pulse
2 | ==================
3 |
4 | .. automodule:: pyLaserPulse.pulse
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | .. rubric:: Functions
13 |
14 | .. autosummary::
15 |
16 | complex_first_order_degree_of_coherence
17 |
18 |
19 |
20 |
21 |
22 | .. rubric:: Classes
23 |
24 | .. autosummary::
25 |
26 | pulse
27 | pulse_from_measured_PSD
28 | pulse_from_pyLaserPulse_simulation
29 | pulse_from_text_data
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/docs/auto_source/pyLaserPulse.pump.rst:
--------------------------------------------------------------------------------
1 | pyLaserPulse.pump
2 | =================
3 |
4 | .. automodule:: pyLaserPulse.pump
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | .. rubric:: Classes
17 |
18 | .. autosummary::
19 |
20 | pump
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/docs/auto_source/pyLaserPulse.single_plot_window.matplotlib_gallery.rst:
--------------------------------------------------------------------------------
1 | pyLaserPulse.single\_plot\_window.matplotlib\_gallery
2 | =====================================================
3 |
4 | .. automodule:: pyLaserPulse.single_plot_window.matplotlib_gallery
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | .. rubric:: Functions
13 |
14 | .. autosummary::
15 |
16 | concatenate_plot_dicts
17 | launch_plot
18 | savePlotFunc
19 |
20 |
21 |
22 |
23 |
24 | .. rubric:: Classes
25 |
26 | .. autosummary::
27 |
28 | MatplotlibGallery
29 | parallelPlotSaver
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/docs/auto_source/pyLaserPulse.sys_info.rst:
--------------------------------------------------------------------------------
1 | pyLaserPulse.sys\_info
2 | ======================
3 |
4 | .. automodule:: pyLaserPulse.sys_info
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/docs/auto_source/pyLaserPulse.utils.rst:
--------------------------------------------------------------------------------
1 | pyLaserPulse.utils
2 | ==================
3 |
4 | .. automodule:: pyLaserPulse.utils
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | .. rubric:: Functions
13 |
14 | .. autosummary::
15 |
16 | PCF_propagation_parameters_K_Saitoh
17 | Sellmeier
18 | check_dict_keys
19 | fft_convolve
20 | find_nearest
21 | get_ESD_and_PSD
22 | get_FWe2M
23 | get_width
24 | interpolate_data_from_file
25 | load_Raman
26 | load_cross_sections
27 | load_target_power_spectral_density
28 | swap_halves
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # For the full list of built-in configuration values, see the documentation:
4 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
5 |
6 | # -- Project information -----------------------------------------------------
7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
8 |
9 | import os
10 | import sys
11 | sys.path.insert(0, os.path.abspath('..'))
12 | sys.path.insert(0, os.path.abspath('../..'))
13 |
14 |
15 | project = 'pyLaserPulse'
16 | copyright = '2023, James Feehan'
17 | author = 'James Feehan'
18 | release = '0.0.0'
19 |
20 | # -- General configuration ---------------------------------------------------
21 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
22 |
23 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon', 'sphinx.ext.autosummary']
24 |
25 | templates_path = ['_templates']
26 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
27 |
28 | autodoc_mock_imports = ['numpy', 'scipy', 'matplotlib', 'PyQt5']
29 |
30 | autodoc_default_options = {
31 | 'members': True,
32 | 'special-members': '__init__',
33 | 'member-order': 'bysource',
34 | }
35 |
36 |
37 | # -- Options for HTML output -------------------------------------------------
38 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
39 |
40 | # html_theme = 'alabaster'
41 | # html_theme = 'sphinx_rtd_theme'
42 | html_theme = "classic"
43 | html_static_path = ['_static']
44 |
--------------------------------------------------------------------------------
/docs/docs/images/ANDi_SCG_grating_compression.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jsfeehan/pyLaserPulse/f3c5a309b24389691ee87807e3157c0b27a7d223/docs/docs/images/ANDi_SCG_grating_compression.png
--------------------------------------------------------------------------------
/docs/docs/videos/simulation_gallery.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jsfeehan/pyLaserPulse/f3c5a309b24389691ee87807e3157c0b27a7d223/docs/docs/videos/simulation_gallery.gif
--------------------------------------------------------------------------------
/docs/images/ANDi_SCG_grating_compression.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jsfeehan/pyLaserPulse/f3c5a309b24389691ee87807e3157c0b27a7d223/docs/images/ANDi_SCG_grating_compression.png
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. pyLaserPulse documentation master file, created by
2 | sphinx-quickstart on Mon Jun 26 01:30:11 2023.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | ##########################
7 | pyLaserPulse documentation
8 | ##########################
9 |
10 | .. toctree::
11 | :maxdepth: 2
12 | :caption: Getting started:
13 |
14 | readme_link
15 |
16 |
17 | .. autosummary::
18 | :toctree: modules
19 | pyLaserPulse.abstract_bases
20 | pyLaserPulse.base_components
21 | pyLaserPulse.bessel_mode_solver
22 | pyLaserPulse.catalogue_components.active_fibres
23 | pyLaserPulse.catalogue_components.passive_fibres
24 | pyLaserPulse.catalogue_components.fibre_components
25 | pyLaserPulse.catalogue_components.bulk_components
26 | pyLaserPulse.coupling
27 | pyLaserPulse.data.paths
28 | pyLaserPulse.exceptions
29 | pyLaserPulse.grid
30 | pyLaserPulse.optical_assemblies
31 | pyLaserPulse.pulse
32 | pyLaserPulse.pump
33 | pyLaserPulse.single_plot_window.matplotlib_gallery
34 | pyLaserPulse.sys_info
35 | pyLaserPulse.utils
36 |
37 | .. toctree::
38 | :maxdepth: 2
39 | :caption: Module reference
40 |
41 | /auto_source/pyLaserPulse.abstract_bases
42 | /auto_source/pyLaserPulse.base_components
43 | /auto_source/pyLaserPulse.bessel_mode_solver
44 | /auto_source/pyLaserPulse.catalogue_components.active_fibres
45 | /auto_source/pyLaserPulse.catalogue_components.passive_fibres
46 | /auto_source/pyLaserPulse.catalogue_components.fibre_components
47 | /auto_source/pyLaserPulse.catalogue_components.bulk_components
48 | /auto_source/pyLaserPulse.coupling
49 | /auto_source/pyLaserPulse.data
50 | /auto_source/pyLaserPulse.exceptions
51 | /auto_source/pyLaserPulse.grid
52 | /auto_source/pyLaserPulse.optical_assemblies
53 | /auto_source/pyLaserPulse.pulse
54 | /auto_source/pyLaserPulse.pump
55 | /auto_source/pyLaserPulse.single_plot_window.matplotlib_gallery
56 | /auto_source/pyLaserPulse.sys_info
57 | /auto_source/pyLaserPulse.utils
58 |
--------------------------------------------------------------------------------
/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 | %SPHINXBUILD% >NUL 2>NUL
14 | if errorlevel 9009 (
15 | echo.
16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
17 | echo.installed, then set the SPHINXBUILD environment variable to point
18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
19 | echo.may add the Sphinx directory to PATH.
20 | echo.
21 | echo.If you don't have Sphinx installed, grab it from
22 | echo.https://www.sphinx-doc.org/
23 | exit /b 1
24 | )
25 |
26 | if "%1" == "" goto help
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/readme_link.rst:
--------------------------------------------------------------------------------
1 | .. include:: ../README.rst
2 |
--------------------------------------------------------------------------------
/docs/videos/simulation_gallery.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jsfeehan/pyLaserPulse/f3c5a309b24389691ee87807e3157c0b27a7d223/docs/videos/simulation_gallery.gif
--------------------------------------------------------------------------------
/examples/fibre_amplifiers/Yb_fibre_CPA_system.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | Simulate a 3-amplifier Yb-doped CPA system using pyLaserPulse.
5 |
6 | The seed pulses have a central wavelength of 1083 nm, a repetition rate of 40
7 | MHz, and are chirped to 3 ps (transform limit of 124 fs). The first component
8 | is a circulator and CFBG setup which stretches the pulses to ~275 ps FWHM.
9 | Three amplifiers follow, with an AOM to reduce the repetition rate to 1 MHz
10 | after the first. Each amplifier is cladding pumped to give the quasi-four-level
11 | dynamics required for high gain at the seed wavelength. The CFBG dispersion
12 | Taylor coefficients are calculated from the initial compressor dispersion and
13 | the net fibre dispersion (note that they are unlikely to be optimal for this
14 | CPA system).
15 |
16 | This example also shows how co-propagating ASE can be passed from one amplifier
17 | to the next using the optical_assemblies module and the co_ASE keyword
18 | argument.
19 |
20 | This simulation takes some time to run. This is because both a broad time and
21 | frequency grid are required for strongly-chirped femtosecond pulses, and this
22 | requires a lot of grid points. Additionally, cladding-pumped amplifiers take a
23 | bit longer to simulate than core-pumped amplifiers because the overlap of the
24 | pump light with the signal core is calculated using a Bessel mode solver and the
25 | doped fibres tend to be longer than in core-pumped amplifiers.
26 |
27 | James Feehan, 19/6/2023
28 | """
29 |
30 | import numpy as np
31 |
32 | from pyLaserPulse import grid
33 | from pyLaserPulse import pulse
34 | from pyLaserPulse import base_components
35 | from pyLaserPulse import optical_assemblies
36 | from pyLaserPulse import single_plot_window
37 | from pyLaserPulse import utils
38 | from pyLaserPulse import data
39 | import pyLaserPulse.catalogue_components.active_fibres as af
40 | import pyLaserPulse.catalogue_components.passive_fibres as pf
41 |
42 |
43 | #############################################
44 | # Choose a directory for saving the data. #
45 | # Leave as None if no data should be saved. #
46 | #############################################
47 | directory = None
48 |
49 |
50 | ############################################################
51 | # Set time-frequency grid, pulse, and component parameters #
52 | ############################################################
53 |
54 | # Time-frequency grid parameters
55 | points = 2**15 # Number of grid points
56 | central_wl = 1083e-9 # Central wavelength, m
57 | max_wl = 1150e-9 # Maximum wavelength, m
58 |
59 | # Laser pulse parameters
60 | tau = 3e-12 # Pulse duration, s
61 | chirp = 20 # 3 ps pulse with bandwidth, strong positive chirp
62 | P_peak = [300, 0.3] # [P_x, P_y], W
63 | f_rep = 40e6 # Repetition frequency, Hz
64 | shape = 'Gauss'
65 |
66 |
67 | ##############################################################
68 | # Instantiate the time-frequency grid and pulse #
69 | ##############################################################
70 | # Time-frequency grid defined using the grid module
71 | g = grid.grid(points, central_wl, max_wl)
72 |
73 | # pulse defined using the pulse module.
74 | # Print some useful data to the terminal
75 | p = pulse.pulse(
76 | tau, P_peak, shape, f_rep, g, chirp=chirp)
77 | p.get_ESD_and_PSD(g, p.field)
78 | p.get_transform_limit(p.field)
79 | T_FWHM = utils.get_width(g.time_window*1e12, np.abs(p.field[0, :])**2)
80 | S_FWHM = utils.get_width(
81 | g.lambda_window*1e9, p.power_spectral_density[0, :])
82 | TL_FWHM = utils.get_width(g.time_window*1e15, p.transform_limit)
83 | print("Starting FWHM duration: %.3f ps"
84 | "\nStarting FWHM spectral width: %.3f nm"
85 | "\nStarting transform limited FWHM duration: %.3f fs"
86 | "\nStarting average power: %.3f mW"
87 | "\nStarting pulse energy: %.3f nJ"
88 | % (T_FWHM, S_FWHM, TL_FWHM, p.average_power*1e3, p.pulse_energy*1e9))
89 |
90 | # Pump and ASE grid
91 | pump_points = 2**9
92 | pump_wl_lims = [900e-9, g.lambda_max]
93 |
94 | # Useful constants
95 | ps = 1e-12
96 |
97 |
98 | ##########################################
99 | # Repeat components and generic settings #
100 | ##########################################
101 | tol = 1e-4 # integration tolerance for CQEM
102 | crosstalk = 1e-5 # General crosstalk value for standard components.
103 | pm_pigtail = pf.PM980_XP(g, 0.3, tol)
104 | dc_pm_pigtail = pf.Nufern_PM_GDF_5_130(g, 0.3, tol)
105 | dc_10_125_pm_pigtail = pf.Nufern_PLMA_GDF_10_125_M(g, 0.3, tol)
106 | dc_25_250_pm_pigtail = pf.Nufern_PLMA_GDF_25_250(g, 0.3, tol)
107 | num_samples = 10 # num field samples per component
108 |
109 |
110 | ######################################################################
111 | # chirped fibre Bragg grating stretcher, amp 1, and AOM pulse picker #
112 | ######################################################################
113 | circulator_1_to_2 = base_components.fibre_component(
114 | g, pm_pigtail, pm_pigtail, 0.2, 100e-9, g.lambda_c, 0.1, 0, 0, crosstalk)
115 | # CFBG dispersion calculated by adding the compressor dispersion and total fibre
116 | # dispersion and then multiplying by -1.
117 | beta_2 = -1 * ps**2 * (
118 | 0.045 + 0.058 + 0.028 + 0.0043 + 0.12 + 0.047 + 0.072 - 13.53)
119 | beta_3 = -1 * ps**3 * (
120 | 6.26e-5 + 7.02e-5 + 7.8e-5 + 1.43e-5 + 1.46e-4 + 1.3e-4 + 2.39e-4
121 | + 0.0582)
122 | beta_4 = -1 * ps**4 * (
123 | -1.48e-5 - 1.338e-5 + 0.995e-6 - 1.526e-6 - 2.788e-5 + 1.659e-6 - 2.54e-5
124 | - 3.53e-4)
125 | beta_5 = -1 * ps**5 * (
126 | 1.283e-8 + 1.16e-8 - 7.8e-10 + 1.33e-9 + 2.419e-8 - 1.3e-9 + 2.217e-8
127 | + 3.04e-6)
128 | cfbg = base_components.fibre_component(
129 | g, pm_pigtail, pm_pigtail, 0.4, 50e-9, g.lambda_c, 1, 0, 0, crosstalk,
130 | order=5, beta_list=[beta_2, beta_3, beta_4, beta_5])
131 | circulator_2_to_3 = base_components.fibre_component(
132 | g, pm_pigtail, pm_pigtail, 0.2, 100e-9, g.lambda_c, 0.1, 0, 0, crosstalk)
133 | combiner1 = base_components.fibre_component(
134 | g, pm_pigtail, dc_pm_pigtail, 0.2, 60e-9, g.lambda_c, 1, 0, 0, crosstalk)
135 | bounds_1 = {'co_pump_wavelength': 976e-9,
136 | 'co_pump_power': 0.5,
137 | 'co_pump_bandwidth': 1e-9,
138 | 'counter_pump_power': 0}
139 | ydf1 = af.Nufern_PM_YDF_5_130_VIII(
140 | g, 5, p.repetition_rate, pump_points, pump_wl_lims,
141 | boundary_conditions=bounds_1, time_domain_gain=True, cladding_pumping=True)
142 | bp1 = base_components.fibre_component(
143 | g, dc_pm_pigtail, dc_pm_pigtail, 0.2, 40e-9, g.lambda_c, 1, 0, 0,
144 | crosstalk, order=4)
145 | time_gate = 20e-9
146 | new_rep_rate = 1e6
147 | reduction = int(p.repetition_rate / new_rep_rate)
148 | aom_loss = 10**-0.35 # 3.5 dB insertion loss
149 | aom_bw = 60e-9 # transmission bandwidth
150 | per = 0.1
151 | theta = 0
152 | beamsplitting = 0
153 | aom = base_components.fibre_pulse_picker(
154 | g, dc_pm_pigtail, dc_pm_pigtail, aom_loss, aom_bw, g.lambda_c, per, theta,
155 | beamsplitting, crosstalk, time_gate, reduction, p.repetition_rate, order=6)
156 | components1 = [
157 | circulator_1_to_2, cfbg, circulator_2_to_3, combiner1, ydf1, bp1, aom]
158 | amp_1 = optical_assemblies.sm_fibre_amplifier(
159 | g, components1, high_res_sampling=num_samples, plot=True,
160 | data_directory=directory, name='amp 1', verbose=True)
161 | p = amp_1.simulate(p)
162 |
163 |
164 | #########
165 | # Amp 2 #
166 | #########
167 | iso1 = base_components.fibre_component(
168 | g, dc_pm_pigtail, dc_pm_pigtail, 0.2, 100e-9, g.lambda_c, 0.05, 0, 0,
169 | crosstalk, order=5)
170 | combiner2 = base_components.fibre_component(
171 | g, dc_pm_pigtail, dc_10_125_pm_pigtail, 0.2, 60e-9, g.lambda_c, 1, 0, 0,
172 | crosstalk)
173 | bounds_2 = {'co_pump_wavelength': 976e-9,
174 | 'co_pump_power': 1,
175 | 'co_pump_bandwidth': 1e-9,
176 | 'counter_pump_power': 0}
177 | ydf2 = af.Nufern_PLMA_YDF_10_125_M(
178 | g, 3, p.repetition_rate, pump_points, pump_wl_lims,
179 | boundary_conditions=bounds_2, time_domain_gain=True, cladding_pumping=True)
180 | bp2 = base_components.fibre_component(
181 | g, dc_10_125_pm_pigtail, dc_10_125_pm_pigtail, 0.2, 40e-9, g.lambda_c, 1,
182 | 0, 0, crosstalk, order=4)
183 | components2 = [iso1, combiner2, ydf2, bp2] # , aom]
184 | amp_2 = optical_assemblies.sm_fibre_amplifier(
185 | g, components2, high_res_sampling=num_samples, plot=True,
186 | data_directory=directory, name='amp 2',
187 | co_ASE=amp_1.co_core_ASE_ESD_output, verbose=True)
188 | p = amp_2.simulate(p)
189 |
190 |
191 | #########
192 | # Amp 3 #
193 | #########
194 | iso2 = base_components.fibre_component(
195 | g, dc_10_125_pm_pigtail, dc_10_125_pm_pigtail, 0.2, 100e-9, g.lambda_c,
196 | 0.05, 0, 0, crosstalk, order=5)
197 | combiner3 = base_components.fibre_component(
198 | g, dc_10_125_pm_pigtail, dc_25_250_pm_pigtail, 0.2, 60e-9, g.lambda_c, 1,
199 | 0, 0, crosstalk)
200 | bounds_3 = {'co_pump_wavelength': 976e-9,
201 | 'co_pump_power': 0,
202 | 'co_pump_bandwidth': 1e-9,
203 | 'counter_pump_power': 5,
204 | 'counter_pump_wavelength': 976e-9,
205 | 'counter_pump_bandwidth': 1e-9}
206 | ydf3 = af.Nufern_PLMA_YDF_25_250(
207 | g, 4, p.repetition_rate, pump_points, pump_wl_lims,
208 | boundary_conditions=bounds_3, time_domain_gain=True, cladding_pumping=True)
209 | components3 = [iso2, combiner3, ydf3]
210 | amp_3 = optical_assemblies.sm_fibre_amplifier(
211 | g, components3, high_res_sampling=num_samples, plot=True,
212 | data_directory=directory, name='amp 3',
213 | co_ASE=amp_2.co_core_ASE_ESD_output, verbose=True)
214 | p = amp_3.simulate(p)
215 |
216 |
217 | ##############
218 | # Compressor #
219 | ##############
220 | loss = 0.04 # percent loss per grating reflection
221 | transmission = 20e-9 # transmission bandwidth
222 | coating = data.paths.materials.reflectivities.gold
223 | epsilon = 1e-1 # Jones parameter for polarization mixing and phase
224 | theta = 0 # Jones parameter for angle subtended by x-axis
225 | beamsplitting = 0 # Useful for output couplers, etc.
226 | l_mm = 1200 # grating lines per mm
227 | sep_initial = 90e-2 # initial guess for grating separation
228 | angle_initial = 0.7 # initial guess for incidence angle, rad
229 | gc = base_components.grating_compressor(
230 | loss, transmission, coating, g.lambda_c, epsilon, theta, beamsplitting,
231 | crosstalk, sep_initial, angle_initial, l_mm, g, order=5, optimize=True)
232 | compressor = optical_assemblies.passive_assembly(
233 | g, [gc], 'compressor', plot=True, data_directory=directory,
234 | verbose=True)
235 | p = compressor.simulate(p)
236 |
237 |
238 | ############
239 | # Plotting #
240 | ############
241 | plot_dicts = [
242 | amp_1.plot_dict, amp_2.plot_dict, amp_3.plot_dict, compressor.plot_dict]
243 | single_plot_window.matplotlib_gallery.launch_plot(plot_dicts=plot_dicts)
244 |
--------------------------------------------------------------------------------
/examples/fibre_amplifiers/Yb_fibre_amplifier_multiple_pumps.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | This example shows, among other things, how to add a pump source which shares a
6 | propagation direction with the pump that is defined when a doped fibre is
7 | instantiated. At instantiation, it is possible to define two pumps with opposite
8 | propagation directions, but it is not possible to define two pumps which share
9 | a propagation direction. To do this, the fibre is first defined in the normal
10 | way, and then the add_pump method is used before the propagation is simulated.
11 | """
12 |
13 | from pyLaserPulse import grid
14 | from pyLaserPulse import pulse
15 | from pyLaserPulse import base_components
16 | from pyLaserPulse.data import paths
17 | from pyLaserPulse import optical_assemblies
18 | from pyLaserPulse import single_plot_window
19 |
20 |
21 | #############################################
22 | # Choose a directory for saving the data. #
23 | # Leave as None if no data should be saved. #
24 | #############################################
25 | directory = None
26 |
27 | ############################################################
28 | # Set time-frequency grid, pulse, and component parameters #
29 | ############################################################
30 |
31 | # Time-frequency grid parameters
32 | points = 2**14 # Number of grid points
33 | central_wl = 1080e-9 # Central wavelength, m
34 | max_wl = 1160e-9 # Maximum wavelength, m
35 |
36 | # Laser pulse parameters
37 | tau = 100e-12 # Pulse duration, s
38 | P_peak = [1500, 1.5] # [P_x, P_y], W
39 | f_rep = 20e6 # Repetition frequency, Hz
40 | shape = 'Gauss'
41 | order = 8 # 8th order supergaussian pulse shape
42 |
43 | # Yb-fibre parameters
44 | L_ydf = 4 # length, m
45 | doping = 8e25 # ion number density, m-3
46 | core_diam = 44e-6 # core diameter in m
47 | NA = 0.028 # core numerical aperture
48 | beat_length = 1e-2 # polarization beat length
49 | n2 = 2.19e-20 # nonlinear index in m/W
50 | fR = 0.18 # Raman contribution to nonlinear response
51 | tol = 1e-5 # Integration error tolerance
52 | ase_points = 2**8 # number of points in pump & ASE grid
53 | ase_wl_lims = [900e-9, max_wl] # wavelength limits for ASE grid
54 | bounds = {'counter_pump_power': 150, # counter-pump power, W
55 | 'counter_pump_wavelength': 976e-9, # counter-pump wavelength, m
56 | 'counter_pump_bandwidth': 1e-9} # counter-pump bandwidth, m
57 | cladding_pumping = {'pump_core_diam': 400e-6, # pump core diameter, m
58 | 'pump_delta_n': 1.45 - 1.375, # pump core ref. index step
59 | 'pump_cladding_n': 1.375} # pump cladding ref. index
60 |
61 | ##############################################################
62 | # Instantiate the time-frequency grid, pulse, and components #
63 | ##############################################################
64 |
65 | # Time-frequency grid defined using the grid module
66 | g = grid.grid(points, central_wl, max_wl)
67 |
68 | # pulse defined using the pulse module
69 | p = pulse.pulse(
70 | tau, P_peak, shape, f_rep, g, order=order, high_res_sampling=True)
71 |
72 | # Define a custom double-clad, large-mode-area Yb-doped fibre and use the
73 | # add_pump method to add another pump source.
74 | ydf = base_components.step_index_active_fibre(
75 | g, L_ydf, paths.materials.loss_spectra.silica,
76 | paths.materials.Raman_profiles.silica, core_diam, NA, beat_length, n2,
77 | fR, tol, doping, paths.fibres.cross_sections.Yb_Al_silica,
78 | p.repetition_rate, ase_points, ase_wl_lims,
79 | paths.materials.Sellmeier_coefficients.silica, bounds, lifetime=1.5e-3,
80 | time_domain_gain=True, cladding_pumping=cladding_pumping)
81 | ydf.add_pump(950e-9, 1e-9, 150, p.repetition_rate, 'counter')
82 |
83 | ################################################################
84 | # Use the optical_assemblies module for automatic inclusion of #
85 | # coupling loss between components and for generating plots. #
86 | ################################################################
87 | component_list = [ydf]
88 | amp = optical_assemblies.sm_fibre_amplifier(
89 | g, component_list, plot=True, name='amp 1', high_res_sampling=100,
90 | data_directory=directory, verbose=True)
91 |
92 | ######################
93 | # Run the simulation #
94 | ######################
95 | p = amp.simulate(p)
96 |
97 | ##########################################################
98 | # Use the matplotlib_gallery module to display the plots #
99 | ##########################################################
100 | if amp.plot:
101 | plot_dicts = [amp.plot_dict]
102 | single_plot_window.matplotlib_gallery.launch_plot(plot_dicts=plot_dicts)
103 |
--------------------------------------------------------------------------------
/examples/fibre_amplifiers/nonlinear_Yb_fibre_amplifier.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | from pyLaserPulse import grid
5 | from pyLaserPulse import pulse
6 | from pyLaserPulse import optical_assemblies
7 | from pyLaserPulse import single_plot_window
8 | import pyLaserPulse.catalogue_components.active_fibres as af
9 | import pyLaserPulse.catalogue_components.fibre_components as fc
10 |
11 |
12 | #############################################
13 | # Choose a directory for saving the data. #
14 | # Leave as None if no data should be saved. #
15 | #############################################
16 | directory = None
17 |
18 | ############################################################
19 | # Set time-frequency grid, pulse, and component parameters #
20 | ############################################################
21 |
22 | # Time-frequency grid parameters
23 | points = 2**9 # Number of grid points
24 | central_wl = 1030e-9 # Central wavelength, m
25 | max_wl = 1200e-9 # Maximum wavelength, m
26 |
27 | # Laser pulse parameters
28 | tau = 150e-15 # Pulse duration, s
29 | P_peak = [150, .15] # [P_x, P_y], W
30 | f_rep = 40e6 # Repetition frequency, Hz
31 | shape = 'sech' # Can also take 'Gauss'
32 |
33 | # isolator-WDM parameters
34 | L_in = 0.2 # input fibre length, m
35 | L_out = 0.2 # output fibre length, m
36 |
37 | # Yb-fibre parameters
38 | L = 1 # length, m
39 | ase_points = 2**8 # number of points in pump & ASE grid
40 | ase_wl_lims = [900e-9, max_wl] # wavelength limits for ASE grid
41 | bounds = {'co_pump_power': 1, # co-pump power, W
42 | 'co_pump_wavelength': 916e-9, # co-pump wavelength, m
43 | 'co_pump_bandwidth': 1e-9, # co-pump bandwidth, m
44 | 'counter_pump_power': 0} # counter-pump power, W
45 |
46 | ##############################################################
47 | # Instantiate the time-frequency grid, pulse, and components #
48 | ##############################################################
49 |
50 | # Time-frequency grid defined using the grid module
51 | g = grid.grid(points, central_wl, max_wl)
52 |
53 | # pulse defined using the pulse module
54 | p = pulse.pulse(tau, P_peak, shape, f_rep, g)
55 |
56 | # Opneti isolator/WDM hybrid component from the catalogue_components module.
57 | iso_wdm = fc.Opneti_PM_isolator_WDM_hybrid(g, L_in, L_out, g.lambda_c)
58 |
59 | # Nufern PM-YSF-HI-HP defined using the catalogue_components module
60 | ydf = af.Nufern_PM_YSF_HI_HP(g, L, p.repetition_rate, ase_points, ase_wl_lims,
61 | bounds, time_domain_gain=True)
62 |
63 | ################################################################
64 | # Use the optical_assemblies module for automatic inclusion of #
65 | # coupling loss between components and for generating plots. #
66 | ################################################################
67 | component_list = [iso_wdm, ydf]
68 | amp = optical_assemblies.sm_fibre_amplifier(
69 | g, component_list, plot=True, name='amp 1', high_res_sampling=200,
70 | data_directory=directory, verbose=True)
71 |
72 | ######################
73 | # Run the simulation #
74 | ######################
75 | p = amp.simulate(p)
76 |
77 | ##########################################################
78 | # Use the matplotlib_gallery module to display the plots #
79 | ##########################################################
80 | if amp.plot:
81 | plot_dicts = [amp.plot_dict]
82 | single_plot_window.matplotlib_gallery.launch_plot(plot_dicts=plot_dicts)
83 |
--------------------------------------------------------------------------------
/examples/fibre_amplifiers/nonlinear_Yb_fibre_amplifier_cladding_pumping.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | from pyLaserPulse import grid
5 | from pyLaserPulse import pulse
6 | from pyLaserPulse import optical_assemblies
7 | from pyLaserPulse import single_plot_window
8 | import pyLaserPulse.catalogue_components.active_fibres as af
9 | import pyLaserPulse.catalogue_components.passive_fibres as pf
10 |
11 |
12 | #############################################
13 | # Choose a directory for saving the data. #
14 | # Leave as None if no data should be saved. #
15 | #############################################
16 | directory = None
17 |
18 | ############################################################
19 | # Set time-frequency grid, pulse, and component parameters #
20 | ############################################################
21 |
22 | # Time-frequency grid parameters
23 | points = 2**11 # Number of grid points
24 | central_wl = 1055e-9 # Central wavelength, m
25 | max_wl = 1200e-9 # Maximum wavelength, m
26 |
27 | # Laser pulse parameters
28 | tau = 300e-15 # Pulse duration, s
29 | P_peak = [1500, 7.5] # [P_x, P_y], W
30 | f_rep = 80e6 # Repetition frequency, Hz
31 | shape = 'sech' # Can also take 'Gauss'
32 |
33 | # SMF pigtail
34 | L_pmf = .3 # length, m
35 |
36 | # Yb-fibre parameters
37 | L_ydf = 8 # length, m
38 | ase_points = 2**8 # number of points in pump & ASE grid
39 | ase_wl_lims = [900e-9, max_wl] # wavelength limits for ASE grid
40 | bounds = {'counter_pump_power': 15, # counter-pump power, W
41 | 'counter_pump_wavelength': 916e-9, # counter-pump wavelength, m
42 | 'counter_pump_bandwidth': 1e-9} # counter-pump bandwidth, m
43 |
44 | ##############################################################
45 | # Instantiate the time-frequency grid, pulse, and components #
46 | ##############################################################
47 |
48 | # Time-frequency grid defined using the grid module
49 | g = grid.grid(points, central_wl, max_wl)
50 |
51 | # pulse defined using the pulse module
52 | p = pulse.pulse(tau, P_peak, shape, f_rep, g, high_res_sampling=True)
53 |
54 | # PM980 'pigtail', defined using the catalogue_components module
55 | pmf = pf.PM980_XP(g, L_pmf, 1e-5)
56 |
57 | # Nufern PM-YSF-HI-HP defined using the catalogue_components module
58 | ydf = af.Nufern_PLMA_YDF_25_250(
59 | g, L_ydf, p.repetition_rate, ase_points, ase_wl_lims, bounds,
60 | time_domain_gain=True, cladding_pumping=True)
61 |
62 | ################################################################
63 | # Use the optical_assemblies module for automatic inclusion of #
64 | # coupling loss between components and for generating plots. #
65 | ################################################################
66 | component_list = [pmf, ydf]
67 | amp = optical_assemblies.sm_fibre_amplifier(
68 | g, component_list, plot=True, name='amp 1', high_res_sampling=100,
69 | data_directory=directory, verbose=True)
70 |
71 | ######################
72 | # Run the simulation #
73 | ######################
74 | p = amp.simulate(p)
75 |
76 | ##########################################################
77 | # Use the matplotlib_gallery module to display the plots #
78 | ##########################################################
79 | if amp.plot:
80 | plot_dicts = [amp.plot_dict]
81 | single_plot_window.matplotlib_gallery.launch_plot(plot_dicts=plot_dicts)
82 |
--------------------------------------------------------------------------------
/examples/fibre_amplifiers/nonlinear_Yb_fibre_amplifier_with_coherence.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | This example shows how the python multiprocessing library can be used to
5 | run pyLaserPulse Yb-fibre amplifier simulations in parallel (on multiple cores
6 | of a CPU) with different quantum noise seeds so that that complex first-order
7 | degree of coherence can be calculated.
8 |
9 | The simulation is wrapped in a function which can then be passed to a
10 | multiprocessing worker pool. The optical_assemblies module works well for this,
11 | but the plot option must be turned off. The function can be replaced by a class
12 | method if you prefer.
13 |
14 | It is recommended that you become familiar with some of the other examples
15 | before reading this one.
16 |
17 | James Feehan
18 | """
19 |
20 |
21 | # Limit the number of threads spawned by any process to 1.
22 | import os
23 | os.environ["OMP_NUM_THREADS"] = "1" # for openBLAS or similar
24 | # os.environ["MKL_NUM_THREADS"] = "1" # For Intel math kernel library
25 |
26 | import psutil
27 | import matplotlib.pyplot as plt
28 | import multiprocessing as mp
29 | import numpy as np
30 | import pyLaserPulse.catalogue_components.fibre_components as fc
31 | import pyLaserPulse.catalogue_components.active_fibres as af
32 | from pyLaserPulse import optical_assemblies
33 | from pyLaserPulse import pulse
34 | from pyLaserPulse import grid
35 |
36 |
37 | def ydfa_sim(_g):
38 | """
39 | Yb-doped fibre amplifier simulation using pyLaserPulse
40 |
41 | Parameters
42 | ----------
43 | _g : pyLaserPulse.grid object
44 |
45 | Notes
46 | -----
47 | The pulse object is instantiated inside this function so that the quantum
48 | noise seed is different to all others in the ensemble.
49 | """
50 | # Laser pulse parameters
51 | tau = 150e-15 # Pulse duration, s
52 | P_peak = [150, .15] # [P_x, P_y], W
53 | f_rep = 40e6 # Repetition frequency, Hz
54 | shape = 'sech' # Can also take 'Gauss'
55 | p = pulse.pulse(tau, P_peak, shape, f_rep, _g)
56 |
57 | # isolator-WDM parameters
58 | L_in = 0.2 # input fibre length, m
59 | L_out = 0.2 # output fibre length, m
60 |
61 | # Yb-fibre parameters
62 | L = 1 # length, m
63 | ase_points = 2**8 # no. of points in pump & ASE grid
64 | ase_wl_lims = [900e-9, _g.lambda_max] # wavelength limits for ASE grid
65 | bounds = {'co_pump_power': 1, # co-pump power, W
66 | 'co_pump_wavelength': 916e-9, # co-pump wavelength, m
67 | 'co_pump_bandwidth': 1e-9, # co-pump bandwidth, m
68 | 'counter_pump_power': 0} # counter-pump power, W
69 | iso_wdm = fc.Opneti_PM_isolator_WDM_hybrid(_g, L_in, L_out, _g.lambda_c)
70 | ydf = af.Nufern_PM_YSF_HI_HP(
71 | _g, L, p.repetition_rate, ase_points, ase_wl_lims, bounds,
72 | time_domain_gain=True)
73 | component_list = [iso_wdm, ydf]
74 | amp = optical_assemblies.sm_fibre_amplifier(
75 | _g, component_list, plot=False, name='amp 1', verbose=False)
76 | p = amp.simulate(p)
77 | return p
78 |
79 |
80 | if __name__ == "__main__":
81 | # Time-frequency grid parameters
82 | points = 2**9 # Number of grid points
83 | central_wl = 1030e-9 # Central wavelength, m
84 | max_wl = 1200e-9 # Maximum wavelength, m
85 | g = grid.grid(points, central_wl, max_wl)
86 |
87 | num_processes = psutil.cpu_count(logical=False) # only use physical cores
88 | num_simulations = 4 * num_processes # no. of simulations in CFODC ensemble
89 | gridlist = [[g]] * num_simulations # iterable of func arguments
90 | pool = mp.get_context("spawn").Pool(
91 | processes=num_processes, maxtasksperchild=1)
92 | output_pulses = pool.starmap(ydfa_sim, gridlist)
93 | pool.close()
94 | pool.join()
95 |
96 | cfodc, lw = pulse.complex_first_order_degree_of_coherence(g, output_pulses)
97 |
98 | # Convert output_pulses[n].power_spectral_density into an array for easier
99 | # plotting
100 | PSDs = np.zeros((num_simulations, 2, g.points))
101 | for i, op in enumerate(output_pulses):
102 | PSDs[i, :, :] = op.power_spectral_density
103 |
104 | colors = ['seagreen', 'lightcoral']
105 | dark_colors = ['darkgreen', 'darkred']
106 | legend1 = []
107 | fig = plt.figure(num=1, figsize=(8, 4))
108 | ax1 = fig.add_subplot(121)
109 | ax2 = fig.add_subplot(122)
110 | for p in range(2): # iterate over polarization basis vectors
111 | for i in range(num_simulations):
112 | ax1.semilogy(
113 | g.lambda_window * 1e9, PSDs[i, p, :], c=colors[p], alpha=0.1)
114 | l, = ax1.semilogy(
115 | g.lambda_window * 1e9, np.average(PSDs[:, p, :], axis=0),
116 | c=dark_colors[p])
117 | ax2.plot(
118 | lw * 1e9, cfodc[p, :], c=['k', 'grey'][p], ls=['-', '--'][p])
119 | legend1.append(l)
120 | ax1.set_ylabel('Power spectral density, mW/nm')
121 | ax1.set_xlabel('Wavelength, nm')
122 | ax2.set_ylabel('Complex first-order degree of coherence')
123 | ax2.set_xlabel('Wavelength, nm')
124 | ax1.set_xlim([1e9 * g.lambda_min, 1e9 * g.lambda_max])
125 | ax2.set_xlim([1e9 * g.lambda_min, 1e9 * g.lambda_max])
126 | ax1.legend(legend1, ['$S_{x}(\\lambda)$', '$S_{y}(\\lambda)$'])
127 | ax2.legend(['$g_{x}(\\lambda)$', '$g_{y}(\\lambda)$'])
128 | fig.tight_layout()
129 | plt.show()
130 |
--------------------------------------------------------------------------------
/examples/fibre_amplifiers/nonlinear_Yb_fibre_amplifier_with_tap_couplers.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | The following is an example of a basic nonlinear Yb-fibre amplifier with a 5%
5 | tap coupler at the input.
6 |
7 | The tapped light is stored in pulse.output, which is a list whose length is
8 | equal to the number of output couplers in an amplifier chain. Each list element
9 | is a numpy array of the field at that point, multiplied by the tap fraction.
10 |
11 | The tapped light is not plotting by the optical_assemblies module, but it is
12 | saved if directory != None.
13 | """
14 |
15 | from pyLaserPulse import grid
16 | from pyLaserPulse import pulse
17 | from pyLaserPulse import optical_assemblies
18 | from pyLaserPulse import single_plot_window
19 | import pyLaserPulse.catalogue_components.active_fibres as af
20 | import pyLaserPulse.catalogue_components.fibre_components as fc
21 |
22 |
23 | #############################################
24 | # Choose a directory for saving the data. #
25 | # Leave as None if no data should be saved. #
26 | #############################################
27 | directory = None
28 |
29 | ############################################################
30 | # Set time-frequency grid, pulse, and component parameters #
31 | ############################################################
32 |
33 | # Time-frequency grid parameters
34 | points = 2**9 # Number of grid points
35 | central_wl = 1030e-9 # Central wavelength, m
36 | max_wl = 1200e-9 # Maximum wavelength, m
37 |
38 | # Laser pulse parameters
39 | tau = 150e-15 # Pulse duration, s
40 | P_peak = [150, .15] # [P_x, P_y], W
41 | f_rep = 40e6 # Repetition frequency, Hz
42 | shape = 'sech' # Can also take 'Gauss'
43 |
44 | # isolator-WDM parameters
45 | L_in = 0.2 # input fibre length, m
46 | L_out = 0.2 # output fibre length, m
47 |
48 | # Yb-fibre parameters
49 | L = 1 # length, m
50 | ase_points = 2**8 # number of points in pump & ASE grid
51 | ase_wl_lims = [900e-9, max_wl] # wavelength limits for ASE grid
52 | bounds = {'co_pump_power': 1, # co-pump power, W
53 | 'co_pump_wavelength': 916e-9, # co-pump wavelength, m
54 | 'co_pump_bandwidth': 1e-9, # co-pump bandwidth, m
55 | 'counter_pump_power': 0} # counter-pump power, W
56 |
57 | ##############################################################
58 | # Instantiate the time-frequency grid, pulse, and components #
59 | ##############################################################
60 |
61 | # Time-frequency grid defined using the grid module
62 | g = grid.grid(points, central_wl, max_wl)
63 |
64 | # pulse defined using the pulse module
65 | p = pulse.pulse(tau, P_peak, shape, f_rep, g, high_res_sampling=True)
66 |
67 | # 50:50 splitter based on an off-the-shelf component
68 | tap = fc.Opneti_1x2_PM_filter_coupler_500mW(
69 | g, L_in, L_out, g.lambda_c, split_fraction=0.95)
70 |
71 | # Opneti isolator/WDM hybrid component from the catalogue_components module.
72 | iso_wdm = fc.Opneti_PM_isolator_WDM_hybrid(g, L_in, L_out, g.lambda_c)
73 |
74 | # Nufern PM-YSF-HI-HP defined using the catalogue_components module
75 | ydf = af.Nufern_PM_YSF_HI_HP(g, L, p.repetition_rate, ase_points, ase_wl_lims,
76 | bounds, time_domain_gain=True)
77 |
78 | ################################################################
79 | # Use the optical_assemblies module for automatic inclusion of #
80 | # coupling loss between components and for generating plots. #
81 | ################################################################
82 | component_list = [tap, iso_wdm, ydf]
83 | amp = optical_assemblies.sm_fibre_amplifier(
84 | g, component_list, plot=True, name='amp 1', high_res_sampling=100,
85 | data_directory=directory, verbose=True)
86 |
87 | ######################
88 | # Run the simulation #
89 | ######################
90 | p = amp.simulate(p)
91 |
92 |
93 | #########################################################
94 | # Use the matplotlib_gallery module to display the plots #
95 | ##########################################################
96 | if amp.plot:
97 | plot_dicts = [amp.plot_dict]
98 | single_plot_window.matplotlib_gallery.launch_plot(plot_dicts=plot_dicts)
99 |
--------------------------------------------------------------------------------
/examples/multimode_fibres/step_index_modal_dispersion.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | import numpy as np
6 | import scipy.constants as const
7 | import matplotlib.pyplot as plt
8 | from mpl_toolkits.axes_grid1 import make_axes_locatable
9 |
10 | from pyLaserPulse import grid
11 | from pyLaserPulse import bessel_mode_solver as bms
12 | from pyLaserPulse import utils as utils
13 | from pyLaserPulse.data import paths
14 |
15 |
16 | if __name__ == "__main__":
17 |
18 | ##########################################
19 | # Define the step-index fibre parameters #
20 | ##########################################
21 | core_rad = 50e-6 / 2
22 | cladding_rad = 125e-6 / 2
23 | NA = 0.22
24 |
25 | ###################################################################
26 | # Choose the wavelength range and maximum number of modes to find #
27 | ###################################################################
28 | g = grid.grid(2**7, 1025e-9, 1055e-9)
29 | max_modes = 50
30 |
31 | beta = [] # propagation constants
32 |
33 | for i, wavelength in enumerate(g.lambda_window):
34 |
35 | ######################################################################
36 | # Use the Sellmeier equation for the fibre core and cladding indices #
37 | ######################################################################
38 | n_cladding = utils.Sellmeier(
39 | wavelength, paths.materials.Sellmeier_coefficients.silica)
40 | n_core = (NA**2 + n_cladding**2)**(0.5)
41 |
42 | ##########################
43 | # Instantiate the solver #
44 | ##########################
45 | solver = bms.bessel_mode_solver(
46 | core_rad, cladding_rad, n_core, n_cladding, wavelength)
47 |
48 | ##################
49 | # Find the modes #
50 | ##################
51 | solver.solve(max_modes=max_modes)
52 |
53 | ###############################################################
54 | # Sort the mode propagation constants in descending order and #
55 | # calculate the phase velocity of each mode #
56 | ###############################################################
57 | sort_idx = np.argsort(solver.beta_arr)[::-1]
58 | beta.append(solver.beta_arr[sort_idx])
59 |
60 | ################################################
61 | # Ensure each array in beta is the same length #
62 | ################################################
63 | max_l = np.inf
64 | for i in range(len(beta)): # Get length of smallest array
65 | l = len(beta[i])
66 | if l < max_l:
67 | max_l = l
68 | for i in range(len(beta)): # Restrict lengths of all arrays
69 | beta[i] = beta[i][0:max_l:1]
70 |
71 | ################################################
72 | # Calculate the group velocity and group index #
73 | ################################################
74 | v_group = 1 / np.gradient(beta, g.omega_window, axis=0)
75 | n_group = const.c / v_group
76 |
77 | fig1, ax1 = plt.subplots()
78 | divider = make_axes_locatable(ax1)
79 | cax = divider.append_axes('right', size='5%', pad=0.05)
80 | im1 = ax1.pcolormesh(
81 | np.linspace(0, max_l - 1, max_l), g.lambda_window * 1e9, n_group)
82 | ax1.set_xlabel('Mode number')
83 | ax1.set_ylabel('Wavelength, nm')
84 | cbar1 = fig1.colorbar(im1, cax=cax, orientation='vertical')
85 | cbar1.set_label('Group index')
86 | fig1.tight_layout()
87 |
88 | fig2, ax2 = plt.subplots()
89 | divider = make_axes_locatable(ax2)
90 | cax = divider.append_axes('right', size='5%', pad=0.05)
91 | im2 = ax2.pcolormesh(
92 | np.linspace(
93 | 0,
94 | max_l - 1,
95 | max_l),
96 | g.lambda_window * 1e9,
97 | 1e-6 * v_group)
98 | ax2.set_xlabel('Mode number')
99 | ax2.set_ylabel('Wavelength, nm')
100 | cbar2 = fig1.colorbar(im2, cax=cax, orientation='vertical')
101 | cbar2.set_label('Group velocity, Mm/s')
102 | fig2.tight_layout()
103 |
104 | plt.show()
105 |
--------------------------------------------------------------------------------
/examples/saving_and_recalling_data/amp 1/grid.npz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jsfeehan/pyLaserPulse/f3c5a309b24389691ee87807e3157c0b27a7d223/examples/saving_and_recalling_data/amp 1/grid.npz
--------------------------------------------------------------------------------
/examples/saving_and_recalling_data/amp 1/optical_assembly.npz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jsfeehan/pyLaserPulse/f3c5a309b24389691ee87807e3157c0b27a7d223/examples/saving_and_recalling_data/amp 1/optical_assembly.npz
--------------------------------------------------------------------------------
/examples/saving_and_recalling_data/amp 1/pulse.npz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jsfeehan/pyLaserPulse/f3c5a309b24389691ee87807e3157c0b27a7d223/examples/saving_and_recalling_data/amp 1/pulse.npz
--------------------------------------------------------------------------------
/examples/saving_and_recalling_data/amplifier_1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | This example demonstrates how to save simulation results. This is done very
5 | easily using the optical_assemblies module, and all that is required is a
6 | valid parent directory string which is passed to the data_directory keyword
7 | argument when instantiating an optical_assemblies class.
8 |
9 | Data is saved independently for each optical assembly. A new directory is
10 | created in data_directory which has the same name as the optical assembly.
11 | Inside this directory, the following files are created:
12 | grid.npz
13 | pulse.npz.
14 | optical_assembly.npz
15 |
16 | These files can be loaded as a dictionary using the numpy.load method. The
17 | files have the following dictionary keys:
18 | grid.npz -- Contains all data required to recreate the pyLaserPulse grid:
19 | points
20 | lambda_c
21 | lambda_max
22 | pulse.npz -- The sample fields are not present if sampling is off:
23 | field
24 | output
25 | power_spectral_density
26 | energy_spectral_density
27 | repetition_rate
28 | sample_points
29 | field_samples
30 | rep_rate_samples
31 | B_integral_samples
32 | optical_assembly.npz:
33 | sample_points
34 | net_co_PSD_samples
35 | net_counter_PSD_samples
36 | boundary_value_solver_ESD_optimization_loss
37 | boundary_value_solver_field_optimization_loss
38 | inversion_vs_distance
39 | net_co_PSD
40 | net_counter_PSD
41 | co_core_ASE_ESD_output -- co_ASE passed to subsequent amplifiers.
42 | pump_points
43 | pump_wl_lims
44 |
45 | Loading data, shown in script amplifier_2.py in the same directory, is easy
46 | to do using the pyLaserPulse.grid.grid_from_pyLaserPulse_simulation and
47 | pyLaserPulse.pulse.pulse_grom_pyLaserPulse_simulation classes.
48 |
49 | James Feehan
50 | 25/6/2023.
51 | """
52 |
53 | import os
54 |
55 | from pyLaserPulse import grid
56 | from pyLaserPulse import pulse
57 | from pyLaserPulse import optical_assemblies
58 | from pyLaserPulse import single_plot_window
59 | import pyLaserPulse.catalogue_components.active_fibres as af
60 | import pyLaserPulse.catalogue_components.fibre_components as fc
61 |
62 |
63 | #############################################
64 | # Choose a directory for saving the data. #
65 | # Leave as None if no data should be saved. #
66 | #############################################
67 | directory = os.path.dirname(os.path.abspath(__file__)) + '/'
68 |
69 | ############################################################
70 | # Set time-frequency grid, pulse, and component parameters #
71 | ############################################################
72 |
73 | # Time-frequency grid parameters
74 | points = 2**11 # Number of grid points
75 | central_wl = 1030e-9 # Central wavelength, m
76 | max_wl = 1200e-9 # Maximum wavelength, m
77 |
78 | # Laser pulse parameters
79 | tau = 3e-12 # Pulse duration, s
80 | P_peak = [.15, 1.5e-4] # [P_x, P_y], W
81 | f_rep = 40e6 # Repetition frequency, Hz
82 | shape = 'Gauss' # Can also take 'sech'
83 |
84 | # isolator-WDM parameters
85 | L_in = 0.2 # input fibre length, m
86 | L_out = 0.2 # output fibre length, m
87 |
88 | # Yb-fibre parameters
89 | L = 1 # length, m
90 | ase_points = 2**8 # number of points in pump & ASE grid
91 | ase_wl_lims = [900e-9, max_wl] # wavelength limits for ASE grid
92 | bounds = {'co_pump_power': .15, # co-pump power, W
93 | 'co_pump_wavelength': 976e-9, # co-pump wavelength, m
94 | 'co_pump_bandwidth': 1e-9, # co-pump bandwidth, m
95 | 'counter_pump_power': 0} # counter-pump power, W
96 |
97 | ##############################################################
98 | # Instantiate the time-frequency grid, pulse, and components #
99 | ##############################################################
100 |
101 | # Time-frequency grid defined using the grid module
102 | g = grid.grid(points, central_wl, max_wl)
103 |
104 | # pulse defined using the pulse module
105 | p = pulse.pulse(tau, P_peak, shape, f_rep, g)
106 |
107 | # Opneti isolator/WDM hybrid component from the catalogue_components module.
108 | iso_wdm = fc.Opneti_PM_isolator_WDM_hybrid(g, L_in, L_out, g.lambda_c)
109 |
110 | # Nufern PM-YSF-HI-HP defined using the catalogue_components module
111 | ydf = af.Nufern_PM_YSF_HI_HP(g, L, p.repetition_rate, ase_points, ase_wl_lims,
112 | bounds, time_domain_gain=True)
113 |
114 | ################################################################
115 | # Use the optical_assemblies module for automatic inclusion of #
116 | # coupling loss between components and for generating plots. #
117 | ################################################################
118 | component_list = [iso_wdm, ydf]
119 | amp = optical_assemblies.sm_fibre_amplifier(
120 | g, component_list, plot=True, name='amp 1', high_res_sampling=100,
121 | data_directory=directory, verbose=True)
122 |
123 | ######################
124 | # Run the simulation #
125 | ######################
126 | p = amp.simulate(p)
127 |
128 | ##########################################################
129 | # Use the matplotlib_gallery module to display the plots #
130 | ##########################################################
131 | if amp.plot:
132 | plot_dicts = [amp.plot_dict]
133 | single_plot_window.matplotlib_gallery.launch_plot(plot_dicts=plot_dicts)
134 |
--------------------------------------------------------------------------------
/examples/saving_and_recalling_data/amplifier_2.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | This example demonstrates how to load simulation results. This is done very
5 | easily using the grid_from_pyLaserPulse_simulation and
6 | pulse_from_pyLaserPulse_simulation classes, and all that is required is a
7 | valid parent directory string which is passed to these classes at
8 | instantiation.
9 |
10 | Amplifiers can accept co-propagating ASE from preceding amplifiers using the
11 | co_ASE keyword argument in the optical_assemblies.sm_fibre_amplifier class
12 | __init__ method. The co-propagating ASE can be loaded from
13 | /amp 1/optical_assembly.npz.
14 |
15 | James Feehan
16 | 25/6/2023.
17 | """
18 |
19 |
20 | import os
21 | import numpy as np
22 |
23 | from pyLaserPulse import grid
24 | from pyLaserPulse import pulse
25 | from pyLaserPulse import optical_assemblies
26 | from pyLaserPulse import single_plot_window
27 | import pyLaserPulse.catalogue_components.active_fibres as af
28 | import pyLaserPulse.catalogue_components.fibre_components as fc
29 | import pyLaserPulse.catalogue_components.passive_fibres as pf
30 | import pyLaserPulse.base_components as bc
31 |
32 |
33 | ############################################################################
34 | # Enter the directory where the data for the preceding amplifier was saved #
35 | ############################################################################
36 | directory = os.path.dirname(os.path.abspath(__file__)) + '/amp 1/'
37 |
38 | ##############################################################
39 | # Instantiate the time-frequency grid, pulse, and components #
40 | ##############################################################
41 |
42 | # Time-frequency grid defined using the grid module
43 | g = grid.grid_from_pyLaserPulse_simulation(directory)
44 |
45 | # pulse defined using the pulse module
46 | p = pulse.pulse_from_pyLaserPulse_simulation(g, directory)
47 |
48 | # amp 1 data
49 | amp_1 = np.load(directory + 'optical_assembly.npz')
50 |
51 | # isolator-WDM parameters
52 | L_in = 0.2 # input fibre length, m
53 | L_out = 0.2 # output fibre length, m
54 |
55 | # Yb-fibre parameters
56 | L = 3 # length, m
57 | ase_points = amp_1['pump_points'] # number of points in pump & ASE grid
58 | ase_wl_lims = amp_1['pump_wl_lims'] # wavelength limits for ASE grid
59 | bounds = {'co_pump_power': 0, # co-pump power, W
60 | 'co_pump_wavelength': 976e-9, # co-pump wavelength, m
61 | 'co_pump_bandwidth': 1e-9, # co-pump bandwidth, m
62 | 'counter_pump_power': 4, # counter-pump power, W
63 | 'counter_pump_wavelength': 976e-9, # counter-pump wavelength, m
64 | 'counter_pump_bandwidth': 1e-9} # counter-pump bandwidth, m
65 |
66 | # isolator
67 | iso = fc.Opneti_high_power_PM_isolator(g, L_in, L_out, g.lambda_c)
68 |
69 | # pump combiner
70 | in_fibre = pf.PM980_XP(g, L_in, 1e-5)
71 | out_fibre = pf.Nufern_PLMA_GDF_25_250(g, 0.5, 1e-5)
72 | combiner = bc.fibre_component(
73 | g, in_fibre, out_fibre, 0.2, 200e-9, g.lambda_c, 1, 0, 0, 1e-5, order=5)
74 |
75 | # YDF
76 | ydf = af.Nufern_PLMA_YDF_25_250(
77 | g, L, p.repetition_rate, ase_points, ase_wl_lims, bounds,
78 | time_domain_gain=True, cladding_pumping=True)
79 |
80 | ################################################################
81 | # Use the optical_assemblies module for automatic inclusion of #
82 | # coupling loss between components and for generating plots. #
83 | ################################################################
84 | component_list = [iso, combiner, ydf]
85 | amp = optical_assemblies.sm_fibre_amplifier(
86 | g, component_list, plot=True, name='amp 2', high_res_sampling=100,
87 | verbose=True, co_ASE=amp_1['co_core_ASE_ESD_output'])
88 |
89 | ######################
90 | # Run the simulation #
91 | ######################
92 | p = amp.simulate(p)
93 |
94 | ##########################################################
95 | # Use the matplotlib_gallery module to display the plots #
96 | ##########################################################
97 | if amp.plot:
98 | plot_dicts = [amp.plot_dict]
99 | single_plot_window.matplotlib_gallery.launch_plot(plot_dicts=plot_dicts)
100 |
--------------------------------------------------------------------------------
/examples/supercontinuum/1040_nm_ZDW.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | import pyLaserPulse.grid as grid
6 | import pyLaserPulse.pulse as pulse
7 | import pyLaserPulse.optical_assemblies as oa
8 | import pyLaserPulse.single_plot_window as spw
9 | import pyLaserPulse.catalogue_components.passive_fibres as pf
10 |
11 |
12 | #############################################
13 | # Choose a directory for saving the data. #
14 | # Leave as None if no data should be saved. #
15 | #############################################
16 | directory = None
17 |
18 |
19 | ##############################################################
20 | # Instantiate the time-frequency grid, pulse, and components #
21 | ##############################################################
22 | # Time-frequency grid defined using the grid module
23 | points = 2**13 # Time-frequency grid points
24 | wl = 1040e-9 # Grid & pulse central wavelength
25 | max_wl = 2000e-9 # Max grid wavelength
26 | g = grid.grid(points, wl, max_wl)
27 |
28 | # pulse defined using the pulse module
29 | duration = 80e-15 # pulse duration, s
30 | Pp = [15e3, 1.5] # Peak power [slow axis, fast axis]
31 | shape = 'sech' # can accept 'Gauss'
32 | rr = 50e6 # repetition rate
33 | delay = -12e-12 # Initial delay from T = 0 s.
34 | p = pulse.pulse(duration, Pp, shape, rr, g, initial_delay=delay)
35 |
36 | # Photonic crystal fibre (NKT SC-5.0-1040-PM) from catalogue_components
37 | lenght = 1 # Fibre length in m
38 | err = 1e-8 # integration error (CQEM)
39 | pcf = pf.NKT_SC_5_1040_PM(g, lenght, err)
40 |
41 |
42 | ################################################################
43 | # Use the optical_assemblies module for automatic inclusion of #
44 | # coupling loss between components and for generating plots. #
45 | ################################################################
46 | scg = oa.passive_assembly(
47 | g,
48 | [pcf],
49 | 'scg',
50 | high_res_sampling=200,
51 | plot=True,
52 | data_directory=directory, verbose=True)
53 |
54 |
55 | ######################
56 | # Run the simulation #
57 | ######################
58 | p = scg.simulate(p)
59 |
60 |
61 | ##########################################################
62 | # Use the matplotlib_gallery module to display the plots #
63 | ##########################################################
64 | plot_dicts = [scg.plot_dict]
65 | spw.matplotlib_gallery.launch_plot(plot_dicts=plot_dicts)
66 |
--------------------------------------------------------------------------------
/examples/supercontinuum/ANDi_SCG_grating_compression.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | from pyLaserPulse import grid
6 | from pyLaserPulse import pulse
7 | from pyLaserPulse import base_components
8 | from pyLaserPulse import data
9 | from pyLaserPulse import optical_assemblies
10 | from pyLaserPulse import single_plot_window
11 | import pyLaserPulse.catalogue_components.passive_fibres as pf
12 |
13 |
14 | #############################################
15 | # Choose a directory for saving the data. #
16 | # Leave as None if no data should be saved. #
17 | #############################################
18 | directory = None
19 |
20 | ############################################################
21 | # Set time-frequency grid, pulse, and component parameters #
22 | ############################################################
23 |
24 | # Time-frequency grid parameters
25 | points = 2**14 # Number of grid points
26 | central_wl = 1050e-9 # Central wavelength, m
27 | max_wl = 8000e-9 # Maximum wavelength, m
28 |
29 | # Laser pulse parameters
30 | tau = 100e-15 # Pulse duration, s
31 | P_peak = [25000, 250] # [P_x, P_y], W
32 | f_rep = 40e6 # Repetition frequency, Hz
33 | shape = 'Gauss' # Can also take 'sech'
34 |
35 | # ANDi photonic crystal fibre parameters
36 | L_beat = 1e-2 # polarization beat length (m)
37 | L = .25 # length, m
38 |
39 | # grating compressor parameters
40 | loss = 0.04 # percent loss per grating reflection
41 | transmission = 700e-9 # transmission bandwidth
42 | coating = data.paths.materials.reflectivities.gold
43 | epsilon = 1e-1 # Jones parameter for polarization mixing and phase
44 | theta = 0 # Jones parameter for angle subtended by x-axis
45 | crosstalk = 1e-3 # polarization crosstalk
46 | beamsplitting = 0 # Useful for output couplers, etc.
47 | l_mm = 600 # grating lines per mm
48 | sep_initial = 1e-2 # initial guess for grating separation
49 | angle_initial = 0.31 # initial guess for incidence angle, rad
50 |
51 | ##############################################################
52 | # Instantiate the time-frequency grid, pulse, and components #
53 | ##############################################################
54 |
55 | # Time-frequency grid defined using the grid module
56 | g = grid.grid(points, central_wl, max_wl)
57 |
58 | # pulse defined using the pulse module
59 | p = pulse.pulse(tau, P_peak, shape, f_rep, g)
60 |
61 | # isolator
62 | iso = base_components.component(
63 | 0.2, 250e-9, g.lambda_c, epsilon, theta, 0, g, crosstalk, order=5)
64 |
65 | # ANDi photonic crystal fibre - NKT NL-1050-NEG-1 - from catalogue_components
66 | pcf = pf.NKT_NL_1050_NEG_1(g, L, 1e-6, L_beat)
67 |
68 | # grating compressor defined using the base_components module
69 | gc = base_components.grating_compressor(
70 | loss, transmission, coating, g.lambda_c, epsilon, theta, beamsplitting,
71 | crosstalk, sep_initial, angle_initial, l_mm, g, order=5, optimize=True)
72 |
73 | ################################################################
74 | # Use the optical_assemblies module for automatic inclusion of #
75 | # coupling loss between components and for generating plots. #
76 | ################################################################
77 |
78 | scg_components = [iso, pcf]
79 | scg = optical_assemblies.passive_assembly(
80 | g, scg_components, 'scg', high_res_sampling=100,
81 | plot=True, data_directory=directory, verbose=True)
82 |
83 | compressor_components = [gc]
84 | compression = optical_assemblies.passive_assembly(
85 | g, compressor_components, 'compressor', plot=True,
86 | data_directory=directory, verbose=True)
87 |
88 | ######################
89 | # Run the simulation #
90 | ######################
91 | p = scg.simulate(p)
92 | p = compression.simulate(p)
93 |
94 | ##########################################################
95 | # Use the matplotlib_gallery module to display the plots #
96 | ##########################################################
97 | if scg.plot or compression.plot:
98 | plot_dicts = [scg.plot_dict, compression.plot_dict]
99 | single_plot_window.matplotlib_gallery.launch_plot(plot_dicts=plot_dicts)
100 |
--------------------------------------------------------------------------------
/examples/supercontinuum/coherence_using_multiprocessing.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | This example shows how the python multiprocessing library can be used to
5 | run pyLaserPulse supercontinuum simulations in parallel (on multiple cores of
6 | a CPU) with different quantum noise seeds so that that complex first-order
7 | degree of coherence can be calculated.
8 |
9 | The supercontinuum simulation is wrapped in a function which can then be
10 | passed to a multiprocessing worker pool. The optical_assemblies module works
11 | well for this, but the plot option must be turned off. The function can be
12 | replaced by a class method if you prefer.
13 |
14 | It is recommended that you become familiar with some of the other examples
15 | before reading this one.
16 |
17 | James Feehan
18 | """
19 |
20 |
21 | # Limit the number of threads spawned by any process to 1.
22 | import os
23 | os.environ["OMP_NUM_THREADS"] = "1" # for openBLAS or similar
24 | # os.environ["MKL_NUM_THREADS"] = "1" # For Intel math kernel library
25 |
26 | import psutil
27 | import matplotlib.pyplot as plt
28 | import multiprocessing as mp
29 | import numpy as np
30 | import pyLaserPulse.catalogue_components.passive_fibres as pf
31 | import pyLaserPulse.optical_assemblies as oa
32 | import pyLaserPulse.pulse as pulse
33 | import pyLaserPulse.grid as grid
34 |
35 |
36 | def scg_sim(_g):
37 | """
38 | Supercontinuum simulation using pyLaserPulse
39 |
40 | Parameters
41 | ----------
42 | _g : pyLaserPulse.grid object
43 |
44 | Notes
45 | -----
46 | The pulse object is instantiated inside this function so that the quantum
47 | noise seed is different to all others in the ensemble.
48 | """
49 | # pulse
50 | duration = 200e-15 # pulse duration, s
51 | Pp = [15e3, 15] # Peak power [slow axis, fast axis]
52 | shape = 'sech' # can accept 'Gauss'
53 | rr = 50e6 # repetition rate
54 | delay = 0 # Initial delay from T = 0 s.
55 | p = pulse.pulse(duration, Pp, shape, rr, _g, initial_delay=delay)
56 |
57 | # Photonic crystal fibre (NKT SC-5.0-1040-PM) from catalogue_components
58 | length = .2 # Fibre length in m
59 | err = 1e-11 # integration error (CQEM)
60 | pcf = pf.NKT_NL_1050_NEG_1(_g, length, err, 1e-2)
61 |
62 | # supercontinuum optical assembly
63 | scg = oa.passive_assembly(_g, [pcf], 'scg', plot=False, verbose=False)
64 |
65 | p = scg.simulate(p)
66 | return p
67 |
68 |
69 | if __name__ == "__main__":
70 | # grid
71 | points = 2**13 # Time-frequency grid points
72 | wl = 1040e-9 # Grid & pulse central wavelength
73 | max_wl = 4000e-9 # Max grid wavelength
74 | g = grid.grid(points, wl, max_wl)
75 |
76 | num_processes = psutil.cpu_count(logical=False) # only use physical cores
77 | num_simulations = 4 * num_processes # no. of simulations in CFODC ensemble
78 | gridlist = [[g]] * num_simulations # iterable of func arguments
79 | pool = mp.get_context("spawn").Pool(
80 | processes=num_processes, maxtasksperchild=1)
81 | output_pulses = pool.starmap(scg_sim, gridlist)
82 | pool.close()
83 | pool.join()
84 |
85 | cfodc, lw = pulse.complex_first_order_degree_of_coherence(g, output_pulses)
86 |
87 | # Convert output_pulses[n].power_spectral_density into an array for easier
88 | # plotting
89 | PSDs = np.zeros((num_simulations, 2, g.points))
90 | for i, op in enumerate(output_pulses):
91 | PSDs[i, :, :] = op.power_spectral_density
92 |
93 | colors = ['seagreen', 'lightcoral']
94 | dark_colors = ['darkgreen', 'darkred']
95 | legend1 = []
96 | fig = plt.figure(num=1, figsize=(8, 4))
97 | ax1 = fig.add_subplot(121)
98 | ax2 = fig.add_subplot(122)
99 | for p in range(2): # iterate over polarization basis vectors
100 | for i in range(num_simulations):
101 | ax1.semilogy(
102 | g.lambda_window * 1e9, PSDs[i, p, :], c=colors[p], alpha=0.1)
103 | l, = ax1.semilogy(
104 | g.lambda_window * 1e9, np.average(PSDs[:, p, :], axis=0),
105 | c=dark_colors[p])
106 | ax2.plot(
107 | lw * 1e9, cfodc[p, :], c=['k', 'grey'][p], ls=['-', '--'][p])
108 | legend1.append(l)
109 | ax1.set_ylabel('Power spectral density, mW/nm')
110 | ax1.set_xlabel('Wavelength, nm')
111 | ax2.set_ylabel('Complex first-order degree of coherence')
112 | ax2.set_xlabel('Wavelength, nm')
113 | ax1.set_xlim([1e9 * g.lambda_min, 1e9 * g.lambda_max])
114 | ax2.set_xlim([1e9 * g.lambda_min, 1e9 * g.lambda_max])
115 | ax1.legend(legend1, ['$S_{x}(\\lambda)$', '$S_{y}(\\lambda)$'])
116 | ax2.legend(['$g_{x}(\\lambda)$', '$g_{y}(\\lambda)$'])
117 | fig.tight_layout()
118 | plt.show()
119 |
--------------------------------------------------------------------------------
/pyLaserPulse/__init__.py:
--------------------------------------------------------------------------------
1 | import pyLaserPulse.base_components
2 | import pyLaserPulse.bessel_mode_solver
3 | import pyLaserPulse.catalogue_components
4 | import pyLaserPulse.catalogue_components.active_fibres
5 | import pyLaserPulse.catalogue_components.passive_fibres
6 | import pyLaserPulse.catalogue_components.bulk_components
7 | import pyLaserPulse.catalogue_components.fibre_components
8 | import pyLaserPulse.grid
9 | import pyLaserPulse.optical_assemblies
10 | import pyLaserPulse.pulse
11 | import pyLaserPulse.pump
12 | import pyLaserPulse.data
13 | import pyLaserPulse.data.paths
14 | import pyLaserPulse.single_plot_window.matplotlib_gallery
15 |
--------------------------------------------------------------------------------
/pyLaserPulse/bessel_mode_solver.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | import numpy as np
6 | import numbers
7 | from scipy.special import jv, kn
8 | from scipy.optimize import root
9 | import matplotlib.pyplot as plt
10 |
11 |
12 | class bessel_mode_solver:
13 | """
14 | Find modes supported by a step index fibre.
15 |
16 | Based loosely on code in pyMMF and D. Marcuse, "Light Transmission Optics",
17 | Van Nostrand Reinhold, New York, 1972.
18 | """
19 |
20 | def __init__(self, core_rad, clad_rad, n_co, n_cl, wl, tol=1e-9):
21 | """
22 | Parameters
23 | ----------
24 | core_rad : float
25 | Radius of the core in m
26 | clad_rad : float
27 | Radius of the cladding in m
28 | n_co : float
29 | Refractive index of the core
30 | n_cl : float
31 | Refractive index of the cladding
32 | wl : float
33 | Wavelength of the light in m
34 | """
35 | self.k = 2 * np.pi / wl
36 | self.core_rad = core_rad
37 | self.clad_rad = clad_rad
38 | self.n_co = n_co
39 | self.n_cl = n_cl
40 | self.V = self.k * self.core_rad * np.sqrt(self.n_co**2 - self.n_cl**2)
41 | self.m = 0
42 | self.number = 0
43 | self.tol = tol
44 |
45 | def LP_eigenvalue_equation(self, u):
46 | """
47 | Find my roots for guided modes!
48 |
49 | Parameters
50 | ----------
51 | u : numpy array
52 | u = r_core * sqrt(n_core^2 * k^2 - beta^2)
53 |
54 | Returns
55 | -------
56 | numpy array
57 | (J_v(u) / (u J_v-1(u))) + (K_v(w) / (w K_v-1(w)))
58 |
59 | Notes
60 | -----
61 | LP modes can be found by finding the roots to this function.
62 | See D. Marcuse, "Light Transmission Optics", Van Nostrand Reinhold,
63 | New York, 1972.
64 | """
65 | w = np.sqrt(self.V**2 - u**2)
66 | term_1 = jv(self.m, u) / (u * jv(self.m - 1, u))
67 | term_2 = kn(self.m, w) / (w * kn(self.m - 1, w))
68 | return term_1 + term_2
69 |
70 | def root_func(self, u):
71 | """
72 | Wrapper for using scipy.optimize.root to find the roots of
73 | self.LP_eigenvalue_equation.
74 |
75 | Parameters
76 | ----------
77 | u : numpy array
78 | u = r_core * sqrt(n_core^2 * k^2 - beta^2)
79 |
80 | Returns
81 | -------
82 | scipy OptimizeResult object
83 | Solution (i.e., the roots of self.LP_eigenvalue_equation).
84 | """
85 | return root(self.LP_eigenvalue_equation, u, tol=self.tol)
86 |
87 | def solve(self, max_modes=500):
88 | """
89 | Find the LP modes of an ideal step-index fibre.
90 |
91 | Parameters
92 | ----------
93 | max_modes : int
94 | Maximum number of modes to find
95 | """
96 | beta_list = []
97 | u_list = []
98 | w_list = []
99 | m_list = []
100 | l_list = []
101 | self.num_modes = 0
102 | roots = [0]
103 | interval = np.arange(0, self.V, self.V * 1e-4)
104 |
105 | with np.errstate(all='ignore'):
106 | # Suppress numpy RuntimeWarning for zero-division error in
107 | # Bessel mode solver. Beyond my control...
108 | while len(roots) and self.num_modes < max_modes:
109 | guesses = np.argwhere(np.abs(np.diff(np.sign(
110 | self.LP_eigenvalue_equation(interval)))))
111 | sols = map(self.root_func, interval[guesses])
112 | roots = [s.x for s in sols if s.success]
113 | roots = np.unique([np.round(r / self.tol) * self.tol
114 | for r in roots if
115 | (r > 0 and r < self.V)]).tolist()
116 | roots_num = len(roots)
117 |
118 | if roots_num:
119 | degeneracy = 1 if self.m == 0 else 2
120 | beta_list.extend(
121 | [np.sqrt((self.k * self.n_co)**2
122 | - (r / self.core_rad)**2) for r in roots]
123 | * degeneracy)
124 | u_list.extend(roots * degeneracy)
125 | w_list.extend([np.sqrt(self.V**2 - r**2) for r in roots]
126 | * degeneracy)
127 | l_list.extend(
128 | [x + 1 for x in range(roots_num)] * degeneracy)
129 | m_list.extend([self.m] * roots_num * degeneracy)
130 | self.num_modes += roots_num * degeneracy
131 | self.m += 1
132 | self.beta_arr = np.asarray(beta_list)
133 | self.u_arr = np.asarray(u_list)
134 | self.w_arr = np.asarray(w_list)
135 | self.m_arr = np.asarray(m_list)
136 | self.l_arr = np.asarray(l_list)
137 |
138 | def make_modes(self, r, num_modes=1):
139 | """
140 | Return an array containing the mode shapes.
141 |
142 | Parameters
143 | ----------
144 | r : numpy array
145 | radial (polar) axis.
146 | num_modes : int.
147 | Number of mode profiles to calculate (starts from LP01)
148 | if num_modes > self.num_modes, default to self.num_modes
149 | """
150 | if num_modes > self.num_modes:
151 | num_modes = self.num_modes
152 | num_modes = int(num_modes)
153 | idx = np.linspace(0, num_modes - 1, num_modes, dtype=int)
154 | m = self.m_arr[idx]
155 | u = self.u_arr[idx]
156 | w = self.w_arr[idx]
157 | r_tiled = r[:, None].repeat(num_modes, axis=1)
158 | R = r_tiled / self.core_rad # normalized radius
159 | self.modes = jv(m, u * R)
160 | idx = r >= self.core_rad
161 | self.modes[idx, :] = (jv(m, u) / kn(m, w)) * kn(m, w * R[idx, :])
162 |
163 | def get_amplitude_distribution(self, std=None):
164 | """
165 | Calculate an incoherent sum of all modes.
166 |
167 | Parameters
168 | ----------
169 | std
170 | If None, then the modes are assumed to contain equal energy. If
171 | numeric, it is used as the standard deviation for a normal
172 | distribution which scales the mode energy (favouring low-order
173 | modes).
174 | """
175 | self.amplitude_distribution = np.abs(self.modes)
176 | self.amplitude_distribution /= np.sum(
177 | self.amplitude_distribution, axis=0)
178 | n_modes = self.modes.shape[0]
179 | if std is not None:
180 | if not isinstance(std, numbers.Number):
181 | raise ValueError("std in bessel_mode_solver.incoherent_sum"
182 | + " Must be a number.")
183 | else:
184 | mode_idx = np.linspace(0, n_modes - 1, n_modes)
185 | scale = np.exp(-mode_idx**2 / std**2)
186 | self.amplitude_distribution = self.amplitude_distribution.T
187 | self.amplitude_distribution *= scale
188 | self.amplitude_distribution = self.amplitude_distribution.T
189 | self.amplitude_distribution = np.sum(
190 | self.amplitude_distribution, axis=1)
191 | self.amplitude_distribution /= np.sum(self.amplitude_distribution)
192 |
193 |
194 | if __name__ == "__main__":
195 | modes = 4000
196 | sol = bessel_mode_solver(125e-6, 160e-6, 1.45, 1.378, 976e-9)
197 | sol.solve(max_modes=modes)
198 |
199 | r = np.linspace(0, sol.clad_rad, 2**11)
200 | sol.make_modes(r, num_modes=modes)
201 | sol.get_amplitude_distribution()
202 | fig = plt.figure(1)
203 | ax = fig.add_subplot(111)
204 | ax.plot(r * 1e6, sol.modes[:, 0:4000], c='seagreen', alpha=0.15)
205 | ax.plot(r * 1e6,
206 | sol.amplitude_distribution / sol.amplitude_distribution.max(),
207 | c='k')
208 | ax.set_ylabel('Amplitude, arb.')
209 | ax.set_xlabel(r'radius, $\mu$m')
210 | plt.show()
211 |
--------------------------------------------------------------------------------
/pyLaserPulse/catalogue_components/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jsfeehan/pyLaserPulse/f3c5a309b24389691ee87807e3157c0b27a7d223/pyLaserPulse/catalogue_components/__init__.py
--------------------------------------------------------------------------------
/pyLaserPulse/catalogue_components/bulk_components.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | Created on Tue Jun 20 09:22:08 2023
5 |
6 | @author: james feehan
7 |
8 | Module of classes for branded bulk components.
9 | """
10 |
11 | import numpy as np
12 |
13 | from pyLaserPulse.data import paths
14 | import pyLaserPulse.base_components as bc
15 | import pyLaserPulse.utils as utils
16 |
17 |
18 | class half_wave_plate(bc.component):
19 | """
20 | Zero-order half wave plate. Assumes perfect half-wave retardation for all
21 | wavelengths.
22 |
23 | Parameters
24 | ----------
25 | grid : pyLaserPulse.grid.grid object
26 | lambda_c : float
27 | Central wavelength of the transmission window in m
28 | angle : float
29 | Angle with respect to the x-axis.
30 | """
31 | def __init__(self, grid, lambda_c, angle):
32 | loss = 0.01
33 | trans_bw = 150e-9
34 | epsilon = -1
35 | beamsplitting = 0
36 | crosstalk = 0
37 | super().__init__(
38 | loss, trans_bw, lambda_c, epsilon, angle, beamsplitting, grid,
39 | crosstalk, output_coupler=False)
40 |
41 |
42 | class quarter_wave_plate(bc.component):
43 | """
44 | Zero-order quarter wave plate. Assumes perfect quarter-wave retardation for
45 | all wavelengths.
46 |
47 | Parameters
48 | ----------
49 | grid : pyLaserPulse.grid.grid object
50 | lambda_c : float
51 | Central wavelength of the transmission window in m
52 | angle : float
53 | Angle with respect to the x-axis.
54 | """
55 | def __init__(self, grid, lambda_c, angle):
56 | loss = 0.01
57 | trans_bw = 150e-9
58 | epsilon = 0 + 1j
59 | beamsplitting = 0
60 | crosstalk = 0
61 | super().__init__(
62 | loss, trans_bw, lambda_c, epsilon, angle, beamsplitting, grid,
63 | crosstalk, output_coupler=False)
64 |
65 |
66 | class Thorlabs_broadband_PBS(bc.component):
67 | """
68 | PBS with parameters roughly based on the Thorlabs broadband PBS range.
69 |
70 | Parameters
71 | ----------
72 | grid : pyLaserPulse.grid.grid object
73 | lambda_c : float
74 | Central wavelength of the transmission window in m
75 | output_coupler : bool
76 | If True, this component behaves like an output coupler or tap.
77 | """
78 | def __init__(self, grid, lambda_c, output_coupler):
79 | loss = 0.075
80 | trans_bw = 150e-9
81 | epsilon = 2e-3
82 | angle = 0
83 | beamsplitting = 1
84 | crosstalk = 1e-3
85 | super().__init__(
86 | loss, trans_bw, lambda_c, epsilon, angle, beamsplitting, grid,
87 | crosstalk, output_coupler=output_coupler,
88 | coupler_type="polarization")
89 |
90 |
91 | class Thorlabs_Laserline_bandpass(bc.component):
92 | """
93 | Bandpass filter with parameters roughly based on the Thorlabs NIR/Laserline
94 | bandpass range.
95 |
96 | Parameters
97 | ----------
98 | grid : pyLaserPulse.grid.grid object
99 | transmission_bandwidth : float
100 | Transmission window FWHM in m.
101 | lambda_c : float
102 | Central wavelength of the transmission window in m
103 | output_coupler : bool
104 | If True, this component behaves like an output coupler or tap.
105 | """
106 | def __init__(self, grid, transmission_bandwidth, lambda_c):
107 | loss = 0.25
108 | epsilon = 1
109 | angle = 0
110 | beamsplitting = 0
111 | crosstalk = 1e-3
112 | super().__init__(
113 | loss, transmission_bandwidth, lambda_c, epsilon, angle,
114 | beamsplitting, grid, crosstalk, output_coupler=False)
115 |
116 |
117 | class Andover_155FS10_25_bandpass(bc.component):
118 | """
119 | Bandpass filter with parameters based on the Andover 155FS10-25 filer.
120 | Transmission bandwidth is 10 nm.
121 |
122 | Parameters
123 | ----------
124 | grid : pyLaserPulse.grid.grid object
125 | peak_transmission : float
126 | Peak of transmission window. The measured data from the Andover website
127 | (component_loss_profiles/Andover_155FS10_25_bandpass) is usually
128 | used, but some papers suggest much higher peak transmission is
129 | possible. The manufacturer's specification is 0.324244.
130 | """
131 | def __init__(self, grid, peak_transmission=0.324244):
132 | loss = 0 # measured profile interpolated from file
133 | epsilon = 1
134 | angle = 0
135 | beamsplitting = 0
136 | crosstalk = 1e-3
137 | trans_bw = 1e-9
138 | super().__init__(
139 | loss, trans_bw, grid.lambda_c, epsilon, angle, beamsplitting, grid,
140 | crosstalk, output_coupler=False)
141 | loss_data = utils.interpolate_data_from_file(
142 | paths.components.loss_spectra.Andover_155FS10_25_bandpass,
143 | grid.lambda_window, 1e-9, 1, interp_kind='linear')
144 | loss_data[loss_data < 0] = 0
145 | loss_data = peak_transmission * loss_data / np.amax(loss_data)
146 | self.transmission_spectrum = utils.fftshift(loss_data)
147 |
148 |
149 | class AA_Optoelectronic_AOM_MT200_A02_980_1100(bc.pulse_picker):
150 | """
151 | AA Optoelectronic AOM MT200-A02-xx, 200 MHz.
152 |
153 | Parameters
154 | ----------
155 | grid : pyLaserPulse.grid.grid object
156 | time_open : float
157 | Time in s that the pulse picker is 'open', i.e., lets light through
158 | Cannot be less than 20 ns (specified rise time of 10 ns).
159 | rate_reduction_factor : int
160 | Ratio of the input and output repetition rates of the pulse picker.
161 | input_rep_rate : float
162 | Repetition rate of the seed laser before the pulse picker.
163 |
164 | Notes
165 | -----
166 | The diffraction efficiency of free-space AOMs is dependent on the wavelength
167 | and beam diameter. The minimum specified diffraction efficiency of 80% is
168 | used in this model.
169 | """
170 | def __init__(self, grid, time_open, rate_reduction_factor, input_rep_rate):
171 | if time_open >= 20e-9:
172 | loss = 1 - (0.98 * 0.8) # 98 % trans., 80 % diff. efficiency
173 | lambda_c = 1040e-9
174 | trans_bw = 120e-9 # specified for 980 - 1100 nm
175 | epsilon = 1
176 | super().__init__(
177 | loss, trans_bw, lambda_c, epsilon, 0, 0, grid, 1e-5, time_open,
178 | rate_reduction_factor, input_rep_rate, order=10)
179 | else:
180 | raise ValueError(
181 | "The AA Optoelectronic MT200-A0.2-xx 200 MHz AOM has a "
182 | "specified rise time of 10 ns. Parameter time_open cannot be "
183 | "less than 20 ns.")
184 |
--------------------------------------------------------------------------------
/pyLaserPulse/coupling.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | Created on Mon Nov 23 16:06:13 2020
5 |
6 | @author: james feehan
7 |
8 | Module of general functions for calculating propagation parameters.
9 | """
10 | import numpy as np
11 |
12 | import pyLaserPulse.abstract_bases as bases
13 | import pyLaserPulse.utils as utils
14 |
15 |
16 | class fibreToFibreCoupling(bases.coupling_transmission_base):
17 | """
18 | Class for handling the transmission loss between two spliced fibres.
19 | """
20 |
21 | def __init__(self, grid, comp1, comp2, name_list_1, name_list_2):
22 | """
23 | Parameters
24 | ----------
25 | grid : pyLaerPulse grid object.
26 | comp_1 : first component object
27 | comp_2 : second component object
28 | name_list_1 : list
29 | Generated by optical_assemblies.py
30 | Names populating the list are obtained as follows:
31 | comp_1.__class__.__name__, comp_1.__class__.__bases__.__name__, ...
32 | name_list_2 : list
33 | Names generated by optical_assemblies.py
34 | Names populating the list are obtained as follows:
35 | comp_2.__class__.__name__, comp_2.__class__.__bases__.__name__, ...
36 | """
37 | super().__init__(grid)
38 | try:
39 | # step_index_fibre to step_index_fibre (passive or active)
40 | self._make_transmission_spectrum(
41 | comp1.effective_MFD, comp2.effective_MFD)
42 | except AttributeError:
43 | types = ('fibre_component', 'fibre_pulse_picker')
44 | # loss between fibre_components/fibre_pulse_pickers
45 | c1 = any(comp in name for name in name_list_1 for comp in types)
46 | c2 = any(comp in name for name in name_list_2 for comp in types)
47 | if c1 and c2:
48 | self._make_transmission_spectrum(
49 | comp1.output_fibre.effective_MFD,
50 | comp2.input_fibre.effective_MFD)
51 | # loss between fibre_component/fibre_pulse_picker and fibre
52 | elif c1:
53 | self._make_transmission_spectrum(
54 | comp1.output_fibre.effective_MFD, comp2.effective_MFD)
55 | # loss between fibre and fibre_component/fibre_pulse_picker
56 | elif c2:
57 | self._make_transmission_spectrum(
58 | comp1.effective_MFD, comp2.input_fibre.effective_MFD)
59 |
60 | def _make_transmission_spectrum(self, MFD_1, MFD_2):
61 | """
62 | Calculate splice transmission between two fibres with arbitrary MFD.
63 |
64 | Parameters
65 | ----------
66 | MFD_1 : numpy array
67 | Mode field diameter as a function of wavelength for component 1
68 | MFD_2 : numpy array
69 | Mode field diameter as a function of wavelength for component 2
70 |
71 | Notes
72 | -----
73 | This method uses the equation for splice loss between two fibres with
74 | an axial offset* chosen to match Fujikura's specification for the
75 | typical loss between two identical lengths of SMF (0.03 dB). The offset
76 | giving this loss was found to be mean(MFD_1, MFD_2) / 12. Angular
77 | offset is not taken into account because the mode properties for the
78 | axial equations used here are not required, and also because equations
79 | for losses associated with tilt and displacement have the same form.
80 |
81 | *D. Marcuse, "Loss analysis of single mode fibre splices", Bell Systems
82 | Technical Journal 56(5), 1977.
83 | """
84 | # Marcuse formula giving typical loss for Fujikura SMF->SMF splice.
85 | d = 0.5 * (MFD_1 + MFD_2) / 12
86 | numerator = 2 * MFD_1 * MFD_2
87 | denominator = MFD_1**2 + MFD_2**2
88 | self.transmission_spectrum = \
89 | (numerator / denominator)**2 * np.exp(-2 * d**2 / denominator)
90 | self.transmission_spectrum = utils.fftshift(self.transmission_spectrum)
91 |
92 |
93 | class freeSpaceToFibreCoupling(bases.coupling_transmission_base):
94 | """
95 | Class for handling the transmission loss between free-space and fibre
96 | components.
97 | """
98 |
99 | def __init__(self, grid, comp):
100 | """
101 | Parameters
102 | ----------
103 | grid : pyLaerPulse grid object.
104 | comp : Component object.
105 | """
106 | super().__init__(grid)
107 | # Possibly include mode-dependent coupling loss later on.
108 | # Used to have an approximation of this using fibre/fibre couling
109 | # loss and a mismatched MFD, but this is not general enough to be
110 | # useful.
111 | # For now, just use Fresnel loss.
112 | self._make_transmission_spectrum(1.0003, comp.signal_ref_index)
113 |
114 | def _make_transmission_spectrum(self, ref_index_1, ref_index_2):
115 | """
116 | Calculate the transmission at the interface between two refractive
117 | indices.
118 |
119 | Parameters
120 | ----------
121 | ref_index_1 : numpy array
122 | Refractive index as a function of wavelength for component 1
123 | ref_index_2 : numpy array
124 | Refractive index as a function of wavelength for component 2
125 |
126 | Notes
127 | -----
128 | Approximation with no polarization dependence. Reasonably accurate for
129 | air/silica boundaries up to an incidence angle of ~10 degrees from
130 | normal, so adequate for angled cleaves.
131 | """
132 | numerator = ref_index_1 - ref_index_2
133 | denominator = ref_index_1 + ref_index_2
134 | self.transmission_spectrum = 1 - (numerator / denominator)**2
135 | self.transmission_spectrum = utils.fftshift(self.transmission_spectrum)
136 |
--------------------------------------------------------------------------------
/pyLaserPulse/data/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jsfeehan/pyLaserPulse/f3c5a309b24389691ee87807e3157c0b27a7d223/pyLaserPulse/data/__init__.py
--------------------------------------------------------------------------------
/pyLaserPulse/data/components/loss_spectra/Andover_155FS10_25_bandpass.dat:
--------------------------------------------------------------------------------
1 | 1.530000000000000000e+03 0.000000000000000000e+00
2 | 1.530099999999999909e+03 0.000000000000000000e+00
3 | 1.530200000000000045e+03 1.402000000000000034e-03
4 | 1.530299999999999955e+03 2.320000000000000014e-03
5 | 1.530400000000000091e+03 3.436000000000000252e-03
6 | 1.530500000000000000e+03 3.376000000000000095e-03
7 | 1.530599999999999909e+03 2.982000000000000275e-03
8 | 1.530700000000000045e+03 3.281000000000000062e-03
9 | 1.530799999999999955e+03 3.151999999999999854e-03
10 | 1.530900000000000091e+03 3.159000000000000349e-03
11 | 1.531000000000000000e+03 3.190999999999999826e-03
12 | 1.531099999999999909e+03 3.148000000000000190e-03
13 | 1.531200000000000045e+03 3.139999999999999996e-03
14 | 1.531299999999999955e+03 3.106999999999999953e-03
15 | 1.531400000000000091e+03 3.062000000000000485e-03
16 | 1.531500000000000000e+03 3.016000000000000018e-03
17 | 1.531599999999999909e+03 2.960999999999999657e-03
18 | 1.531700000000000045e+03 2.907999999999999995e-03
19 | 1.531799999999999955e+03 2.857000000000000164e-03
20 | 1.531900000000000091e+03 2.814000000000000095e-03
21 | 1.532000000000000000e+03 2.784000000000000016e-03
22 | 1.532099999999999909e+03 2.770000000000000327e-03
23 | 1.532200000000000045e+03 2.776999999999999955e-03
24 | 1.532299999999999955e+03 2.807000000000000033e-03
25 | 1.532400000000000091e+03 2.863999999999999792e-03
26 | 1.532500000000000000e+03 2.949000000000000232e-03
27 | 1.532599999999999909e+03 3.064000000000000317e-03
28 | 1.532700000000000045e+03 3.209000000000000047e-03
29 | 1.532799999999999955e+03 3.382000000000000024e-03
30 | 1.532900000000000091e+03 3.583000000000000247e-03
31 | 1.533000000000000000e+03 3.809000000000000320e-03
32 | 1.533099999999999909e+03 4.054000000000000312e-03
33 | 1.533200000000000045e+03 4.315000000000000259e-03
34 | 1.533299999999999955e+03 4.583999999999999533e-03
35 | 1.533400000000000091e+03 4.854999999999999941e-03
36 | 1.533500000000000000e+03 5.120999999999999684e-03
37 | 1.533599999999999909e+03 5.372000000000000039e-03
38 | 1.533700000000000045e+03 5.602000000000000209e-03
39 | 1.533799999999999955e+03 5.802000000000000733e-03
40 | 1.533900000000000091e+03 5.967000000000000082e-03
41 | 1.534000000000000000e+03 6.092000000000000193e-03
42 | 1.534099999999999909e+03 6.172999999999999668e-03
43 | 1.534200000000000045e+03 6.209000000000000109e-03
44 | 1.534299999999999955e+03 6.203000000000000180e-03
45 | 1.534400000000000091e+03 6.157000000000000146e-03
46 | 1.534500000000000000e+03 6.079000000000000202e-03
47 | 1.534599999999999909e+03 5.975000000000000276e-03
48 | 1.534700000000000045e+03 5.857000000000000227e-03
49 | 1.534799999999999955e+03 5.735000000000000514e-03
50 | 1.534900000000000091e+03 5.620000000000000863e-03
51 | 1.535000000000000000e+03 5.523000000000000131e-03
52 | 1.535099999999999909e+03 5.455999999999999912e-03
53 | 1.535200000000000045e+03 5.425999999999999400e-03
54 | 1.535299999999999955e+03 5.440000000000000391e-03
55 | 1.535400000000000091e+03 5.504000000000000212e-03
56 | 1.535500000000000000e+03 5.620000000000000863e-03
57 | 1.535599999999999909e+03 5.787999999999999742e-03
58 | 1.535700000000000045e+03 6.005000000000000789e-03
59 | 1.535799999999999955e+03 6.268000000000000134e-03
60 | 1.535900000000000091e+03 6.572000000000000584e-03
61 | 1.536000000000000000e+03 6.909999999999999476e-03
62 | 1.536099999999999909e+03 7.275000000000000216e-03
63 | 1.536200000000000045e+03 7.660000000000000142e-03
64 | 1.536299999999999955e+03 8.056999999999999926e-03
65 | 1.536400000000000091e+03 8.460999999999999771e-03
66 | 1.536500000000000000e+03 8.866000000000000617e-03
67 | 1.536599999999999909e+03 9.265000000000000666e-03
68 | 1.536700000000000045e+03 9.655000000000000387e-03
69 | 1.536799999999999955e+03 1.003200000000000099e-02
70 | 1.536900000000000091e+03 1.039400000000000046e-02
71 | 1.537000000000000000e+03 1.073900000000000028e-02
72 | 1.537099999999999909e+03 1.106600000000000118e-02
73 | 1.537200000000000045e+03 1.137499999999999969e-02
74 | 1.537299999999999955e+03 1.166800000000000129e-02
75 | 1.537400000000000091e+03 1.194700000000000102e-02
76 | 1.537500000000000000e+03 1.221500000000000016e-02
77 | 1.537599999999999909e+03 1.247600000000000098e-02
78 | 1.537700000000000045e+03 1.273400000000000053e-02
79 | 1.537799999999999955e+03 1.299500000000000134e-02
80 | 1.537900000000000091e+03 1.326500000000000075e-02
81 | 1.538000000000000000e+03 1.355100000000000054e-02
82 | 1.538099999999999909e+03 1.385699999999999953e-02
83 | 1.538200000000000045e+03 1.419100000000000049e-02
84 | 1.538299999999999955e+03 1.455700000000000049e-02
85 | 1.538400000000000091e+03 1.495900000000000007e-02
86 | 1.538500000000000000e+03 1.540000000000000049e-02
87 | 1.538599999999999909e+03 1.588000000000000175e-02
88 | 1.538700000000000045e+03 1.639900000000000038e-02
89 | 1.538799999999999955e+03 1.695500000000000132e-02
90 | 1.538900000000000091e+03 1.754500000000000157e-02
91 | 1.539000000000000000e+03 1.816300000000000206e-02
92 | 1.539099999999999909e+03 1.880500000000000227e-02
93 | 1.539200000000000045e+03 1.946600000000000066e-02
94 | 1.539299999999999955e+03 2.014099999999999918e-02
95 | 1.539400000000000091e+03 2.082800000000000276e-02
96 | 1.539500000000000000e+03 2.152499999999999900e-02
97 | 1.539599999999999909e+03 2.223299999999999929e-02
98 | 1.539700000000000045e+03 2.295399999999999871e-02
99 | 1.539799999999999955e+03 2.369400000000000325e-02
100 | 1.539900000000000091e+03 2.445900000000000157e-02
101 | 1.540000000000000000e+03 2.525600000000000067e-02
102 | 1.540099999999999909e+03 2.609200000000000061e-02
103 | 1.540200000000000045e+03 2.697599999999999998e-02
104 | 1.540299999999999955e+03 2.791199999999999931e-02
105 | 1.540400000000000091e+03 2.890500000000000014e-02
106 | 1.540500000000000000e+03 2.995699999999999752e-02
107 | 1.540599999999999909e+03 3.106599999999999986e-02
108 | 1.540700000000000045e+03 3.223000000000000170e-02
109 | 1.540799999999999955e+03 3.344500000000000250e-02
110 | 1.540900000000000091e+03 3.470400000000000568e-02
111 | 1.541000000000000000e+03 3.600300000000000028e-02
112 | 1.541099999999999909e+03 3.733500000000000013e-02
113 | 1.541200000000000045e+03 3.869700000000000223e-02
114 | 1.541299999999999955e+03 4.008600000000000357e-02
115 | 1.541400000000000091e+03 4.150299999999999823e-02
116 | 1.541500000000000000e+03 4.295099999999999613e-02
117 | 1.541599999999999909e+03 4.443399999999999433e-02
118 | 1.541700000000000045e+03 4.595900000000000679e-02
119 | 1.541799999999999955e+03 4.753399999999999986e-02
120 | 1.541900000000000091e+03 4.916699999999999543e-02
121 | 1.542000000000000000e+03 5.086700000000000249e-02
122 | 1.542099999999999909e+03 5.264000000000000623e-02
123 | 1.542200000000000045e+03 5.449400000000000077e-02
124 | 1.542299999999999955e+03 5.643200000000000299e-02
125 | 1.542400000000000091e+03 5.845799999999999608e-02
126 | 1.542500000000000000e+03 6.057299999999999490e-02
127 | 1.542599999999999909e+03 6.277800000000000047e-02
128 | 1.542700000000000045e+03 6.507399999999999296e-02
129 | 1.542799999999999955e+03 6.745900000000000507e-02
130 | 1.542900000000000091e+03 6.993400000000001004e-02
131 | 1.543000000000000000e+03 7.249900000000000788e-02
132 | 1.543099999999999909e+03 7.515399999999999858e-02
133 | 1.543200000000000045e+03 7.790099999999999802e-02
134 | 1.543299999999999955e+03 8.074099999999999333e-02
135 | 1.543400000000000091e+03 8.367499999999999938e-02
136 | 1.543500000000000000e+03 8.670799999999999341e-02
137 | 1.543599999999999909e+03 8.984000000000000319e-02
138 | 1.543700000000000045e+03 9.307399999999999007e-02
139 | 1.543799999999999955e+03 9.641299999999999870e-02
140 | 1.543900000000000091e+03 9.985900000000001719e-02
141 | 1.544000000000000000e+03 1.034130000000000049e-01
142 | 1.544099999999999909e+03 1.070770000000000055e-01
143 | 1.544200000000000045e+03 1.108520000000000061e-01
144 | 1.544299999999999955e+03 1.147409999999999958e-01
145 | 1.544400000000000091e+03 1.187420000000000003e-01
146 | 1.544500000000000000e+03 1.228580000000000089e-01
147 | 1.544599999999999909e+03 1.270860000000000045e-01
148 | 1.544700000000000045e+03 1.314259999999999873e-01
149 | 1.544799999999999955e+03 1.358740000000000225e-01
150 | 1.544900000000000091e+03 1.404279999999999973e-01
151 | 1.545000000000000000e+03 1.450799999999999867e-01
152 | 1.545099999999999909e+03 1.498250000000000137e-01
153 | 1.545200000000000045e+03 1.546540000000000137e-01
154 | 1.545299999999999955e+03 1.595550000000000024e-01
155 | 1.545400000000000091e+03 1.645200000000000273e-01
156 | 1.545500000000000000e+03 1.695349999999999913e-01
157 | 1.545599999999999909e+03 1.745889999999999942e-01
158 | 1.545700000000000045e+03 1.796679999999999944e-01
159 | 1.545799999999999955e+03 1.847609999999999808e-01
160 | 1.545900000000000091e+03 1.898549999999999960e-01
161 | 1.546000000000000000e+03 1.949410000000000032e-01
162 | 1.546099999999999909e+03 2.000080000000000191e-01
163 | 1.546200000000000045e+03 2.050460000000000060e-01
164 | 1.546299999999999955e+03 2.100499999999999867e-01
165 | 1.546400000000000091e+03 2.150100000000000067e-01
166 | 1.546500000000000000e+03 2.199210000000000054e-01
167 | 1.546599999999999909e+03 2.247779999999999778e-01
168 | 1.546700000000000045e+03 2.295740000000000003e-01
169 | 1.546799999999999955e+03 2.343039999999999845e-01
170 | 1.546900000000000091e+03 2.389630000000000087e-01
171 | 1.547000000000000000e+03 2.435450000000000115e-01
172 | 1.547099999999999909e+03 2.480430000000000135e-01
173 | 1.547200000000000045e+03 2.524510000000000365e-01
174 | 1.547299999999999955e+03 2.567630000000000190e-01
175 | 1.547400000000000091e+03 2.609710000000000085e-01
176 | 1.547500000000000000e+03 2.650679999999999703e-01
177 | 1.547599999999999909e+03 2.690460000000000074e-01
178 | 1.547700000000000045e+03 2.728980000000000294e-01
179 | 1.547799999999999955e+03 2.766170000000000018e-01
180 | 1.547900000000000091e+03 2.801960000000000006e-01
181 | 1.548000000000000000e+03 2.836279999999999912e-01
182 | 1.548099999999999909e+03 2.869059999999999944e-01
183 | 1.548200000000000045e+03 2.900260000000000060e-01
184 | 1.548299999999999955e+03 2.929820000000000202e-01
185 | 1.548400000000000091e+03 2.957710000000000061e-01
186 | 1.548500000000000000e+03 2.983890000000000153e-01
187 | 1.548599999999999909e+03 3.008350000000000191e-01
188 | 1.548700000000000045e+03 3.031099999999999905e-01
189 | 1.548799999999999955e+03 3.052159999999999873e-01
190 | 1.548900000000000091e+03 3.071570000000000134e-01
191 | 1.549000000000000000e+03 3.089390000000000192e-01
192 | 1.549099999999999909e+03 3.105700000000000127e-01
193 | 1.549200000000000045e+03 3.120600000000000041e-01
194 | 1.549299999999999955e+03 3.134199999999999764e-01
195 | 1.549400000000000091e+03 3.146630000000000260e-01
196 | 1.549500000000000000e+03 3.157999999999999696e-01
197 | 1.549599999999999909e+03 3.168429999999999858e-01
198 | 1.549700000000000045e+03 3.178050000000000042e-01
199 | 1.549799999999999955e+03 3.186939999999999773e-01
200 | 1.549900000000000091e+03 3.195179999999999687e-01
201 | 1.550000000000000000e+03 3.202830000000000399e-01
202 | 1.550099999999999909e+03 3.209899999999999975e-01
203 | 1.550200000000000045e+03 3.216410000000000102e-01
204 | 1.550299999999999955e+03 3.222320000000000184e-01
205 | 1.550400000000000091e+03 3.227600000000000469e-01
206 | 1.550500000000000000e+03 3.232189999999999785e-01
207 | 1.550599999999999909e+03 3.236020000000000008e-01
208 | 1.550700000000000045e+03 3.239009999999999945e-01
209 | 1.550799999999999955e+03 3.241120000000000112e-01
210 | 1.550900000000000091e+03 3.242269999999999874e-01
211 | 1.551000000000000000e+03 3.242439999999999767e-01
212 | 1.551099999999999909e+03 3.241590000000000305e-01
213 | 1.551200000000000045e+03 3.239730000000000110e-01
214 | 1.551299999999999955e+03 3.236880000000000313e-01
215 | 1.551400000000000091e+03 3.233050000000000090e-01
216 | 1.551500000000000000e+03 3.228300000000000058e-01
217 | 1.551599999999999909e+03 3.222679999999999989e-01
218 | 1.551700000000000045e+03 3.216220000000000190e-01
219 | 1.551799999999999955e+03 3.208969999999999878e-01
220 | 1.551900000000000091e+03 3.200959999999999916e-01
221 | 1.552000000000000000e+03 3.192180000000000017e-01
222 | 1.552099999999999909e+03 3.182630000000000181e-01
223 | 1.552200000000000045e+03 3.172250000000000347e-01
224 | 1.552299999999999955e+03 3.160990000000000189e-01
225 | 1.552400000000000091e+03 3.148740000000000427e-01
226 | 1.552500000000000000e+03 3.135399999999999854e-01
227 | 1.552599999999999909e+03 3.120850000000000013e-01
228 | 1.552700000000000045e+03 3.104939999999999922e-01
229 | 1.552799999999999955e+03 3.087559999999999749e-01
230 | 1.552900000000000091e+03 3.068569999999999909e-01
231 | 1.553000000000000000e+03 3.047870000000000301e-01
232 | 1.553099999999999909e+03 3.025370000000000004e-01
233 | 1.553200000000000045e+03 3.001020000000000354e-01
234 | 1.553299999999999955e+03 2.974769999999999914e-01
235 | 1.553400000000000091e+03 2.946639999999999815e-01
236 | 1.553500000000000000e+03 2.916650000000000076e-01
237 | 1.553599999999999909e+03 2.884850000000000469e-01
238 | 1.553700000000000045e+03 2.851339999999999986e-01
239 | 1.553799999999999955e+03 2.816190000000000082e-01
240 | 1.553900000000000091e+03 2.779530000000000056e-01
241 | 1.554000000000000000e+03 2.741460000000000008e-01
242 | 1.554099999999999909e+03 2.702100000000000057e-01
243 | 1.554200000000000045e+03 2.661550000000000304e-01
244 | 1.554299999999999955e+03 2.619920000000000027e-01
245 | 1.554400000000000091e+03 2.577289999999999859e-01
246 | 1.554500000000000000e+03 2.533730000000000149e-01
247 | 1.554599999999999909e+03 2.489310000000000134e-01
248 | 1.554700000000000045e+03 2.444079999999999864e-01
249 | 1.554799999999999955e+03 2.398069999999999924e-01
250 | 1.554900000000000091e+03 2.351330000000000087e-01
251 | 1.555000000000000000e+03 2.303900000000000114e-01
252 | 1.555099999999999909e+03 2.255810000000000037e-01
253 | 1.555200000000000045e+03 2.207110000000000183e-01
254 | 1.555299999999999955e+03 2.157870000000000066e-01
255 | 1.555400000000000091e+03 2.108140000000000014e-01
256 | 1.555500000000000000e+03 2.058010000000000117e-01
257 | 1.555599999999999909e+03 2.007560000000000178e-01
258 | 1.555700000000000045e+03 1.956890000000000018e-01
259 | 1.555799999999999955e+03 1.906090000000000007e-01
260 | 1.555900000000000091e+03 1.855260000000000242e-01
261 | 1.556000000000000000e+03 1.804500000000000270e-01
262 | 1.556099999999999909e+03 1.753920000000000201e-01
263 | 1.556200000000000045e+03 1.703600000000000114e-01
264 | 1.556299999999999955e+03 1.653630000000000100e-01
265 | 1.556400000000000091e+03 1.604099999999999970e-01
266 | 1.556500000000000000e+03 1.555090000000000083e-01
267 | 1.556599999999999909e+03 1.506679999999999964e-01
268 | 1.556700000000000045e+03 1.458939999999999959e-01
269 | 1.556799999999999955e+03 1.411960000000000159e-01
270 | 1.556900000000000091e+03 1.365810000000000080e-01
271 | 1.557000000000000000e+03 1.320580000000000087e-01
272 | 1.557099999999999909e+03 1.276339999999999975e-01
273 | 1.557200000000000045e+03 1.233159999999999951e-01
274 | 1.557299999999999955e+03 1.191119999999999957e-01
275 | 1.557400000000000091e+03 1.150259999999999894e-01
276 | 1.557500000000000000e+03 1.110640000000000099e-01
277 | 1.557599999999999909e+03 1.072270000000000029e-01
278 | 1.557700000000000045e+03 1.035159999999999969e-01
279 | 1.557799999999999955e+03 9.993100000000000593e-02
280 | 1.557900000000000091e+03 9.646599999999999620e-02
281 | 1.558000000000000000e+03 9.311700000000000532e-02
282 | 1.558099999999999909e+03 8.987799999999999956e-02
283 | 1.558200000000000045e+03 8.674099999999999866e-02
284 | 1.558299999999999955e+03 8.369799999999999462e-02
285 | 1.558400000000000091e+03 8.074199999999999433e-02
286 | 1.558500000000000000e+03 7.786500000000000365e-02
287 | 1.558599999999999909e+03 7.506300000000000472e-02
288 | 1.558700000000000045e+03 7.233299999999999452e-02
289 | 1.558799999999999955e+03 6.967199999999999782e-02
290 | 1.558900000000000091e+03 6.708100000000000174e-02
291 | 1.559000000000000000e+03 6.456199999999999439e-02
292 | 1.559099999999999909e+03 6.211900000000000061e-02
293 | 1.559200000000000045e+03 5.975699999999999762e-02
294 | 1.559299999999999955e+03 5.748100000000000431e-02
295 | 1.559400000000000091e+03 5.529600000000000487e-02
296 | 1.559500000000000000e+03 5.320599999999999635e-02
297 | 1.559599999999999909e+03 5.121500000000000358e-02
298 | 1.559700000000000045e+03 4.932500000000000079e-02
299 | 1.559799999999999955e+03 4.753500000000000086e-02
300 | 1.559900000000000091e+03 4.584399999999999586e-02
301 | 1.560000000000000000e+03 4.424800000000000261e-02
302 | 1.560099999999999909e+03 4.274100000000000121e-02
303 | 1.560200000000000045e+03 4.131499999999999756e-02
304 | 1.560299999999999955e+03 3.996199999999999752e-02
305 | 1.560400000000000091e+03 3.867199999999999804e-02
306 | 1.560500000000000000e+03 3.743400000000000200e-02
307 | 1.560599999999999909e+03 3.623699999999999838e-02
308 | 1.560700000000000045e+03 3.507199999999999901e-02
309 | 1.560799999999999955e+03 3.392699999999999882e-02
310 | 1.560900000000000091e+03 3.279599999999999876e-02
311 | 1.561000000000000000e+03 3.167400000000000077e-02
312 | 1.561099999999999909e+03 3.055600000000000330e-02
313 | 1.561200000000000045e+03 2.944199999999999942e-02
314 | 1.561299999999999955e+03 2.833600000000000008e-02
315 | 1.561400000000000091e+03 2.724100000000000132e-02
316 | 1.561500000000000000e+03 2.616699999999999929e-02
317 | 1.561599999999999909e+03 2.512100000000000444e-02
318 | 1.561700000000000045e+03 2.411500000000000102e-02
319 | 1.561799999999999955e+03 2.315900000000000250e-02
320 | 1.561900000000000091e+03 2.226300000000000154e-02
321 | 1.562000000000000000e+03 2.143499999999999919e-02
322 | 1.562099999999999909e+03 2.067899999999999947e-02
323 | 1.562200000000000045e+03 1.999700000000000089e-02
324 | 1.562299999999999955e+03 1.938899999999999998e-02
325 | 1.562400000000000091e+03 1.884900000000000117e-02
326 | 1.562500000000000000e+03 1.836799999999999891e-02
327 | 1.562599999999999909e+03 1.793800000000000255e-02
328 | 1.562700000000000045e+03 1.754599999999999910e-02
329 | 1.562799999999999955e+03 1.717899999999999983e-02
330 | 1.562900000000000091e+03 1.682700000000000168e-02
331 | 1.563000000000000000e+03 1.647999999999999812e-02
332 | 1.563099999999999909e+03 1.612799999999999997e-02
333 | 1.563200000000000045e+03 1.576899999999999830e-02
334 | 1.563299999999999955e+03 1.539800000000000023e-02
335 | 1.563400000000000091e+03 1.501600000000000157e-02
336 | 1.563500000000000000e+03 1.462499999999999911e-02
337 | 1.563599999999999909e+03 1.423000000000000133e-02
338 | 1.563700000000000045e+03 1.383599999999999934e-02
339 | 1.563799999999999955e+03 1.344799999999999988e-02
340 | 1.563900000000000091e+03 1.307000000000000002e-02
341 | 1.564000000000000000e+03 1.270699999999999955e-02
342 | 1.564099999999999909e+03 1.235999999999999946e-02
343 | 1.564200000000000045e+03 1.203000000000000076e-02
344 | 1.564299999999999955e+03 1.171799999999999925e-02
345 | 1.564400000000000091e+03 1.142099999999999886e-02
346 | 1.564500000000000000e+03 1.113600000000000007e-02
347 | 1.564599999999999909e+03 1.086200000000000013e-02
348 | 1.564700000000000045e+03 1.059399999999999925e-02
349 | 1.564799999999999955e+03 1.032999999999999891e-02
350 | 1.564900000000000091e+03 1.006799999999999883e-02
351 | 1.565000000000000000e+03 9.806000000000000480e-03
352 | 1.565099999999999909e+03 9.546000000000000665e-03
353 | 1.565200000000000045e+03 9.286000000000000851e-03
354 | 1.565299999999999955e+03 9.029000000000000567e-03
355 | 1.565400000000000091e+03 8.776000000000000814e-03
356 | 1.565500000000000000e+03 8.529999999999999388e-03
357 | 1.565599999999999909e+03 8.290999999999999759e-03
358 | 1.565700000000000045e+03 8.061000000000000457e-03
359 | 1.565799999999999955e+03 7.842000000000000012e-03
360 | 1.565900000000000091e+03 7.631999999999999895e-03
361 | 1.566000000000000000e+03 7.429999999999999973e-03
362 | 1.566099999999999909e+03 7.234000000000000846e-03
363 | 1.566200000000000045e+03 7.039999999999999383e-03
364 | 1.566299999999999955e+03 6.843000000000000124e-03
365 | 1.566400000000000091e+03 6.637999999999999803e-03
366 | 1.566500000000000000e+03 6.420000000000000359e-03
367 | 1.566599999999999909e+03 6.181999999999999995e-03
368 | 1.566700000000000045e+03 5.921999999999999313e-03
369 | 1.566799999999999955e+03 5.636000000000000384e-03
370 | 1.566900000000000091e+03 5.324999999999999872e-03
371 | 1.567000000000000000e+03 4.990999999999999777e-03
372 | 1.567099999999999909e+03 4.640000000000000027e-03
373 | 1.567200000000000045e+03 4.283000000000000348e-03
374 | 1.567299999999999955e+03 3.930000000000000333e-03
375 | 1.567400000000000091e+03 3.597000000000000371e-03
376 | 1.567500000000000000e+03 3.300000000000000416e-03
377 | 1.567599999999999909e+03 3.054999999999999990e-03
378 | 1.567700000000000045e+03 2.877000000000000217e-03
379 | 1.567799999999999955e+03 2.778000000000000087e-03
380 | 1.567900000000000091e+03 2.766000000000000229e-03
381 | 1.568000000000000000e+03 2.842000000000000342e-03
382 | 1.568099999999999909e+03 3.003000000000000027e-03
383 | 1.568200000000000045e+03 3.236999999999999860e-03
384 | 1.568299999999999955e+03 3.527999999999999886e-03
385 | 1.568400000000000091e+03 3.851000000000000256e-03
386 | 1.568500000000000000e+03 4.183000000000000086e-03
387 | 1.568599999999999909e+03 4.490000000000000067e-03
388 | 1.568700000000000045e+03 4.752000000000000147e-03
389 | 1.568799999999999955e+03 4.939999999999999947e-03
390 | 1.568900000000000091e+03 5.022999999999999687e-03
391 | 1.569000000000000000e+03 5.039000000000000076e-03
392 | 1.569099999999999909e+03 4.897000000000000311e-03
393 | 1.569200000000000045e+03 4.675000000000000336e-03
394 | 1.569299999999999955e+03 4.494999999999999864e-03
395 | 1.569400000000000091e+03 3.905000000000000051e-03
396 | 1.569500000000000000e+03 3.830000000000000071e-03
397 | 1.569599999999999909e+03 3.561000000000000363e-03
398 | 1.569700000000000045e+03 2.273999999999999980e-03
399 | 1.569799999999999955e+03 1.542999999999999884e-03
400 | 1.569900000000000091e+03 0.000000000000000000e+00
401 | 1.570000000000000000e+03 0.000000000000000000e+00
402 |
--------------------------------------------------------------------------------
/pyLaserPulse/data/components/loss_spectra/Thorlabs_IO_J_1030.dat:
--------------------------------------------------------------------------------
1 | 1.010403354994710639e+03,7.524374532525476278e-01
2 | 1.011011787819253414e+03,7.528270033251165394e-01
3 | 1.011654022467381992e+03,7.532210878429747014e-01
4 | 1.012363860762681952e+03,7.536328979196903477e-01
5 | 1.013073699057981912e+03,7.540224479922593703e-01
6 | 1.013783537353281986e+03,7.544170571566798378e-01
7 | 1.014527177472167637e+03,7.548049208652982678e-01
8 | 1.015270817591053401e+03,7.551573709309558069e-01
9 | 1.016014457709939052e+03,7.555098209966135681e-01
10 | 1.016758097828824702e+03,7.558369756030133280e-01
11 | 1.017501737947710467e+03,7.561405211141060123e-01
12 | 1.018245378066596118e+03,7.564272029856935475e-01
13 | 1.018989018185481882e+03,7.567020803096276005e-01
14 | 1.019732658304367533e+03,7.569533485382542448e-01
15 | 1.020476298423253184e+03,7.571826940355242952e-01
16 | 1.021219938542138948e+03,7.573985486211901597e-01
17 | 1.021963578661024599e+03,7.575992259313014676e-01
18 | 1.022707218779910363e+03,7.577813532379571448e-01
19 | 1.023450858898796014e+03,7.579449305411570803e-01
20 | 1.024194499017681665e+03,7.580848987490498292e-01
21 | 1.024938139136567315e+03,7.582130624092888738e-01
22 | 1.025681779255453193e+03,7.583243624300227692e-01
23 | 1.026425419374338844e+03,7.584137397194001817e-01
24 | 1.027169059493224495e+03,7.584879397332227047e-01
25 | 1.027912699612110146e+03,7.585419033796392263e-01
26 | 1.028656339730996024e+03,7.585857488423526362e-01
27 | 1.029399979849881674e+03,7.586177897574124529e-01
28 | 1.030143619968767325e+03,7.586228488492640087e-01
29 | 1.030887260087652976e+03,7.586042988458082670e-01
30 | 1.031630900206538627e+03,7.585739442946990430e-01
31 | 1.032374540325424505e+03,7.585267261040846698e-01
32 | 1.033118180444310156e+03,7.584609579100145549e-01
33 | 1.033861820563195806e+03,7.583884442601422915e-01
34 | 1.034605460682081457e+03,7.582889487870621004e-01
35 | 1.035349100800967108e+03,7.581742760384270197e-01
36 | 1.036092740919852986e+03,7.580477987421383457e-01
37 | 1.036836381038738637e+03,7.579044578063445226e-01
38 | 1.037580021157624287e+03,7.577408805031446981e-01
39 | 1.038323661276509938e+03,7.575621259243900951e-01
40 | 1.039067301395395589e+03,7.573799986177344179e-01
41 | 1.039810941514281467e+03,7.571658303960191461e-01
42 | 1.040554581633167118e+03,7.569347985347986141e-01
43 | 1.041298221752052768e+03,7.567003939456768968e-01
44 | 1.042041861870938419e+03,7.564423802612482151e-01
45 | 1.042785501989824297e+03,7.561641302094131989e-01
46 | 1.043529142108709948e+03,7.558841937936278121e-01
47 | 1.044272782227595599e+03,7.555806482825350168e-01
48 | 1.045016422346481249e+03,7.552551800400857385e-01
49 | 1.045760062465366900e+03,7.549246527057847933e-01
50 | 1.046503702584252778e+03,7.545823208238303659e-01
51 | 1.047247342703138429e+03,7.542079480268160108e-01
52 | 1.047990982822024080e+03,7.538335752298016557e-01
53 | 1.048734622940909730e+03,7.534473978851338183e-01
54 | 1.049478263059795381e+03,7.530392978091091649e-01
55 | 1.049883884942824125e+03,7.528166977676411520e-01
56 |
--------------------------------------------------------------------------------
/pyLaserPulse/data/fibres/cross_sections/Er_silica.dat:
--------------------------------------------------------------------------------
1 | 9.320000000000000000e+02 0.000000000000000000e+00 0.000000000000000000e+00
2 | 9.360000000000000000e+02 0.000000000000000000e+00 1.312336000000000007e-27
3 | 9.397726999999999862e+02 0.000000000000000000e+00 2.624672000000000013e-27
4 | 9.437500000000000000e+02 0.000000000000000000e+00 5.249344000000000027e-27
5 | 9.477273000000000138e+02 0.000000000000000000e+00 7.874016000000000399e-27
6 | 9.522726999999999862e+02 0.000000000000000000e+00 1.574802999999999923e-26
7 | 9.568182000000000471e+02 0.000000000000000000e+00 3.149605999999999846e-26
8 | 9.602273000000000138e+02 0.000000000000000000e+00 5.380577000000000544e-26
9 | 9.636363999999999805e+02 0.000000000000000000e+00 8.005249000000000199e-26
10 | 9.676136000000000195e+02 0.000000000000000000e+00 1.089239000000000008e-25
11 | 9.710226999999999862e+02 0.000000000000000000e+00 1.351706000000000046e-25
12 | 9.744317999999999529e+02 0.000000000000000000e+00 1.640419999999999956e-25
13 | 9.761363999999999805e+02 0.000000000000000000e+00 1.850393999999999887e-25
14 | 9.772726999999999862e+02 0.000000000000000000e+00 1.942256999999999984e-25
15 | 9.784090999999999667e+02 0.000000000000000000e+00 1.994750999999999957e-25
16 | 9.795454999999999472e+02 0.000000000000000000e+00 1.942256999999999984e-25
17 | 9.806817999999999529e+02 0.000000000000000000e+00 1.824147000000000016e-25
18 | 9.812500000000000000e+02 0.000000000000000000e+00 1.666667000000000057e-25
19 | 9.829545000000000528e+02 0.000000000000000000e+00 1.482939999999999998e-25
20 | 9.840909000000000333e+02 0.000000000000000000e+00 1.364828999999999935e-25
21 | 9.863636000000000195e+02 0.000000000000000000e+00 1.115486000000000109e-25
22 | 9.875000000000000000e+02 0.000000000000000000e+00 9.580051999999999835e-26
23 | 9.903409000000000333e+02 0.000000000000000000e+00 7.742782000000000390e-26
24 | 9.914773000000000138e+02 0.000000000000000000e+00 6.430446000000000562e-26
25 | 9.943182000000000471e+02 0.000000000000000000e+00 5.380577000000000544e-26
26 | 9.994317999999999529e+02 0.000000000000000000e+00 3.805773999999999760e-26
27 | 1.005682000000000016e+03 0.000000000000000000e+00 2.362205000000000133e-26
28 | 1.011364000000000033e+03 0.000000000000000000e+00 1.312336000000000114e-26
29 | 1.018182000000000016e+03 0.000000000000000000e+00 6.561680000000000571e-27
30 | 1.026704999999999927e+03 0.000000000000000000e+00 3.937008000000000199e-27
31 | 1.036364000000000033e+03 0.000000000000000000e+00 2.624672000000000013e-27
32 | 1.044885999999999967e+03 0.000000000000000000e+00 1.312336000000000007e-27
33 | 1.053409000000000106e+03 0.000000000000000000e+00 0.000000000000000000e+00
34 | 1.063067999999999984e+03 0.000000000000000000e+00 0.000000000000000000e+00
35 | 1.071590999999999894e+03 0.000000000000000000e+00 0.000000000000000000e+00
36 | 1.077272999999999911e+03 0.000000000000000000e+00 0.000000000000000000e+00
37 | 1.332012999999999920e+03 0.000000000000000000e+00 0.000000000000000000e+00
38 | 1.352569999999999936e+03 0.000000000000000000e+00 0.000000000000000000e+00
39 | 1.374411000000000058e+03 0.000000000000000000e+00 0.000000000000000000e+00
40 | 1.393040999999999940e+03 0.000000000000000000e+00 0.000000000000000000e+00
41 | 1.401392000000000053e+03 0.000000000000000000e+00 0.000000000000000000e+00
42 | 1.405246000000000095e+03 0.000000000000000000e+00 6.561680000000000571e-27
43 | 1.410384999999999991e+03 0.000000000000000000e+00 1.181101999999999962e-26
44 | 1.418094000000000051e+03 0.000000000000000000e+00 1.837270000000000019e-26
45 | 1.425161000000000058e+03 5.250161145703777885e-28 2.362205000000000133e-26
46 | 1.430942000000000007e+03 2.249649369903586483e-27 3.149605999999999846e-26
47 | 1.437365999999999985e+03 5.374351543365397663e-27 4.461942000000000247e-26
48 | 1.443147999999999911e+03 8.268084115192906265e-27 6.036744999999999884e-26
49 | 1.446359999999999900e+03 1.023658611519290587e-26 7.349080999999999711e-26
50 | 1.448929000000000087e+03 1.181101999999999962e-26 8.402651000000000365e-26
51 | 1.453425999999999931e+03 1.870091517576849423e-26 9.842520000000000427e-26
52 | 1.456637999999999920e+03 2.362205000000000133e-26 1.089239000000000008e-25
53 | 1.459849999999999909e+03 2.918373000000000043e-26 1.233596000000000078e-25
54 | 1.461777000000000044e+03 3.152708049231378450e-26 1.364828999999999935e-25
55 | 1.464346999999999980e+03 3.465235852305891615e-26 1.535433000000000106e-25
56 | 1.468201000000000022e+03 4.053624471463166334e-26 1.732283000000000053e-25
57 | 1.472698000000000093e+03 4.855601942714848270e-26 1.929134000000000096e-25
58 | 1.477194999999999936e+03 5.774278000000000075e-26 2.086613999999999824e-25
59 | 1.482334000000000060e+03 6.824131208358290936e-26 2.217848000000000006e-25
60 | 1.490042999999999893e+03 8.399043317294047873e-26 2.388450999999999852e-25
61 | 1.497751999999999953e+03 1.049869000000000019e-25 2.506562000000000145e-25
62 | 1.502891000000000076e+03 1.264240996368353986e-25 2.598425000000000013e-25
63 | 1.506744999999999891e+03 1.452300505967815703e-25 2.690289000000000204e-25
64 | 1.509957000000000107e+03 1.627297000000000068e-25 2.834645999999999815e-25
65 | 1.512527000000000044e+03 1.850393999999999887e-25 3.005249000000000121e-25
66 | 1.515096000000000004e+03 2.171860080155649643e-25 3.202099999999999934e-25
67 | 1.517023999999999887e+03 2.388450999999999852e-25 3.385826999999999993e-25
68 | 1.518951000000000022e+03 2.703412000000000093e-25 3.682677000000000117e-25
69 | 1.520236000000000104e+03 2.913440482096546748e-25 3.879527999999999929e-25
70 | 1.520877999999999929e+03 3.018372999999999874e-25 4.028871000000000038e-25
71 | 1.521519999999999982e+03 3.142947978988329096e-25 4.212598000000000098e-25
72 | 1.522805000000000064e+03 3.385735085603115124e-25 4.448818999999999900e-25
73 | 1.523448000000000093e+03 3.503936999999999961e-25 4.555422999999999793e-25
74 | 1.524731999999999971e+03 3.864752904280157508e-25 4.750655999999999928e-25
75 | 1.525375000000000000e+03 3.963254999999999813e-25 4.855642999999999549e-25
76 | 1.526017000000000053e+03 4.094488000000000129e-25 4.960630000000000088e-25
77 | 1.527301999999999907e+03 4.219159999999992168e-25 5.039370000000000067e-25
78 | 1.529229000000000042e+03 4.304461999999999830e-25 5.039370000000000067e-25
79 | 1.530513999999999896e+03 4.234452505967830010e-25 4.960630000000000088e-25
80 | 1.531798999999999978e+03 4.081364999999999781e-25 4.842519999999999660e-25
81 | 1.532441000000000031e+03 3.871391000000000080e-25 4.711286000000000397e-25
82 | 1.533084000000000060e+03 3.779456010894943407e-25 4.580052000000000216e-25
83 | 1.533726000000000113e+03 3.687664000000000021e-25 4.383201999999999809e-25
84 | 1.533726000000000113e+03 3.687664000000000021e-25 4.265092000000000300e-25
85 | 1.534367999999999938e+03 3.549975733852162122e-25 3.989501000000000049e-25
86 | 1.535010999999999967e+03 3.412073000000000229e-25 3.805773999999999989e-25
87 | 1.535653000000000020e+03 3.324629401660611344e-25 3.556429999999999840e-25
88 | 1.538222999999999956e+03 3.091854211394776264e-25 3.293962999999999801e-25
89 | 1.540150000000000091e+03 3.005249000000000121e-25 3.070866000000000211e-25
90 | 1.542719000000000051e+03 2.917771683705238318e-25 2.913385999999999794e-25
91 | 1.545931000000000040e+03 2.845898597509449108e-25 2.690289000000000204e-25
92 | 1.548500999999999976e+03 2.808399000000000173e-25 2.480315000000000044e-25
93 | 1.552355000000000018e+03 2.707176865243496441e-25 2.296587999999999985e-25
94 | 1.555567000000000007e+03 2.629054278671512024e-25 2.073490999999999936e-25
95 | 1.558136999999999944e+03 2.541557333333339979e-25 1.902886999999999995e-25
96 | 1.560707000000000107e+03 2.427822000000000166e-25 1.719159999999999935e-25
97 | 1.563276000000000067e+03 2.312363365815692470e-25 1.496062999999999887e-25
98 | 1.566488000000000056e+03 2.084036377334995989e-25 1.259842999999999949e-25
99 | 1.570984999999999900e+03 1.732283000000000053e-25 1.023622000000000032e-25
100 | 1.576124000000000024e+03 1.415699029772327552e-25 7.611549000000000303e-26
101 | 1.580621000000000095e+03 1.220472000000000095e-25 6.036744999999999884e-26
102 | 1.586403000000000020e+03 1.049856733610065572e-25 4.593175999999999970e-26
103 | 1.594111000000000104e+03 8.934898607342059388e-26 3.674541000000000247e-26
104 | 1.600535000000000082e+03 7.731955650278900344e-26 3.149605999999999846e-26
105 | 1.607602000000000089e+03 6.517964039045251991e-26 2.493437999999999933e-26
106 | 1.614667999999999893e+03 5.413443207822543301e-26 2.230971000000000124e-26
107 | 1.620450000000000045e+03 4.578556054142871258e-26 1.837270000000000019e-26
108 | 1.623662000000000035e+03 4.213980441619084431e-26 1.706036999999999932e-26
109 | 1.628157999999999902e+03 3.776631175025951332e-26 1.443570000000000123e-26
110 | 1.630000000000000000e+03 3.609400384987897597e-26 1.400000000000000054e-26
111 | 1.650000000000000000e+03 000000000000000000000000 000000000000000000000000
112 | 1.680000000000000000e+03 000000000000000000000000 000000000000000000000000
113 | 1.710000000000000000e+03 000000000000000000000000 000000000000000000000000
114 |
--------------------------------------------------------------------------------
/pyLaserPulse/data/materials/Sellmeier_coefficients/silica.dat:
--------------------------------------------------------------------------------
1 | B coefficient C coefficient
2 | 0.6961663 0.0684043
3 | 0.4079426 0.1162414
4 | 0.8974794 9.896161
5 |
--------------------------------------------------------------------------------
/pyLaserPulse/data/materials/reflectivity_spectra/aluminium.dat:
--------------------------------------------------------------------------------
1 | 4.041799e+00 0.000000e+00
2 | 4.483690e+00 0.000000e+00
3 | 4.871742e+00 0.000000e+00
4 | 5.404371e+00 0.000000e+00
5 | 6.582044e+00 0.000000e+00
6 | 8.184433e+00 0.000000e+00
7 | 1.060818e+01 0.000000e+00
8 | 1.201472e+01 0.000000e+00
9 | 1.541203e+01 0.000000e+00
10 | 2.060774e+01 0.000000e+00
11 | 2.784241e+01 0.000000e+00
12 | 3.321316e+01 0.000000e+00
13 | 3.840645e+01 1.926782e-03
14 | 4.349966e+01 3.853565e-03
15 | 4.876072e+01 7.707129e-03
16 | 5.353769e+01 1.541426e-02
17 | 5.878500e+01 2.697495e-02
18 | 6.256966e+01 4.046243e-02
19 | 6.591188e+01 5.587669e-02
20 | 6.871732e+01 7.321773e-02
21 | 7.090692e+01 9.633911e-02
22 | 7.241397e+01 1.233141e-01
23 | 7.395453e+01 1.522158e-01
24 | 7.552937e+01 1.830443e-01
25 | 7.713620e+01 2.119461e-01
26 | 7.795785e+01 2.331407e-01
27 | 7.879297e+01 2.601156e-01
28 | 7.881030e+01 2.813102e-01
29 | 7.883551e+01 3.121387e-01
30 | 7.968960e+01 3.506744e-01
31 | 8.055777e+01 3.949904e-01
32 | 8.143051e+01 4.335260e-01
33 | 8.146959e+01 4.797688e-01
34 | 8.151520e+01 5.337187e-01
35 | 8.239338e+01 5.664740e-01
36 | 8.244445e+01 6.262042e-01
37 | 8.333930e+01 6.666667e-01
38 | 8.424386e+01 7.071291e-01
39 | 8.515824e+01 7.475915e-01
40 | 8.519060e+01 7.842004e-01
41 | 8.700645e+01 8.169557e-01
42 | 8.885212e+01 8.400771e-01
43 | 9.073331e+01 8.593449e-01
44 | 9.362257e+01 8.805395e-01
45 | 9.862745e+01 8.998073e-01
46 | 1.049772e+02 9.132948e-01
47 | 1.105781e+02 9.229287e-01
48 | 1.152731e+02 9.306358e-01
49 | 1.278759e+02 9.306358e-01
50 | 1.448282e+02 9.287091e-01
51 | 1.674705e+02 9.287091e-01
52 | 1.916500e+02 9.267823e-01
53 | 2.216124e+02 9.267823e-01
54 | 2.643558e+02 9.248555e-01
55 | 3.186386e+02 9.248555e-01
56 | 3.840678e+02 9.248555e-01
57 | 4.534248e+02 9.248555e-01
58 | 5.188905e+02 9.229287e-01
59 | 5.937845e+02 9.171484e-01
60 | 6.865475e+02 9.075145e-01
61 | 7.458621e+02 8.940270e-01
62 | 7.773598e+02 8.805395e-01
63 | 8.186705e+02 8.709056e-01
64 | 8.358197e+02 8.689788e-01
65 | 8.804306e+02 8.805395e-01
66 | 9.084301e+02 8.978805e-01
67 | 9.373201e+02 9.152216e-01
68 | 9.772547e+02 9.364162e-01
69 | 1.062046e+03 9.556840e-01
70 | 1.190566e+03 9.653179e-01
71 | 1.376809e+03 9.730250e-01
72 | 1.575719e+03 9.788054e-01
73 | 1.860308e+03 9.807322e-01
74 | 2.265690e+03 9.807322e-01
75 | 2.702792e+03 9.826590e-01
76 | 3.257783e+03 9.826590e-01
77 | 3.926893e+03 9.865125e-01
78 | 4.985274e+03 9.865125e-01
79 | 6.328911e+03 9.865125e-01
80 | 7.788600e+03 9.884393e-01
81 | 9.100195e+03 9.884393e-01
82 | 1.074356e+04 9.884393e-01
83 | 1.349839e+04 9.884393e-01
84 | 1.644017e+04 9.903661e-01
85 | 2.044210e+04 9.884393e-01
86 | 2.790667e+04 9.884393e-01
87 | 3.095895e+04 9.922929e-01
88 |
--------------------------------------------------------------------------------
/pyLaserPulse/data/materials/reflectivity_spectra/gold.dat:
--------------------------------------------------------------------------------
1 | 4.132611e+01 6.358382e-02
2 | 4.398059e+01 6.358382e-02
3 | 4.680932e+01 7.129094e-02
4 | 4.983193e+01 1.021195e-01
5 | 5.250110e+01 1.310212e-01
6 | 5.417075e+01 1.483622e-01
7 | 5.530992e+01 1.541426e-01
8 | 5.825736e+01 1.579961e-01
9 | 5.947415e+01 1.502890e-01
10 | 6.134716e+01 1.387283e-01
11 | 6.460340e+01 1.233141e-01
12 | 6.733430e+01 1.136802e-01
13 | 6.946039e+01 1.098266e-01
14 | 7.240528e+01 1.117534e-01
15 | 7.469896e+01 1.175337e-01
16 | 8.033743e+01 1.310212e-01
17 | 8.820613e+01 1.368015e-01
18 | 9.386432e+01 1.290944e-01
19 | 9.783212e+01 1.194605e-01
20 | 1.030353e+02 1.136802e-01
21 | 1.085195e+02 1.117534e-01
22 | 1.179233e+02 1.213873e-01
23 | 1.268093e+02 1.233141e-01
24 | 1.349465e+02 1.175337e-01
25 | 1.406790e+02 1.271676e-01
26 | 1.436460e+02 1.387283e-01
27 | 1.497481e+02 1.483622e-01
28 | 1.544980e+02 1.579961e-01
29 | 1.593954e+02 1.657033e-01
30 | 1.661400e+02 1.599229e-01
31 | 1.731769e+02 1.579961e-01
32 | 1.805226e+02 1.618497e-01
33 | 1.862487e+02 1.714836e-01
34 | 1.941761e+02 1.888247e-01
35 | 2.024530e+02 2.119461e-01
36 | 2.155131e+02 2.369942e-01
37 | 2.246950e+02 2.581888e-01
38 | 2.342776e+02 2.832370e-01
39 | 2.468213e+02 3.102119e-01
40 | 2.546759e+02 3.294798e-01
41 | 2.627751e+02 3.468208e-01
42 | 2.739432e+02 3.583815e-01
43 | 2.915685e+02 3.680154e-01
44 | 3.102966e+02 3.680154e-01
45 | 3.267930e+02 3.603083e-01
46 | 3.371048e+02 3.545279e-01
47 | 3.513618e+02 3.468208e-01
48 | 3.701080e+02 3.564547e-01
49 | 3.898464e+02 3.641618e-01
50 | 4.106212e+02 3.680154e-01
51 | 4.415542e+02 3.680154e-01
52 | 4.507857e+02 3.622351e-01
53 | 4.650007e+02 3.545279e-01
54 | 4.746845e+02 3.410405e-01
55 | 4.845893e+02 3.314066e-01
56 | 4.897119e+02 3.448940e-01
57 | 4.948986e+02 3.603083e-01
58 | 4.949678e+02 3.737958e-01
59 | 5.002302e+02 3.930636e-01
60 | 5.055586e+02 4.142582e-01
61 | 5.056900e+02 4.393064e-01
62 | 5.058214e+02 4.643545e-01
63 | 5.063880e+02 5.722543e-01
64 | 5.113116e+02 5.048170e-01
65 | 5.114547e+02 5.317919e-01
66 | 5.169750e+02 5.664740e-01
67 | 5.225549e+02 6.011561e-01
68 | 5.227534e+02 6.377649e-01
69 | 5.283534e+02 6.647399e-01
70 | 5.340774e+02 7.032755e-01
71 | 5.398527e+02 7.398844e-01
72 | 5.401010e+02 7.842004e-01
73 | 5.517126e+02 8.342967e-01
74 | 5.693834e+02 8.728324e-01
75 | 5.875614e+02 9.017341e-01
76 | 6.126925e+02 9.383430e-01
77 | 6.522037e+02 9.614644e-01
78 | 7.014618e+02 9.788054e-01
79 | 8.111926e+02 9.865125e-01
80 | 9.478347e+02 9.903661e-01
81 | 1.073530e+03 9.922929e-01
82 | 1.254337e+03 9.942197e-01
83 | 1.677199e+03 9.942197e-01
84 | 1.980117e+03 9.961464e-01
85 | 2.731309e+03 9.942197e-01
86 | 3.326558e+03 9.961464e-01
87 | 3.927207e+03 9.942197e-01
88 | 4.883280e+03 9.942197e-01
89 | 6.009551e+03 9.961464e-01
90 | 7.708823e+03 9.961464e-01
91 | 9.685498e+03 9.961464e-01
92 | 1.179608e+04 9.961464e-01
93 |
--------------------------------------------------------------------------------
/pyLaserPulse/data/materials/reflectivity_spectra/silver.dat:
--------------------------------------------------------------------------------
1 | 1.226665e+01 0.000000e+00
2 | 1.332910e+01 5.780347e-03
3 | 1.478667e+01 7.707129e-03
4 | 1.590089e+01 9.633911e-03
5 | 1.674825e+01 1.348748e-02
6 | 1.745901e+01 1.926782e-02
7 | 1.838793e+01 1.541426e-02
8 | 1.936704e+01 1.541426e-02
9 | 2.039869e+01 1.734104e-02
10 | 2.170982e+01 2.119461e-02
11 | 2.334573e+01 2.312139e-02
12 | 2.510542e+01 2.697495e-02
13 | 2.756383e+01 3.082852e-02
14 | 3.026359e+01 3.660886e-02
15 | 3.220878e+01 4.046243e-02
16 | 3.392653e+01 4.816956e-02
17 | 3.500267e+01 5.780347e-02
18 | 3.611221e+01 6.551060e-02
19 | 3.883340e+01 6.743738e-02
20 | 4.090281e+01 7.129094e-02
21 | 4.308509e+01 8.092486e-02
22 | 4.445262e+01 9.248555e-02
23 | 4.586447e+01 1.059730e-01
24 | 4.683178e+01 1.175337e-01
25 | 4.782236e+01 1.348748e-01
26 | 4.934321e+01 1.522158e-01
27 | 5.090836e+01 1.618497e-01
28 | 5.306670e+01 1.637765e-01
29 | 5.417399e+01 1.541426e-01
30 | 5.645725e+01 1.329480e-01
31 | 5.763413e+01 1.213873e-01
32 | 5.883673e+01 1.117534e-01
33 | 6.069087e+01 1.021195e-01
34 | 6.326270e+01 1.021195e-01
35 | 6.526806e+01 1.098266e-01
36 | 6.733699e+01 1.175337e-01
37 | 6.947011e+01 1.233141e-01
38 | 7.167368e+01 1.329480e-01
39 | 7.394566e+01 1.406551e-01
40 | 7.628813e+01 1.464355e-01
41 | 8.035510e+01 1.522158e-01
42 | 8.550967e+01 1.445087e-01
43 | 9.005384e+01 1.348748e-01
44 | 9.288988e+01 1.233141e-01
45 | 9.581524e+01 1.117534e-01
46 | 1.030208e+02 1.001927e-01
47 | 1.062695e+02 9.248555e-02
48 | 1.142681e+02 8.670520e-02
49 | 1.203478e+02 8.285164e-02
50 | 1.294061e+02 7.707129e-02
51 | 1.362912e+02 7.321773e-02
52 | 1.406059e+02 7.707129e-02
53 | 1.435685e+02 8.670520e-02
54 | 1.481224e+02 9.633911e-02
55 | 1.528238e+02 1.078998e-01
56 | 1.593285e+02 1.252408e-01
57 | 1.626921e+02 1.387283e-01
58 | 1.661334e+02 1.560694e-01
59 | 1.732080e+02 1.753372e-01
60 | 1.787164e+02 1.926782e-01
61 | 1.843963e+02 2.080925e-01
62 | 1.902528e+02 2.215800e-01
63 | 1.962876e+02 2.312139e-01
64 | 2.067642e+02 2.427746e-01
65 | 2.177913e+02 2.504817e-01
66 | 2.317991e+02 2.581888e-01
67 | 2.466783e+02 2.543353e-01
68 | 2.652452e+02 2.485549e-01
69 | 2.735875e+02 2.331407e-01
70 | 2.822035e+02 2.215800e-01
71 | 2.880747e+02 2.061657e-01
72 | 2.940681e+02 1.907514e-01
73 | 2.970757e+02 1.714836e-01
74 | 3.032078e+02 1.406551e-01
75 | 3.032563e+02 1.560694e-01
76 | 3.063089e+02 1.213873e-01
77 | 3.094232e+02 9.633911e-02
78 | 3.094541e+02 1.059730e-01
79 | 3.126066e+02 8.285164e-02
80 | 3.157723e+02 5.394990e-02
81 | 3.158165e+02 6.743738e-02
82 | 3.223612e+02 4.431599e-02
83 | 3.257754e+02 5.973025e-02
84 | 3.258927e+02 9.441233e-02
85 | 3.295353e+02 1.657033e-01
86 | 3.296803e+02 2.080925e-01
87 | 3.298649e+02 2.620424e-01
88 | 3.334652e+02 3.082852e-01
89 | 3.336919e+02 3.737958e-01
90 | 3.375836e+02 4.913295e-01
91 | 3.414730e+02 5.953757e-01
92 | 3.490026e+02 6.974952e-01
93 | 3.564844e+02 7.418112e-01
94 | 3.640756e+02 7.726397e-01
95 | 3.796251e+02 8.034682e-01
96 | 3.958307e+02 8.323699e-01
97 | 4.127033e+02 8.554913e-01
98 | 4.485293e+02 8.786127e-01
99 | 4.774254e+02 8.959538e-01
100 | 5.029275e+02 9.113680e-01
101 | 5.465420e+02 9.267823e-01
102 | 6.001094e+02 9.383430e-01
103 | 6.940132e+02 9.499037e-01
104 | 8.366054e+02 9.595376e-01
105 | 9.775869e+02 9.691715e-01
106 | 1.095887e+03 9.788054e-01
107 | 1.215798e+03 9.865125e-01
108 | 1.348748e+03 9.884393e-01
109 | 1.543543e+03 9.903661e-01
110 | 1.822248e+03 9.884393e-01
111 | 2.151405e+03 9.922929e-01
112 | 2.675161e+03 9.922929e-01
113 | 3.361186e+03 9.942197e-01
114 | 4.136321e+03 9.942197e-01
115 | 5.361254e+03 9.942197e-01
116 | 6.666443e+03 9.942197e-01
117 | 9.100741e+03 9.942197e-01
118 | 1.167408e+04 9.942197e-01
119 | 1.268445e+04 9.942197e-01
120 |
--------------------------------------------------------------------------------
/pyLaserPulse/data/paths.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | Created on Fri Jun 24 15:02:23 2022
6 |
7 | @author: James Feehan
8 |
9 | Get absolute paths for data files independently of the user profile, OS, etc.
10 | """
11 |
12 |
13 | import os
14 |
15 |
16 | main_dir = os.path.dirname(os.path.abspath(__file__))
17 |
18 |
19 | class _components:
20 | class _loss_spectra:
21 | def __init__(self):
22 | local_dir = main_dir + '/components/loss_spectra/'
23 | self.Andover_155FS10_25_bandpass = local_dir + \
24 | 'Andover_155FS10_25_bandpass.dat'
25 | self.fused_WDM_976_1030 = local_dir + \
26 | 'fused_WDM_976_1030.dat'
27 | self.Opneti_95_5_PM_fibre_tap_fast_axis_blocked = local_dir + \
28 | 'Opneti_95_5_PM_fibre_tap_fast_axis_blocked.dat'
29 | self.Opneti_high_power_isolator_HPMIS_1030_1_250_5 = local_dir + \
30 | 'Opneti_high_power_isolator_HPMIS_1030_1_250_5.dat'
31 | self.Opneti_microoptic_976_1030_wdm = local_dir + \
32 | 'Opneti_microoptic_976_1030_wdm.dat'
33 | self.Opneti_microoptic_isolator_PMIS_S_P_1030_F = local_dir + \
34 | 'Opneti_microoptic_isolator_PMIS_S_P_1030_F.dat'
35 | self.Thorlabs_IO_J_1030 = local_dir + 'Thorlabs_IO_J_1030.dat'
36 |
37 | def __init__(self):
38 | self.loss_spectra = self._loss_spectra()
39 |
40 |
41 | class _materials:
42 | class _loss_spectra:
43 | def __init__(self):
44 | local_dir = main_dir + '/materials/loss_spectra/'
45 | self.silica = local_dir + 'silica.dat'
46 |
47 | class _Raman_profiles:
48 | def __init__(self):
49 | local_dir = main_dir + '/materials/Raman_profiles/'
50 | self.silica = local_dir + 'silica.dat'
51 |
52 | class _reflectivities:
53 | def __init__(self):
54 | local_dir = main_dir + '/materials/reflectivity_spectra/'
55 | self.aluminium = local_dir + 'aluminium.dat'
56 | self.gold = local_dir + 'gold.dat'
57 | self.silver = local_dir + 'silver.dat'
58 |
59 | class _Sellmeier_coefficients:
60 | def __init__(self):
61 | local_dir = main_dir + '/materials/Sellmeier_coefficients/'
62 | self.silica = local_dir + 'silica.dat'
63 |
64 | def __init__(self):
65 | self.loss_spectra = self._loss_spectra()
66 | self.Raman_profiles = self._Raman_profiles()
67 | self.reflectivities = self._reflectivities()
68 | self.Sellmeier_coefficients = self._Sellmeier_coefficients()
69 |
70 |
71 | class _fibres:
72 | class _cross_sections:
73 | def __init__(self):
74 | local_dir = main_dir + '/fibres/cross_sections/'
75 | self.Er_silica = local_dir + 'Er_silica.dat'
76 | self.Tm_silica = local_dir + 'Tm_silica.dat'
77 | self.Yb_Al_silica = local_dir + 'Yb_Al_silica.dat'
78 | self.Yb_Nufern_PM_YSF_HI_HP = local_dir + \
79 | 'Yb_Nufern_PM_YSF_HI_HP.dat'
80 |
81 | def __init__(self):
82 | self.cross_sections = self._cross_sections()
83 |
84 |
85 | class _single_plot_window:
86 | def __init__(self):
87 | # need to remove 'data' from main_dir
88 | local_dir = main_dir[0:-4] + '/single_plot_window/'
89 | self.icon = local_dir + 'icon.png'
90 |
91 |
92 | fibres = _fibres()
93 | materials = _materials()
94 | components = _components()
95 | single_plot_window = _single_plot_window()
96 |
--------------------------------------------------------------------------------
/pyLaserPulse/exceptions.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | Created on Sun Apr 24 16:59:13 2022
5 |
6 | @author: james feehan
7 |
8 | Custom exceptions: for when things go wrong.
9 | """
10 |
11 |
12 | class PropagationMethodNotConvergingError(Exception):
13 | """Raised when an iterative propagation method does not converge"""
14 | pass
15 |
16 |
17 | class BoundaryConditionError(Exception):
18 | """
19 | Raised when the boundary conditions in active_fibre_base are inadequately
20 | defined
21 | """
22 | pass
23 |
24 |
25 | class NanFieldError(Exception):
26 | """Raised when the pulse field has NaN values"""
27 | pass
28 |
--------------------------------------------------------------------------------
/pyLaserPulse/grid.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | Created on Mon Nov 23 15:50:35 2020
5 |
6 | @author: james feehan
7 |
8 | Grid class
9 | """
10 |
11 |
12 | import os
13 | import numpy as np
14 | import scipy.constants as const
15 |
16 | import pyLaserPulse.utils as utils
17 |
18 |
19 | class grid:
20 | """
21 | Time-frequency grid class.
22 | """
23 |
24 | def __init__(self, points, lambda_c, lambda_max):
25 | """
26 | Parameters
27 | ----------
28 | points : int
29 | Number of points in the time-frequency grid. Must be an integer
30 | power of 2.
31 | lambda_c : float
32 | Central wavelength of the frequency grid in m.
33 | lambda_max : float
34 | Maximum wavelength of the frequency grid in m.
35 |
36 | Attributes
37 | ----------
38 | points : int
39 | Number of points in the time-frequency grid. Integer power of 2.
40 | midpoint : int
41 | int(points / 2) -- The central point of the time-frequency grid.
42 | lambda_c : float
43 | The central wavelength of the frequency grid in m
44 | lambda_max : float
45 | The maximum wavelength of the frequency grid in m.
46 | omega_c : float
47 | The central angular frequency of the angular frequency grid in
48 | rad Hz.
49 | f_range : float
50 | The range (or span) of the frequency grid in Hz
51 | df : float
52 | The resolution of the frequency grid in Hz
53 | f_c : float
54 | The central frequency of the frequency grid in Hz.
55 | dOmega : float
56 | The resolution of the angular frequency grid in rad Hz
57 | t_range : float
58 | The range (or span) of the time grid in s
59 | dt : float
60 | The resolution of the time grid in s
61 | time_window : numpy array
62 | The time grid in s
63 | omega : numpy array
64 | The angular frequency grid in rad Hz centred at 0 rad Hz
65 | omega_window : numpy array
66 | The angular frequency grid in rad Hz
67 | omega_window_shift : numpy array
68 | The FFTshifted angular frequency grid in rad Hx
69 | f_window : numpy array
70 | The frequency grid in Hz
71 | lambda_window : numpy array
72 | The wavelength window in m
73 | d_wl : numpy array
74 | The resolution of the wavelength window. See notes; the wavelength
75 | window is not evenly spaced.
76 | energy_window : numpy array
77 | The energy window in J, given by Planck's constant * f_window
78 | energy_window_shift : numpy array
79 | The FFTshifted energy window in J.
80 | FFT_scale : float
81 | Scaling multiplier or divider for FFTs and IFFts, respectively.
82 | FFT_scale = sqrt(2 * pi) / dt
83 |
84 | Notes
85 | -----
86 | The grids are calculated as follows:
87 |
88 | f_range = 2 (c / lambda_c - c / lambda_max), where c = 299792457 m/s
89 | df = f_range / points
90 | t_range = 1 / df
91 | dt = t_range / points
92 | time_window = dt * linspace(-1*midpoint, midpoint-1, points)
93 | dOmega = 2 * pi * df
94 | omega_window = dOmega * linspace(-1*midpoint, midpoint-1, points)
95 | lambda_window = 2 * pi * c / omega_window, where c = 299792458 m/s
96 | """
97 | self.points = points
98 | self.midpoint = int(self.points / 2)
99 | axis = np.linspace(-1 * self.midpoint,
100 | (self.points - 1) / 2, self.points)
101 | self.lambda_c = lambda_c
102 | self.lambda_max = lambda_max
103 | self.omega_c = 2 * np.pi * const.c / self.lambda_c
104 | self.f_c = self.omega_c / (2 * np.pi)
105 |
106 | self.f_range = 2 * (const.c / self.lambda_c -
107 | const.c / self.lambda_max)
108 | self.df = self.f_range / self.points
109 | self.dOmega = 2 * np.pi * self.df
110 | self.t_range = 1 / self.df
111 | self.dt = self.t_range / self.points
112 | self.time_window = self.dt * axis
113 | self.omega = self.dOmega * axis
114 | self.omega_window = self.omega_c + self.omega
115 | self.omega_window_shift = utils.fftshift(self.omega_window)
116 | self.f_window = self.omega_window / (2 * np.pi)
117 | self.lambda_window = const.c / self.f_window
118 | self.lambda_min = self.lambda_window.min()
119 | self.d_wl = np.gradient(-1 * self.lambda_window)
120 | self.energy_window = const.h * self.f_window
121 | self.energy_window_shift = utils.fftshift(self.energy_window)
122 |
123 | self.FFT_scale = ((2 * np.pi)**0.5) / self.dt
124 |
125 | def save(self, directory):
126 | """
127 | Save the grid information to a file in directory.
128 |
129 | Parameters
130 | ----------
131 | directory : string
132 | directory to which the data will be saved.
133 |
134 | Notes
135 | -----
136 | Only information required to recreate the grid object is saved.
137 |
138 | Data is saved using the numpy.savez method. Data can be accessed using
139 | the numpy.load method, which will return a dictionary with the
140 | following keys:
141 | points : Number of data points in the time-frequency grid
142 | lambda_c : Central wavelength in m
143 | lambda_max : Maximum grid wavelength in m.
144 | """
145 | np.savez(directory + 'grid.npz',
146 | points=self.points,
147 | lambda_c=self.lambda_c,
148 | lambda_max=self.lambda_max)
149 |
150 |
151 | class grid_from_pyLaserPulse_simulation(grid):
152 | """
153 | Time-frequency grid class.
154 | """
155 |
156 | def __init__(self, data_directory):
157 | """
158 | Parameters
159 | ----------
160 | data_direcotry : str
161 | Absolute path of directory containing grid.npz data file produced
162 | by a previous pyLaserPulse simulation.
163 |
164 | Attributes
165 | ----------
166 | points : int
167 | Number of points in the time-frequency grid. Integer power of 2.
168 | midpoint : int
169 | int(points / 2) -- The central point of the time-frequency grid.
170 | lambda_c : float
171 | The central wavelength of the frequency grid in m
172 | lambda_max : float
173 | The maximum wavelength of the frequency grid in m.
174 | omega_c : float
175 | The central angular frequency of the angular frequency grid in
176 | rad Hz.
177 | f_range : float
178 | The range (or span) of the frequency grid in Hz
179 | df : float
180 | The resolution of the frequency grid in Hz
181 | dOmega : float
182 | The resolution of the angular frequency grid in rad Hz
183 | t_range : float
184 | The range (or span) of the time grid in s
185 | dt : float
186 | The resolution of the time grid in s
187 | time_window : numpy array
188 | The time grid in s
189 | omega : numpy array
190 | The angular frequency grid in rad Hz centred at 0 rad Hz
191 | omega_window : numpy array
192 | The angular frequency grid in rad Hz
193 | omega_window_shift : numpy array
194 | The FFTshifted angular frequency grid in rad Hx
195 | f_window : numpy array
196 | The frequency grid in Hz
197 | lambda_window : numpy array
198 | The wavelength window in m
199 | d_wl : numpy array
200 | The resolution of the wavelength window. See notes; the wavelength
201 | window is not evenly spaced.
202 | energy_window : numpy array
203 | The energy window in J, given by Planck's constant * f_window
204 | energy_window_shift : numpy array
205 | The FFTshifted energy window in J.
206 | FFT_scale : float
207 | Scaling multiplier or divider for FFTs and IFFts, respectively.
208 | FFT_scale = sqrt(2 * pi) / dt
209 |
210 | Notes
211 | -----
212 | The grids are calculated as follows:
213 |
214 | f_range = 2 (c / lambda_c - c / lambda_max), where c = 299792457 m/s
215 | df = f_range / points
216 | t_range = 1 / df
217 | dt = t_range / points
218 | time_window = dt * linspace(-1*midpoint, midpoint-1, points)
219 | dOmega = 2 * pi * df
220 | omega_window = dOmega * linspace(-1*midpoint, midpoint-1, points)
221 | lambda_window = 2 * pi * c / omega_window, where c = 299792458 m/s
222 | """
223 | if not data_directory.endswith(os.sep):
224 | grid_data = np.load(data_directory + os.sep + 'grid.npz')
225 | else:
226 | grid_data = np.load(data_directory + 'grid.npz')
227 | super().__init__(
228 | grid_data['points'],
229 | grid_data['lambda_c'],
230 | grid_data['lambda_max'])
231 |
--------------------------------------------------------------------------------
/pyLaserPulse/pump.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | Created on Sun Dec 20 22:28:09 2020
5 |
6 | @author: james feehan
7 |
8 | Class for pump light
9 | """
10 |
11 | import numpy as np
12 | import scipy.constants as const
13 | import pyLaserPulse.utils as utils
14 |
15 |
16 | class pump:
17 | """
18 | Class for pump light.
19 | """
20 |
21 | def __init__(self, bandwidth, lambda_c, energy, points=2**5,
22 | lambda_lims=None, ASE_scaling=None, direction="co"):
23 | """
24 | Parameters
25 | ----------
26 | bandwidth : float
27 | Spectral width of the pump light in m
28 | lambda_c : float
29 | Central wavelength of the pump light in m
30 | energy : float
31 | Optical energy of the pump light.
32 | This is usually defined as the pump power divided by the repetition
33 | rate of the seed laser pulses being amplified.
34 | points : int
35 | Power of 2 (default is 2**5).
36 | lambda_lims : list
37 | [Lower, Upper] limits of the wavelength grid (in m).
38 | Defaults to None, which should be used if full ASE simulation is
39 | not required (co-pumping only).
40 | ASE_scaling : float
41 | Scale the ASE contribution in the Giles model
42 | by 1 - pulse_time_window * repetition_rate. This ensures correct
43 | scaling of the ASE power 'emitted' outside of the pulse temporal
44 | window. The pulse class has as scaling factor of
45 | pulse_time_window * repetition_rate).
46 | direction : str
47 | 'co' or 'counter' for co-propagating and counter-propagating
48 | geometries, respectively.
49 | """
50 | self.points = points
51 | self.midpoint = int(self.points / 2)
52 | self.lambda_c = lambda_c
53 | self.bandwidth = bandwidth
54 | self.energy = energy # starting energy
55 | axis = np.linspace(-1 * self.midpoint, (self.points - 1) / 2,
56 | self.points)
57 | self.lambda_lims = lambda_lims
58 | self.ASE_scaling = ASE_scaling
59 |
60 | if lambda_lims is None: # base wavelength grid on bandwidth
61 | lambda_range = None
62 | if self.bandwidth <= 10e-9:
63 | lambda_range = 10e-9
64 | else:
65 | lambda_range = self.bandwidth + 2e-9
66 | lambda_max = lambda_c + lambda_range / 2
67 | omega_range = 4 * np.pi * \
68 | (const.c / lambda_c - const.c / lambda_max)
69 | self.omega_c = 2 * np.pi * const.c / self.lambda_c
70 | elif lambda_lims[0] < lambda_c < lambda_lims[1]:
71 | omega_lims = [2 * np.pi * const.c /
72 | lim for lim in lambda_lims[::-1]]
73 | omega_range = np.abs(np.diff(omega_lims))
74 | self.omega_c = np.average(omega_lims)
75 | else:
76 | raise ValueError(
77 | "The pump wavelength lies outside the grid wavelength"
78 | " range:\nlambda_c = %f nm,\nlambda_lims[0] = %f nm"
79 | "\nlambda_lims[1] = %f nm."
80 | % (1e9 * lambda_c, 1e9 * lambda_lims[0],
81 | 1e9 * lambda_lims[1]))
82 |
83 | self.ASE_scaling = ASE_scaling
84 |
85 | self.dOmega = omega_range / self.points
86 | self.omega_window = self.dOmega * axis + self.omega_c
87 | self.energy_window = const.hbar * self.omega_window
88 | self.lambda_window = 2 * np.pi * const.c / self.omega_window
89 | self.d_wl = np.gradient(-1 * self.lambda_window)
90 |
91 | # Without the following, pump spectrum is often just a single point.
92 | if self.bandwidth < 2 * np.max(self.d_wl):
93 | self.bandwidth = 2 * np.max(self.d_wl)
94 |
95 | # Assume top-hat spectral shape.
96 | self.spectrum = np.zeros((self.points))
97 | self.spectrum[np.abs(self.lambda_window - self.lambda_c)**2
98 | < np.abs(self.bandwidth / 2)**2] = 1
99 | self.spectrum /= np.sum(self.spectrum * self.dOmega)
100 | self.spectrum *= energy
101 | # Polarization
102 | self.spectrum = 0.5 * self.spectrum[None, :].repeat(2, axis=0)
103 |
104 | self.direction = np.ones_like(self.spectrum) # propagation step sign
105 | if direction == "counter":
106 | # This is the counter-propagated spectrum AT THE SIGNAL INPUT.
107 | self.propagated_spectrum = np.zeros_like(self.spectrum)
108 | self.direction *= -1
109 |
110 | self.high_res_samples = []
111 | self.high_res_sample_points = []
112 |
113 | def get_ESD_and_PSD(self, spectrum, repetition_rate):
114 | """
115 | Calculate the energy spectral density and the power spectral density
116 | of the pump light.
117 |
118 | Parameters
119 | ----------
120 | spectrum : numpy array
121 | The spectrum to convert to ESD or PSD
122 | repetition_rate : float
123 | Repetition rate of the seed laser.
124 | See pyLaserPulse.pulse.repetition_rate
125 |
126 | Returns
127 | -------
128 | numpy array
129 | spectrum converted to energy spectral density in J / m
130 | numpy array
131 | spectrum converted to power spectral density in mW / nm
132 | """
133 | energy_spectral_density, power_spectral_density = \
134 | utils.get_ESD_and_PSD(
135 | self.lambda_window, spectrum, repetition_rate)
136 | return energy_spectral_density, power_spectral_density
137 |
--------------------------------------------------------------------------------
/pyLaserPulse/single_plot_window/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jsfeehan/pyLaserPulse/f3c5a309b24389691ee87807e3157c0b27a7d223/pyLaserPulse/single_plot_window/__init__.py
--------------------------------------------------------------------------------
/pyLaserPulse/single_plot_window/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jsfeehan/pyLaserPulse/f3c5a309b24389691ee87807e3157c0b27a7d223/pyLaserPulse/single_plot_window/icon.png
--------------------------------------------------------------------------------
/pyLaserPulse/single_plot_window/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
207 |
--------------------------------------------------------------------------------
/pyLaserPulse/single_plot_window/matplotlib_gallery.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python3
2 | # -*-encoding: utf-8 -*-
3 |
4 | from PyQt5 import QtWidgets, QtGui, QtCore
5 | import numpy as np # required for some fmt commands
6 | import multiprocessing as mp
7 | import time
8 |
9 | from pyLaserPulse.single_plot_window.mplwidget import MplWidget
10 | from pyLaserPulse.single_plot_window.plot_gallery import Ui_MainWindow
11 | from pyLaserPulse.data import paths
12 | import pyLaserPulse.sys_info as si
13 | import pyLaserPulse.single_plot_window.single_matplotlib_window as smw
14 |
15 | # Taskbar icon in windows
16 | if si.OS == "Windows":
17 | import ctypes
18 | myappid = 'MPLGallery' # arbitrary string
19 | ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
20 |
21 |
22 | def launch_plot(plot_dicts):
23 | """
24 | Launch the MatplotlibGallery.
25 |
26 | plot_dicts: list.
27 | List of plot dictionaries produced by optical assembly classes.
28 | """
29 | app = QtWidgets.QApplication(['MPLGallery'])
30 | app.setQuitOnLastWindowClosed(True)
31 | # dpi = app.screens()[0].physicalDotsPerInch()
32 | dpi = 96
33 | plotWindow = MatplotlibGallery(
34 | plot_dicts=plot_dicts, windowTitle='pyLaserPulse simulation gallery',
35 | dpi=dpi)
36 | plotWindow.show()
37 | app.exec()
38 |
39 |
40 | def concatenate_plot_dicts(dict_list):
41 | """
42 | Take all dictionaries in list dict_list and turn them into one dictionary.
43 |
44 | dict_list: list of dictionaries.
45 | """
46 | dictionary = {}
47 | for d in dict_list:
48 | dictionary.update(d)
49 | return dictionary
50 |
51 |
52 | def savePlotFunc(plotInfo, plotName, directory):
53 | """
54 | Almost the same as MatplotlibGaller.makePlot.
55 |
56 | Redefining AGAIN because MplWidget objects aren't pickleable.
57 | Need to replot everything to use multiprocessing library. This ends up
58 | being faster in the long run.
59 |
60 | I don't think it's possible to define the thumbnails or the pop-out plots
61 | from MatplotlibGallery.onMplWidgetClick using this function. Returning
62 | an smw.MatplotlibWindow object doesn't seem to work ('wrapper to C/C++
63 | object has been deleted').
64 |
65 | Args:
66 | plotInfo: tuple.
67 | (axis object, list of formatting commands as strings).
68 | plotName: string. Name given to the plot.
69 | directory: string. Absolute directory to which plots are to be saved.
70 | """
71 |
72 | window = smw.MatplotlibWindow(windowTitle=plotName)
73 | window.ui.plotWidget.canvas.axes.clear()
74 | ax = plotInfo[0]
75 | fmt = plotInfo[1]
76 |
77 | fontsize = 13
78 | labelsize = 12
79 | titlesize = 14
80 | legendsize = 11
81 |
82 | if len(ax.lines) > 0: # line plot
83 | # Set line data
84 | for l in ax.lines:
85 | data = l.get_data()
86 | d = l.__dict__
87 | if ax.get_yscale() == 'log':
88 | window.ui.plotWidget.canvas.axes.semilogy(
89 | data[0], data[1], d['_marker']._marker,
90 | ls=d['_linestyle'], lw=d['_linewidth'],
91 | alpha=d['_alpha'], c=d['_color'],
92 | markeredgecolor=d['_markeredgecolor'],
93 | markersize=d['_markersize'],
94 | markerfacecolor=d['_markerfacecolor'],
95 | markeredgewidth=d['_markeredgewidth'])
96 | else:
97 | window.ui.plotWidget.canvas.axes.plot(
98 | data[0], data[1], d['_marker']._marker,
99 | ls=d['_linestyle'], lw=d['_linewidth'],
100 | alpha=d['_alpha'], c=d['_color'],
101 | markeredgecolor=d['_markeredgecolor'],
102 | markersize=d['_markersize'],
103 | markerfacecolor=d['_markerfacecolor'],
104 | markeredgewidth=d['_markeredgewidth'])
105 | else:
106 | # hack to get pcolormesh working.
107 | # p made member of ax in optical assembly class.
108 | X = ax.p._coordinates.T[0, 0:-1, 0] \
109 | + np.diff(ax.p._coordinates.T[0, :, 0]) / 2
110 | Y = ax.p._coordinates.T[1, 0, 0:-1] \
111 | + np.diff(ax.p._coordinates.T[1, 0, :]) / 2
112 | data = ax.p.get_array()
113 | data = data.reshape(len(Y), len(X))
114 | im = window.ui.plotWidget.canvas.axes.pcolormesh(
115 | X, Y, data, cmap=ax.p.__dict__['cmap'],
116 | norm=ax.p.__dict__['_norm'])
117 | cb = window.ui.plotWidget.canvas.figure.colorbar(im)
118 | cb.set_label(label=ax.colorbar_label, size=labelsize)
119 | cb.ax.tick_params(labelsize=labelsize)
120 | window.ui.plotWidget.canvas.axes.set_position(ax.get_position())
121 | window.ui.plotWidget.canvas.axes.set_xlabel(
122 | ax.get_xlabel(), fontsize=fontsize)
123 | window.ui.plotWidget.canvas.axes.set_ylabel(
124 | ax.get_ylabel(), fontsize=fontsize)
125 | window.ui.plotWidget.canvas.axes.tick_params(
126 | axis='both', labelsize=labelsize)
127 | window.ui.plotWidget.canvas.axes.set_ylim(ax.get_ylim())
128 | window.ui.plotWidget.canvas.axes.set_xlim(ax.get_xlim())
129 | window.ui.plotWidget.canvas.axes.set_title(
130 | ax.get_title(), fontsize=titlesize)
131 |
132 | if 'axes.legend' not in fmt:
133 | hndl, lbl = ax.get_legend_handles_labels()
134 | window.ui.plotWidget.canvas.axes.legend(
135 | hndl, lbl, frameon=False, fontsize=legendsize)
136 |
137 | # Execute additional formatting commands
138 | for cmd in fmt:
139 | if 'self.' in cmd:
140 | cmd = cmd.replace('self.', 'plotWidget.canvas.')
141 | cmd = cmd.replace('plotWidget', 'window.ui.plotWidget')
142 | if not cmd.startswith('window.ui.plotWidget.canvas.'):
143 | cmd = 'window.ui.plotWidget.canvas.' + cmd
144 | exec(cmd)
145 | window.ui.plotWidget.canvas.figure.tight_layout()
146 | window.ui.plotWidget.canvas.draw()
147 | figFile = (directory + '/' + plotName + '.png')
148 | window.ui.plotWidget.canvas.figure.savefig(figFile, dpi=180)
149 | time.sleep(0.5)
150 | window.close()
151 |
152 |
153 | class MatplotlibGallery(QtWidgets.QMainWindow):
154 | class signals(QtCore.QObject):
155 | saveAllPlots = QtCore.pyqtSignal(list, list, str, list)
156 |
157 | signals = signals()
158 |
159 | def __init__(self, plot_dicts=[], windowTitle=None, dpi=96):
160 | """
161 | Application that puts all plots in a single window.
162 | Plots can be opened in their own large window by clicking on the
163 | thumnails.
164 |
165 | plot_dicts: list.
166 | List containing all dictionaries of plot information from the
167 | amplifier objects.
168 | The following naming convention MUST be followed:
169 | "amplifier name: plot name"
170 | windowTitle: str.
171 | Title of the window.
172 | dpi: int
173 | Screen dpi.
174 |
175 | James Feehan, 17/5/2022
176 | """
177 | super(MatplotlibGallery, self).__init__()
178 | self.setWindowIcon(QtGui.QIcon(paths.single_plot_window.icon))
179 | self.ui = Ui_MainWindow()
180 | self.ui.setupUi(self)
181 | self.setWindowTitle(windowTitle)
182 | self.dpi = dpi
183 | self.cols = 3 # number of plot columns
184 | self.thumbnailSize = 350
185 | self.galleryLayout = QtWidgets.QVBoxLayout()
186 | self.ui.groupBox_gallery.setLayout(self.galleryLayout)
187 | self.plotDicts = plot_dicts
188 |
189 | self.changeStatusLabel("Initializing.")
190 |
191 | self.plotNames = []
192 | for d in self.plotDicts:
193 | pn = list(d.keys())
194 | pn.sort()
195 | self.plotNames.append(pn)
196 |
197 | self.groupBoxLabelsFromDict()
198 | self.makeGalleryGroupBoxes()
199 | self.populateGalleryGroupBoxes()
200 |
201 | self.ui.actionSave_all_plots.triggered.connect(
202 | self.saveAllPlots)
203 |
204 | self.saveAllPlotsThread = QtCore.QThread()
205 |
206 | self.pps = parallelPlotSaver()
207 | self.signals.saveAllPlots.connect(self.pps.saveAllPlots)
208 | self.pps.signals.finished.connect(self.changeStatusLabel)
209 | self.pps.moveToThread(self.saveAllPlotsThread)
210 |
211 | self.saveAllPlotsThread.start()
212 |
213 | self.changeStatusLabel("Ready.")
214 |
215 | def groupBoxLabelsFromDict(self):
216 | """
217 | Creates a list of labels for the amplifier group boxes using
218 | self.plot_dicts.keys(). The following naming convention MUST be
219 | followed:
220 | "GROUPNAME: plot title".
221 | """
222 | self.groupBoxLabels = []
223 | for d in self.plotDicts:
224 | self.groupBoxLabels.extend(list(d.keys()))
225 | self.groupBoxLabels = [l.split(':')[0] for l in self.groupBoxLabels]
226 | self.groupBoxLabels = list(dict.fromkeys(self.groupBoxLabels))
227 |
228 | def makeGalleryGroupBoxes(self):
229 | """
230 | Populate self.ui.groupBox_gallery with one group box for each
231 | amplifier simulated.
232 |
233 | The groupBoxes are enumerated and labelled according to
234 | self.groupBoxLabels, so self.groupBoxLabelsFromDict() must be called
235 | first and the naming convention must be followed.
236 | """
237 | self.galleryGroupBoxList = []
238 | self.galleryGroupBoxLayouts = []
239 | for l in self.groupBoxLabels:
240 | lo = QtWidgets.QGridLayout()
241 | self.galleryGroupBoxLayouts.append(lo)
242 | gb = QtWidgets.QGroupBox(l)
243 | gb.setMinimumSize(360, 360)
244 | gb.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
245 | QtWidgets.QSizePolicy.Expanding)
246 | gb.setLayout(lo)
247 | self.galleryGroupBoxList.append(gb)
248 | self.galleryLayout.addWidget(gb)
249 |
250 | def populateGalleryGroupBoxes(self):
251 | """
252 | Add MplWidgets to all groupboxes in self.galleryGroupBoxList.
253 | """
254 | for i, l in enumerate(self.groupBoxLabels):
255 | num = len(self.plotDicts[i].keys())
256 | row = 0
257 | col = 0
258 | for j in range(num):
259 | if (j % self.cols) == 0:
260 | col = 0
261 | row += 1
262 | mplw = MplWidget()
263 | mplw.setFixedSize(self.thumbnailSize, self.thumbnailSize)
264 | mplw.name = self.plotNames[i][j]
265 | self.makePlot(mplw, self.plotDicts[i][mplw.name])
266 | mplw.canvas.mpl_connect(
267 | 'button_press_event',
268 | lambda state, x=self.plotDicts[i][mplw.name],
269 | y=mplw.name: self.onMplWidgetClick(x, y))
270 | self.galleryGroupBoxLayouts[i].addWidget(mplw, row, col)
271 | col += 1
272 | self.galleryGroupBoxList[i].setMinimumSize(
273 | int(self.cols * self.thumbnailSize),
274 | int(1.05 * row * self.thumbnailSize))
275 |
276 | def onMplWidgetClick(self, plotInfo, plotName):
277 | """
278 | Launch new window containing only an MplWidget and plot the data in
279 | plotdict.
280 |
281 | Args:
282 | plotInfo: tuple.
283 | (axis object, list of formatting commands as strings).
284 | plotName: string.
285 | groupBoxLabel: None or string. Name of the groupBox that the plots
286 | are put in. This is prepended to the file name when saving all
287 | plots.
288 | """
289 | window = smw.MatplotlibWindow(self, windowTitle=plotName)
290 | self.makePlot(window.ui.plotWidget, plotInfo, thumbnail=False)
291 | window.show()
292 |
293 | def makePlot(self, plotWidget, plotInfo, thumbnail=True):
294 | """
295 | Update plotWidget.
296 |
297 | Args:
298 | plotWidget: MplWidget object.
299 | plotInfo: tuple.
300 | (axis object, list of formatting commands as strings).
301 | thumbnail: bool (default True).
302 | Misses font formatting, legend, etc assuming that the resulting
303 | plot will be too small to read these features.
304 | """
305 | plotWidget.canvas.axes.clear()
306 | ax = plotInfo[0]
307 | fmt = plotInfo[1]
308 |
309 | if thumbnail:
310 | fontsize = 8
311 | labelsize = 8
312 | titlesize = 9
313 | legendsize = 8
314 | else:
315 | fontsize = 13
316 | labelsize = 12
317 | titlesize = 14
318 | legendsize = 11
319 |
320 | if len(ax.lines) > 0: # line plot
321 | # Set line data
322 | for l in ax.lines:
323 | data = l.get_data()
324 | d = l.__dict__
325 | if ax.get_yscale() == 'log':
326 | plotWidget.canvas.axes.semilogy(
327 | data[0], data[1], d['_marker']._marker,
328 | ls=d['_linestyle'], lw=d['_linewidth'],
329 | alpha=d['_alpha'], c=d['_color'],
330 | markeredgecolor=d['_markeredgecolor'],
331 | markersize=d['_markersize'],
332 | markerfacecolor=d['_markerfacecolor'],
333 | markeredgewidth=d['_markeredgewidth'])
334 | else:
335 | plotWidget.canvas.axes.plot(
336 | data[0], data[1], d['_marker']._marker,
337 | ls=d['_linestyle'], lw=d['_linewidth'],
338 | alpha=d['_alpha'], c=d['_color'],
339 | markeredgecolor=d['_markeredgecolor'],
340 | markersize=d['_markersize'],
341 | markerfacecolor=d['_markerfacecolor'],
342 | markeredgewidth=d['_markeredgewidth'])
343 | else:
344 | # hack to get pcolormesh working.
345 | # p made member of ax in optical assembly class.
346 | X = ax.p._coordinates.T[0, 0:-1, 0] \
347 | + np.diff(ax.p._coordinates.T[0, :, 0]) / 2
348 | Y = ax.p._coordinates.T[1, 0, 0:-1] \
349 | + np.diff(ax.p._coordinates.T[1, 0, :]) / 2
350 | data = ax.p.get_array()
351 | data = data.reshape(len(Y), len(X))
352 | im = plotWidget.canvas.axes.pcolormesh(
353 | X, Y, data, cmap=ax.p.__dict__['cmap'],
354 | norm=ax.p.__dict__['_norm'])
355 | cb = plotWidget.canvas.figure.colorbar(im)
356 | cb.set_label(label=ax.colorbar_label, size=labelsize)
357 | cb.ax.tick_params(labelsize=labelsize)
358 | plotWidget.canvas.axes.set_position(ax.get_position())
359 | plotWidget.canvas.axes.set_xlabel(ax.get_xlabel(), fontsize=fontsize)
360 | plotWidget.canvas.axes.set_ylabel(ax.get_ylabel(), fontsize=fontsize)
361 | plotWidget.canvas.axes.tick_params(axis='both', labelsize=labelsize)
362 | plotWidget.canvas.axes.set_ylim(ax.get_ylim())
363 | plotWidget.canvas.axes.set_xlim(ax.get_xlim())
364 | plotWidget.canvas.axes.set_title(ax.get_title(), fontsize=titlesize)
365 |
366 | # if not thumbnail:
367 | if 'axes.legend' not in fmt:
368 | hndl, lbl = ax.get_legend_handles_labels()
369 | plotWidget.canvas.axes.legend(
370 | hndl, lbl, frameon=False, fontsize=legendsize)
371 |
372 | # Execute additional formatting commands
373 | for cmd in fmt:
374 | if 'plotWidget.canvas' not in cmd:
375 | cmd = 'plotWidget.canvas.' + cmd
376 | exec(cmd)
377 | if thumbnail:
378 | plotWidget.canvas.figure.set_size_inches(
379 | self.thumbnailSize/self.dpi, self.thumbnailSize/self.dpi)
380 | plotWidget.canvas.figure.tight_layout()
381 | plotWidget.canvas.draw()
382 |
383 | def saveAllPlots(self):
384 | """
385 | Make and save, but do not display, all plots in the gallery.
386 |
387 | A dialogue box opens so that that user can select the directory where
388 | the plots are to be saved.
389 | """
390 | self.saveAllPlotsDir = QtWidgets.QFileDialog.getExistingDirectory(
391 | self, 'Select folder')
392 | self.changeStatusLabel("Saving all plots to %s" % self.saveAllPlotsDir)
393 | self.signals.saveAllPlots.emit(
394 | self.plotDicts, self.plotNames, self.saveAllPlotsDir,
395 | self.groupBoxLabels)
396 |
397 | def changeStatusLabel(self, statString):
398 | """
399 | Change the status label test.
400 | """
401 | fullStr = "Status: " + statString
402 | self.ui.label_status.setText(fullStr)
403 |
404 |
405 | class parallelPlotSaver(QtCore.QObject):
406 | """
407 | Worker class for saving all plots in parallel
408 | """
409 | class signals(QtCore.QObject):
410 | finished = QtCore.pyqtSignal(str)
411 |
412 | signals = signals()
413 |
414 | @QtCore.pyqtSlot(list, list, str, list)
415 | def saveAllPlots(self, plotDicts, plotNames, saveDir, groupBoxLabels):
416 | if si.OS == 'Windows': # multiprocessing not working on Windows?
417 | for i, l in enumerate(groupBoxLabels):
418 | for n in iter(plotNames[i]):
419 | n2 = n.replace(':', ' -') # Windows-friendly name
420 | savePlotFunc(plotDicts[i][n], n2, saveDir)
421 | self.signals.finished.emit("Ready.")
422 | else:
423 | args = []
424 | for i, l in enumerate(groupBoxLabels):
425 | for n in iter(plotNames[i]):
426 | n2 = n.replace(':', ' -') # Windows-friendly name
427 | args.append((plotDicts[i][n], n2, saveDir))
428 | with mp.Pool(processes=mp.cpu_count(), maxtasksperchild=1) as p:
429 | r = p.starmap(savePlotFunc, iter(args))
430 | if r:
431 | self.signals.finished.emit("Ready.")
432 |
--------------------------------------------------------------------------------
/pyLaserPulse/single_plot_window/mplwidget.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python3
2 | # -*-encoding: utf-8 -*-
3 |
4 | from PyQt5 import QtWidgets # *
5 | from matplotlib.backends.backend_qt5agg import FigureCanvas
6 | from matplotlib.figure import Figure
7 |
8 |
9 | class MplWidget(QtWidgets.QWidget):
10 | def __init__(self, parent=None):
11 | QtWidgets.QWidget.__init__(self, parent)
12 | self.canvas = FigureCanvas(Figure())
13 | vertical_layout = QtWidgets.QVBoxLayout()
14 | vertical_layout.addWidget(self.canvas)
15 | self.canvas.axes = self.canvas.figure.add_subplot(111)
16 | self.setLayout(vertical_layout)
17 |
--------------------------------------------------------------------------------
/pyLaserPulse/single_plot_window/plotGallery.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 1140
10 | 803
11 |
12 |
13 |
14 | MainWindow
15 |
16 |
17 |
18 | -
19 |
20 |
21 | true
22 |
23 |
24 |
25 |
26 | 0
27 | 0
28 | 1120
29 | 717
30 |
31 |
32 |
33 |
-
34 |
35 |
36 |
37 | 0
38 | 0
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | -
51 |
52 |
53 |
54 | 16777215
55 | 30
56 |
57 |
58 |
59 |
60 | 12
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
87 |
88 |
89 |
90 | Save all plots
91 |
92 |
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/pyLaserPulse/single_plot_window/plotWidget.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 688
10 | 615
11 |
12 |
13 |
14 | MainWindow
15 |
16 |
17 |
18 | -
19 |
20 |
21 |
22 | 0
23 | 0
24 |
25 |
26 |
27 |
28 |
29 |
30 |
40 |
41 |
42 |
43 |
44 | MplWidget
45 | QWidget
46 |
47 | 1
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/pyLaserPulse/single_plot_window/plot_gallery.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'plotGallery.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.12
6 | #
7 | # WARNING! All changes made in this file will be lost!
8 |
9 | from PyQt5 import QtCore, QtGui, QtWidgets
10 |
11 |
12 | class Ui_MainWindow(object):
13 | def setupUi(self, MainWindow):
14 | MainWindow.setObjectName("MainWindow")
15 | MainWindow.resize(1140, 803)
16 | self.centralwidget = QtWidgets.QWidget(MainWindow)
17 | self.centralwidget.setObjectName("centralwidget")
18 | self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
19 | self.gridLayout.setObjectName("gridLayout")
20 | self.scrollArea = QtWidgets.QScrollArea(self.centralwidget)
21 | self.scrollArea.setWidgetResizable(True)
22 | self.scrollArea.setObjectName("scrollArea")
23 | self.scrollAreaWidgetContents_2 = QtWidgets.QWidget()
24 | self.scrollAreaWidgetContents_2.setGeometry(QtCore.QRect(0, 0, 1120, 717))
25 | self.scrollAreaWidgetContents_2.setObjectName("scrollAreaWidgetContents_2")
26 | self.verticalLayout = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents_2)
27 | self.verticalLayout.setObjectName("verticalLayout")
28 | self.groupBox_gallery = QtWidgets.QGroupBox(self.scrollAreaWidgetContents_2)
29 | self.groupBox_gallery.setMinimumSize(QtCore.QSize(0, 0))
30 | self.groupBox_gallery.setTitle("")
31 | self.groupBox_gallery.setObjectName("groupBox_gallery")
32 | self.verticalLayout.addWidget(self.groupBox_gallery)
33 | self.scrollArea.setWidget(self.scrollAreaWidgetContents_2)
34 | self.gridLayout.addWidget(self.scrollArea, 0, 0, 1, 1)
35 | self.label_status = QtWidgets.QLabel(self.centralwidget)
36 | self.label_status.setMaximumSize(QtCore.QSize(16777215, 30))
37 | font = QtGui.QFont()
38 | font.setPointSize(12)
39 | self.label_status.setFont(font)
40 | self.label_status.setText("")
41 | self.label_status.setObjectName("label_status")
42 | self.gridLayout.addWidget(self.label_status, 1, 0, 1, 1)
43 | MainWindow.setCentralWidget(self.centralwidget)
44 | self.menubar = QtWidgets.QMenuBar(MainWindow)
45 | self.menubar.setGeometry(QtCore.QRect(0, 0, 1140, 21))
46 | self.menubar.setObjectName("menubar")
47 | self.menufile = QtWidgets.QMenu(self.menubar)
48 | self.menufile.setObjectName("menufile")
49 | MainWindow.setMenuBar(self.menubar)
50 | self.statusbar = QtWidgets.QStatusBar(MainWindow)
51 | self.statusbar.setObjectName("statusbar")
52 | MainWindow.setStatusBar(self.statusbar)
53 | self.actionSave_all_plots = QtWidgets.QAction(MainWindow)
54 | self.actionSave_all_plots.setObjectName("actionSave_all_plots")
55 | self.menufile.addAction(self.actionSave_all_plots)
56 | self.menubar.addAction(self.menufile.menuAction())
57 |
58 | self.retranslateUi(MainWindow)
59 | QtCore.QMetaObject.connectSlotsByName(MainWindow)
60 |
61 | def retranslateUi(self, MainWindow):
62 | _translate = QtCore.QCoreApplication.translate
63 | MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
64 | self.menufile.setTitle(_translate("MainWindow", "File"))
65 | self.actionSave_all_plots.setText(_translate("MainWindow", "Save all plots"))
66 |
67 |
68 |
--------------------------------------------------------------------------------
/pyLaserPulse/single_plot_window/plot_widget.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'plotWidget.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.14.1
6 | #
7 | # WARNING! All changes made in this file will be lost!
8 |
9 |
10 | from PyQt5 import QtCore, QtWidgets
11 |
12 | from pyLaserPulse.single_plot_window.mplwidget import MplWidget
13 |
14 |
15 | class Ui_MainWindow(object):
16 | def setupUi(self, MainWindow):
17 | MainWindow.setObjectName("MainWindow")
18 | MainWindow.resize(688, 615)
19 | self.centralwidget = QtWidgets.QWidget(MainWindow)
20 | self.centralwidget.setObjectName("centralwidget")
21 | self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
22 | self.verticalLayout.setObjectName("verticalLayout")
23 | self.plotWidget = MplWidget(self.centralwidget)
24 | sizePolicy = QtWidgets.QSizePolicy(
25 | QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
26 | sizePolicy.setHorizontalStretch(0)
27 | sizePolicy.setVerticalStretch(0)
28 | sizePolicy.setHeightForWidth(
29 | self.plotWidget.sizePolicy().hasHeightForWidth())
30 | self.plotWidget.setSizePolicy(sizePolicy)
31 | self.plotWidget.setObjectName("plotWidget")
32 | self.verticalLayout.addWidget(self.plotWidget)
33 | MainWindow.setCentralWidget(self.centralwidget)
34 | self.menubar = QtWidgets.QMenuBar(MainWindow)
35 | self.menubar.setGeometry(QtCore.QRect(0, 0, 688, 22))
36 | self.menubar.setObjectName("menubar")
37 | MainWindow.setMenuBar(self.menubar)
38 | self.statusbar = QtWidgets.QStatusBar(MainWindow)
39 | self.statusbar.setObjectName("statusbar")
40 | MainWindow.setStatusBar(self.statusbar)
41 |
42 | self.retranslateUi(MainWindow)
43 | QtCore.QMetaObject.connectSlotsByName(MainWindow)
44 |
45 | def retranslateUi(self, MainWindow):
46 | _translate = QtCore.QCoreApplication.translate
47 | MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
48 | # from mplwidget import MplWidget
49 |
--------------------------------------------------------------------------------
/pyLaserPulse/single_plot_window/single_matplotlib_window.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python3
2 | # -*-encoding: utf-8 -*-
3 |
4 | from PyQt5 import QtWidgets
5 | from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavBar
6 | from pyLaserPulse.single_plot_window.plot_widget import Ui_MainWindow
7 |
8 |
9 | class MatplotlibWindow(QtWidgets.QMainWindow):
10 | def __init__(self, parent=None, windowTitle=None):
11 | """
12 | Application that puts all plots in a single window.
13 |
14 | James Feehan, 14/5/2022
15 | """
16 | super(MatplotlibWindow, self).__init__(parent)
17 | self.ui = Ui_MainWindow()
18 | self.ui.setupUi(self)
19 | self.setWindowTitle(windowTitle)
20 | self.addToolBar(NavBar(self.ui.plotWidget.canvas, self))
21 |
--------------------------------------------------------------------------------
/pyLaserPulse/sys_info.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | import platform
5 |
6 | OS = platform.system()
7 |
--------------------------------------------------------------------------------
/pyLaserPulse/utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | Created on Mon Jul 25 00:35:01 2022
5 |
6 | @author: james feehan
7 |
8 | General functions.
9 | """
10 |
11 |
12 | import numpy as np
13 | import math
14 | from scipy.interpolate import interp1d
15 | import scipy.constants as const
16 |
17 |
18 | fft = np.fft.fft
19 | ifft = np.fft.ifft
20 | fftshift = np.fft.fftshift
21 | ifftshift = np.fft.ifftshift
22 |
23 |
24 | def check_dict_keys(key_list, d, param_name):
25 | """
26 | Check that all items in key_list are keys in dict d.
27 |
28 | Parameters
29 | ----------
30 | key_list : list
31 | keys (string) that are expected in dict d.
32 | d : dict
33 | param_name : string
34 | Name assigned to dictionary d in caller.
35 |
36 | Raises
37 | ------
38 | KeyError
39 | Raised if an item in key_list is not in dict d.
40 | """
41 | if not isinstance(d, dict):
42 | raise TypeError("%s must be a dictionary." % param_name)
43 | for item in key_list:
44 | try:
45 | d[item]
46 | except KeyError as err:
47 | msg = "Dictionary %s must contain key '%s'"
48 | raise KeyError(msg % (param_name, item)) from err
49 |
50 |
51 | def find_nearest(search_value, container):
52 | """
53 | Find the value in 'container' which is closest to 'search_value'
54 |
55 | Parameters
56 | ----------
57 | search_value
58 | Value to look for in container
59 | container
60 | Iterable of numeric values
61 |
62 | Returns
63 | -------
64 | int
65 | Index in container of value nearest to search_value
66 | int, float, etc.
67 | Value in container closest to search_value
68 | """
69 | try:
70 | container = np.asarray(container)
71 | except Exception:
72 | pass
73 | index = np.argmin(np.abs(container - search_value))
74 | value = container[index]
75 | return index, value
76 |
77 |
78 | def get_width(axis, data, meas_point=0.5):
79 | """
80 | Return the width of a distribution in array data.
81 |
82 | Parameters
83 | ----------
84 | axis : int
85 | Axis of data over which the width measurement is taken
86 | data : numpy array
87 | Distribution whose width is being measured
88 | meas_point : float
89 | Fraction of maximum value of data at which the width is measured.
90 | E.g., meas_point = 0.5 gives FWHM.
91 |
92 | Returns
93 | -------
94 | float, float64
95 | Width of the distribution in data measured at meas_point.
96 | """
97 | # must make sure that the measurement is done with the data centred
98 | # on axis origin.
99 | points = len(axis)
100 | half_points = int(points / 2)
101 | maximum = np.amax(data)
102 | idx_maximum = np.argmax(data)
103 | data = np.roll(data, -idx_maximum + half_points)
104 |
105 | idx_1 = find_nearest(meas_point * maximum, data[0:half_points])[0]
106 | idx_2 = find_nearest(
107 | meas_point * maximum, data[half_points::])[0] + half_points
108 | FWHM = np.abs(axis[idx_2] - axis[idx_1])
109 | return FWHM
110 |
111 |
112 | def get_FWe2M(axis, data):
113 | """
114 | Return the full width at 1/e^2 of the maximum pulse duration.
115 |
116 | Parameters
117 | ----------
118 | axis : int
119 | Axis of data over which the width measurement is taken
120 | data : numpy array
121 | Distribution whose width is being measured.
122 |
123 | Returns
124 | -------
125 | float, float64
126 | Full-width at 1/e^2 of the distribution in data.
127 | """
128 | return get_width(axis, data, meas_point=np.exp(-2))
129 |
130 |
131 | def swap_halves(arr, axis=-1):
132 | """
133 | Swap the halves of an even-element array over a given axis. E.g., over the
134 | 0th axis of a 1-D array:
135 |
136 | Parameters
137 | ----------
138 | arr : numpy array
139 | axis : int
140 |
141 | Returns
142 | -------
143 | numpy array
144 | arr, but the halves are swapped:
145 | arr[half1:half2] --> arr[half2:half1]
146 | """
147 | pts = arr.shape[axis]
148 | half_1 = arr.take(indices=range(0, int(pts / 2)), axis=axis)
149 | half_2 = arr.take(indices=range(int(pts / 2), pts), axis=axis)
150 | return np.append(half_2, half_1, axis=axis)
151 |
152 |
153 | def interpolate_data_from_file(filename, axis, axis_scale, data_scale,
154 | interp_kind='linear', fill_value='extrapolate',
155 | input_log=False, return_log=False):
156 | """
157 | Interpolate single-column data from a text file onto a grid axis.
158 |
159 | Parameters
160 | ----------
161 | filename : string
162 | Absolute path to the data file
163 | axis : numpy array
164 | New axis to interpolate onto
165 | axis_scale : float
166 | Scaling for the new axis
167 | data_scale : float
168 | Scaling of the data
169 | interp_kind
170 | Specifies the kind of interpolation as a string or as an integer
171 | specifying the order of the spline interpolator to use. See
172 | documentation for scipy.interpolate.interp1d.
173 | fill_value
174 | if an ndarray (or float), this value will be used to fill in for
175 | requested points outside of the data range. Default NaN if not provided
176 | If a two-element tuple, the first value is used as a fill value for
177 | x_new < x[0] and the second element is usd for x_new > x[-1].
178 | If 'extrapolate', then points outside the data range will be
179 | extrpolated. This is the default.
180 | input_log : bool
181 | Needs to be True if the data in the data file has units of dB.
182 | return_log : bool
183 | If True, returns -1 * np.log(10**(-1 * data / 10))
184 |
185 | Returns
186 | numpy array
187 | data from filename inteprolated onto axis.
188 | """
189 | try:
190 | data = np.loadtxt(filename, delimiter=",", dtype=np.float64)
191 | except ValueError:
192 | data = np.loadtxt(filename, delimiter="\t", dtype=np.float64)
193 | if input_log:
194 | # Convert from log_10 to linear scale
195 | data[:, 1] = 10**(data[:, 1] / 10)
196 | data[:, 1] = data_scale * data[:, 1]
197 |
198 | # Make sure all data values appear in order
199 | sorted_indices = np.argsort(data[:, 0])
200 | data[:, 0] = axis_scale * data[sorted_indices, 0]
201 | data[:, 1] = data[sorted_indices, 1]
202 |
203 | # Interpolate,
204 | data_func = interp1d(data[:, 0], data[:, 1], kind=interp_kind,
205 | fill_value=fill_value, bounds_error=False)
206 | data = data_func(axis)
207 | if return_log:
208 | data = -1 * np.log(10**(-1 * data / 10))
209 | return data
210 |
211 |
212 | def load_Raman(filename, time_window, dt):
213 | """
214 | Load parallel and perpendicular Raman contributions from text files and
215 | interpolate onto the time-domain grid.
216 |
217 | Parameters
218 | ----------
219 | filename : string
220 | Absolute path to the Raman data file.
221 | time_window : numpy array
222 | pyLaserPulse.grid.grid.time_window
223 | dt : float
224 | Resolution of time_window (pyLaserPulse.grid.grid.dt)
225 |
226 | Returns
227 | numpy array
228 | Raman data from filename interpolated onto time_window.
229 | """
230 | data = np.loadtxt(filename, delimiter='\t', skiprows=4)
231 | scaling = np.loadtxt(filename, skiprows=1, max_rows=1)
232 | time = data[:, 0] * 1e-15
233 | Raman = data[:, 1:3]
234 | time_1 = np.linspace(0, np.amax(time), len(time))
235 | time_2 = np.linspace(0, np.amax(time), int(np.amax(time) / dt))
236 | interp_Raman = interp1d(time_1, Raman, axis=0, kind='cubic')(time_2)
237 | Raman = np.zeros((len(time_window), 2))
238 | Raman[0:len(interp_Raman), :] = interp_Raman
239 | Raman = fft(Raman / (np.sum(Raman, axis=0)), axis=0) * (2 * np.pi)**.5 / dt
240 |
241 | # Scale // and _|_ components
242 | Raman[:, 1] = (1 / scaling) * Raman[:, 1]
243 | return Raman
244 |
245 |
246 | def load_cross_sections(filename, delimiter, axis, axis_scale,
247 | interp_kind='linear'):
248 | """
249 | Load active dopant cross-section data.
250 |
251 | Parameters
252 | ----------
253 | filename : string
254 | Absolute path to the cross section data file
255 | delimiter : string
256 | Delimiter used in the cross section data file
257 | axis : numpy array
258 | New wavelength axis to interpolate the data onto.
259 | See pyLaserPulse.grid.grid.lambda_window
260 | axis_scale : float
261 | Scaling for the interpolation axis
262 | interp_kind
263 | See documentation for scipy.interpolate.interp1d
264 |
265 | Returns
266 | -------
267 | numpy array
268 | absorption cross section data interpolated onto axis
269 | numpy array
270 | emission cross section data interpolated onto axis
271 | list
272 | [min_wavelength, max_wavelength]
273 |
274 | Notes
275 | -----
276 | Expects three-column text file of data formatted as follows:
277 | Wavelength delimiter Emission delimiter Absorption
278 | """
279 | data = np.loadtxt(filename, delimiter=delimiter)
280 | sorted_indices = np.argsort(data[:, 0])
281 | data = data[sorted_indices, :]
282 | data[:, 0] *= axis_scale
283 | min_wl = data[:, 0].min()
284 | max_wl = data[:, 0].max()
285 | absorption_func = interp1d(data[:, 0], data[:, 2], kind=interp_kind,
286 | fill_value='extrapolate')
287 | emission_func = interp1d(data[:, 0], data[:, 1], kind=interp_kind,
288 | fill_value='extrapolate')
289 | absorption = absorption_func(axis)
290 | emission = emission_func(axis)
291 |
292 | # Data with a fine wavelength grid and high dynamic range can result in
293 | # bad values after interpolation. Fix these.
294 | absorption[absorption < 0] = 0
295 | emission[emission < 0] = 0
296 | return absorption, emission, [min_wl, max_wl]
297 |
298 |
299 | def load_target_power_spectral_density(energy, repetition_rate, filename, axis,
300 | d_axis, axis_scale, background,
301 | PSD_scale, interp_kind='linear',
302 | fill_value=0, input_log=False):
303 | """
304 | Load a file containing some target power spectral density.
305 | use PSD_scale to convert from W/m to mW/nm, etc.
306 |
307 | Parameters
308 | ----------
309 | energy : float
310 | Energy in Joules
311 | repetition_rate : float
312 | filename : string
313 | Absolute path to the data file containing the power spectral density
314 | axis : numpy array
315 | Axis onto which the data in filename is interpolated.
316 | If the data is given as a function of wavelength in the data file, use
317 | pyLaserPulse.grid.grid.lambda_window
318 | If the data is given as a function of angular frequency in the data
319 | file, use pyLaserPulse.grid.grid.omega_window.
320 | d_axis : numpy array
321 | Resolution of axis
322 | axis_scale : float
323 | Scaling for axis
324 | background : float
325 | Background value to subtract
326 | PSD_scale : float
327 | Scaling for the power spectral density
328 | interp_kind : See scipy.interpolate.interp1d documentation.
329 | fill_value : See scipy.interpolate.interp1d documentation. Default 0
330 | input_log : bool
331 | Needs to be True if the data in filename is logarithmic.
332 |
333 | Returns
334 | -------
335 | numpy array
336 | Target power spectral density interpolated onto axis.
337 | """
338 | target = interpolate_data_from_file(filename, axis, axis_scale, 1,
339 | interp_kind, fill_value,
340 | input_log=input_log)
341 | target -= background
342 | target[target < 0] = 0
343 | target *= repetition_rate * energy / np.sum(target)
344 | target *= PSD_scale / d_axis
345 | return target
346 |
347 |
348 | def get_ESD_and_PSD(lambda_window, spectrum, repetition_rate):
349 | """
350 | Calculate the energy spectral density and the power spectral density
351 | from real-valued spectrum. The PSD is normalized to mW/nm.
352 |
353 | Parameters
354 | ----------
355 | lambda_window : numpy array
356 | Wavelength grid in m. See pyLaserPulse.grid.grid.lambda_window
357 | spectrum : numpy array
358 | Spectral data
359 | repetition_rate : float
360 | Repetition rate of the pulse source.
361 |
362 | Returns
363 | -------
364 | numpy array
365 | Energy spectral density in J/m
366 | numpy array
367 | Power spectral density in W/nm
368 | """
369 | energy_spectral_density = \
370 | spectrum * 2 * np.pi * const.c / lambda_window**2
371 | power_spectral_density = \
372 | energy_spectral_density * repetition_rate * 1e-6
373 | return energy_spectral_density, power_spectral_density
374 |
375 |
376 | def Sellmeier(lambda_window, f):
377 | """
378 | Calculate the refractive index as a function of wavelength for fused silica
379 |
380 | Parameters
381 | ----------
382 | lambda_window : numpy array
383 | Wavelength grid in m. See pyLaserPulse.grid.grid.lambda_window
384 | f : string
385 | Absolute path to file containing Sellmeier coefficients.
386 |
387 | Returns
388 | -------
389 | numpy array
390 | Refractive index as a function of wavelength.
391 | """
392 | lw = 1e6 * lambda_window
393 | coeffs = np.loadtxt(f, skiprows=1)
394 | n_sq = 1
395 | for B, C in iter(coeffs):
396 | n_sq += B * lw**2 / (lw**2 - C**2)
397 | return np.sqrt(n_sq)
398 |
399 |
400 | def fft_convolve(arr1, arr2):
401 | """
402 | Use the convolution theorem to do faster convolutions.
403 |
404 | Parameters
405 | ----------
406 | arr1 : numpy array
407 | arr2 : numpy array
408 |
409 | Returns
410 | -------
411 | numpy array
412 | The convolution of arr1 and arr2
413 |
414 | Notes
415 | -----
416 | arr1 and arr2 must have the same shape
417 | """
418 | if arr1.shape != arr2.shape:
419 | raise ValueError("arr1 and arr2 must have the same shape.")
420 | conv = ifft(fft(arr1) * fft(arr2))
421 | return conv
422 |
423 |
424 | def PCF_propagation_parameters_K_Saitoh(
425 | lambda_window, grid_midpoint, omega_window, a, b, c, d, hole_pitch,
426 | hole_diam_over_pitch, core_radius, Sellmeier_file):
427 | """
428 | Calculate V, mode_ref_index, D, beta_2 for hexagonal-lattice PCF.
429 |
430 | Parameters
431 | ----------
432 | lambda_window : numpy array
433 | Wavelength grid in m. See pyLaserPulse.grid.grid.lambda_window
434 | grid_midpoint : int
435 | Middle index of the time-frequency grid.
436 | See pyLaserPulse.grid.grid.midpoint
437 | omega_window : numpy array
438 | Angular frequency grid in rad Hz.
439 | See pyLaserPulse.grid.grid.omega_window
440 | a : numpy array
441 | 4x4 array. See reference in notes (K. Saitoh)
442 | b : numpy array
443 | 4x4 array. See reference in notes (K. Saitoh)
444 | c : numpy array
445 | 4x4 array. See reference in notes (K. Saitoh)
446 | d : numpy array
447 | 4x4 array. See reference in notes (K. Saitoh)
448 | hole_pitch : float
449 | Separation of neighbouring air holes in the hexagonal-lattice PCF
450 | structure.
451 | hole_diam_over_pitch : float
452 | Ratio of the air hole diameter to the hole pitch.
453 | core_radius : float
454 | Appriximate core radius in m
455 | Sellmeier_file : string
456 | Absolute path to the Sellmeier coefficients.
457 | See pyLaserPulse.data.paths.materials.Sellmeier_coefficients.
458 |
459 | Returns
460 | -------
461 | numpy array
462 | V-number as a function of wavelength
463 | numpy array
464 | Refractive index as a function of wavelength
465 | numpy array
466 | Fibre dispersion in ps / (nm km)
467 | numpy array
468 | Fibre dispersion in s^2 / m
469 |
470 | Notes
471 | -----
472 | K. Saitoh et al., "Empirical relations for simple design of
473 | photonic crystal fibres",Opt. Express 13(1), 267--274 (2005).
474 | """
475 | material_ref_index = Sellmeier(
476 | lambda_window, Sellmeier_file)
477 | n_central = material_ref_index[grid_midpoint]
478 |
479 | A = np.zeros((4), dtype=float)
480 | B = np.zeros((4), dtype=float)
481 | for i in range(np.shape(a)[1]):
482 | A[i] = \
483 | a[0, i] + a[1, i] * (hole_diam_over_pitch)**b[0, i] \
484 | + a[2, i] * (hole_diam_over_pitch)**b[1, i] \
485 | + a[3, i] * (hole_diam_over_pitch)**b[2, i]
486 | B[i] = \
487 | c[0, i] + c[1, i] * (hole_diam_over_pitch)**d[0, i] \
488 | + c[2, i] * (hole_diam_over_pitch)**d[1, i] \
489 | + c[3, i] * (hole_diam_over_pitch)**d[2, i]
490 |
491 | V = A[0] + A[1] / (
492 | 1 + A[2] * np.exp(
493 | A[3] * lambda_window / hole_pitch))
494 | W = B[0] + B[1] / (
495 | 1 + B[2] * np.exp(
496 | B[3] * lambda_window / hole_pitch))
497 |
498 | n_FSM = np.sqrt(n_central**2
499 | - (lambda_window * V
500 | / (2 * np.pi * core_radius))**2)
501 | ref_index = np.sqrt((lambda_window * W
502 | / (2 * np.pi * core_radius))**2 + n_FSM**2)
503 |
504 | k = 2 * np.pi * material_ref_index / lambda_window
505 | v_group = np.gradient(omega_window, k, edge_order=2)
506 | beta = 1 / v_group
507 | beta2_MAT = np.gradient(beta, omega_window, edge_order=2)
508 | D_MAT = -2 * np.pi * const.c * beta2_MAT / lambda_window**2
509 |
510 | decimate = 1
511 | max_points = 512
512 | if grid_midpoint > max_points: # i.e., grid size is > 1024
513 | # Required because very fine grids can result in noisy gradient
514 | # calculations
515 | decimate = int(grid_midpoint / max_points)
516 |
517 | tmp = np.gradient(ref_index[::decimate], lambda_window[::decimate],
518 | edge_order=2)
519 | tmp = np.gradient(tmp, lambda_window[::decimate], edge_order=2)
520 | D_WG = -1 * (lambda_window[::decimate] / const.c) * tmp
521 | D = D_WG + D_MAT[::decimate]
522 | beta_2 = -1 * lambda_window[::decimate]**2 * D / (2 * np.pi * const.c)
523 |
524 | if decimate > 1: # Interpolate D and beta_2 onto original grid
525 | # kind='linear' produces artefacts. No difference seen between
526 | # kind='quadratic' and kind='cubic'.
527 | f = interp1d(lambda_window[::decimate], D, kind='quadratic',
528 | fill_value='extrapolate')
529 | D = f(lambda_window)
530 | f = interp1d(lambda_window[::decimate], beta_2, kind='quadratic',
531 | fill_value='extrapolate')
532 | beta_2 = f(lambda_window)
533 | return V, ref_index, D, beta_2
534 |
535 |
536 | def Taylor_expansion(coeffs, axis, axis_centre=0):
537 | """
538 | Taylor expansion.
539 |
540 | Parameters
541 | ----------
542 | coeffs : iterable of floats (list, tuple, numpy array)
543 | Taylor coefficients. Can be obtained from curve data using, e.g.,
544 | numpy.polyfit.
545 | axis : numpy array
546 | Axis over which the Taylor expansion is the be calculated.
547 | axis_centre : float
548 | Centre of axis. Default is zero. The Taylor expansion will be calculated
549 | over axis - axis_centre.
550 |
551 | Notes
552 | -----
553 | For retrieving Taylor coefficients for dispersion curves, see
554 | pyLaserPulse.utils.get_Taylor_coeffs_from_beta2.
555 | """
556 | TE = np.zeros_like(axis, dtype=np.complex128)
557 | for i, tc in enumerate(coeffs):
558 | TE += tc * (axis - axis_centre)**i / math.factorial(i)
559 | return TE
560 |
561 |
562 | def get_Taylor_coeffs_from_beta2(beta_2, grid):
563 | """
564 | Calculate the Taylor coefficients which describe the propagation constant
565 | calculated using, e.g., Gloge, Saitoh (depending on what is being
566 | simulated).
567 |
568 | Parameters
569 | ----------
570 | beta_2 : numpy array
571 | Dispersion curve in s^2 / m
572 | grid : pyLaserPulse.grid.grid object
573 |
574 | Returns
575 | -------
576 | beta : numpy array
577 | Complex part of the propagation constant.
578 |
579 | Notes
580 | -----
581 | Second-order gradient of beta with respect to omega will give the dispersion
582 | curve to within the accuracy of the Taylor expansion.
583 |
584 | This function could be used for retrieving the Taylor coefficients for,
585 | e.g., grating compressors, but analytic formulae should be used instead
586 | where available.
587 | """
588 | idx_max = grid.points - 1
589 | idx_min = 0
590 | lim = 2 * grid.lambda_c
591 | if grid.lambda_max > lim:
592 | # Get min and max indices for Taylor coefficient calculations.
593 | # Only required for very large frequency grid spans.
594 | # From testing, seems that 2x central wavelength is a good upper limit.
595 | # Recall indexing for grid.lambda_window is reversed.
596 | idx_min = find_nearest(lim, grid.lambda_window)[0]
597 | idx_max = find_nearest(-1 * grid.omega[idx_min], grid.omega)[0]
598 |
599 | # Truncate to 11th order. In testing, >11th order could't be found reliably.
600 | tc = np.polyfit(
601 | grid.omega[idx_min:idx_max], beta_2[idx_min:idx_max], 9)[::-1]
602 | Taylors = np.zeros((len(tc) + 2))
603 | Taylors[2::] = tc
604 |
605 | beta = Taylor_expansion(Taylors, grid.omega)
606 |
607 | return Taylors, beta
608 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | matplotlib==3.7.1
2 | numpy==1.25.0
3 | psutil==5.9.5
4 | PyQt5==5.15.9
5 | PyQt5_sip==12.12.1
6 | scipy==1.11.0
7 | setuptools==75.1.0
8 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import find_packages
2 | from setuptools import setup
3 |
4 | VERSION = '0.0.0'
5 |
6 | long_description = '''pyLaserPulse is a comprehensive simulation toolbox for
7 | modelling polarization-resolved, on-axis laser pulse propagation through
8 | nonlinear, dispersive, passive, and active optical fibre assemblies,
9 | stretchers, and compressors in python.'''
10 |
11 | with open('requirements.txt') as f:
12 | required = f.read().splitlines()
13 |
14 | setup(
15 | name='pyLaserPulse',
16 | version=VERSION,
17 | author='James S. Feehan',
18 | author_email='pylaserpulse@outlook.com',
19 | url="https://github.com/jsfeehan/pyLaserPulse",
20 | description='Python module for pulsed fibre laser and amplifier simulations',
21 | long_description=long_description,
22 | license='GPLv3',
23 | install_requires=required,
24 | packages=find_packages(),
25 | package_data={
26 | 'pyLaserPulse.data': [
27 | 'components/loss_spectra/*.dat',
28 | 'fibres/cross_sections/*.dat',
29 | 'materials/loss_spectra/*.dat',
30 | 'materials/Raman_profiles/*.dat',
31 | 'materials/reflectivity_spectra/*.dat',
32 | 'materials/Sellmeier_coefficients/*.dat'
33 | ],
34 | 'pyLaserPulse.single_plot_window':[
35 | '*.png',
36 | '*.ui'
37 | ]
38 | }
39 | )
40 |
--------------------------------------------------------------------------------