├── .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 | 24 | 44 | 46 | 50 | 54 | 58 | 59 | 62 | 66 | 70 | 71 | 74 | 82 | 83 | 86 | 93 | 94 | 97 | 104 | 105 | 116 | 125 | 135 | 145 | 146 | 148 | 149 | 151 | image/svg+xml 152 | 154 | 155 | 156 | 157 | 158 | 162 | 172 | 177 | 179 | 191 | 196 | 203 | 204 | 205 | 206 | 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 | 71 | 72 | 73 | 0 74 | 0 75 | 1140 76 | 21 77 | 78 | 79 | 80 | 81 | File 82 | 83 | 84 | 85 | 86 | 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 | 31 | 32 | 33 | 0 34 | 0 35 | 688 36 | 22 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | MplWidget 45 | QWidget 46 |
mplwidget.h
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 | --------------------------------------------------------------------------------