├── .gitattributes ├── .gitignore ├── .travis.yml ├── COPYING ├── README.rst ├── config.py ├── docs ├── Makefile ├── make.bat └── source │ ├── conf.py │ ├── example_simple.rst │ ├── examples.rst │ ├── general.rst │ ├── index.rst │ ├── pynlo.rst │ └── readme_link.rst ├── requirements-doc.txt ├── setup.py └── src ├── examples ├── Dudley_SSFM.py ├── SSFM_coherence.py ├── fundamental_SSFM.py ├── pulsed_dfg.py ├── self_similar_propagation.py ├── simple_SSFM.py └── supercontinuum_with_FROG.py ├── pynlo ├── __init__.py ├── devices │ ├── __init__.py │ └── grating_compressor.py ├── interactions │ ├── FourWaveMixing │ │ ├── SSFM.py │ │ ├── __init__.py │ │ └── global_variables.py │ ├── ThreeWaveMixing │ │ ├── DFG_integrand.py │ │ ├── __init__.py │ │ ├── field_classes.py │ │ └── multi_run_rw.py │ └── __init__.py ├── light │ ├── DerivedPulses.py │ ├── PulseBase.py │ ├── __init__.py │ ├── beam.py │ └── high_V_waveguide.py ├── media │ ├── __init__.py │ ├── crystals │ │ ├── Boyd.py │ │ ├── CrystalContainer.py │ │ ├── Readme.txt │ │ ├── XTAL_AgGaS.py │ │ ├── XTAL_AgGaSe2.py │ │ ├── XTAL_PPLN.py │ │ └── __init__.py │ └── fibers │ │ ├── JSONFiberLoader.py │ │ ├── __init__.py │ │ ├── calculators.py │ │ ├── fiber.py │ │ ├── fiber_config.txt │ │ └── general_fibers.txt └── util │ ├── Calculators │ └── DispersiveWave_ResonanceFrequency.py │ ├── __init__.py │ ├── ode_solve │ ├── __init__.py │ ├── dopr853.py │ ├── dopr853_constants.py │ ├── dopr853_controller.py │ ├── example.py │ ├── steppers.py │ └── test.py │ └── pynlo_ffts.py └── validation ├── Old and Partial Tests ├── Agrawal_SSFM.py ├── Dudley_SSFM.py ├── Dudley_crosscheck.py ├── Hult_SSFM.py ├── PPLN_SHG.py ├── RIFS_SSFM.py ├── devices_gratingcompressor_treacy.py ├── devices_gratingcompressor_treacy_test.py ├── dfg_test.py ├── dfg_test_2.py ├── dfg_unit_test.py ├── ppln_generate_random_design.py ├── ppln_generate_stepped_apodized_design.py ├── ppln_phasematching.py └── pulse_test.py ├── __init__.py ├── devices_gratingcompressor_treacy_test.py ├── dfg_unit_test.py └── pulse_test.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | # Docs build directory 49 | docs/build/* 50 | build/* 51 | # Compiled python 52 | *.pyc 53 | # Setup tools directories 54 | pyNLO.egg-info/* 55 | src/pyNLO.egg-info/* 56 | dist/* 57 | 58 | # we should generally ignore images, pdf files, etc. in case those get in somehow: 59 | *.png 60 | *.jpg 61 | *.jpeg 62 | *.tif 63 | *.tiff 64 | *.pdf 65 | *.doc -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | matrix: 3 | include: 4 | - python: '2.7' 5 | env: DEPS="libgfortran=1.0 numpy=1.9 scipy=0.17 matplotlib=1.4 pillow<3 pandas=0.16 6 | scikit-image=0.11 pyyaml numba=0.20 pytables" 7 | - python: '3.5' 8 | env: DEPS="libgfortran=1.0 numpy scipy matplotlib pillow<3 pandas scikit-image 9 | pyyaml numba pytables" 10 | - python: '3.6' 11 | env: DEPS="libgfortran=1.0 numpy scipy matplotlib pandas scikit-image pyyaml numba 12 | pytables" 13 | addons: 14 | apt: 15 | packages: 16 | - libfftw3-dev 17 | - libatlas-dev 18 | - libatlas-base-dev 19 | - liblapack-dev 20 | - libblas-dev 21 | - gfortran 22 | install: 23 | - conda update --yes conda 24 | - conda config --add channels soft-matter 25 | - conda create -n testenv --yes $DEPS pip nose setuptools python=$TRAVIS_PYTHON_VERSION 26 | - source activate testenv 27 | - echo $PATH 28 | - which python 29 | - conda info 30 | - conda list 31 | - pip install pyfftw 32 | - python setup.py install 33 | - echo $PATH 34 | - ls -lh 35 | before_install: 36 | - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then wget http://repo.continuum.io/miniconda/Miniconda-3.5.5-Linux-x86_64.sh 37 | -O miniconda.sh; else wget http://repo.continuum.io/miniconda/Miniconda3-3.5.5-Linux-x86_64.sh 38 | -O miniconda.sh; fi 39 | - chmod +x miniconda.sh 40 | - "./miniconda.sh -b -p /home/travis/mc" 41 | - export PATH=/home/travis/mc/bin:$PATH 42 | script: 43 | - python -m unittest discover src -v -p "*test.py" 44 | deploy: 45 | provider: pypi 46 | user: ycasg 47 | password: 48 | secure: S5ASMmOGMdnosvndMMAwEebQSiXHeqaZBZcOxdjKysftlVlK6QiVDOPYu250q7TFlHXI6oMBcud8mZDOShMUl+g13wi5iPjEo9J4BnyKpitCe2oanMEphLmkofMjtLybwE0uvmaonAyB5WjHDt8R0Y8UKIq9K3/ZJlfGCPz6JV0jzp3erljCt0Co9CyAc+/ridIz+5EFdk6sHQgzS/aOKrc+UodVJYQCtmIq2VCtSLqf/rjJW9J40lsswYTmqpl35otrxg3gL4Jq5JbjuLs3In4bKCYGX6x4uBEZqPzJAgs3IEo1CAM4BFMzZB+u3tIltKYc7LGo1RFIFPa9PYnSdYZdqW+XefmIQJ8w0bKdaR8jEH+5dJtLHis0IqjrmJkrxV6f4ciayPK+EzqA5aWzGW4uxc2/5A+QO4L1xR3hFd3mlfVZDgV9yDpu4A1BveyRxHM0yVP4HJVw+N7FQF2rdHk8MR5xm+2+Rxy6XY0qwdUf+r/7JVEskkc67MpXHRjChABIfO2qloj3mP7fMp64aVGJzci7tUmAn+fNXSBS21RHKkDssSo8p+Mr4Qd6xE44zAIEQjJqM946/Ztk7jnVDkkSBBZNrD1axN7PX7VrU+Y4rxyp0vv2Bxaoo+/v8QnvdS/4Wym09Phv1lSfEy8XOeu78IKuBukl88zuxI64JI0= 49 | on: 50 | tags: true 51 | branch: master 52 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | pyNLO: Nonlinear optics modeling for Python 2 | =========================================== 3 | 4 | This README is best viewed at http://pynlo.readthedocs.io/en/latest/readme_link.html 5 | 6 | Complete documentation is available at http://pynlo.readthedocs.io/ 7 | 8 | .. image:: https://cloud.githubusercontent.com/assets/1107796/13850062/17f09ea8-ec1e-11e5-9311-b94df29c01cb.png 9 | :width: 330px 10 | :alt: PyNLO 11 | :align: right 12 | 13 | 14 | Introduction 15 | ------------ 16 | 17 | PyNLO provides an easy-to-use, object-oriented set of tools for modeling the nonlinear interaction of light with materials. It provides many functionalities for representing pulses of light, beams of light, and nonlinear materials, such as crystals and fibers. Also, it features methods for simulating both three-wave-mixing processes (such as DFG), as well as four-wave-mixing processes such as supercontinuum generation. 18 | 19 | Features: 20 | - A solver for the propagation of light through a Chi-3 material, useful for simulation pulse compression and supercontinuum generation in an optical fiber. This solver is highly efficient, thanks to an adaptive-step-size implementation of the "Fourth-order Runge-Kutta in the Interaction Picture " (RK4IP) method of `Hult (2007) `_. 21 | 22 | - A solver for simulating Chi-2 processes such as difference frequency generation. 23 | 24 | - A flexible object-oriented system for treating laser pulses, beams, fibers, and crystals. 25 | 26 | - ...and much more! 27 | 28 | 29 | Installation 30 | ------------ 31 | 32 | PyNLO requires Python 2, and is tested on Python 2.7 (Python 3 compatibility is a work-in-progress). If you don't already have Python, we recommend an "all in one" Python package such as the `Anaconda Python Distribution `_, which is available for free. 33 | 34 | With pip 35 | ~~~~~~~~ 36 | 37 | The latest "official release" can be installed from PyPi with :: 38 | 39 | pip install pynlo 40 | 41 | The up-to-the-minute latest version can be installed from GitHub with :: 42 | 43 | pip install git+https://github.com/pyNLO/PyNLO.git 44 | 45 | 46 | With setuptools 47 | ~~~~~~~~~~~~~~~ 48 | 49 | Alternatively, you can download the latest version from the `PyNLO Github site `_ (look for the "download zip" button), `cd` to the PyNLO directory, and use :: 50 | 51 | python setup.py install 52 | 53 | Or, if you wish to edit the PyNLO source code without re-installing each time :: 54 | 55 | python setup.py develop 56 | 57 | 58 | Documentation 59 | ------------- 60 | The complete documentation for PyNLO is availabe at https://pynlo.readthedocs.org. 61 | 62 | 63 | Example of use 64 | -------------- 65 | 66 | The following example demonstrates how to use PyNLO to simulate the propagation of a 50 fs pulse through a nonlinear fiber using the split-step Fourier model (SSFM). Note that the actual propagation of the pulse takes up just a few lines of code. Most of the other code is simply plotting the results. 67 | 68 | This example is contained in examples/simple_SSFM.py 69 | 70 | .. code-block:: python 71 | 72 | import numpy as np 73 | import matplotlib.pyplot as plt 74 | import pynlo 75 | 76 | FWHM = 0.050 # pulse duration (ps) 77 | pulseWL = 1550 # pulse central wavelength (nm) 78 | EPP = 50e-12 # Energy per pulse (J) 79 | GDD = 0.0 # Group delay dispersion (ps^2) 80 | TOD = 0.0 # Third order dispersion (ps^3) 81 | 82 | Window = 10.0 # simulation window (ps) 83 | Steps = 100 # simulation steps 84 | Points = 2**13 # simulation points 85 | 86 | beta2 = -120 # (ps^2/km) 87 | beta3 = 0.00 # (ps^3/km) 88 | beta4 = 0.005 # (ps^4/km) 89 | 90 | Length = 20 # length in mm 91 | 92 | Alpha = 0.0 # attentuation coefficient (dB/cm) 93 | Gamma = 1000 # Gamma (1/(W km) 94 | 95 | fibWL = pulseWL # Center WL of fiber (nm) 96 | 97 | Raman = True # Enable Raman effect? 98 | Steep = True # Enable self steepening? 99 | 100 | alpha = np.log((10**(Alpha * 0.1))) * 100 # convert from dB/cm to 1/m 101 | 102 | 103 | # set up plots for the results: 104 | fig = plt.figure(figsize=(8,8)) 105 | ax0 = plt.subplot2grid((3,2), (0, 0), rowspan=1) 106 | ax1 = plt.subplot2grid((3,2), (0, 1), rowspan=1) 107 | ax2 = plt.subplot2grid((3,2), (1, 0), rowspan=2, sharex=ax0) 108 | ax3 = plt.subplot2grid((3,2), (1, 1), rowspan=2, sharex=ax1) 109 | 110 | 111 | ######## This is where the PyNLO magic happens! ############################ 112 | 113 | # create the pulse! 114 | pulse = pynlo.light.DerivedPulses.SechPulse(power = 1, # Power will be scaled by set_epp 115 | T0_ps = FWHM/1.76, 116 | center_wavelength_nm = pulseWL, 117 | time_window_ps = Window, 118 | GDD=GDD, TOD=TOD, 119 | NPTS = Points, 120 | frep_MHz = 100, 121 | power_is_avg = False) 122 | # set the pulse energy! 123 | pulse.set_epp(EPP) 124 | 125 | # create the fiber! 126 | fiber1 = pynlo.media.fibers.fiber.FiberInstance() 127 | fiber1.generate_fiber(Length * 1e-3, center_wl_nm=fibWL, betas=(beta2, beta3, beta4), 128 | gamma_W_m=Gamma * 1e-3, gvd_units='ps^n/km', gain=-alpha) 129 | 130 | # Propagation 131 | evol = pynlo.interactions.FourWaveMixing.SSFM.SSFM(local_error=0.005, USE_SIMPLE_RAMAN=True, 132 | disable_Raman = np.logical_not(Raman), 133 | disable_self_steepening = np.logical_not(Steep)) 134 | 135 | y, AW, AT, pulse_out = evol.propagate(pulse_in=pulse, fiber=fiber1, n_steps=Steps) 136 | 137 | ########## That's it! Physics complete. Just plotting commands from here! ################ 138 | 139 | 140 | F = pulse.F_THz # Frequency grid of pulse (THz) 141 | 142 | def dB(num): 143 | return 10 * np.log10(np.abs(num)**2) 144 | 145 | zW = dB( np.transpose(AW)[:, (F > 0)] ) 146 | zT = dB( np.transpose(AT) ) 147 | 148 | y_mm = y * 1e3 # convert distance to mm 149 | 150 | ax0.plot(pulse_out.F_THz, dB(pulse_out.AW), color = 'r') 151 | ax1.plot(pulse_out.T_ps, dB(pulse_out.AT), color = 'r') 152 | 153 | ax0.plot(pulse.F_THz, dB(pulse.AW), color = 'b') 154 | ax1.plot(pulse.T_ps, dB(pulse.AT), color = 'b') 155 | 156 | extent = (np.min(F[F > 0]), np.max(F[F > 0]), 0, Length) 157 | ax2.imshow(zW, extent=extent, 158 | vmin=np.max(zW) - 40.0, vmax=np.max(zW), 159 | aspect='auto', origin='lower') 160 | 161 | extent = (np.min(pulse.T_ps), np.max(pulse.T_ps), np.min(y_mm), Length) 162 | ax3.imshow(zT, extent=extent, 163 | vmin=np.max(zT) - 40.0, vmax=np.max(zT), 164 | aspect='auto', origin='lower') 165 | 166 | 167 | ax0.set_ylabel('Intensity (dB)') 168 | ax0.set_ylim( - 80, 0) 169 | ax1.set_ylim( - 40, 40) 170 | 171 | ax2.set_ylabel('Propagation distance (mm)') 172 | ax2.set_xlabel('Frequency (THz)') 173 | ax2.set_xlim(0,400) 174 | 175 | ax3.set_xlabel('Time (ps)') 176 | 177 | plt.show() 178 | 179 | 180 | Here are the results: 181 | 182 | .. image:: https://cloud.githubusercontent.com/assets/1107796/14987706/d5dec8cc-110d-11e6-90eb-3cf14294b603.png 183 | :width: 500px 184 | :alt: results 185 | :align: center 186 | 187 | 188 | Contributing 189 | ------------ 190 | 191 | We welcome suggestions for improvement, questions, comments, etc. The best way to to open a new issue here: https://github.com/pyNLO/PyNLO/issues/. 192 | 193 | 194 | License 195 | ------- 196 | PyNLO is licensed under the `GPLv3 license `_. This means that you are free to use PyNLO for any **open-source** project. Of course, PyNLO is provided "as is" with absolutely no warrenty. 197 | 198 | 199 | References 200 | ---------- 201 | [1] Johan Hult, "A Fourth-Order Runge–Kutta in the Interaction Picture Method for Simulating Supercontinuum Generation in Optical Fibers," J. Lightwave Technol. 25, 3770-3775 (2007) https://www.osapublishing.org/jlt/abstract.cfm?uri=jlt-25-12-3770 202 | 203 | 204 | 205 | 206 | 207 | 208 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import sys, os 4 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon'] 5 | templates_path = ['/home/docs/checkouts/readthedocs.org/readthedocs/templates/sphinx', 'templates', '_templates', '.templates'] 6 | source_suffix = '.rst' 7 | master_doc = 'index' 8 | project = u'PyNLO' 9 | copyright = u'' 10 | version = 'latest' 11 | release = 'latest' 12 | exclude_patterns = ['_build'] 13 | pygments_style = 'sphinx' 14 | htmlhelp_basename = 'pynlo' 15 | file_insertion_enabled = False 16 | latex_documents = [ 17 | ('index', 'pynlo.tex', u'PyNLO Documentation', 18 | u'', 'manual'), 19 | ] 20 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True' 21 | if on_rtd: 22 | if sys.version[0] == '3': # Python 3 23 | from unittest.mock import MagicMock 24 | elif sys.version[0] == '2': # Python 2 25 | from mock import Mock as MagicMock 26 | else: 27 | raise ImportError("Don't know how to import MagicMock.") 28 | 29 | class Mock(MagicMock): 30 | @classmethod 31 | def __getattr__(cls, name): 32 | return Mock() 33 | 34 | MOCK_MODULES = ['pyfftw', 'scipy', 'numpy', 'matplotlib', 'matplotlib.pyplot'] 35 | print "Mocking ", MOCK_MODULES 36 | sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES) 37 | 38 | 39 | ########################################################################### 40 | # auto-created readthedocs.org specific configuration # 41 | ########################################################################### 42 | 43 | 44 | # 45 | # The following code was added during an automated build on readthedocs.org 46 | # It is auto created and injected for every build. The result is based on the 47 | # conf.py.tmpl file found in the readthedocs.org codebase: 48 | # https://github.com/rtfd/readthedocs.org/blob/master/readthedocs/doc_builder/templates/doc_builder/conf.py.tmpl 49 | # 50 | 51 | 52 | import sys 53 | import os.path 54 | from six import string_types 55 | 56 | from sphinx import version_info 57 | 58 | from recommonmark.parser import CommonMarkParser 59 | 60 | # Only Sphinx 1.3+ 61 | if version_info[0] == 1 and version_info[1] > 2: 62 | 63 | # Markdown Support 64 | if 'source_suffix' in globals(): 65 | if isinstance(source_suffix, string_types) and source_suffix != '.md': 66 | source_suffix = [source_suffix, '.md'] 67 | elif '.md' not in source_suffix: 68 | source_suffix.append('.md') 69 | else: 70 | source_suffix = ['.rst', '.md'] 71 | 72 | if 'source_parsers' in globals(): 73 | if '.md' not in source_parsers: 74 | source_parsers['.md'] = CommonMarkParser 75 | else: 76 | source_parsers = { 77 | '.md': CommonMarkParser, 78 | } 79 | 80 | if globals().get('source_suffix', False): 81 | if isinstance(source_suffix, string_types): 82 | SUFFIX = source_suffix 83 | else: 84 | SUFFIX = source_suffix[0] 85 | else: 86 | SUFFIX = '.rst' 87 | 88 | #Add RTD Template Path. 89 | if 'templates_path' in globals(): 90 | templates_path.insert(0, '/home/docs/checkouts/readthedocs.org/readthedocs/templates/sphinx') 91 | else: 92 | templates_path = ['/home/docs/checkouts/readthedocs.org/readthedocs/templates/sphinx', 'templates', '_templates', 93 | '.templates'] 94 | 95 | # Add RTD Static Path. Add to the end because it overwrites previous files. 96 | if not 'html_static_path' in globals(): 97 | html_static_path = [] 98 | if os.path.exists('_static'): 99 | html_static_path.append('_static') 100 | html_static_path.append('/home/docs/checkouts/readthedocs.org/readthedocs/templates/sphinx/_static') 101 | 102 | # Add RTD Theme only if they aren't overriding it already 103 | using_rtd_theme = False 104 | if 'html_theme' in globals(): 105 | if html_theme in ['default']: 106 | # Allow people to bail with a hack of having an html_style 107 | if not 'html_style' in globals(): 108 | import sphinx_rtd_theme 109 | html_theme = 'sphinx_rtd_theme' 110 | html_style = None 111 | html_theme_options = {} 112 | if 'html_theme_path' in globals(): 113 | html_theme_path.append(sphinx_rtd_theme.get_html_theme_path()) 114 | else: 115 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 116 | 117 | using_rtd_theme = True 118 | else: 119 | import sphinx_rtd_theme 120 | html_theme = 'sphinx_rtd_theme' 121 | html_style = None 122 | html_theme_options = {} 123 | if 'html_theme_path' in globals(): 124 | html_theme_path.append(sphinx_rtd_theme.get_html_theme_path()) 125 | else: 126 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 127 | using_rtd_theme = True 128 | 129 | # Force theme on setting 130 | if globals().get('RTD_NEW_THEME', False): 131 | html_theme = 'sphinx_rtd_theme' 132 | html_style = None 133 | html_theme_options = {} 134 | using_rtd_theme = True 135 | 136 | if globals().get('RTD_OLD_THEME', False): 137 | html_style = 'rtd.css' 138 | html_theme = 'default' 139 | 140 | if globals().get('websupport2_base_url', False): 141 | websupport2_base_url = 'https://readthedocs.org//websupport' 142 | if 'http' not in settings.MEDIA_URL: 143 | websupport2_static_url = 'https://media.readthedocs.org/static/' 144 | else: 145 | websupport2_static_url = 'https://media.readthedocs.org//static' 146 | 147 | 148 | #Add project information to the template context. 149 | context = { 150 | 'using_theme': using_rtd_theme, 151 | 'html_theme': html_theme, 152 | 'current_version': "latest", 153 | 'MEDIA_URL': "https://media.readthedocs.org/", 154 | 'PRODUCTION_DOMAIN': "readthedocs.org", 155 | 'versions': [ 156 | ("latest", "/en/latest/"), 157 | ], 158 | 'downloads': [ 159 | ], 160 | 'slug': 'pynlo', 161 | 'name': u'PyNLO', 162 | 'rtd_language': u'en', 163 | 'canonical_url': 'http://pynlo.readthedocs.org/en/latest/', 164 | 'analytics_code': '', 165 | 'single_version': False, 166 | 'conf_py_path': '/./', 167 | 'api_host': 'https://readthedocs.org/', 168 | 'github_user': 'ycasg', 169 | 'github_repo': 'PyNLO', 170 | 'github_version': 'master', 171 | 'display_github': True, 172 | 'bitbucket_user': 'None', 173 | 'bitbucket_repo': 'None', 174 | 'bitbucket_version': 'master', 175 | 'display_bitbucket': False, 176 | 'READTHEDOCS': True, 177 | 'using_theme': (html_theme == "default"), 178 | 'new_theme': (html_theme == "sphinx_rtd_theme"), 179 | 'source_suffix': SUFFIX, 180 | 'user_analytics_code': '', 181 | 'global_analytics_code': 'UA-17997319-1', 182 | 183 | 'commit': '180d51c6', 184 | 185 | } 186 | if 'html_context' in globals(): 187 | html_context.update(context) 188 | else: 189 | html_context = context 190 | 191 | # Add custom RTD extension 192 | if 'extensions' in globals(): 193 | extensions.append("readthedocs_ext.readthedocs") 194 | else: 195 | extensions = ["readthedocs_ext.readthedocs"] 196 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " applehelp to make an Apple Help Book" 34 | @echo " devhelp to make HTML files and a Devhelp project" 35 | @echo " epub to make an epub" 36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 37 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 39 | @echo " text to make text files" 40 | @echo " man to make manual pages" 41 | @echo " texinfo to make Texinfo files" 42 | @echo " info to make Texinfo files and run them through makeinfo" 43 | @echo " gettext to make PO message catalogs" 44 | @echo " changes to make an overview of all changed/added/deprecated items" 45 | @echo " xml to make Docutils-native XML files" 46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 47 | @echo " linkcheck to check all external links for integrity" 48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 49 | @echo " coverage to run coverage check of the documentation (if enabled)" 50 | 51 | clean: 52 | rm -rf $(BUILDDIR)/* 53 | 54 | html: 55 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 56 | @echo 57 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 58 | 59 | dirhtml: 60 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 61 | @echo 62 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 63 | 64 | singlehtml: 65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 66 | @echo 67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 68 | 69 | pickle: 70 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 71 | @echo 72 | @echo "Build finished; now you can process the pickle files." 73 | 74 | json: 75 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 76 | @echo 77 | @echo "Build finished; now you can process the JSON files." 78 | 79 | htmlhelp: 80 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 81 | @echo 82 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 83 | ".hhp project file in $(BUILDDIR)/htmlhelp." 84 | 85 | qthelp: 86 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 87 | @echo 88 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 89 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 90 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pyNLO.qhcp" 91 | @echo "To view the help file:" 92 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pyNLO.qhc" 93 | 94 | applehelp: 95 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 96 | @echo 97 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 98 | @echo "N.B. You won't be able to view it unless you put it in" \ 99 | "~/Library/Documentation/Help or install it in your application" \ 100 | "bundle." 101 | 102 | devhelp: 103 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 104 | @echo 105 | @echo "Build finished." 106 | @echo "To view the help file:" 107 | @echo "# mkdir -p $$HOME/.local/share/devhelp/pyNLO" 108 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pyNLO" 109 | @echo "# devhelp" 110 | 111 | epub: 112 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 113 | @echo 114 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 115 | 116 | latex: 117 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 118 | @echo 119 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 120 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 121 | "(use \`make latexpdf' here to do that automatically)." 122 | 123 | latexpdf: 124 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 125 | @echo "Running LaTeX files through pdflatex..." 126 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 127 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 128 | 129 | latexpdfja: 130 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 131 | @echo "Running LaTeX files through platex and dvipdfmx..." 132 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 133 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 134 | 135 | text: 136 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 137 | @echo 138 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 139 | 140 | man: 141 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 142 | @echo 143 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 144 | 145 | texinfo: 146 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 147 | @echo 148 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 149 | @echo "Run \`make' in that directory to run these through makeinfo" \ 150 | "(use \`make info' here to do that automatically)." 151 | 152 | info: 153 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 154 | @echo "Running Texinfo files through makeinfo..." 155 | make -C $(BUILDDIR)/texinfo info 156 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 157 | 158 | gettext: 159 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 160 | @echo 161 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 162 | 163 | changes: 164 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 165 | @echo 166 | @echo "The overview file is in $(BUILDDIR)/changes." 167 | 168 | linkcheck: 169 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 170 | @echo 171 | @echo "Link check complete; look for any errors in the above output " \ 172 | "or in $(BUILDDIR)/linkcheck/output.txt." 173 | 174 | doctest: 175 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 176 | @echo "Testing of doctests in the sources finished, look at the " \ 177 | "results in $(BUILDDIR)/doctest/output.txt." 178 | 179 | coverage: 180 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 181 | @echo "Testing of coverage in the sources finished, look at the " \ 182 | "results in $(BUILDDIR)/coverage/python.txt." 183 | 184 | xml: 185 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 186 | @echo 187 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 188 | 189 | pseudoxml: 190 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 191 | @echo 192 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 193 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source 10 | set I18NSPHINXOPTS=%SPHINXOPTS% source 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | echo. coverage to run coverage check of the documentation if enabled 41 | goto end 42 | ) 43 | 44 | if "%1" == "clean" ( 45 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 46 | del /q /s %BUILDDIR%\* 47 | goto end 48 | ) 49 | 50 | 51 | REM Check if sphinx-build is available and fallback to Python version if any 52 | %SPHINXBUILD% 2> nul 53 | if errorlevel 9009 goto sphinx_python 54 | goto sphinx_ok 55 | 56 | :sphinx_python 57 | 58 | set SPHINXBUILD=python -m sphinx.__init__ 59 | %SPHINXBUILD% 2> nul 60 | if errorlevel 9009 ( 61 | echo. 62 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 63 | echo.installed, then set the SPHINXBUILD environment variable to point 64 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 65 | echo.may add the Sphinx directory to PATH. 66 | echo. 67 | echo.If you don't have Sphinx installed, grab it from 68 | echo.http://sphinx-doc.org/ 69 | exit /b 1 70 | ) 71 | 72 | :sphinx_ok 73 | 74 | 75 | if "%1" == "html" ( 76 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 80 | goto end 81 | ) 82 | 83 | if "%1" == "dirhtml" ( 84 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 88 | goto end 89 | ) 90 | 91 | if "%1" == "singlehtml" ( 92 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 93 | if errorlevel 1 exit /b 1 94 | echo. 95 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 96 | goto end 97 | ) 98 | 99 | if "%1" == "pickle" ( 100 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 101 | if errorlevel 1 exit /b 1 102 | echo. 103 | echo.Build finished; now you can process the pickle files. 104 | goto end 105 | ) 106 | 107 | if "%1" == "json" ( 108 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 109 | if errorlevel 1 exit /b 1 110 | echo. 111 | echo.Build finished; now you can process the JSON files. 112 | goto end 113 | ) 114 | 115 | if "%1" == "htmlhelp" ( 116 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 117 | if errorlevel 1 exit /b 1 118 | echo. 119 | echo.Build finished; now you can run HTML Help Workshop with the ^ 120 | .hhp project file in %BUILDDIR%/htmlhelp. 121 | goto end 122 | ) 123 | 124 | if "%1" == "qthelp" ( 125 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 129 | .qhcp project file in %BUILDDIR%/qthelp, like this: 130 | <<<<<<< HEAD 131 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PyNLO.qhcp 132 | echo.To view the help file: 133 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PyNLO.ghc 134 | ======= 135 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pyNLO.qhcp 136 | echo.To view the help file: 137 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pyNLO.ghc 138 | >>>>>>> origin/master 139 | goto end 140 | ) 141 | 142 | if "%1" == "devhelp" ( 143 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 144 | if errorlevel 1 exit /b 1 145 | echo. 146 | echo.Build finished. 147 | goto end 148 | ) 149 | 150 | if "%1" == "epub" ( 151 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 152 | if errorlevel 1 exit /b 1 153 | echo. 154 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 155 | goto end 156 | ) 157 | 158 | if "%1" == "latex" ( 159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 160 | if errorlevel 1 exit /b 1 161 | echo. 162 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 163 | goto end 164 | ) 165 | 166 | if "%1" == "latexpdf" ( 167 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 168 | cd %BUILDDIR%/latex 169 | make all-pdf 170 | cd %~dp0 171 | echo. 172 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 173 | goto end 174 | ) 175 | 176 | if "%1" == "latexpdfja" ( 177 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 178 | cd %BUILDDIR%/latex 179 | make all-pdf-ja 180 | cd %~dp0 181 | echo. 182 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 183 | goto end 184 | ) 185 | 186 | if "%1" == "text" ( 187 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 188 | if errorlevel 1 exit /b 1 189 | echo. 190 | echo.Build finished. The text files are in %BUILDDIR%/text. 191 | goto end 192 | ) 193 | 194 | if "%1" == "man" ( 195 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 196 | if errorlevel 1 exit /b 1 197 | echo. 198 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 199 | goto end 200 | ) 201 | 202 | if "%1" == "texinfo" ( 203 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 204 | if errorlevel 1 exit /b 1 205 | echo. 206 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 207 | goto end 208 | ) 209 | 210 | if "%1" == "gettext" ( 211 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 212 | if errorlevel 1 exit /b 1 213 | echo. 214 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 215 | goto end 216 | ) 217 | 218 | if "%1" == "changes" ( 219 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 220 | if errorlevel 1 exit /b 1 221 | echo. 222 | echo.The overview file is in %BUILDDIR%/changes. 223 | goto end 224 | ) 225 | 226 | if "%1" == "linkcheck" ( 227 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 228 | if errorlevel 1 exit /b 1 229 | echo. 230 | echo.Link check complete; look for any errors in the above output ^ 231 | or in %BUILDDIR%/linkcheck/output.txt. 232 | goto end 233 | ) 234 | 235 | if "%1" == "doctest" ( 236 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 237 | if errorlevel 1 exit /b 1 238 | echo. 239 | echo.Testing of doctests in the sources finished, look at the ^ 240 | results in %BUILDDIR%/doctest/output.txt. 241 | goto end 242 | ) 243 | 244 | if "%1" == "coverage" ( 245 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage 246 | if errorlevel 1 exit /b 1 247 | echo. 248 | echo.Testing of coverage in the sources finished, look at the ^ 249 | results in %BUILDDIR%/coverage/python.txt. 250 | goto end 251 | ) 252 | 253 | if "%1" == "xml" ( 254 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 255 | if errorlevel 1 exit /b 1 256 | echo. 257 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 258 | goto end 259 | ) 260 | 261 | if "%1" == "pseudoxml" ( 262 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 263 | if errorlevel 1 exit /b 1 264 | echo. 265 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 266 | goto end 267 | ) 268 | 269 | :end 270 | -------------------------------------------------------------------------------- /docs/source/example_simple.rst: -------------------------------------------------------------------------------- 1 | Supercontinuum generation example 2 | ================================= 3 | 4 | Here is an example of supercontinuum generation in a fiber :: 5 | 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import pynlo 9 | 10 | FWHM = 0.050 # pulse duration (ps) 11 | pulseWL = 1550 # pulse central wavelength (nm) 12 | EPP = 50e-12 # Energy per pulse (J) 13 | GDD = 0.0 # Group delay dispersion (ps^2) 14 | TOD = 0.0 # Third order dispersion (ps^3) 15 | 16 | Window = 10.0 # simulation window (ps) 17 | Steps = 50 # simulation steps 18 | Points = 2**13 # simulation points 19 | 20 | beta2 = -120 # (ps^2/km) 21 | beta3 = 0.00 # (ps^3/km) 22 | beta4 = 0.005 # (ps^4/km) 23 | 24 | Length = 20 # length in mm 25 | 26 | Alpha = 0.0 # attentuation coefficient (dB/cm) 27 | Gamma = 1000 # Gamma (1/(W km) 28 | 29 | fibWL = pulseWL # Center WL of fiber (nm) 30 | 31 | Raman = True # Enable Raman effect? 32 | Steep = True # Enable self steepening? 33 | 34 | alpha = np.log((10**(Alpha * 0.1))) * 100 # convert from dB/cm to 1/m 35 | 36 | 37 | # set up plots for the results: 38 | fig = plt.figure(figsize=(10,10)) 39 | ax0 = plt.subplot2grid((3,2), (0, 0), rowspan=1) 40 | ax1 = plt.subplot2grid((3,2), (0, 1), rowspan=1) 41 | ax2 = plt.subplot2grid((3,2), (1, 0), rowspan=2, sharex=ax0) 42 | ax3 = plt.subplot2grid((3,2), (1, 1), rowspan=2, sharex=ax1) 43 | 44 | 45 | ######## This is where the PyNLO magic happens! ############################ 46 | 47 | # create the pulse! 48 | pulse = pynlo.light.DerivedPulses.SechPulse(1, FWHM/1.76, pulseWL, time_window_ps=Window, 49 | GDD=GDD, TOD=TOD, NPTS=Points, frep_MHz=100, power_is_avg=False) 50 | pulse.set_epp(EPP) # set the pulse energy 51 | 52 | # create the fiber! 53 | fiber1 = pynlo.media.fibers.fiber.FiberInstance() 54 | fiber1.generate_fiber(Length * 1e-3, center_wl_nm=fibWL, betas=(beta2, beta3, beta4), 55 | gamma_W_m=Gamma * 1e-3, gvd_units='ps^n/km', gain=-alpha) 56 | 57 | # Propagation 58 | evol = pynlo.interactions.FourWaveMixing.SSFM.SSFM(local_error=0.001, USE_SIMPLE_RAMAN=True, 59 | disable_Raman=np.logical_not(Raman), 60 | disable_self_steepening=np.logical_not(Steep)) 61 | 62 | y, AW, AT, pulse_out = evol.propagate(pulse_in=pulse, fiber=fiber1, n_steps=Steps) 63 | 64 | ########## That's it! Physic done. Just boring plots from here! ################ 65 | 66 | 67 | F = pulse.W_mks / (2 * np.pi) * 1e-12 # convert to THz 68 | 69 | def dB(num): 70 | return 10 * np.log10(np.abs(num)**2) 71 | 72 | zW = dB( np.transpose(AW)[:, (F > 0)] ) 73 | zT = dB( np.transpose(AT) ) 74 | 75 | y = y * 1e3 # convert distance to mm 76 | 77 | 78 | ax0.plot(F[F > 0], zW[-1], color='r') 79 | ax1.plot(pulse.T_ps,zT[-1], color='r') 80 | 81 | ax0.plot(F[F > 0], zW[0], color='b') 82 | ax1.plot(pulse.T_ps, zT[0], color='b') 83 | 84 | 85 | extent = (np.min(F[F > 0]), np.max(F[F > 0]), 0, Length) 86 | ax2.imshow(zW, extent=extent, vmin=np.max(zW) - 60.0, 87 | vmax=np.max(zW), aspect='auto', origin='lower') 88 | 89 | extent = (np.min(pulse.T_ps), np.max(pulse.T_ps), np.min(y), Length) 90 | ax3.imshow(zT, extent=extent, vmin=np.max(zT) - 60.0, 91 | vmax=np.max(zT), aspect='auto', origin='lower') 92 | 93 | 94 | ax0.set_ylabel('Intensity (dB)') 95 | 96 | ax2.set_xlabel('Frequency (THz)') 97 | ax3.set_xlabel('Time (ps)') 98 | 99 | ax2.set_ylabel('Propagation distance (mm)') 100 | 101 | ax2.set_xlim(0,400) 102 | 103 | ax0.set_ylim(-80,0) 104 | ax1.set_ylim(-40,40) 105 | 106 | plt.show() 107 | 108 | 109 | Output: 110 | 111 | .. image:: https://cloud.githubusercontent.com/assets/1107796/13656636/c741acfc-e625-11e5-80e0-c291c17e2b2a.png 112 | :width: 500px 113 | :alt: example_result 114 | 115 | 116 | -------------------------------------------------------------------------------- /docs/source/examples.rst: -------------------------------------------------------------------------------- 1 | Examples 2 | ======== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | example_simple -------------------------------------------------------------------------------- /docs/source/general.rst: -------------------------------------------------------------------------------- 1 | General information on PyNLO 2 | ============================ 3 | 4 | 5 | Package Organization 6 | -------------------- 7 | In pyNLO, object-oriented programming is used to mimic the physics of nonlinear interactions. Whenever possible, each physical entity with intrinic properties -- for example an optical pulse or nonlinear fiber -- is mapped to a single Python class. These classes keep track of the objects' properties, calculate interactions between them and other objects, and provide simple calculator-type helper functions. 8 | 9 | 10 | References 11 | ---------- 12 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. PyNLO documentation master file, created by 2 | sphinx-quickstart on Mon Dec 28 22:42:21 2015. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to PyNLO's documentation! 7 | ================================= 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | readme_link 15 | general 16 | pynlo 17 | examples 18 | 19 | 20 | Indices and tables 21 | ================== 22 | 23 | * :ref:`genindex` 24 | * :ref:`modindex` 25 | * :ref:`search` 26 | 27 | -------------------------------------------------------------------------------- /docs/source/pynlo.rst: -------------------------------------------------------------------------------- 1 | PyNLO package 2 | ============= 3 | 4 | 5 | 6 | pynlo.light 7 | ----------- 8 | 9 | The **light** module contains modules to model light pulses. 10 | 11 | 12 | pynlo.light.PulseBase 13 | ~~~~~~~~~~~~~~~~~~~~~ 14 | 15 | .. autoclass:: pynlo.light.PulseBase.Pulse 16 | :members: 17 | :special-members: 18 | :show-inheritance: 19 | 20 | pynlo.light.DerivedPulses 21 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 22 | 23 | .. autoclass:: pynlo.light.DerivedPulses.SechPulse 24 | :members: 25 | :special-members: 26 | 27 | .. autoclass:: pynlo.light.DerivedPulses.GaussianPulse 28 | :members: 29 | :special-members: 30 | :show-inheritance: 31 | 32 | .. autoclass:: pynlo.light.DerivedPulses.FROGPulse 33 | :members: 34 | :special-members: 35 | :show-inheritance: 36 | 37 | .. autoclass:: pynlo.light.DerivedPulses.NoisePulse 38 | :members: 39 | :special-members: 40 | :show-inheritance: 41 | 42 | .. autoclass:: pynlo.light.DerivedPulses.CWPulse 43 | :members: 44 | :special-members: 45 | :show-inheritance: 46 | 47 | pynlo.light.beam 48 | ~~~~~~~~~~~~~~~~ 49 | 50 | .. autoclass:: pynlo.light.beam.OneDBeam 51 | :members: 52 | :special-members: 53 | :show-inheritance: 54 | 55 | 56 | 57 | pynlo.interactions 58 | ------------------ 59 | 60 | The pynlo.interactions module contains sub-modules to simulate the interaction in both three-wave-mixing (like DFG) and four-wave mixing (like supercontinuum generation). 61 | 62 | 63 | pynlo.interactions.FourWaveMixing 64 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 65 | 66 | This module implements the Split-step Fourier Method to solve the Generalized Nonlinear Schrodiner Equation and simulate the propagation of pulses in a Chi-3 nonlinear medium. 67 | 68 | .. autoclass:: pynlo.interactions.FourWaveMixing.SSFM.SSFM 69 | :members: __init__, propagate, propagate_to_gain_goal, calculate_coherence 70 | :show-inheritance: 71 | 72 | 73 | pynlo.interactions.ThreeWaveMixing 74 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 75 | 76 | This module simulated DFG in a Chi-2 medium. 77 | 78 | .. autoclass:: pynlo.interactions.ThreeWaveMixing.DFG_integrand.dfg_problem 79 | :members: 80 | :undoc-members: 81 | :special-members: 82 | :show-inheritance: 83 | 84 | .. autoclass:: pynlo.interactions.ThreeWaveMixing.DFG.NLmix 85 | :members: 86 | :undoc-members: 87 | :special-members: 88 | :show-inheritance: 89 | 90 | 91 | 92 | pynlo.media 93 | ----------- 94 | 95 | The **media** module contains sub-modules for modeling fibers and crystals. 96 | 97 | 98 | pynlo.media.fibers 99 | ~~~~~~~~~~~~~~~~~~ 100 | 101 | These classes are used to model fibers or fiber-like waveguides. 102 | 103 | .. autoclass:: pynlo.media.fibers.fiber.FiberInstance 104 | :members: 105 | :undoc-members: 106 | :special-members: 107 | :show-inheritance: 108 | 109 | .. automodule:: pynlo.media.fibers.calculators 110 | :members: 111 | :show-inheritance: 112 | 113 | 114 | pynlo.crystals 115 | ~~~~~~~~~~~~~~ 116 | 117 | These classes are used to model various nonlinear crystals. 118 | 119 | .. autoclass:: pynlo.media.crystals.CrystalContainer.Crystal 120 | :members: 121 | :special-members: 122 | :show-inheritance: 123 | 124 | .. autoclass:: pynlo.media.crystals.PPLN.PPLN 125 | :members: 126 | :special-members: 127 | :show-inheritance: 128 | 129 | .....More undocumented crystals here.... 130 | 131 | 132 | 133 | 134 | pynlo.util.ode_solve 135 | -------------------- 136 | These classes are an adaptation of the very nice *Numerical Recipes* ODE solvers into Python. The solver is divided into two parts: specific step iterators (eg Dopri853) and the framework for stepping through the ODE (steppers) 137 | 138 | 139 | Dormand-Prince 853 Stepper 140 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 141 | 142 | .. autoclass:: pynlo.util.ode_solve.dopr853.StepperDopr853 143 | :members: 144 | :special-members: 145 | :undoc-members: 146 | :show-inheritance: 147 | 148 | Steppers and helpers 149 | ~~~~~~~~~~~~~~~~~~~~ 150 | 151 | .. autoclass:: pynlo.util.ode_solve.steppers.Output 152 | :members: 153 | :special-members: 154 | :show-inheritance: 155 | 156 | .. autoclass:: pynlo.util.ode_solve.steppers.StepperBase 157 | :members: 158 | :special-members: 159 | :show-inheritance: 160 | 161 | .. autoclass:: pynlo.util.ode_solve.steppers.ODEint 162 | :members: 163 | :special-members: 164 | :show-inheritance: 165 | 166 | 167 | 168 | pynlo.devices 169 | ------------- 170 | 171 | .. autoclass:: pynlo.devices.grating_compressor.TreacyCompressor 172 | :members: 173 | :show-inheritance: 174 | 175 | 176 | -------------------------------------------------------------------------------- /docs/source/readme_link.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../../README.rst -------------------------------------------------------------------------------- /requirements-doc.txt: -------------------------------------------------------------------------------- 1 | jsonpickle>=0.9.2 2 | jsonschema>=2.5.1 3 | mock>=1.0.1 4 | nose>=1.3.7 5 | nose-cov>=1.6 6 | nose-fixes>=1.3 7 | unittest2>=1.0.1 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | #from distutils.core import setup 4 | # By popular demand... 5 | from setuptools import setup 6 | import os 7 | 8 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True' 9 | if not on_rtd: 10 | import numpy as np 11 | install_requires=[ 12 | "jsonpickle>=0.9.2", 13 | "jsonschema>=2.5.1", 14 | "numpy>=1.9.2", 15 | "scipy>=0.15.1" 16 | ] 17 | else: 18 | np = None 19 | install_requires=[] 20 | 21 | # "mock>=1.0.1", 22 | # "nose>=1.3.7", 23 | # "nose-cov>=1.6", 24 | # "nose-fixes>=1.3", 25 | 26 | setup(name='pyNLO', 27 | version='0.1.2', 28 | description='Python nonlinear optics', 29 | author='Gabe Ycas', 30 | author_email='ycasg@colorado.edu', 31 | url='https://github.com/pyNLO/PyNLO', 32 | install_requires=install_requires, 33 | packages=['pynlo', 34 | 'pynlo.devices', 35 | 'pynlo.interactions', 36 | 'pynlo.interactions.ThreeWaveMixing', 37 | 'pynlo.interactions.FourWaveMixing', 38 | 'pynlo.light', 39 | 'pynlo.media', 40 | 'pynlo.media.crystals', 41 | 'pynlo.media.fibers', 42 | 'pynlo.util', 43 | 'pynlo.util.ode_solve'], 44 | package_dir = {'': 'src'}, 45 | package_data = {'pynlo': ['media/fibers/*.txt']}, 46 | extras_require = { 47 | 'DFG': ["pyfftw"], 48 | }, 49 | ) 50 | -------------------------------------------------------------------------------- /src/examples/Dudley_SSFM.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Apr 15 15:39:12 2014 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | @author: dim1 19 | """ 20 | 21 | # This script simulates supercontinuum generation in a silica fiber. 22 | # It basically reproduces Fig. 3 from 23 | # Dudley, Gentry, and Cohen: Supercontinuum generation in photonic crystal fiber, 24 | # Rev. Mod. Phys., Vol. 78, No. 4, October-December 2006 25 | import numpy as np 26 | import matplotlib.pyplot as plt 27 | from pynlo.interactions.FourWaveMixing import SSFM 28 | from pynlo.media.fibers import fiber 29 | from pynlo.light.DerivedPulses import SechPulse 30 | 31 | dz = 1e-3 32 | steps = 100 33 | range1 = np.arange(steps) 34 | 35 | centerwl = 835.0 36 | fiber_length = 0.15 37 | 38 | pump_power = 1.0e4 # Peak power 39 | pump_pulse_length = 28.4e-3 40 | 41 | npoints = 2**13 42 | 43 | init = SechPulse(power = pump_power, 44 | T0_ps = pump_pulse_length, 45 | center_wavelength_nm = centerwl, 46 | time_window_ps = 10.0, 47 | GDD = 0, TOD = 0.0, 48 | NPTS = npoints, 49 | frep_MHz = 100.0, 50 | power_is_avg = False) 51 | 52 | fiber1 = fiber.FiberInstance() 53 | fiber1.load_from_db( fiber_length, 'dudley') 54 | 55 | evol = SSFM.SSFM(dz = dz, local_error = 0.001, USE_SIMPLE_RAMAN = True) 56 | y = np.zeros(steps) 57 | AW = np.zeros((init.NPTS, steps)) 58 | AT = np.copy(AW) 59 | 60 | y, AW, AT, pulse1 = evol.propagate(pulse_in = init, fiber = fiber1, 61 | n_steps = steps) 62 | 63 | wl = init.wl_nm 64 | 65 | loWL = 400 66 | hiWL = 1400 67 | 68 | iis = np.logical_and(wl>loWL,wl-1,init.T_ps<5) 71 | 72 | xW = wl[iis] 73 | xT = init.T_ps[iisT] 74 | zW_in = np.transpose(AW)[:,iis] 75 | zT_in = np.transpose(AT)[:,iisT] 76 | zW = 10*np.log10(np.abs(zW_in)**2) 77 | zT = 10*np.log10(np.abs(zT_in)**2) 78 | mlIW = np.max(zW) 79 | mlIT = np.max(zT) 80 | 81 | D = fiber1.Beta2_to_D(init) 82 | beta = fiber1.Beta2(init) 83 | 84 | plt.figure() 85 | plt.subplot(121) 86 | plt.plot(wl,D,'x') 87 | plt.xlim(400,1600) 88 | plt.ylim(-400,300) 89 | plt.xlabel('Wavelength (nm)') 90 | plt.ylabel('D (ps/nm/km)') 91 | plt.subplot(122) 92 | plt.plot(wl,beta*1000,'x') 93 | plt.xlim(400,1600) 94 | plt.ylim(-350,200) 95 | plt.xlabel('Wavelength (nm)') 96 | plt.ylabel(r'$\beta_2$ (ps$^2$/km)') 97 | 98 | plt.figure() 99 | plt.subplot(121) 100 | plt.pcolormesh(xW, y, zW, vmin = mlIW - 40.0, vmax = mlIW) 101 | plt.autoscale(tight=True) 102 | plt.xlim([loWL, hiWL]) 103 | plt.xlabel('Wavelength (nm)') 104 | plt.ylabel('Distance (m)') 105 | 106 | plt.subplot(122) 107 | plt.pcolormesh(xT, y, zT, vmin = mlIT - 40.0, vmax = mlIT) 108 | plt.autoscale(tight=True) 109 | plt.xlabel('Delay (ps)') 110 | plt.ylabel('Distance (m)') 111 | 112 | plt.show() -------------------------------------------------------------------------------- /src/examples/SSFM_coherence.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import pynlo 4 | import matplotlib.cm as cm 5 | import scipy.signal 6 | 7 | pulseWL = 1550 # pulse central wavelength (nm) 8 | 9 | GDD = 0.0 # Group delay dispersion (ps^2) 10 | TOD = 0.0 # Third order dispersion (ps^3) 11 | 12 | Window = 10.0 # simulation window (ps) 13 | 14 | beta2 = -40 # (ps^2/km) 15 | beta3 = 0.00 # (ps^3/km) 16 | beta4 = 0.001 # (ps^4/km) 17 | 18 | Length = 60 # length in mm 19 | 20 | Alpha = 0.0 # attentuation coefficient (dB/cm) 21 | Gamma = 1000 # Gamma (1/(W km) 22 | 23 | fibWL = pulseWL # Center WL of fiber (nm) 24 | 25 | Raman = True # Enable Raman effect? 26 | Steep = True # Enable self steepening? 27 | 28 | alpha = np.log((10**(Alpha * 0.1))) * 100 # convert from dB/cm to 1/m 29 | 30 | # select a method for include noise on the input pulse: 31 | noise_type = 'sqrt_N_freq' 32 | # noise_type = 'one_photon_freq' 33 | 34 | 35 | # DRAFT - use these parameters for a quick calculation 36 | trials = 2 37 | error = 0.1 # error desired by the integrator. Usually 0.001 is plenty good. Use larger values for speed 38 | Steps = 100 # simulation steps 39 | Points = 2**13 # simulation points 40 | 41 | # FINAL - use these parameters for beautiful results that take a long time 42 | # trials = 5 43 | # error = 0.001 # error desired by the integrator. Usually 0.001 is plenty good. Use larger values for speed 44 | # Steps = 300 # simulation steps 45 | # Points = 2**12 # simulation points 46 | 47 | 48 | # Coherent (use these parameters for a mostly-coherent pulse) 49 | # FWHM = 0.2 # pulse duration (ps) 50 | # EPP = 100e-12 # Energy per pulse (J) 51 | 52 | # Not-so-coherent (use these parameters for a mostly-coherent pulse) 53 | FWHM = 0.35 # pulse duration (ps) 54 | EPP = 180e-12 # Energy per pulse (J) 55 | 56 | 57 | # Incoherent (use these parameters for a mostly incoherent pulse) 58 | # FWHM = 0.8 # pulse duration (ps) 59 | # EPP = 800e-12 # Energy per pulse (J) 60 | 61 | 62 | # set up plots for the results: 63 | fig = plt.figure(figsize=(11,8)) 64 | ax0 = plt.subplot2grid((3,3), (0, 0), rowspan=1) 65 | ax1 = plt.subplot2grid((3,3), (1, 0), rowspan=2, sharex=ax0) 66 | 67 | ax2 = plt.subplot2grid((3,3), (0, 1), rowspan=1) 68 | ax3 = plt.subplot2grid((3,3), (1, 1), rowspan=2, sharex=ax2) 69 | 70 | ax4 = plt.subplot2grid((3,3), (0, 2), rowspan=1) 71 | ax5 = plt.subplot2grid((3,3), (1, 2), rowspan=2, sharex=ax4) 72 | 73 | # create the fiber! 74 | fiber1 = pynlo.media.fibers.fiber.FiberInstance() 75 | fiber1.generate_fiber(Length * 1e-3, center_wl_nm=fibWL, betas=(beta2, beta3, beta4), 76 | gamma_W_m=Gamma * 1e-3, gvd_units='ps^n/km', gain=-alpha) 77 | 78 | # Propagation 79 | evol = pynlo.interactions.FourWaveMixing.SSFM.SSFM(local_error=error, USE_SIMPLE_RAMAN=True, 80 | disable_Raman = np.logical_not(Raman), 81 | disable_self_steepening = np.logical_not(Steep)) 82 | 83 | # create the pulse! 84 | pulse = pynlo.light.DerivedPulses.SechPulse(power = 1, # Power will be scaled by set_epp 85 | T0_ps = FWHM/1.76, 86 | center_wavelength_nm = pulseWL, 87 | time_window_ps = Window, 88 | GDD=GDD, TOD=TOD, 89 | NPTS = Points, 90 | frep_MHz = 100, 91 | power_is_avg = False) 92 | 93 | pulse.set_epp(EPP) # set the pulse energy 94 | 95 | g12, results = evol.calculate_coherence(pulse_in=pulse, fiber=fiber1, n_steps=Steps, 96 | num_trials=trials, noise_type=noise_type) 97 | 98 | def dB(num): 99 | return 10 * np.log10(np.abs(num)**2) 100 | 101 | for y, AW, AT, pulse_in, pulse_out in results: 102 | F = pulse_out.F_THz # Frequency grid of pulse (THz) 103 | AW = AW.transpose() 104 | zW = dB(AW[:, (F > 0)] ) 105 | ax0.plot(F[F>0], zW[0], color = 'b') 106 | ax0.plot(F[F>0], zW[-1], color = 'r') 107 | 108 | 109 | zT = dB( np.transpose(AT) ) 110 | ax4.plot(pulse_out.T_ps, dB(pulse_out.AT), color = 'r') 111 | ax4.plot(pulse.T_ps, dB( pulse.AT), color = 'b') 112 | 113 | 114 | g12 = g12.transpose() 115 | 116 | g12_line = g12[-1][F>0] 117 | 118 | ax2.plot(F[F>0],g12_line, color='r') 119 | 120 | 121 | extent = (np.min(F[F > 0]), np.max(F[F > 0]), 0, Length) 122 | ax1.imshow(zW, extent=extent, 123 | vmin=np.max(zW) - 40.0, vmax=np.max(zW), 124 | aspect='auto', origin='lower') 125 | 126 | 127 | extent = (np.min(F), np.max(F), 0, Length) 128 | ax3.imshow(g12, extent=extent, clim=(0,1), aspect='auto', origin='lower', cmap=cm.inferno) 129 | 130 | extent = (np.min(pulse.T_ps), np.max(pulse.T_ps), 0, Length) 131 | ax5.imshow(zT, extent=extent, 132 | vmin=np.max(zT) - 40.0, vmax=np.max(zT), 133 | aspect='auto', origin='lower') 134 | 135 | 136 | ax2.axhline(0, alpha=0.3, color='r') 137 | ax2.axhline(1, alpha=0.3, color='g') 138 | 139 | ax0.set_ylabel('Intensity (dB)') 140 | ax2.set_ylabel('Coherence (g_12)') 141 | ax4.set_ylabel('Intensity (dB)') 142 | 143 | ax0.set_ylim( -60, 20) 144 | ax2.set_ylim(-0.2, 1.2) 145 | ax4.set_ylim( -20, 40) 146 | 147 | 148 | ax1.set_ylabel('Propagation distance (mm)') 149 | for ax in (ax1,ax3): 150 | ax.set_xlabel('Frequency (THz)') 151 | ax.set_xlim(0,400) 152 | 153 | 154 | ax5.set_xlabel('Time (ps)') 155 | 156 | title = '%i nm, %i fs, %i pJ'%(pulseWL, FWHM*1e3, EPP*1e12) 157 | 158 | ax2.set_title(title, fontsize=16, weight='bold') 159 | plt.tight_layout() 160 | 161 | plt.savefig(title+'.png', dpi=200) 162 | 163 | plt.show() -------------------------------------------------------------------------------- /src/examples/fundamental_SSFM.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Apr 21 13:29:08 2014 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | 19 | @author: dim1 20 | """ 21 | 22 | import numpy as np 23 | import matplotlib.pyplot as plt 24 | from pynlo.interactions import FourWaveMixing 25 | from pynlo.media.fibers import fiber 26 | from pynlo.light.DerivedPulses import SechPulse 27 | 28 | 29 | plt.close('all') 30 | 31 | steps = 200 32 | 33 | centerwl = 850.0 34 | gamma = 1e-3 35 | fiber_length = 1600.0 36 | T0 = 1 37 | beta = [-1, 0] 38 | P0 = (abs(beta[0] * 1e-3) / gamma / T0**2) * 16 39 | 40 | init =SechPulse(power = P0, 41 | T0_ps = T0, 42 | center_wavelength_nm = centerwl, 43 | time_window_ps = 10.0, 44 | NPTS = 2**13) 45 | 46 | fiber1 = fiber.FiberInstance() 47 | fiber1.generate_fiber(fiber_length, centerwl, beta, gamma, 0, "ps^n/km") 48 | 49 | evol = FourWaveMixing.SSFM.SSFM(disable_Raman = True, disable_self_steepening = True, 50 | local_error = 0.001, suppress_iteration = True) 51 | 52 | y = np.zeros(steps) 53 | AW = np.complex64(np.zeros((init.NPTS, steps))) 54 | AT = np.complex64(np.copy(AW)) 55 | 56 | y, AW, AT, pulse_out = evol.propagate(pulse_in = init, fiber = fiber1, 57 | n_steps = steps) 58 | 59 | wl = init.wl_nm 60 | 61 | loWL = 820 62 | hiWL = 870 63 | 64 | iis = np.logical_and(wl>loWL,wl-5,init.T_ps<5) 67 | 68 | xW = wl[iis] 69 | xT = init.T_ps[iisT] 70 | zW_in = np.transpose(AW)[:,iis] 71 | zT_in = np.transpose(AT)[:,iisT] 72 | zW = 10*np.log10(np.abs(zW_in)**2) 73 | zT = 10*np.log10(np.abs(zT_in)**2) 74 | mlIW = np.max(zW) 75 | mlIT = np.max(zT) 76 | 77 | D = fiber1.Beta2_to_D(init) 78 | 79 | x = (init.V_THz) / (2* np.pi) * T0 80 | x2 = init.T_ps / T0 81 | b2 = beta[0] / 1e3 # in ps^2 / m 82 | LD = T0**2 / abs(b2) 83 | ynew = y / LD 84 | 85 | plt.figure() 86 | plt.subplot(121) 87 | plt.pcolormesh(x2, ynew, 10*np.log10(np.abs(np.transpose(AT))**2), 88 | vmin = mlIT - 20.0, vmax = mlIT, cmap = plt.cm.gray) 89 | plt.autoscale(tight=True) 90 | plt.xlim([-4, 4]) 91 | plt.xlabel(r'($T / T_0)$') 92 | plt.ylabel(r'Distance ($z/L_{NL})$') 93 | plt.subplot(122) 94 | plt.pcolormesh(x, ynew, 10*np.log10(np.abs(np.transpose(AW))**2), 95 | vmin = mlIW - 20.0, vmax = mlIW, cmap = plt.cm.gray) 96 | plt.autoscale(tight=True) 97 | plt.xlim([-4, 4]) 98 | plt.xlabel(r'($\nu - \nu_0) \times T_0$') 99 | plt.ylabel(r'Distance ($z/L_{NL})$') 100 | 101 | #plt.figure() 102 | #plt.subplot(121) 103 | #plt.pcolormesh(xW, y, zW, vmin = mlIW - 40.0, vmax = mlIW) 104 | #plt.autoscale(tight=True) 105 | #plt.xlim([loWL, hiWL]) 106 | #plt.xlabel('Wavelength (nm)') 107 | #plt.ylabel('Distance (m)') 108 | # 109 | #plt.subplot(122) 110 | #plt.pcolormesh(xT, y, zT, vmin = mlIT - 40.0, vmax = mlIT) 111 | #plt.autoscale(tight=True) 112 | #plt.xlabel('Delay (ps)') 113 | #plt.ylabel('Distance (m)') 114 | 115 | plt.show() -------------------------------------------------------------------------------- /src/examples/pulsed_dfg.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Pulsed Difference Frequency Generation 4 | 5 | This module sets up and then runs a difference frequency generation (DFG) 6 | simulation. The specific case is for two gaussian pulses at 1064 nm and 1550 nm 7 | mixing in periodically poled lithium niobate (PPLN.) 8 | 9 | """ 10 | 11 | import numpy as np 12 | import matplotlib.pyplot as plt 13 | import matplotlib.animation as animation 14 | 15 | from pynlo.light.DerivedPulses import GaussianPulse 16 | from pynlo.media.crystals import PPLN 17 | from pynlo.interactions.ThreeWaveMixing import dfg_problem 18 | 19 | from pynlo.util import ode_solve 20 | from pynlo.util.ode_solve import dopr853 21 | from pynlo.light import OneDBeam 22 | 23 | from mpl_toolkits.axes_grid1.inset_locator import inset_axes 24 | 25 | from IPython.display import HTML 26 | 27 | plt.close('all') 28 | 29 | 30 | crystallength = 10.0e-3 31 | crystal = PPLN(45, length = crystallength) 32 | 33 | fr = 100.0 34 | n_saves = 50 35 | 36 | signal_wl = 1560. 37 | signal_power = 0.100 38 | signal_pulse_length = 0.150 39 | 40 | pump_wl = 1048. 41 | pump_power = 0.06 42 | pump_waist = 50.0e-6 43 | pump_pulse_length = 0.150 44 | 45 | 46 | signal_in = GaussianPulse(power = signal_power, 47 | T0_ps = signal_pulse_length, 48 | center_wavelength_nm = signal_wl, 49 | time_window_ps = 20.0, 50 | NPTS = 2**10, 51 | frep_MHz = fr, 52 | power_is_avg = True) 53 | signal_in.add_time_offset(-0.50) 54 | twind = signal_in.time_window_ps 55 | npoints = signal_in.NPTS 56 | 57 | w0 = signal_in.calculate_weighted_avg_frequency_mks()*1.0e-12 * 2.0 * np.pi 58 | 59 | # Use ideal quasi-phase matching period 60 | crystal.set_pp(crystal.calculate_poling_period(pump_wl, signal_wl, None)) 61 | 62 | pump_in = GaussianPulse(power = pump_power, 63 | T0_ps = pump_pulse_length, 64 | center_wavelength_nm = pump_wl, 65 | time_window_ps = 20.0, 66 | NPTS = 2**10, 67 | frep_MHz = fr, 68 | power_is_avg = True) 69 | # 70 | 71 | # Calculate peak optical intensity with class objects. Calculate the mean wavelength of 72 | # each pulse, calculate the waist size (propagating from the crystal center out) 73 | P0_p = np.max(np.abs(pump_in.AT)**2) 74 | P0_s = np.max(np.abs(signal_in.AT)**2) 75 | 76 | pump_beam = OneDBeam(pump_waist, this_pulse = pump_in) 77 | sgnl_beam = OneDBeam(pump_waist, this_pulse = signal_in) 78 | 79 | n_pump = pump_beam.get_n_in_crystal(pump_in, crystal) 80 | n_sgnl = sgnl_beam.get_n_in_crystal(signal_in, crystal) 81 | 82 | waist_p = pump_beam.calculate_waist(crystal.length_mks / 2.0, 83 | n_s = n_pump[int(len(n_pump)/2.0)]) 84 | waist_s = pump_beam.calculate_waist(crystal.length_mks / 2.0, 85 | n_s = n_pump[int(len(n_sgnl)/2.0)]) 86 | 87 | I_p = 2.0*P0_p/(np.pi * np.mean(waist_p)**2) 88 | I_s = 2.0*P0_s/(np.pi * np.mean(waist_s)**2) 89 | 90 | print('Peak signal + idler intensity is ',np.sum((I_p+I_s))*1.0e-13,' GW/cm**2' ) 91 | 92 | 93 | integrand = dfg_problem(pump_in, signal_in, crystal, 94 | disable_SPM = True, pump_waist = pump_waist, plot_beam_overlaps = False) 95 | 96 | # Set up integrator 97 | rtol = 1.0e-6 98 | atol = 1.0e-6 99 | x0 = 0.0 100 | x1 = crystallength 101 | hmin = 0.0 102 | h1 = 0.00001 103 | out = ode_solve.Output(n_saves) 104 | beam_dim_fig = plt.figure() 105 | a = ode_solve.ODEint(integrand.ystart, x0, x1, atol, rtol, h1,hmin, out,\ 106 | dopr853.StepperDopr853, integrand) 107 | 108 | a.integrate() 109 | idler_in = integrand.idlr_in 110 | 111 | res = integrand.process_stepper_output(a.out) 112 | epp_to_avg = pump_in.frep_mks*1e3 113 | print("pump power in: ", pump_in.calc_epp() * epp_to_avg , "mW" ) 114 | print( "signal power in: ", signal_in.calc_epp() * epp_to_avg, "mW" ) 115 | print( "pump power out: ", res.get_pump(n_saves-1).calc_epp()*epp_to_avg, "mW" ) 116 | print( "signal power out: ", res.get_sgnl(n_saves-1).calc_epp()*epp_to_avg, "mW") 117 | print("Idler power out: ", res.get_idlr(n_saves-1).calc_epp()*epp_to_avg , "mW" ) 118 | 119 | # Create animation 120 | idlr_power_series = [] 121 | for x in range(res.n_saves): 122 | p = res.get_idlr(x) 123 | idlr_power_series.append(p.calc_epp()) 124 | # Plot 5 things: 125 | # Pump, Signal, and Idler in time domain 126 | # Idler in spectral domain 127 | # Inset of idler power as function of time 128 | fig = plt.figure(figsize = (5.5, 6) ) 129 | 130 | ### Create initial plot 131 | p = res.get_pump(0) 132 | ic = np.argmax(abs(p.AT)**2) 133 | tc = p.T_mks[ic] 134 | 135 | ax1 = plt.subplot(211) 136 | p = res.get_pump(x) 137 | line_pump_t, = ax1.plot((p.T_mks - tc)* 1e15, abs(p.AT)**2 / 138 | np.max(abs(res.pump_max_temporal)**2), 'b', label = 'pump', linewidth = 2) 139 | p = res.get_sgnl(x) 140 | line_sgnl_t, = ax1.plot((p.T_mks - tc)* 1e15, abs(p.AT)**2 / 141 | np.max(abs(res.sgnl_max_temporal)**2), 'k', label = 'sgnl', linewidth = 2) 142 | p = res.get_idlr(x) 143 | line_idlr_t, =ax1.plot((p.T_mks - tc)* 1e15, abs(p.AT)**2 / 144 | np.max(abs(res.idlr_max_temporal)**2), 'r', label = 'idlr', linewidth = 2) 145 | ax1.set_xlabel("Time (fs)") 146 | ax1.set_ylabel("Normalized Power") 147 | plt.legend() 148 | ax1.set_xlim(-3000*pump_pulse_length,3000*pump_pulse_length) 149 | ax1.set_ylim(0, 1) 150 | 151 | ax2 = plt.subplot(212) 152 | line_idlr_f, = plt.plot(p.wl_mks*1.0e9, 153 | abs(p.AW)**2 / np.max(res.idlr_max_field**2), 154 | label = 'idler', linewidth = 2) 155 | plt.ylim(0, 1) 156 | plt.xlim(2700, 3900) 157 | plt.xlabel("Wavelength (nm)") 158 | plt.ylabel("Normalized Power (arb.)") 159 | axins = inset_axes(ax2, width = '20%',height = '10%', loc = 1) 160 | line_inset, = axins.plot(1000*np.array(res.zs[0:0]),idlr_power_series[0:0] ) 161 | axins.set_ylim(0, 1.1*max(idlr_power_series)) 162 | axins.set_xlim(res.zs[0], 1000.0*res.zs[-1]) 163 | axins.set_xticks([]) 164 | axins.set_yticks([]) 165 | 166 | def update(ctr): 167 | x = ctr % res.n_saves 168 | p = res.get_pump(x) 169 | ic = np.argmax(abs(p.AT)**2) 170 | tc = p.T_mks[ic] 171 | x_ax = (p.T_mks - tc)* 1e15 172 | line_pump_t.set_ydata(abs(p.AT)**2 / np.max(abs(res.pump_max_temporal)**2)) 173 | p = res.get_sgnl(x) 174 | line_sgnl_t.set_ydata(abs(p.AT)**2 / np.max(abs(res.sgnl_max_temporal)**2)) 175 | p = res.get_idlr(x) 176 | line_idlr_t.set_ydata(abs(p.AT)**2 / np.max(abs(res.idlr_max_temporal)**2)) 177 | line_pump_t.set_xdata(x_ax) 178 | line_sgnl_t.set_xdata(x_ax) 179 | line_idlr_t.set_xdata(x_ax) 180 | 181 | line_idlr_f.set_ydata(abs(p.AW)**2 / np.max(res.idlr_max_field**2)) 182 | line_inset.set_ydata(idlr_power_series[0:x]) 183 | line_inset.set_xdata(1000*np.array(res.zs[0:x])) 184 | anim = animation.FuncAnimation(fig, update, interval=10) 185 | HTML(anim.to_jshtml()) 186 | 187 | -------------------------------------------------------------------------------- /src/examples/self_similar_propagation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Apr 21 13:29:08 2014 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | 19 | @author: dim1 20 | """ 21 | 22 | import numpy as np 23 | import matplotlib.pyplot as plt 24 | from pynlo.interactions import FourWaveMixing 25 | from pynlo.media.fibers import fiber 26 | from pynlo.light.DerivedPulses import GaussianPulse 27 | 28 | 29 | plt.close('all') 30 | 31 | steps = 50 32 | 33 | centerwl = 1064.0 34 | gamma = 6.0e-3 35 | fiber_length = 6 36 | T0 = 0.500 37 | beta = [24000.0*1e-6, 0.0] 38 | P0 = 1 39 | 40 | init =GaussianPulse(P0, T0, centerwl, NPTS = 2**14, time_window_ps = 40) 41 | init.set_AT(init.AT*np.sqrt(12e-12 / init.calc_epp())) 42 | fiber1 = fiber.FiberInstance() 43 | fiber1.generate_fiber(fiber_length, centerwl, beta, gamma, 0.8, "ps^n/m") 44 | 45 | evol = FourWaveMixing.SSFM.SSFM(disable_Raman = False, disable_self_steepening = False) 46 | 47 | y = np.zeros(steps) 48 | AW = np.complex64(np.zeros((init.NPTS, steps))) 49 | AT = np.complex64(np.copy(AW)) 50 | 51 | y, AW, AT, pulse_out = evol.propagate(pulse_in = init, fiber = fiber1, 52 | n_steps = steps) 53 | print pulse_out.calc_epp() 54 | wl = init.wl_nm 55 | 56 | loWL = 1000 57 | hiWL = 1100 58 | 59 | iis = np.logical_and(wl>loWL,wl-5,init.T_ps<5) 62 | 63 | xW = wl[iis] 64 | xT = init.T_ps[iisT] 65 | zW_in = np.transpose(AW)[:,iis] 66 | zT_in = np.transpose(AT)[:,iisT] 67 | zW = 10*np.log10(np.abs(zW_in)**2) 68 | zT = 10*np.log10(np.abs(zT_in)**2) 69 | mlIW = np.max(zW) 70 | mlIT = np.max(zT) 71 | 72 | D = fiber1.Beta2_to_D(init) 73 | 74 | x = (init.V_THz) / (2* np.pi) * T0 75 | x2 = init.T_ps / T0 76 | b2 = beta[0] / 1e3 # in ps^2 / m 77 | LD = T0**2 / abs(b2) 78 | ynew = y / LD 79 | 80 | plt.figure() 81 | plt.subplot(121) 82 | plt.pcolormesh(x2, ynew, 10*np.log10(np.abs(np.transpose(AT))**2), 83 | vmin = mlIW - 20.0, vmax = mlIW, cmap = plt.cm.gray) 84 | plt.autoscale(tight=True) 85 | plt.xlim([-4, 4]) 86 | plt.xlabel(r'($T / T_0)$') 87 | plt.ylabel(r'Distance ($z/L_{NL})$') 88 | plt.subplot(122) 89 | plt.pcolormesh(x, ynew, 10*np.log10(np.abs(np.transpose(AW))**2), 90 | vmin = mlIW - 20.0, vmax = mlIW, cmap = plt.cm.gray) 91 | plt.autoscale(tight=True) 92 | plt.xlim([-4, 4]) 93 | plt.xlabel(r'($\nu - \nu_0) \times T_0$') 94 | plt.ylabel(r'Distance ($z/L_{NL})$') 95 | 96 | #plt.figure() 97 | #plt.subplot(121) 98 | #plt.pcolormesh(xW, y, zW, vmin = mlIW - 40.0, vmax = mlIW) 99 | #plt.autoscale(tight=True) 100 | #plt.xlim([loWL, hiWL]) 101 | #plt.xlabel('Wavelength (nm)') 102 | #plt.ylabel('Distance (m)') 103 | # 104 | #plt.subplot(122) 105 | #plt.pcolormesh(xT, y, zT, vmin = mlIT - 40.0, vmax = mlIT) 106 | #plt.autoscale(tight=True) 107 | #plt.xlabel('Delay (ps)') 108 | #plt.ylabel('Distance (m)') 109 | plt.figure() 110 | plt.subplot(121) 111 | plt.plot(pulse_out.wl_nm, abs(pulse_out.AW)**2/max(abs(pulse_out.AW)**2)) 112 | plt.plot(pulse_out.wl_nm, abs(init.AW)**2/max(abs(init.AW)**2)) 113 | plt.xlim(centerwl - 10, centerwl + 10) 114 | plt.subplot(122) 115 | pulse_out.dechirp_pulse() 116 | plt.plot(pulse_out.T_ps*1000, abs(pulse_out.AT)**2) 117 | plt.show() -------------------------------------------------------------------------------- /src/examples/simple_SSFM.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import pynlo 4 | 5 | FWHM = 0.050 # pulse duration (ps) 6 | pulseWL = 1550 # pulse central wavelength (nm) 7 | EPP = 50e-12 # Energy per pulse (J) 8 | GDD = 0.0 # Group delay dispersion (ps^2) 9 | TOD = 0.0 # Third order dispersion (ps^3) 10 | 11 | Window = 10.0 # simulation window (ps) 12 | Steps = 100 # simulation steps 13 | Points = 2**13 # simulation points 14 | 15 | beta2 = -120 # (ps^2/km) 16 | beta3 = 0.00 # (ps^3/km) 17 | beta4 = 0.005 # (ps^4/km) 18 | 19 | Length = 20 # length in mm 20 | 21 | Alpha = 0.0 # attentuation coefficient (dB/cm) 22 | Gamma = 1000 # Gamma (1/(W km) 23 | 24 | fibWL = pulseWL # Center WL of fiber (nm) 25 | 26 | Raman = True # Enable Raman effect? 27 | Steep = True # Enable self steepening? 28 | 29 | alpha = np.log((10**(Alpha * 0.1))) * 100 # convert from dB/cm to 1/m 30 | 31 | 32 | # set up plots for the results: 33 | fig = plt.figure(figsize=(8,8)) 34 | ax0 = plt.subplot2grid((3,2), (0, 0), rowspan=1) 35 | ax1 = plt.subplot2grid((3,2), (0, 1), rowspan=1) 36 | ax2 = plt.subplot2grid((3,2), (1, 0), rowspan=2, sharex=ax0) 37 | ax3 = plt.subplot2grid((3,2), (1, 1), rowspan=2, sharex=ax1) 38 | 39 | 40 | ######## This is where the PyNLO magic happens! ############################ 41 | 42 | # create the pulse! 43 | pulse = pynlo.light.DerivedPulses.SechPulse(power = 1, # Power will be scaled by set_epp 44 | T0_ps = FWHM/1.76, 45 | center_wavelength_nm = pulseWL, 46 | time_window_ps = Window, 47 | GDD=GDD, TOD=TOD, 48 | NPTS = Points, 49 | frep_MHz = 100, 50 | power_is_avg = False) 51 | # set the pulse energy! 52 | pulse.set_epp(EPP) 53 | 54 | # create the fiber! 55 | fiber1 = pynlo.media.fibers.fiber.FiberInstance() 56 | fiber1.generate_fiber(Length * 1e-3, center_wl_nm=fibWL, betas=(beta2, beta3, beta4), 57 | gamma_W_m=Gamma * 1e-3, gvd_units='ps^n/km', gain=-alpha) 58 | 59 | # Propagation 60 | evol = pynlo.interactions.FourWaveMixing.SSFM.SSFM(local_error=0.005, USE_SIMPLE_RAMAN=True, 61 | disable_Raman = np.logical_not(Raman), 62 | disable_self_steepening = np.logical_not(Steep)) 63 | 64 | y, AW, AT, pulse_out = evol.propagate(pulse_in=pulse, fiber=fiber1, n_steps=Steps) 65 | 66 | ########## That's it! Physics complete. Just plotting commands from here! ################ 67 | 68 | 69 | F = pulse.F_THz # Frequency grid of pulse (THz) 70 | 71 | def dB(num): 72 | return 10 * np.log10(np.abs(num)**2) 73 | 74 | zW = dB( np.transpose(AW)[:, (F > 0)] ) 75 | zT = dB( np.transpose(AT) ) 76 | 77 | y_mm = y * 1e3 # convert distance to mm 78 | 79 | ax0.plot(pulse_out.F_THz, dB(pulse_out.AW), color = 'r') 80 | ax1.plot(pulse_out.T_ps, dB(pulse_out.AT), color = 'r') 81 | 82 | ax0.plot(pulse.F_THz, dB(pulse.AW), color = 'b') 83 | ax1.plot(pulse.T_ps, dB(pulse.AT), color = 'b') 84 | 85 | extent = (np.min(F[F > 0]), np.max(F[F > 0]), 0, Length) 86 | ax2.imshow(zW, extent=extent, 87 | vmin=np.max(zW) - 40.0, vmax=np.max(zW), 88 | aspect='auto', origin='lower') 89 | 90 | extent = (np.min(pulse.T_ps), np.max(pulse.T_ps), np.min(y_mm), Length) 91 | ax3.imshow(zT, extent=extent, 92 | vmin=np.max(zT) - 40.0, vmax=np.max(zT), 93 | aspect='auto', origin='lower') 94 | 95 | 96 | ax0.set_ylabel('Intensity (dB)') 97 | ax0.set_ylim( - 80, 0) 98 | ax1.set_ylim( - 40, 40) 99 | 100 | ax2.set_ylabel('Propagation distance (mm)') 101 | ax2.set_xlabel('Frequency (THz)') 102 | ax2.set_xlim(0,400) 103 | 104 | ax3.set_xlabel('Time (ps)') 105 | 106 | plt.show() -------------------------------------------------------------------------------- /src/examples/supercontinuum_with_FROG.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import pynlo 4 | 5 | FWHM = 0.050 # pulse duration (ps) 6 | pulseWL = 1550 # pulse central wavelength (nm) 7 | EPP = 50e-12 # Energy per pulse (J) 8 | GDD = 0.0 # Group delay dispersion (ps^2) 9 | TOD = 0.0 # Third order dispersion (ps^3) 10 | 11 | Window = 10.0 # simulation window (ps) 12 | Steps = 40 # simulation steps 13 | Points = 2**13 # simulation points 14 | error = 0.2 15 | 16 | beta2 = -120 # (ps^2/km) 17 | beta3 = 0.00 # (ps^3/km) 18 | beta4 = 0.005 # (ps^4/km) 19 | 20 | Length = 10 # length in mm 21 | 22 | Alpha = 0.0 # attentuation coefficient (dB/cm) 23 | Gamma = 1000 # Gamma (1/(W km) 24 | 25 | fibWL = pulseWL # Center WL of fiber (nm) 26 | 27 | Raman = True # Enable Raman effect? 28 | Steep = True # Enable self steepening? 29 | 30 | alpha = np.log((10**(Alpha * 0.1))) * 100 # convert from dB/cm to 1/m 31 | 32 | 33 | # set up plots for the results: 34 | fig = plt.figure(figsize=(8,8)) 35 | ax0 = plt.subplot2grid((3,2), (0, 0), rowspan=1) 36 | ax1 = plt.subplot2grid((3,2), (0, 1), rowspan=1) 37 | ax2 = plt.subplot2grid((3,2), (1, 0), rowspan=2, sharex=ax0) 38 | ax3 = plt.subplot2grid((3,2), (1, 1), rowspan=2, sharex=ax1) 39 | 40 | 41 | ######## This is where the PyNLO magic happens! ############################ 42 | 43 | # create the pulse! 44 | pulse = pynlo.light.DerivedPulses.SechPulse(power = 1, # Power will be scaled by set_epp 45 | T0_ps = FWHM/1.76, 46 | center_wavelength_nm = pulseWL, 47 | time_window_ps = Window, 48 | GDD=GDD, TOD=TOD, 49 | NPTS = Points, 50 | frep_MHz = 100, 51 | power_is_avg = False) 52 | # set the pulse energy! 53 | pulse.set_epp(EPP) 54 | 55 | # create the fiber! 56 | fiber1 = pynlo.media.fibers.fiber.FiberInstance() 57 | fiber1.generate_fiber(Length * 1e-3, center_wl_nm=fibWL, betas=(beta2, beta3, beta4), 58 | gamma_W_m=Gamma * 1e-3, gvd_units='ps^n/km', gain=-alpha) 59 | 60 | # Propagation 61 | evol = pynlo.interactions.FourWaveMixing.SSFM.SSFM(local_error=error, USE_SIMPLE_RAMAN=True, 62 | disable_Raman = np.logical_not(Raman), 63 | disable_self_steepening = np.logical_not(Steep)) 64 | 65 | y, AW, AT, pulse_out = evol.propagate(pulse_in=pulse, fiber=fiber1, n_steps=Steps) 66 | 67 | ########## That's it! Physics complete. Just plotting commands from here! ################ 68 | 69 | 70 | F = pulse.F_THz # Frequency grid of pulse (THz) 71 | 72 | def dB(num): 73 | return 10 * np.log10(np.abs(num)**2) 74 | 75 | zW = dB( np.transpose(AW)[:, (F > 0)] ) 76 | zT = dB( np.transpose(AT) ) 77 | 78 | y_mm = y * 1e3 # convert distance to mm 79 | 80 | ax0.plot(pulse_out.F_THz, dB(pulse_out.AW), color = 'r') 81 | ax1.plot(pulse_out.T_ps, dB(pulse_out.AT), color = 'r') 82 | 83 | ax0.plot(pulse.F_THz, dB(pulse.AW), color = 'b') 84 | ax1.plot(pulse.T_ps, dB(pulse.AT), color = 'b') 85 | 86 | extent = (np.min(F[F > 0]), np.max(F[F > 0]), 0, Length) 87 | ax2.imshow(zW, extent=extent, 88 | vmin=np.max(zW) - 40.0, vmax=np.max(zW), 89 | aspect='auto', origin='lower') 90 | 91 | extent = (np.min(pulse.T_ps), np.max(pulse.T_ps), np.min(y_mm), Length) 92 | ax3.imshow(zT, extent=extent, 93 | vmin=np.max(zT) - 40.0, vmax=np.max(zT), 94 | aspect='auto', origin='lower') 95 | 96 | 97 | ax0.set_ylabel('Intensity (dB)') 98 | ax0.set_ylim( - 80, 0) 99 | ax1.set_ylim( - 40, 40) 100 | 101 | ax2.set_ylabel('Propagation distance (mm)') 102 | ax2.set_xlabel('Frequency (THz)') 103 | ax2.set_xlim(0,400) 104 | 105 | ax3.set_xlabel('Time (ps)') 106 | 107 | fig, axs = plt.subplots(1,2,figsize=(10,5)) 108 | 109 | 110 | for ax, gate_type in zip(axs,('xfrog', 'frog')): 111 | DELAYS, FREQS, extent, spectrogram = pulse_out.spectrogram(gate_type=gate_type, gate_function_width_ps=0.05, time_steps=1000) 112 | ax.imshow(spectrogram, aspect='auto', extent=extent) 113 | ax.set_xlabel('Time (ps)') 114 | ax.set_ylabel('Frequency (THz)') 115 | ax.set_title(gate_type) 116 | 117 | plt.show() -------------------------------------------------------------------------------- /src/pynlo/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 08 11:57:31 2015 4 | 5 | @author: ycasg 6 | """ 7 | from . import devices 8 | from . import interactions 9 | from . import light 10 | from . import media 11 | from . import util -------------------------------------------------------------------------------- /src/pynlo/devices/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jul 23 11:05:27 2015 4 | 5 | @author: ycasg 6 | """ 7 | 8 | from . import grating_compressor -------------------------------------------------------------------------------- /src/pynlo/devices/grating_compressor.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jul 23 09:40:41 2015 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | @author: ycasg 19 | """ 20 | from __future__ import absolute_import 21 | from __future__ import division 22 | from __future__ import print_function 23 | 24 | import numpy as np 25 | from scipy import constants, misc, signal, integrate 26 | 27 | 28 | class TreacyCompressor: 29 | """ This class calculates the effects of a grating-based pulse compressor, 30 | as described in 31 | E. B. Treacy, "Optical Pulse Compression With Diffraction Gratings", 32 | IEEE Journal of Quantum Electronics QE5(9), p454 (1969): 33 | http://dx.doi.org/10.1109/JQE.1969.1076303 34 | 35 | It implements eqn 5b from Treacy1969: :: 36 | 37 | -4 pi^2 c b 38 | {1} dt/dw = ------------------------------------- 39 | w^3 d^2 (1- (2 pi c/ wd - sin gamma)^2) 40 | 41 | 42 | where gamma is the diffraction angle, w is the angular frequency, d is 43 | the grating ruling period, and b is the slant distance between gratings, :: 44 | 45 | {1b} b = G sec(gamma - theta) 46 | 47 | where G is the grating separation and theta is the acute angle between 48 | indicent and diffracted rays (text before eq 4). The grating equation :: 49 | relates the angles (generalized eq 3): 50 | 51 | {2} sin(gamma - theta) + sin(gamma) = m lambda / d 52 | 53 | More conventionally, the grating equation is cast in terms of the 54 | incident and diffracted ray angles, :: 55 | 56 | {3} sin(alpha) + sin(beta) = m lambda / d. 57 | 58 | It makes sense to solve {3} using the grating specifications (eg for 59 | optimum incident angle a) and then derive Treacy's theta and gamma: :: 60 | 61 | {4} gamma = alpha theta = gamma - alpha 62 | 63 | This code only considers first order diffraction, as most gratings are 64 | designed for this (eg LightSmyth transmission gratings.) 65 | """ 66 | d = 0.0 # grating period (meters) 67 | g = 0.0 # incident beam angle wrt grating 68 | 69 | def __init__(self, lines_per_mm, incident_angle_degrees): 70 | """ Initialize with the two parameters intrinsic to the grating, the 71 | ruling density and design angle of incidence. """ 72 | self.d = 1.0e-3 / lines_per_mm 73 | self.g = incident_angle_degrees * 2.0*np.pi / 360.0 74 | 75 | def calc_theta(self, wavelength_nm, display_angle = False): 76 | l = wavelength_nm * 1.0e-9 77 | # First solve the grating equation {3} for the diffracted angle 78 | if np.any((l/self.d - np.sin(self.g))< -1) or np.any((l/self.d - np.sin(self.g)) > 1): 79 | print( "Bad value for argument of arcsin: ",\ 80 | l/self.d - np.sin(self.g),'. You are probably asking for diffraction of an impossible color (this wavelength is ',l*1e9,'nm. Coercing to [-1,1].' ) 81 | val = l/self.d - np.sin(self.g) 82 | if type(val) == np.ndarray: 83 | val[val>1] = 1 84 | val[val<-1] = -1 85 | 86 | alpha = np.arcsin(val ) 87 | if display_angle: 88 | print ('diffraction angle = ',alpha * 360.0/(2.0*np.pi) ) 89 | # Then calculate gamma (ok, this is not much work because b = gamma). 90 | # Calculate theta from {4}: 91 | theta = self.g-alpha 92 | return theta 93 | def calc_dt_dw_singlepass(self, wavelength_nm, 94 | grating_separation_meters, 95 | verbose = False): 96 | c = constants.speed_of_light 97 | G = grating_separation_meters 98 | l = wavelength_nm * 1.0e-9 99 | w = 2.0 * np.pi * c / l 100 | theta = self.calc_theta(wavelength_nm, display_angle = verbose) 101 | gamma = self.g 102 | b = G / np.cos(gamma - theta) 103 | 104 | return (-4.0 * np.pi**2 * c * b)/ (w**3 * self.d**2 * 105 | (1.0 - (2.0*np.pi*c/(w*self.d) - np.sin(gamma))**2 )) 106 | 107 | def calc_dphi_domega(self, omega, 108 | grating_separation_meters, 109 | verbose = False): 110 | c = constants.speed_of_light 111 | wavelength_nm = 1.0e9*2.0 * np.pi * c / omega 112 | G = grating_separation_meters 113 | theta = self.calc_theta(wavelength_nm, display_angle = verbose) 114 | gamma = self.g 115 | b = G / np.cos(gamma - theta) 116 | p = b*(1.+np.cos(theta)) 117 | return p/c 118 | 119 | def calc_compressor_gdd(self, wavelength_nm, grating_separation_meters): 120 | return 2.0 * self.calc_dt_dw_singlepass(wavelength_nm, 121 | grating_separation_meters, 122 | verbose = False) 123 | 124 | 125 | def calc_compressor_HOD(self, wavelength_nm, grating_separation_meters, dispersion_order): 126 | """ Calculate higher order dispersion by taking w - derivatives of 127 | dt/dw """ 128 | if dispersion_order < 3: 129 | raise ValueError('Order must be > 2. For TOD, specify 3.') 130 | w_of_l = lambda x: 2.0*np.pi*constants.speed_of_light / (x*1.0e-9) 131 | l_of_w = lambda x: 1.0e9*2.0*np.pi*constants.speed_of_light / x 132 | 133 | fn = lambda x:self.calc_compressor_gdd(l_of_w(x), grating_separation_meters) 134 | return misc.derivative(fn, 135 | w_of_l(wavelength_nm), 136 | n = dispersion_order - 2, 137 | dx = 2.0*np.pi*100.0e6, # Use dx = 100 MHz 138 | order = 101 ) # Why not use 101's order? 139 | 140 | 141 | def calc_compressor_dnphi_domega_n(self, wavelength_nm, grating_separation_meters, dispersion_order): 142 | """ Calculate higher order dispersion by taking w - derivatives of 143 | dt/dw """ 144 | if dispersion_order < 1: 145 | raise ValueError('Order must be > 2. For GDD, specify 1.') 146 | w_of_l = lambda x: 2.0*np.pi*constants.speed_of_light / (x*1.0e-9) 147 | fn = lambda x:self.calc_dphi_domega(x, grating_separation_meters) 148 | w0 = w_of_l(wavelength_nm) 149 | ws = np.linspace(w0 - 10.0e12,w0 + 10.0e12, 101) 150 | dphidw = fn(ws) 151 | y = signal.savgol_filter(dphidw, window_length = 11, 152 | polyorder = 7, deriv = dispersion_order) 153 | dw = (ws[1] - ws[0])*10**(-15*(1+dispersion_order)) 154 | return y[50]/(dw**dispersion_order) 155 | 156 | 157 | def apply_phase_to_pulse(self, grating_separation_meters, pulse): 158 | """ Apply grating disersion (all orders) to a Pulse instance. Phase is 159 | computed by numerical integration of dphi/domega (from Treacy) """ 160 | w0 = pulse.center_frequency_THz * 2.0*np.pi*1.0e12 161 | integrand = lambda x: 2.0 * self.calc_dphi_domega(x, grating_separation_meters) 162 | calc_phase = lambda x:integrate.quad(integrand, w0, x, 163 | epsabs = 1.0e-8,epsrel = 1.0e-8,)[0] 164 | vec_calc_phase = np.vectorize(calc_phase) 165 | phase = vec_calc_phase(pulse.W_mks) 166 | groupdelay = np.polyder(np.polyfit(pulse.W_mks, phase, 2))[0] 167 | pulse.apply_phase_W(phase + pulse.V_mks * groupdelay) 168 | -------------------------------------------------------------------------------- /src/pynlo/interactions/FourWaveMixing/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: latin1 -*- 2 | # Copyright (C) 2006-2010 João Luís Silva 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 2, 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, write to the Free Software Foundation, 16 | # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | # 18 | 19 | 20 | #----------------------------------------------------------------------- 21 | # Evolution of the pulse through the material 22 | #----------------------------------------------------------------------- 23 | 24 | 25 | 26 | from pynlo.interactions.FourWaveMixing import SSFM 27 | from . import SSFM -------------------------------------------------------------------------------- /src/pynlo/interactions/FourWaveMixing/global_variables.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jun 04 13:56:18 2015 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | @author: ycasg 19 | """ 20 | from __future__ import absolute_import 21 | from __future__ import division 22 | from __future__ import print_function 23 | 24 | ### Global variables ### 25 | # USE_PYFFTW : True - > use pyfftw 26 | # False - > use numpy fft 27 | USE_PYFFTW = False 28 | # USE_FREQUENCY_DOMAIN_RAMAN : 29 | # True - > calculate Raman respose in frequency domain (older) 30 | # False - > calculate Raman reponse in time domain (Modern version) 31 | USE_FREQUENCY_DOMAIN_RAMAN = False 32 | # USE_SIMPLE_RAMAN : 33 | # True - > use classic (Agarwal 1989) sin(t/t1)exp(-t/t2) response 34 | # False - > use more modern three-time version (Lin & Agarwal 2006) 35 | PRE_FFTSHIFT = True -------------------------------------------------------------------------------- /src/pynlo/interactions/ThreeWaveMixing/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: latin1 -*- 2 | # Copyright (C) 2006-2010 João Luís Silva 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 2, 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, write to the Free Software Foundation, 16 | # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | # 18 | 19 | 20 | #----------------------------------------------------------------------- 21 | # Evolution of the pulse through the material 22 | #----------------------------------------------------------------------- 23 | 24 | 25 | from pynlo.interactions.ThreeWaveMixing.DFG_integrand import dfg_problem 26 | -------------------------------------------------------------------------------- /src/pynlo/interactions/ThreeWaveMixing/field_classes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 08 14:08:12 2015 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | 19 | Containers for three wave mixing fields (pump, signal, idler) 20 | 21 | @author: ycasg 22 | """ 23 | from __future__ import absolute_import 24 | from __future__ import division 25 | from __future__ import print_function 26 | 27 | import numpy as np 28 | import exceptions 29 | 30 | PUMP = 0 31 | SGNL = 1 32 | IDLR = 2 33 | 34 | class beam_parameters: 35 | """ Class for managing physical beam parameters of a three-wave mixing 36 | problem. Initally, only supports fixed beam size, but could/should be 37 | extended to spatial overlap by physical diffraction. """ 38 | pump_waist = None 39 | idlr_waist = None 40 | sgnl_waist = None 41 | waists = [] 42 | def __init__(self, pump, sgnl, idlr): 43 | self.pump_waist = pump 44 | self.sgnl_waist = sgnl 45 | self.idlr_waist = idlr 46 | self.waists = [pump, sgnl, idlr] 47 | 48 | def calculate_overlap(self, A, B): 49 | """ calculate overlap integral between fields A and B. A & B must be 50 | integers between 0-2 (0 = pump, 1 = signal, 3 = idler.)""" 51 | if (type(A) is not int) or (type(B) is not int): 52 | e = exceptions.TypeError('A & B must both be integers.') 53 | raise e 54 | if A < 0 or A>3 or B<0 or B>3: 55 | e = exceptions.ValueError('A & B must be in range [0,3].') 56 | raise e 57 | return (self.waists[A]+self.waists[B]) / 2.0 58 | 59 | 60 | 61 | class ThreeFields: 62 | """ Simple class for holding pump, signal, and idler fields. This daata is 63 | a glorified 3xN array of complex128. Can easily be made a C/C++ type. """ 64 | fields = None 65 | field_derivs = None 66 | ks = None 67 | k0s = [0.0, 0.0, 0.0] 68 | NPTS = 0 69 | def __init__(self, NPTS): 70 | self.NPTS = NPTS 71 | self.create_fields() 72 | def create_fields(self): 73 | """ Allocate arrays for field k's and complex amplitudes.""" 74 | self.fields = np.zeros( (3, self.NPTS), dtype = np.complex128) 75 | self.field_derivs = np.zeros( (3, self.NPTS), dtype = np.complex128) 76 | self.ks = np.zeros( (3, self.NPTS), dtype = np.double) 77 | 78 | def set_pump(self, field): 79 | self.fields[PUMP,:] = field 80 | def set_sgnl(self, field): 81 | self.fields[SGNL,:] = field 82 | def set_idlr(self, field): 83 | self.fields[IDLR,:] = field 84 | 85 | def get_pump(self): 86 | return self.fields[PUMP,:] 87 | def get_sgnl(self): 88 | return self.fields[SGNL,:] 89 | def get_idlr(self): 90 | return self.fields[IDLR,:] 91 | 92 | def set_all(self, fields): 93 | self.fields[:] = fields 94 | def get_all(self): 95 | return self.fields 96 | 97 | def create_copy(self): 98 | new_obj = ThreeFields(self.NPTS) 99 | new_obj.set_all(self.get_all()) 100 | return new_obj 101 | 102 | -------------------------------------------------------------------------------- /src/pynlo/interactions/ThreeWaveMixing/multi_run_rw.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | DFG integation results writer. Saves numerically integrated 4 | "DFGintegrand" using pyTables / HDF5. 5 | """ 6 | from __future__ import absolute_import 7 | from __future__ import division 8 | from __future__ import print_function 9 | 10 | import numpy as np 11 | import tables 12 | from pynlo.util.pynlo_ffts import IFFT_t 13 | from pynlo.light.PulseBase import Pulse 14 | 15 | class DFGReader: 16 | """ Class to read saved DFG modeling runs.""" 17 | run_ctr = 0 18 | int_ctr = 0 19 | root_name = "" 20 | root = None 21 | tables_file = None 22 | 23 | def __init__(self, file_name, param_name): 24 | """ Initialize by opening HDF5 file with results for reading. """ 25 | self.tables_file = tables.open_file(file_name, mode = 'r') 26 | self.root = self.tables_file.get_node('/' + param_name) 27 | self.root_name = '/' + param_name 28 | self.run_ctr = 0 29 | for n in self.tables_file.list_nodes(self.root_name): 30 | self.run_ctr += 1 31 | self.frep_Hz = self.tables_file.get_node(self.root_name+'/run0/frep_Hz')[0] 32 | 33 | 34 | def get_run_param(self, run_num = None): 35 | """ Return value of changing paramter for run 'run_num'. By default, 36 | returns value for last run.""" 37 | if run_num is None: 38 | run_num = self.run_ctr - 1 39 | if run_num < 0 or run_num >= self.run_ctr: 40 | raise IndexError(str(run_num)+' not a valid run index.') 41 | param = self.tables_file.get_node(self.root_name+'/run'+str(run_num)+\ 42 | '/param')[0] 43 | return param 44 | 45 | def get_run(self, run_num = None, z_num = None): 46 | """ Return tuple of (pump, signal, idler) pulses for run # n at z index 47 | z_num . By default, returns last z step of last run.""" 48 | if run_num is None: 49 | run_num = self.run_ctr - 1 50 | if run_num < 0 or run_num >= self.run_ctr: 51 | raise IndexError(str(run_num)+' not a valid run index.') 52 | if z_num is None: 53 | z_num = -1 54 | 55 | run = self.root_name+'/run'+str(run_num) 56 | 57 | # Read in and generate pump pulse 58 | pump_field = self.tables_file.get_node(run+'/pump')[:,z_num] 59 | pump_Twind = self.tables_file.get_node(run+'/pump_Twind')[0] 60 | pump_center = self.tables_file.get_node(run+'/pump_center_nm')[0] 61 | p = Pulse() 62 | p.set_NPTS(len(pump_field)) 63 | p.set_center_wavelength_nm(pump_center) 64 | p.set_time_window_ps(pump_Twind) 65 | p.set_AW(pump_field) 66 | p.set_frep_MHz(self.frep_Hz * 1.0e-6) 67 | 68 | # Read in and generate signal pulse 69 | sgnl_field = self.tables_file.get_node(run+'/sgnl')[:,z_num] 70 | sgnl_Twind = self.tables_file.get_node(run+'/sgnl_Twind')[0] 71 | sgnl_center = self.tables_file.get_node(run+'/sgnl_center_nm')[0] 72 | s = Pulse() 73 | s.set_NPTS(len(sgnl_field)) 74 | s.set_center_wavelength_nm(sgnl_center) 75 | s.set_time_window_ps(sgnl_Twind) 76 | s.set_AW(sgnl_field) 77 | s.set_frep_MHz(self.frep_Hz * 1.0e-6) 78 | 79 | # Read in and generate idler pulse 80 | idlr_field = self.tables_file.get_node(run+'/idlr')[:,z_num] 81 | idlr_Twind = self.tables_file.get_node(run+'/idlr_Twind')[0] 82 | idlr_center = self.tables_file.get_node(run+'/idlr_center_nm')[0] 83 | i = Pulse() 84 | i.set_NPTS(len(idlr_field)) 85 | i.set_center_wavelength_nm(idlr_center) 86 | i.set_time_window_ps(idlr_Twind) 87 | i.set_AW(idlr_field) 88 | i.set_frep_MHz(self.frep_Hz * 1.0e-6) 89 | 90 | return (p, s, i) 91 | 92 | def get_next_run(self, z_num = None) : 93 | if self.int_ctr < self.run_ctr: 94 | self.int_ctr += 1 95 | return self.get_run(self.int_ctr - 1, z_num) 96 | 97 | class DFGWriter: 98 | run_ctr = 0 99 | def __init__(self, file_name, param_name): 100 | self.tables_file = tables.open_file(file_name, mode = 'a') 101 | try: 102 | self.root = self.tables_file.create_group('/', param_name) 103 | except tables.NodeError: 104 | print ('Parameter "'+param_name+'" already in table; deleting existing data.') 105 | self.tables_file.remove_node('/', param_name) 106 | self.root = self.tables_file.create_group('/', param_name) 107 | 108 | def add_run(self,integrand, odesolver, param): 109 | grp = self.tables_file.create_group(self.root, 'run'+str(self.run_ctr), title=str(param)) 110 | npts = odesolver.nvar / 3 111 | count = odesolver.out.nsave 112 | 113 | # Store pump, signal, idler fields 114 | atom = tables.Atom.from_dtype(odesolver.out.ysave[0:count, 0 : npts].T.dtype) 115 | data_shape = odesolver.out.ysave[0:count, 0 : npts].T.shape 116 | pump_data = self.tables_file.createCArray(grp, 'pump', atom, data_shape) 117 | pump_data[:] = odesolver.out.ysave[0:count, 0 : npts].T 118 | sgnl_data = self.tables_file.createCArray(grp, 'sgnl', atom, data_shape) 119 | sgnl_data[:] = odesolver.out.ysave[0:count, npts : 2 * npts].T 120 | idlr_data = self.tables_file.createCArray(grp, 'idlr', atom, data_shape) 121 | idlr_data[:] = odesolver.out.ysave[0:count, 2*npts : 3 * npts].T 122 | 123 | # By the design of the pulse class, the three things we need are center 124 | # wavelength, time window, and rep rate (N points is set by the array sizes) 125 | atom = tables.Atom.from_dtype(np.dtype(np.double)) 126 | data_shape = (1,) 127 | pump_twind = self.tables_file.createCArray(grp, 'pump_Twind', atom, data_shape) 128 | pump_twind[:] = integrand.pump.time_window_ps 129 | sgnl_twind = self.tables_file.createCArray(grp, 'sgnl_Twind', atom, data_shape) 130 | sgnl_twind[:] = integrand.sgnl.time_window_ps 131 | idlr_twind = self.tables_file.createCArray(grp, 'idlr_Twind', atom, data_shape) 132 | idlr_twind[:] = integrand.idlr.time_window_ps 133 | 134 | atom = tables.Atom.from_dtype(np.dtype(np.double)) 135 | data_shape = (1,) 136 | pump_wlc = self.tables_file.createCArray(grp, 'pump_center_nm', atom, data_shape) 137 | pump_wlc[:] = integrand.pump.center_wavelength_nm 138 | sgnl_wlc = self.tables_file.createCArray(grp, 'sgnl_center_nm', atom, data_shape) 139 | sgnl_wlc[:] = integrand.sgnl.center_wavelength_nm 140 | idlr_wlc = self.tables_file.createCArray(grp, 'idlr_center_nm', atom, data_shape) 141 | idlr_wlc[:] = integrand.idlr.center_wavelength_nm 142 | 143 | atom = tables.Atom.from_dtype(np.dtype(np.double)) 144 | data_shape = (1,) 145 | fr = self.tables_file.createCArray(grp, 'frep_Hz', atom, data_shape) 146 | fr[:] = integrand.pump.frep_Hz 147 | 148 | atom = tables.Atom.from_dtype(np.dtype(np.double)) 149 | data_shape = (1,) 150 | para = self.tables_file.createCArray(grp, 'param', atom, data_shape) 151 | para[:] = param 152 | 153 | self.tables_file.flush() 154 | self.run_ctr += 1 155 | -------------------------------------------------------------------------------- /src/pynlo/interactions/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: latin1 -*- 2 | # Copyright (C) 2006-2010 João Luís Silva 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 2, 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, write to the Free Software Foundation, 16 | # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | # 18 | 19 | 20 | #----------------------------------------------------------------------- 21 | # Evolution of the pulse through the material 22 | #----------------------------------------------------------------------- 23 | 24 | from . import FourWaveMixing 25 | from . import ThreeWaveMixing -------------------------------------------------------------------------------- /src/pynlo/light/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Apr 03 09:38:19 2014 4 | 5 | """ 6 | 7 | from .PulseBase import Pulse 8 | from .beam import OneDBeam 9 | 10 | from . import beam 11 | from . import DerivedPulses 12 | from . import PulseBase 13 | from .high_V_waveguide import OneDBeam_highV_WG -------------------------------------------------------------------------------- /src/pynlo/light/beam.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jun 11 10:08:31 2015 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | 19 | @author: ycasg 20 | """ 21 | from __future__ import absolute_import 22 | from __future__ import division 23 | from __future__ import print_function 24 | 25 | import numpy as np 26 | from scipy import constants, optimize 27 | 28 | class OneDBeam: 29 | """ Simple Gaussian beam class for propagation and calculating field 30 | intensities. Contains beam shape and propagation axis information. The 31 | beam waist is held independent of index of refraction, from which the 32 | confocal parameter and beam geometry can be calculated. 33 | 34 | According to Boyd, who cites Klienman (1966) and Ward and New (1969), 35 | it is generally true that the confocal parameter is conserved in 36 | harmonic generation and DFG. This parameter is 37 | b = 2 pi w0**2 / lambda.""" 38 | _w0 = 1.0 39 | _lambda0 = None 40 | _crystal_ID = None 41 | _n_s_cache = None 42 | 43 | def __init__(self, waist_meters = 1.0, this_pulse = None, axis = None): 44 | """ Initialize class instance. From waist, confocal parameter is derived. 45 | A Pulse class is input, and it is assumed that each color focuses to 46 | the same waist size at the same point. From this, the (chromatic) confocal 47 | parameter b(lambda) is calculated""" 48 | self._lambda0 = this_pulse.wl_mks 49 | self.axis = axis 50 | self.set_w0( waist_meters ) 51 | 52 | def calc_confocal(self, n_s = 1.0): 53 | return (2.0*np.pi) * self.waist**2 * (n_s/self._lambda0) 54 | 55 | def set_w0(self, w0): 56 | self._w0 = w0 57 | 58 | def _get_w0(self): 59 | return self._w0 60 | 61 | waist = property(_get_w0) 62 | 63 | def calculate_waist(self, z, n_s = 1.0): 64 | """ Calculate the beam waist a distance z from the focus. The expression 65 | is : 66 | w(z) = w0 (1+ ( 2z/b)**2 )**1/2 """ 67 | b = self.calc_confocal(n_s) 68 | return self.waist * np.sqrt(1. + ( 2.0* z / b)**2 ) 69 | 70 | def calculate_zR(self, n_s = 1.0): 71 | """ Calculate Rayleigh range, accounting for index of refraction. """ 72 | return self.calc_confocal(n_s) / 2.0 73 | 74 | def calculate_R(self, z, n_s = 1.0): 75 | """ Calculate beam curvature. : 76 | R(z) = z * [ 1 + (z_R/ z)**2 ]""" 77 | z_r = self.calculate_zR(n_s) 78 | return z * (1 + (z_r/z)**2) 79 | 80 | def calculate_gouy_phase(self, z, n_s): 81 | """ Return the Gouy phase shift due to focusing a distance z in a crystal, 82 | where it is assumed that the focus is at crystal_length / 2.0. Return 83 | is exp(i psi), as in eq 37 in Siegman Ch 17.4, where A ~ exp(-ikz + i psi).""" 84 | z_r = self.calculate_zR(n_s) 85 | psi_gouy = np.arctan2(z, z_r ) 86 | return np.exp(1j*psi_gouy) 87 | 88 | def _rtP_to_a(self, n_s, z, waist = None): 89 | """ Calculate conversion constant from electric field to average power from 90 | indices of refraction: A = P_to_a * rtP """ 91 | if waist is None: 92 | waist = self.calculate_waist(z, n_s) 93 | return 1.0 / np.sqrt( np.pi * waist**2 * n_s * \ 94 | constants.epsilon_0 * constants.speed_of_light) 95 | 96 | def rtP_to_a(self, n_s, z = None): 97 | """ Calculate conversion constant from electric field to average power from 98 | pulse and crystal class instances: A ** 2 = rtP_to_a**2 * P """ 99 | return self._rtP_to_a(n_s, z, self.waist) 100 | 101 | def rtP_to_a_2(self, pulse_instance, crystal_instance, z = None, waist = None): 102 | """ Calculate conversion constant from electric field to average power from 103 | pulse and crystal class instances: A ** 2 = rtP_to_a**2 * P """ 104 | n_s = self.get_n_in_crystal(pulse_instance, crystal_instance) 105 | return self._rtP_to_a(n_s, z, waist) 106 | 107 | def calc_overlap_integral(self, z, this_pulse, othr_pulse, othr_beam,\ 108 | crystal_instance, reverse_order = False): 109 | """ Calculate overlap integral (field-square) between this beam and Beam instance 110 | second_beam inside of a crystal. If reverse_order is true, then the 111 | order of second_beam will be reversed. """ 112 | n1 = self.get_n_in_crystal(this_pulse, crystal_instance) 113 | n2 = othr_beam.get_n_in_crystal(othr_pulse, crystal_instance) 114 | zr1 = self.calculate_zR(n1) 115 | zr2 = othr_beam.calculate_zR(n2) 116 | k1 = self.get_k_in_crystal(this_pulse, crystal_instance) 117 | k2 = othr_beam.get_k_in_crystal(othr_pulse, crystal_instance) 118 | 119 | # This expression only accounts for beam size 120 | #return (2*w2*w2/(w1**2+w2**2))**2 121 | # Expression below accounts for curvature: 122 | return (4*k1*k2*zr1*zr2)/(k2**2*(z**2 + zr1**2) - 2*k1*k2*(z**2 - zr1*zr2) + k1**2*(z**2 + zr2**2)) 123 | 124 | def set_waist_to_match_confocal(self, this_pulse, othr_pulse, othr_beam,\ 125 | crystal_instance): 126 | """ Calculate waist w0 for a beam match confocal parameters with othr_beam """ 127 | 128 | n1 = self.get_n_in_crystal(this_pulse, crystal_instance) 129 | n2 = othr_beam.get_n_in_crystal(othr_pulse, crystal_instance) 130 | zr = othr_beam.calculate_zR(n2) 131 | w0 = np.sqrt( 2.0*zr*self._lambda0/(2.0*np.pi*n1)) 132 | self.set_w0(w0) 133 | 134 | def set_waist_to_match_central_waist(self, this_pulse,w0_center,crystal_instance): 135 | """ Calculate waist w0 for a beam match so that all confocal parameters 136 | are equal while matching waist w0_center at center color of this beam """ 137 | 138 | n1 = self.get_n_in_crystal(this_pulse, crystal_instance) 139 | zr = (np.pi) * w0_center**2 * (n1[len(n1)>>1]/self._lambda0[len(self._lambda0)>>1]) 140 | w0 = np.sqrt( 2*zr*self._lambda0/(2.0*np.pi*n1)) 141 | self.set_w0(w0) 142 | 143 | def calc_optimal_beam_overlap_in_crystal(self, this_pulse, othr_pulse, othr_beam,\ 144 | crystal_instance, L = None): 145 | """ Calculate waist w0 for a beam to maximuze the integral (field-square) 146 | between it beam and Beam instance second_beam integrated along the 147 | length of a crystal. If L is not specified, then the crystal length 148 | is used. """ 149 | if L is None: 150 | L = crystal_instance.length_mks 151 | n1 = self.get_n_in_crystal(this_pulse, crystal_instance) 152 | n2 = othr_beam.get_n_in_crystal(othr_pulse, crystal_instance) 153 | 154 | zr2 = othr_beam.calculate_zR(n2) 155 | k1 = self.get_k_in_crystal(this_pulse, crystal_instance) 156 | k2 = othr_beam.get_k_in_crystal(othr_pulse, crystal_instance) 157 | 158 | obj_fn = lambda zr1: -1.0*np.sum((4*k1*k2*zr1*abs(zr2) *\ 159 | np.arctan( ((k1 - k2)*L)/(k2*zr1 + k1*abs(zr2)))/\ 160 | ((k1 - k2)*(k2*zr1 + k1*abs(zr2))))) 161 | 162 | result = optimize.minimize(obj_fn, zr2, method = 'Powell') 163 | # From w0**2 = b lambda/ 2 pi n: 164 | w0_out = np.sqrt( 2.0 *result.x*self._lambda0/(2.0*np.pi*n1)) 165 | return w0_out 166 | 167 | def get_n_in_crystal(self, pulse_instance, crystal_instance): 168 | return crystal_instance.get_pulse_n(pulse_instance, self.axis) 169 | 170 | def get_k_in_crystal(self, pulse_instance, crystal_instance): 171 | return crystal_instance.get_pulse_k(pulse_instance, self.axis) -------------------------------------------------------------------------------- /src/pynlo/light/high_V_waveguide.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jun 11 10:08:31 2015 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | 19 | @author: ycasg 20 | """ 21 | from __future__ import absolute_import 22 | from __future__ import division 23 | from __future__ import print_function 24 | 25 | import numpy as np 26 | from scipy import constants 27 | 28 | class OneDBeam_highV_WG: 29 | """ Class for propagation and calculating field intensities in a waveguide. 30 | Contains beam shape and propagation axis information. The mode area is 31 | held constant for all colors, and does not change with z. 32 | """ 33 | _Aeff = 1.0 34 | _lambda0 = None 35 | _crystal_ID = None 36 | _n_s_cache = None 37 | 38 | def __init__(self, Aeff_squm = 10.0, this_pulse = None, axis = None): 39 | """ Initialize class instance. Calculations are done from the effective 40 | area. """ 41 | self._lambda0 = this_pulse.wl_mks 42 | self.axis = axis 43 | self.set_Aeff( Aeff_squm*1e-12 ) 44 | 45 | 46 | def set_Aeff(self, Aeff): 47 | self._Aeff = Aeff 48 | 49 | def _get_Aeff(self): 50 | return self._Aeff 51 | 52 | Aeff = property(_get_Aeff) 53 | 54 | 55 | def calculate_gouy_phase(self, z, n_s): 56 | """ Return the Gouy phase shift, which in a waveguide is constant (1.0)""" 57 | return 1.0 58 | 59 | def _rtP_to_a(self, n_s, z, waist = None): 60 | """ Calculate conversion constant from electric field to average power from 61 | indices of refraction: A = P_to_a * rtP """ 62 | return 1.0 / np.sqrt( self._Aeff * n_s * \ 63 | constants.epsilon_0 * constants.speed_of_light) 64 | 65 | def rtP_to_a(self, n_s, z = None): 66 | """ Calculate conversion constant from electric field to average power from 67 | pulse and crystal class instances: A ** 2 = rtP_to_a**2 * P """ 68 | return self._rtP_to_a(n_s, z) 69 | 70 | def rtP_to_a_2(self, pulse_instance, crystal_instance, z = None, waist = None): 71 | """ Calculate conversion constant from electric field to average power from 72 | pulse and crystal class instances: A ** 2 = rtP_to_a**2 * P """ 73 | n_s = self.get_n_in_crystal(pulse_instance, crystal_instance) 74 | return self._rtP_to_a(n_s, z) 75 | 76 | def calc_overlap_integral(self, z, this_pulse, othr_pulse, othr_beam,\ 77 | crystal_instance, reverse_order = False): 78 | """ Calculate overlap integral (field-square) between this beam and Beam instance 79 | second_beam inside of a crystal. In a high V number waveguide, the 80 | modes have the same size, so 1.0 is returned.""" 81 | return 1.0 82 | 83 | 84 | def get_n_in_crystal(self, pulse_instance, crystal_instance): 85 | return crystal_instance.get_pulse_n(pulse_instance, self.axis) 86 | 87 | def get_k_in_crystal(self, pulse_instance, crystal_instance): 88 | return crystal_instance.get_pulse_k(pulse_instance, self.axis) -------------------------------------------------------------------------------- /src/pynlo/media/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from . import crystals 3 | from . import fibers 4 | -------------------------------------------------------------------------------- /src/pynlo/media/crystals/Boyd.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri May 29 11:23:17 2015 4 | 5 | This file is part of pyNLO. 6 | 7 | pyNLO is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | pyNLO is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pyNLO. If not, see 19 | @author: ycasg 20 | """ 21 | from __future__ import absolute_import 22 | from __future__ import division 23 | from __future__ import print_function 24 | 25 | def load(crystal_instance, params): 26 | """ Load simple crystal with fixed refractive index and deff. """ 27 | crystal_instance.mode = 'simple' 28 | crystal_instance.n0 = 2. 29 | crystal_instance.deff = 4e-12 30 | crystal_instance.n2 = 0 31 | 32 | def get_index(crystal_instance, wl): 33 | return crystal_instance.n0 34 | crystal_instance.get_n = get_index -------------------------------------------------------------------------------- /src/pynlo/media/crystals/Readme.txt: -------------------------------------------------------------------------------- 1 | Status as of 6/3/2015 2 | 3 | I've decided to structure crystal information as 4 | 5 | crystals package 6 | - __init__.py loads up crystals 7 | - CrystalContainer contains skeletal CrystalInstance class 8 | - Crystal-specific files subclass CrystalInstance 9 | 10 | So far I have implemented this for PPLN (PPLN.py) and AgGaSe. All other materials need to be adapted. -------------------------------------------------------------------------------- /src/pynlo/media/crystals/XTAL_AgGaS.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri May 29 11:23:17 2015 4 | 5 | Sellemeier coefficients and nonlinear parameter for AsGaS 6 | This file is part of pyNLO. 7 | 8 | pyNLO is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | pyNLO is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with pyNLO. If not, see . 20 | @author: ycasg 21 | """ 22 | from __future__ import absolute_import 23 | from __future__ import division 24 | from __future__ import print_function 25 | 26 | import numpy as np 27 | from pynlo.media.crystals.CrystalContainer import Crystal 28 | import exceptions 29 | 30 | class AgGaSe2(Crystal): 31 | def __init__(self, theta = 0.0, **params): 32 | """ Load AgGaSe2 data. theta : crystal angle (radians)""" 33 | Crystal.__init__(self, params) 34 | self.mode = 'BPM' 35 | 36 | self.Ao = 5.814100 37 | self.Bo = 0.0867547 38 | self.Co = 0.0356502 39 | self.Do = 176380.0 40 | self.Eo = 112586195.0 41 | self.Fo = 0.0821721 42 | self.Go = -0.315646 43 | self.Jo = 0.506566 44 | self.Ko = -6.582197 45 | 46 | self.ao = 3.156983 47 | self.bo = 4.430430 48 | self.co = 6.604280 49 | self.do = 2.225043 50 | 51 | self.Ae = 5.530050 52 | self.Be = 0.0510941 53 | self.Ce = 0.141109 54 | self.De = 4253.78 55 | self.Ee = 4304924.0 56 | self.Fe = 0.195314 57 | self.Ge = -0.0910735 58 | self.Je = 0. 59 | self.Ke = 0. 60 | 61 | self.ae = 2.359877 62 | self.be = 2.566664 63 | self.ce = 0. 64 | self.de = 2.383834 65 | 66 | 67 | self.theta = theta 68 | self.n2 = 0.0 # no data easily found... 69 | self.deff = 8.69e-12 # from SNLO, original given in pm / V 70 | def set_theta(self, angle): 71 | self.theta = angle 72 | def n(self, wl_nm, axis = "mix"): 73 | """ Axis specifies crystal axis, either o, e, or mix. If mix, class 74 | instances value for theta sets mixing angle (0 = pure ordinary). 75 | Following experimental results from Willer, Blanke, Schade 76 | 'Difference frequency generation in AgGaSe2: sellmeier and 77 | temperature-dispersion equations', use rational-exponent 78 | Sellmeier from Roberts (1996) """ 79 | wl_um = wl_nm * 1.0e-3 80 | no = np.sqrt( self.Ao + 81 | self.Bo/(np.power(wl_um, self.ao) - self.Co) + 82 | self.Fo/(np.power(wl_um, self.bo) - self.Go) + 83 | self.Jo/(np.power(wl_um, self.co) - self.Ko) + 84 | self.Do/( 1. - self.Eo / np.power(wl_um, self.do) ) ) 85 | 86 | ne = np.sqrt( self.Ae + 87 | self.Be/(np.power(wl_um, self.ae) - self.Ce) + 88 | self.Fe/(np.power(wl_um, self.be) - self.Ge) + 89 | self.Je/(np.power(wl_um, self.ce) - self.Ke) + 90 | self.De/( 1. - self.Ee /np.power(wl_um, self.de) ) ) 91 | 92 | if axis == 'mix': 93 | return 1.0 / np.sqrt(np.sin(self.theta)**2/ne**2 + 94 | np.cos(self.theta)**2/no**2) 95 | elif axis == 'o': 96 | return no 97 | elif axis == 'e': 98 | return ne 99 | raise exceptions.ValueError("Axis was ",str(axis),"; must be 'mix', 'o', or 'e'") 100 | def phasematch(self, pump_wl_nm, sgnl_wl_nm, idlr_wl_nm, return_wavelength = False): 101 | """ Phase match mixing between pump (aligned to a mix of ne and no) and 102 | signal and idler (aligned to ordinary axis.)""" 103 | RET_WL = False 104 | new_wl = 0.0 105 | if pump_wl_nm is None: 106 | pump_wl_nm = 1.0/(1.0/idlr_wl_nm + 1.0/sgnl_wl_nm) 107 | print('Setting pump to ',pump_wl_nm ) 108 | RET_WL = True 109 | new_wl = pump_wl_nm 110 | if sgnl_wl_nm is None: 111 | sgnl_wl_nm = 1.0/(1.0/pump_wl_nm - 1.0/idlr_wl_nm) 112 | print('Setting signal to ',sgnl_wl_nm) 113 | RET_WL = True 114 | new_wl = sgnl_wl_nm 115 | if idlr_wl_nm is None: 116 | idlr_wl_nm = 1.0/(1.0/pump_wl_nm - 1.0/sgnl_wl_nm) 117 | print('Setting idler to ',idlr_wl_nm) 118 | RET_WL = True 119 | new_wl = idlr_wl_nm 120 | 121 | kp_0 = 2*np.pi/pump_wl_nm 122 | ks = self.n(sgnl_wl_nm, axis = 'o')*2*np.pi/sgnl_wl_nm 123 | ki = self.n(idlr_wl_nm, axis = 'o')*2*np.pi/idlr_wl_nm 124 | 125 | n_soln = (ks+ki) / kp_0 126 | n_e = self.n(pump_wl_nm, 'e') 127 | n_o = self.n(pump_wl_nm, 'o') 128 | print('n_e @ pump: ',n_e, ';\t n_o @ pump: ',n_o) 129 | a = n_e**2 - n_o**2 130 | b = 0.0 131 | c = n_o**2 - n_e**2 * n_o**2 / (n_soln**2) 132 | x = ( -b + np.sqrt(b**2-4*a*c) )/ (2.0 * a) 133 | if x < 0: 134 | x = ( -b - np.sqrt(b**2-4*a*c) )/ (2.0 * a) 135 | if np.isnan(np.arccos(x)) : 136 | raise exceptions.AttributeError('No phase matching condition.') 137 | theta = np.arccos(x) 138 | print('Angle set to ',360*theta / (2.0*np.pi) ) 139 | if RET_WL and return_wavelength: 140 | return (theta, new_wl) 141 | else: 142 | return theta -------------------------------------------------------------------------------- /src/pynlo/media/crystals/XTAL_AgGaSe2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri May 29 11:23:17 2015 4 | 5 | Sellemeier coefficients and nonlinear parameter for AsGaSe_2 6 | This file is part of pyNLO. 7 | 8 | pyNLO is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | pyNLO is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with pyNLO. If not, see . 20 | @author: ycasg 21 | """ 22 | from __future__ import absolute_import 23 | from __future__ import division 24 | from __future__ import print_function 25 | 26 | import numpy as np 27 | from pynlo.media.crystals.CrystalContainer import Crystal 28 | 29 | 30 | class AgGaSe2(Crystal): 31 | def __init__(self, theta = 0.0, **params): 32 | """ Load AgGaSe2 data. theta : crystal angle (radians)""" 33 | Crystal.__init__(self, params) 34 | self.mode = 'BPM' 35 | 36 | self.Ao = 6.849065 37 | self.Bo = 0.417863 38 | self.Co = 0.178080 39 | self.Do = 1.209374 40 | self.Eo = 915.345 41 | self.Fo = 0.000442 42 | self.Go = 0.889242 43 | 44 | self.ao = 1.970203 45 | self.bo = 0.340086 46 | self.co = 1.921292 47 | 48 | self.Ae = 6.675232 49 | self.Be = 0.436579 50 | self.Ce = 0.229775 51 | self.De = 3.252722 52 | self.Ee = 3129.32 53 | self.Fe = 0.012063 54 | self.Ge = 0.213957 55 | 56 | self.ae = 1.893694 57 | self.be = 4.269152 58 | self.ce = 2.047204 59 | 60 | 61 | self.theta = theta 62 | self.n2 = 35e-15 / 100**2 # from Nikogosyan, originally given in cm^2 / W 63 | self.deff = 28.5e-12 # from SNLO, original given in pm / V 64 | def set_theta(self, angle): 65 | self.theta = angle 66 | def n(self, wl_nm, axis = "mix"): 67 | """ Axis specifies crystal axis, either o, e, or mix. If mix, class 68 | instances value for theta sets mixing angle (0 = pure ordinary). 69 | Following experimental results from Willer, Blanke, Schade 70 | 'Difference frequency generation in AgGaSe2: sellmeier and 71 | temperature-dispersion equations', use rational-exponent 72 | Sellmeier from Roberts (1996) """ 73 | wl_um = wl_nm * 1.0e-3 74 | no = np.sqrt( self.Ao + 75 | self.Bo/(np.power(wl_um, self.ao) - self.Co) + 76 | self.Fo/(np.power(wl_um, self.bo) - self.Go) + 77 | self.Do/( 1. - self.Eo / np.power(wl_um, self.co) ) ) 78 | 79 | ne = np.sqrt( self.Ae + 80 | self.Be/(np.power(wl_um, self.ae) - self.Ce) + 81 | self.Fe/(np.power(wl_um, self.be) - self.Ge) + 82 | self.De/( 1. - self.Ee /np.power(wl_um, self.ce) ) ) 83 | 84 | if axis == 'mix': 85 | return 1.0 / np.sqrt(np.sin(self.theta)**2/ne**2 + 86 | np.cos(self.theta)**2/no**2) 87 | elif axis == 'o': 88 | return no 89 | elif axis == 'e': 90 | return ne 91 | raise ValueError("Axis was ",str(axis),"; must be 'mix', 'o', or 'e'") 92 | def phasematch(self, pump_wl_nm, sgnl_wl_nm, idlr_wl_nm, return_wavelength = False): 93 | """ Phase match mixing between pump (aligned to a mix of ne and no) and 94 | signal and idler (aligned to ordinary axis.)""" 95 | RET_WL = False 96 | new_wl = 0.0 97 | if pump_wl_nm is None: 98 | pump_wl_nm = 1.0/(1.0/idlr_wl_nm + 1.0/sgnl_wl_nm) 99 | print ('Setting pump to ',pump_wl_nm) 100 | RET_WL = True 101 | new_wl = pump_wl_nm 102 | if sgnl_wl_nm is None: 103 | sgnl_wl_nm = 1.0/(1.0/pump_wl_nm - 1.0/idlr_wl_nm) 104 | print ('Setting signal to ',sgnl_wl_nm) 105 | RET_WL = True 106 | new_wl = sgnl_wl_nm 107 | if idlr_wl_nm is None: 108 | idlr_wl_nm = 1.0/(1.0/pump_wl_nm - 1.0/sgnl_wl_nm) 109 | print ('Setting idler to ',idlr_wl_nm) 110 | RET_WL = True 111 | new_wl = idlr_wl_nm 112 | 113 | kp_0 = 2*np.pi/pump_wl_nm 114 | ks = self.n(sgnl_wl_nm, axis = 'o')*2*np.pi/sgnl_wl_nm 115 | ki = self.n(idlr_wl_nm, axis = 'o')*2*np.pi/idlr_wl_nm 116 | 117 | n_soln = (ks+ki) / kp_0 118 | n_e = self.n(pump_wl_nm, 'e') 119 | n_o = self.n(pump_wl_nm, 'o') 120 | print ('n_e @ pump: ',n_e, ';\t n_o @ pump: ',n_o) 121 | a = n_e**2 - n_o**2 122 | b = 0.0 123 | c = n_o**2 - n_e**2 * n_o**2 / (n_soln**2) 124 | x = ( -b + np.sqrt(b**2-4*a*c) )/ (2.0 * a) 125 | if x < 0: 126 | x = ( -b - np.sqrt(b**2-4*a*c) )/ (2.0 * a) 127 | if np.isnan(np.arccos(x)) : 128 | raise AttributeError('No phase matching condition.') 129 | theta = np.arccos(x) 130 | print ('Angle set to ',360*theta / (2.0*np.pi) ) 131 | if RET_WL and return_wavelength: 132 | return (theta, new_wl) 133 | else: 134 | return theta -------------------------------------------------------------------------------- /src/pynlo/media/crystals/XTAL_PPLN.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri May 29 11:23:17 2015 4 | 5 | Sellemeier coefficients and nonlinear parameter for PPLN 6 | This file is part of pyNLO. 7 | 8 | pyNLO is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | pyNLO is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with pyNLO. If not, see . 20 | @author: ycasg 21 | """ 22 | from __future__ import absolute_import 23 | from __future__ import division 24 | from __future__ import print_function 25 | 26 | import numpy as np 27 | from pynlo.media.crystals.CrystalContainer import Crystal 28 | 29 | class DengSellmeier: 30 | """ Temperature dependent refractive index for e axis of PPLN, using 31 | equations from Deng et al.""" 32 | a1 = 5.39121 33 | a2 = 0.100473 34 | a3 = 0.20692 35 | a4 = 100.0 36 | a5 = 11.34927 37 | a6 = 1.544e-2 38 | b1 = 4.96827e-7 39 | b2 = 3.862e-8 40 | b3 = -0.89e-8 41 | b4 = 2.657e-5 42 | b5 = 9.62119e-10 43 | T = 0 44 | def __init__(self, T): 45 | self.set_T_degC(T) 46 | def set_T_degC(self, T): 47 | self.T = T 48 | def n(self, wl_nm, axis = None): 49 | wl_um = wl_nm * 1.0e-3 50 | f = (self.T - 24.5)*(self.T+570.82) 51 | return np.sqrt(self.a1 + self.b1*f +\ 52 | (self.a2 + self.b2*f)/(wl_um**2-(self.a3+self.b3*f)**2) +\ 53 | (self.a4 + self.b4*f)/(wl_um**2 - self.a5**2) -\ 54 | (self.a6 + self.b5*f) * wl_um**2) 55 | class Gayer5PctSellmeier: 56 | """ Temperature dependent refractive index for e axis of PPLN, 5pct Mg, 57 | using equations from Gayer et al.""" 58 | a1 = 5.756 59 | a2 = 0.0983 60 | a3 = 0.2020 61 | a4 = 189.32 62 | a5 = 12.52 63 | a6 = 1.32e-2 64 | b1 = 2.860e-6 65 | b2 = 4.700e-8 66 | b3 = 6.113e-8 67 | b4 = 1.516e-4 68 | T = 30 69 | def __init__(self, T): 70 | self.set_T_degC(T) 71 | def set_T_degC(self, T): 72 | self.T = T 73 | def n(self, wl_nm, axis = None): 74 | wl_um = wl_nm * 1.0e-3 75 | f = (self.T - 24.5)*(self.T+570.82) 76 | return np.sqrt(self.a1 + self.b1*f +\ 77 | (self.a2 + self.b2*f)/(wl_um**2-(self.a3+self.b3*f)**2) +\ 78 | (self.a4 + self.b4*f)/(wl_um**2 - self.a5**2) -\ 79 | self.a6 * wl_um**2) 80 | class PPLN(Crystal): 81 | 82 | def __init__(self, T, **params): 83 | Crystal.__init__(self, params) 84 | self.load(T) 85 | def load(self, T, data_source = "Gayer_5pctMg"): 86 | """ Load PPLN data. params -- 'T' : crystal temperature 87 | Uses parameters from: 88 | * Deng: Deng et al, Opt. Comm. 268, 1, 1 pp 110-114 89 | 'Improvement to Sellmeier equation for periodically poled LiNbO3 90 | crystal using mid-infrared difference-frequency generation' 91 | * Gayer_5pctMg: Appl. Phys. B 91, 343–348 (2008) 92 | 'Temperature and wavelength dependent refractive index equations 93 | for MgO-doped congruent and stoichiometric LiNbO3' 94 | """ 95 | self.T = T 96 | self.mode = 'PP' 97 | self.sellmeier_type = data_source 98 | 99 | 100 | self.sellmeier_calculators = {'Deng' :DengSellmeier(T), 101 | 'Gayer_5pctMg':Gayer5PctSellmeier(T)} 102 | self.n = self.sellmeier_calculators[data_source].n 103 | self.set_xtalT = self.sellmeier_calculators[data_source].set_T_degC 104 | 105 | self.deff= 14.9e-12 # from SNLO 106 | self.n2= 3e-15 / 100**2 # from Nikogosyan 107 | self.pp= lambda x: 30.49e-6 108 | self._crystal_properties['damage_threshold_GW_per_sqcm'] = 4.0 109 | self._crystal_properties['damage_threshold_info'] = """ This 4 GW/cm^2 number is from Covesion. According 110 | to their website, it is from a 200 fs pulses source at 1550 nm.""" 111 | def set_pp(self, p) : 112 | if p.__class__ is tuple: 113 | self.pp = lambda x: p[0] 114 | else: 115 | self.pp = lambda x: p(x) 116 | def set_T(self, T_degC): 117 | self.T = T_degC 118 | self.set_xtalT(T_degC) 119 | 120 | def calculate_poling_period(self, pump_wl_nm, sgnl_wl_nm, idlr_wl_nm, 121 | delta_k_L = 3.2, silent=False): 122 | """ Calculate poling period [meters] for pump, signal, and idler -- each a 123 | PINT object (with units.) If one is None, then it is calculated by 124 | energy conservation. """ 125 | RET_wl_nm = False 126 | new_wl_nm = None 127 | if pump_wl_nm is None: 128 | pump_wl_nm = 1.0/(1.0/idlr_wl_nm + 1.0/sgnl_wl_nm) 129 | if not silent: 130 | print ('Setting pump to ',pump_wl_nm) 131 | RET_wl_nm = True 132 | new_wl_nm = pump_wl_nm 133 | if sgnl_wl_nm is None: 134 | sgnl_wl_nm = 1.0/(1.0/pump_wl_nm - 1.0/idlr_wl_nm) 135 | if not silent: 136 | print ('Setting signal to ',sgnl_wl_nm) 137 | RET_wl_nm = True 138 | new_wl_nm = sgnl_wl_nm 139 | if idlr_wl_nm is None: 140 | idlr_wl_nm = 1.0/(1.0/pump_wl_nm - 1.0/sgnl_wl_nm) 141 | if not silent: 142 | print ('Setting idler to ',idlr_wl_nm,' nm') 143 | RET_wl_nm = True 144 | new_wl_nm = idlr_wl_nm 145 | 146 | kp = self.n(pump_wl_nm)*2*np.pi/pump_wl_nm 147 | ks = self.n(sgnl_wl_nm)*2*np.pi/sgnl_wl_nm 148 | ki = self.n(idlr_wl_nm)*2*np.pi/idlr_wl_nm 149 | if self.length_mks is not None: 150 | delta_k_set_pt = delta_k_L / self.length_nm 151 | else: 152 | delta_k_set_pt = 0 153 | deltak = kp-ks-ki - delta_k_set_pt 154 | period_meter = np.pi/deltak*1.0e-9 155 | if not silent: 156 | print ('period is ',2.0*period_meter*1.0e6,' um') 157 | if RET_wl_nm: 158 | return (period_meter*2, new_wl_nm) 159 | else: 160 | return period_meter*2 161 | -------------------------------------------------------------------------------- /src/pynlo/media/crystals/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Created on Thu Oct 23 15:52:45 2014 3 | 4 | @author: Dan 5 | """ 6 | from __future__ import absolute_import 7 | from __future__ import division 8 | from __future__ import print_function 9 | 10 | 11 | from pynlo.media.crystals.XTAL_PPLN import PPLN 12 | from pynlo.media.crystals.XTAL_AgGaSe2 import AgGaSe2 -------------------------------------------------------------------------------- /src/pynlo/media/fibers/JSONFiberLoader.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri May 29 11:00:38 2015 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | @author: ycasg 19 | """ 20 | from __future__ import absolute_import 21 | from __future__ import division 22 | from __future__ import print_function 23 | 24 | import jsonpickle 25 | import os 26 | 27 | class JSONFiberLoader: 28 | """ Load fiber parameters from pickle file. """ 29 | fiber_names = None 30 | def __init__(self, fiber_collection="general_fibers", file_dir = None): 31 | """ Initialize by reading pickles fiber parameters. If you have a pickle 32 | containing your own fiber types, change general_fibers to your own 33 | (.pickle will be appended.)""" 34 | if file_dir is None: 35 | root = os.path.abspath(os.path.dirname(__file__)) 36 | else: 37 | root = file_dir 38 | picklefile = os.path.join(root, fiber_collection+'.txt') 39 | file_handle = open(picklefile, 'r') 40 | data= file_handle.read() 41 | self.fibers = jsonpickle.decode(data) 42 | file_handle.close() 43 | def print_fiber_list(self): 44 | """ Print list of all fibers in database. """ 45 | self.fiber_names = [] 46 | for each in self.fibers.keys(): 47 | print ( 'fiber: ',each ) 48 | self.fiber_names.append(each) 49 | def get_fiber(self, name): 50 | """ Retrieve fiber parameters for fiber "name" """ 51 | fiberspecs = self.fibers[name] 52 | return fiberspecs 53 | -------------------------------------------------------------------------------- /src/pynlo/media/fibers/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Apr 02 14:10:46 2014 4 | 5 | @author: dim1 6 | """ 7 | 8 | from pynlo.media.fibers import JSONFiberLoader 9 | from pynlo.media.fibers import fiber 10 | from pynlo.media.fibers.calculators import DTabulationToBetas 11 | from . import fiber -------------------------------------------------------------------------------- /src/pynlo/media/fibers/calculators.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Jan 28 13:56:17 2014 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public gLicense as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | @author: dim1 19 | """ 20 | 21 | from __future__ import absolute_import 22 | from __future__ import division 23 | from __future__ import print_function 24 | 25 | 26 | import numpy as np 27 | from scipy.special import factorial 28 | from scipy import constants 29 | import matplotlib.pyplot as plt 30 | 31 | def DTabulationToBetas(lambda0, DData, polyOrder, DDataIsFile = True, return_diagnostics = False): 32 | """ Read in a tabulation of D vs Lambda. Returns betas in array 33 | [beta2, beta3, ...]. If return_diagnostics is True, then return 34 | (betas, fit_x_axis (omega in THz), data (ps^2), fit (ps^2) ) """ 35 | # 36 | # Expand about lambda0 37 | makePlots = 0 38 | if DDataIsFile: 39 | DTab = np.genfromtxt(DData,delimiter=',',skiprows=1) 40 | else: 41 | DTab = DData[:] 42 | 43 | # Units of D are ps/nm/km 44 | # Convert to s/m/m 45 | DTab[:,1] = DTab[:,1] * 1e-12 * 1e9 * 1e-3 46 | c = constants.speed_of_light 47 | 48 | omegaAxis = 2*np.pi*c / (DTab[:,0]*1e-9) - 2*np.pi*c /(lambda0 * 1e-9) 49 | # Convert from D to beta via beta2 = -D * lambda^2 / (2*pi*c) 50 | 51 | betaTwo = -DTab[:,1] * (DTab[:,0]*1e-9)**2 / (2*np.pi*c) 52 | # The units of beta2 for the GNLSE solver are ps^2/m; convert 53 | betaTwo = betaTwo * 1e24 54 | # Also convert angular frequency to rad/ps 55 | omegaAxis = omegaAxis * 1e-12 # s/ps 56 | 57 | # How betas are interpreted in gnlse.m: 58 | #B=0; 59 | #for i=1:length(betas) 60 | # B = B + betas(i)/factorial(i+1).*V.^(i+1); 61 | #end 62 | 63 | # Fit beta2 with high-order polynomial 64 | polyFitCo = np.polyfit(omegaAxis, betaTwo, polyOrder) 65 | 66 | Betas = polyFitCo[::-1] 67 | 68 | polyFit = np.zeros((len(omegaAxis),)) 69 | 70 | for i in range(len(Betas)): 71 | Betas[i] = Betas[i] * factorial(i) 72 | polyFit = polyFit + Betas[i] / factorial(i)*omegaAxis**i 73 | 74 | if makePlots == 1: 75 | # try: 76 | # set(0,'CurrentFigure',dispfig); 77 | # catch ME 78 | # dispfig = figure('WindowStyle', 'docked'); 79 | # end 80 | plt.plot(omegaAxis, betaTwo,'o') 81 | plt.plot(omegaAxis, polyFit) 82 | plt.show() 83 | if return_diagnostics: 84 | return Betas, omegaAxis, betaTwo, polyFit 85 | else: 86 | return Betas 87 | -------------------------------------------------------------------------------- /src/pynlo/media/fibers/fiber_config.txt: -------------------------------------------------------------------------------- 1 | [Fiber DB] 2 | file: general_fibers -------------------------------------------------------------------------------- /src/pynlo/media/fibers/general_fibers.txt: -------------------------------------------------------------------------------- 1 | { 2 | "dudley": { 3 | "_id": { 4 | "py/object": "bson.objectid.ObjectId", 5 | "py/state": { 6 | "py/bytes": "SM=B7=DFi@\t=10P=C2=D6=90" 7 | } 8 | }, 9 | "dispersion_data": [ 10 | -11.83, 11 | 0.081038, 12 | -9.5205e-05, 13 | 2.0737e-07, 14 | -5.3943e-10, 15 | 1.3486e-12, 16 | -2.5495e-15, 17 | 3.0524e-18, 18 | -1.714e-21 19 | ], 20 | "dispersion_format": "GVD", 21 | "dispersion_gvd_center_wavelength": 835.0, 22 | "dispersion_gvd_units": "ps^n/km", 23 | "gamma": 0.11, 24 | "is_gain": false, 25 | "name": "dudley" 26 | } 27 | } -------------------------------------------------------------------------------- /src/pynlo/util/Calculators/DispersiveWave_ResonanceFrequency.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Jul 31 08:50:30 2015 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | 19 | @author: ycasg 20 | """ 21 | from __future__ import absolute_import 22 | from __future__ import division 23 | from __future__ import print_function 24 | 25 | import numpy as np 26 | from matplotlib import pyplot as plt 27 | 28 | import math 29 | from scipy import optimize 30 | 31 | from pynlo.media.fibers import fiber 32 | from pynlo.media.fibers.calculators import DTabulationToBetas 33 | ############################################################################### 34 | """ 35 | Solve the dispersive wave resonance equation, (2) from "Dispersive wave 36 | blue-shift in supercontinuum generation", Dane R. Austin, C. Martijn de Sterke, 37 | Benjamin J. Eggleton, Thomas G. Brown 38 | """ 39 | ############################################################################### 40 | ## Fiber parameters 41 | fiber1 = fiber.FiberInstance() 42 | fiber1.fiberloader.print_fiber_list() 43 | fibername = 'PMHNLF_2_2_FASTAXIS_LOWER_D' 44 | fiber1.load_from_db( 1, fibername) 45 | 46 | center_wavelength_nm = 1560.0 47 | poly_order = 2 48 | 49 | betas, omegaAxis, data, fit = DTabulationToBetas(center_wavelength_nm, 50 | np.transpose(np.vstack((fiber1.x,fiber1.y))), 51 | poly_order, 52 | DDataIsFile = False, 53 | return_diagnostics = True) 54 | plt.figure(figsize = (12, 6)) 55 | plt.title(fibername) 56 | plt.subplot(121) 57 | plt.plot(omegaAxis / (2.0*np.pi), data* 1.0e6, label = 'OFS Data' ) 58 | plt.plot(omegaAxis / (2.0*np.pi), fit* 1.0e6, label = 'Fit' ) 59 | plt.ylabel('GVD (fs^2 / m)') 60 | plt.xlabel('Frequency from 1560 nm (THz)') 61 | plt.legend(loc=2) 62 | plt.subplot(122) 63 | plt.plot(omegaAxis / (2.0*np.pi), (data - fit)* 1.0e6) 64 | plt.ylabel('Fit Residuals (fs^2 / m)') 65 | plt.xlabel('Frequency from 1560 nm (THz)') 66 | 67 | ############################################################################### 68 | ## Pulse parameters 69 | # Calculate P0 from frep, Pavg, and pulse length 70 | 71 | Pavg = 200.0e-3 72 | fr = 160.0e6 73 | t0 = 100e-15 74 | 75 | EPP = Pavg / fr 76 | 77 | P0 = 0.94 * EPP / t0 # Gaussian pulse 78 | 79 | ############################################################################### 80 | ## Solve 81 | def fn(x): 82 | eqn = 0 83 | for n in xrange(len(betas)): 84 | print betas[n] * np.power(x, n+2) / math.factorial(n+2) 85 | eqn += betas[n] * np.power(x, n+2) / math.factorial(n+2) 86 | eqn -= fiber1.gamma * P0 / 2.0 87 | return abs(eqn) 88 | 89 | result = optimize.minimize(fn, -betas[0]/betas[1]) -------------------------------------------------------------------------------- /src/pynlo/util/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 08 11:57:31 2015 4 | 5 | @author: ycasg 6 | """ 7 | 8 | from pynlo.util.pynlo_ffts import FFT_t, IFFT_t -------------------------------------------------------------------------------- /src/pynlo/util/ode_solve/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 08 11:57:31 2015 4 | 5 | @author: ycasg 6 | """ 7 | 8 | from pynlo.util.ode_solve.steppers import ODEint 9 | from pynlo.util.ode_solve.steppers import Output -------------------------------------------------------------------------------- /src/pynlo/util/ode_solve/dopr853_constants.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file is part of pyNLO. 3 | 4 | pyNLO 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 of the License, or 7 | (at your option) any later version. 8 | 9 | pyNLO 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 pyNLO. If not, see . 16 | 17 | Constants for Dopri853, obtain from Numerical Recipies. 18 | """ 19 | 20 | c2 = 0.526001519587677318785587544488e-01 21 | c3 = 0.789002279381515978178381316732e-01 22 | c4 = 0.118350341907227396726757197510e+00 23 | c5 = 0.281649658092772603273242802490e+00 24 | c6 = 0.333333333333333333333333333333e+00 25 | c7 = 0.25e+00 26 | c8 = 0.307692307692307692307692307692e+00 27 | c9 = 0.651282051282051282051282051282e+00 28 | c10 = 0.6e+00 29 | c11 = 0.857142857142857142857142857142e+00 30 | c14 = 0.1e+00 31 | c15 = 0.2e+00 32 | c16 = 0.777777777777777777777777777778e+00 33 | 34 | b1 = 5.42937341165687622380535766363e-2 35 | b6 = 4.45031289275240888144113950566e0 36 | b7 = 1.89151789931450038304281599044e0 37 | b8 = -5.8012039600105847814672114227e0 38 | b9 = 3.1116436695781989440891606237e-1 39 | b10 = -1.52160949662516078556178806805e-1 40 | b11 = 2.01365400804030348374776537501e-1 41 | b12 = 4.47106157277725905176885569043e-2 42 | 43 | bhh1 = 0.244094488188976377952755905512e+00 44 | bhh2 = 0.733846688281611857341361741547e+00 45 | bhh3 = 0.220588235294117647058823529412e-01 46 | 47 | er1 = 0.1312004499419488073250102996e-01 48 | er6 = -0.1225156446376204440720569753e+01 49 | er7 = -0.4957589496572501915214079952e+00 50 | er8 = 0.1664377182454986536961530415e+01 51 | er9 = -0.3503288487499736816886487290e+00 52 | er10 = 0.3341791187130174790297318841e+00 53 | er11 = 0.8192320648511571246570742613e-01 54 | er12 = -0.2235530786388629525884427845e-01 55 | 56 | a21 = 5.26001519587677318785587544488e-2 57 | a31 = 1.97250569845378994544595329183e-2 58 | a32 = 5.91751709536136983633785987549e-2 59 | a41 = 2.95875854768068491816892993775e-2 60 | a43 = 8.87627564304205475450678981324e-2 61 | a51 = 2.41365134159266685502369798665e-1 62 | a53 = -8.84549479328286085344864962717e-1 63 | a54 = 9.24834003261792003115737966543e-1 64 | a61 = 3.7037037037037037037037037037e-2 65 | a64 = 1.70828608729473871279604482173e-1 66 | a65 = 1.25467687566822425016691814123e-1 67 | a71 = 3.7109375e-2 68 | a74 = 1.70252211019544039314978060272e-1 69 | a75 = 6.02165389804559606850219397283e-2 70 | a76 = -1.7578125e-2 71 | 72 | a81 = 3.70920001185047927108779319836e-2 73 | a84 = 1.70383925712239993810214054705e-1 74 | a85 = 1.07262030446373284651809199168e-1 75 | a86 = -1.53194377486244017527936158236e-2 76 | a87 = 8.27378916381402288758473766002e-3 77 | a91 = 6.24110958716075717114429577812e-1 78 | a94 = -3.36089262944694129406857109825e0 79 | a95 = -8.68219346841726006818189891453e-1 80 | a96 = 2.75920996994467083049415600797e1 81 | a97 = 2.01540675504778934086186788979e1 82 | a98 = -4.34898841810699588477366255144e1 83 | a101 = 4.77662536438264365890433908527e-1 84 | a104 = -2.48811461997166764192642586468e0 85 | a105 = -5.90290826836842996371446475743e-1 86 | a106 = 2.12300514481811942347288949897e1 87 | a107 = 1.52792336328824235832596922938e1 88 | a108 = -3.32882109689848629194453265587e1 89 | a109 = -2.03312017085086261358222928593e-2 90 | 91 | a111 = -9.3714243008598732571704021658e-1 92 | a114 = 5.18637242884406370830023853209e0 93 | a115 = 1.09143734899672957818500254654e0 94 | a116 = -8.14978701074692612513997267357e0 95 | a117 = -1.85200656599969598641566180701e1 96 | a118 = 2.27394870993505042818970056734e1 97 | a119 = 2.49360555267965238987089396762e0 98 | a1110 = -3.0467644718982195003823669022e0 99 | a121 = 2.27331014751653820792359768449e0 100 | a124 = -1.05344954667372501984066689879e1 101 | a125 = -2.00087205822486249909675718444e0 102 | a126 = -1.79589318631187989172765950534e1 103 | a127 = 2.79488845294199600508499808837e1 104 | a128 = -2.85899827713502369474065508674e0 105 | a129 = -8.87285693353062954433549289258e0 106 | a1210 = 1.23605671757943030647266201528e1 107 | a1211 = 6.43392746015763530355970484046e-1 108 | 109 | a141 = 5.61675022830479523392909219681e-2 110 | a147 = 2.53500210216624811088794765333e-1 111 | a148 = -2.46239037470802489917441475441e-1 112 | a149 = -1.24191423263816360469010140626e-1 113 | a1410 = 1.5329179827876569731206322685e-1 114 | a1411 = 8.20105229563468988491666602057e-3 115 | a1412 = 7.56789766054569976138603589584e-3 116 | a1413 = -8.298e-3 117 | 118 | a151 = 3.18346481635021405060768473261e-2 119 | a156 = 2.83009096723667755288322961402e-2 120 | a157 = 5.35419883074385676223797384372e-2 121 | a158 = -5.49237485713909884646569340306e-2 122 | a1511 = -1.08347328697249322858509316994e-4 123 | a1512 = 3.82571090835658412954920192323e-4 124 | a1513 = -3.40465008687404560802977114492e-4 125 | a1514 = 1.41312443674632500278074618366e-1 126 | a161 = -4.28896301583791923408573538692e-1 127 | a166 = -4.69762141536116384314449447206e0 128 | a167 = 7.68342119606259904184240953878e0 129 | a168 = 4.06898981839711007970213554331e0 130 | a169 = 3.56727187455281109270669543021e-1 131 | a1613 = -1.39902416515901462129418009734e-3 132 | a1614 = 2.9475147891527723389556272149e0 133 | a1615 = -9.15095847217987001081870187138e0 134 | 135 | d41 = -0.84289382761090128651353491142e+01 136 | d46 = 0.56671495351937776962531783590e+00 137 | d47 = -0.30689499459498916912797304727e+01 138 | d48 = 0.23846676565120698287728149680e+01 139 | d49 = 0.21170345824450282767155149946e+01 140 | d410 = -0.87139158377797299206789907490e+00 141 | d411 = 0.22404374302607882758541771650e+01 142 | d412 = 0.63157877876946881815570249290e+00 143 | d413 = -0.88990336451333310820698117400e-01 144 | d414 = 0.18148505520854727256656404962e+02 145 | d415 = -0.91946323924783554000451984436e+01 146 | d416 = -0.44360363875948939664310572000e+01 147 | 148 | d51 = 0.10427508642579134603413151009e+02 149 | d56 = 0.24228349177525818288430175319e+03 150 | d57 = 0.16520045171727028198505394887e+03 151 | d58 = -0.37454675472269020279518312152e+03 152 | d59 = -0.22113666853125306036270938578e+02 153 | d510 = 0.77334326684722638389603898808e+01 154 | d511 = -0.30674084731089398182061213626e+02 155 | d512 = -0.93321305264302278729567221706e+01 156 | d513 = 0.15697238121770843886131091075e+02 157 | d514 = -0.31139403219565177677282850411e+02 158 | d515 = -0.93529243588444783865713862664e+01 159 | d516 = 0.35816841486394083752465898540e+02 160 | 161 | d61 = 0.19985053242002433820987653617e+02 162 | d66 = -0.38703730874935176555105901742e+03 163 | d67 = -0.18917813819516756882830838328e+03 164 | d68 = 0.52780815920542364900561016686e+03 165 | d69 = -0.11573902539959630126141871134e+02 166 | d610 = 0.68812326946963000169666922661e+01 167 | d611 = -0.10006050966910838403183860980e+01 168 | d612 = 0.77771377980534432092869265740e+00 169 | d613 = -0.27782057523535084065932004339e+01 170 | d614 = -0.60196695231264120758267380846e+02 171 | d615 = 0.84320405506677161018159903784e+02 172 | d616 = 0.11992291136182789328035130030e+02 173 | 174 | d71 = -0.25693933462703749003312586129e+02 175 | d76 = -0.15418974869023643374053993627e+03 176 | d77 = -0.23152937917604549567536039109e+03 177 | d78 = 0.35763911791061412378285349910e+03 178 | d79 = 0.93405324183624310003907691704e+02 179 | d710 = -0.37458323136451633156875139351e+02 180 | d711 = 0.10409964950896230045147246184e+03 181 | d712 = 0.29840293426660503123344363579e+02 182 | d713 = -0.43533456590011143754432175058e+02 183 | d714 = 0.96324553959188282948394950600e+02 184 | d715 = -0.39177261675615439165231486172e+02 185 | d716 = -0.14972683625798562581422125276e+03 -------------------------------------------------------------------------------- /src/pynlo/util/ode_solve/dopr853_controller.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Jun 10 09:50:51 2015 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | @author: ycasg 19 | """ 20 | from __future__ import absolute_import 21 | from __future__ import division 22 | from __future__ import print_function 23 | 24 | import numpy as np 25 | 26 | DEBUG = False 27 | 28 | # Implemtentation of Controller struct from NR: 29 | class Controller: 30 | hnext = 0.0 31 | errold = 0.0 32 | reject = False 33 | def __init__(self): 34 | self.reject = False 35 | self.errold = 1.0e-4 36 | self.hnext = 0.0 37 | def success(self, err, h): 38 | beta=0.0 39 | alpha=1.0/8.0-beta*0.2 40 | safe=0.9 41 | minscale=0.333 42 | maxscale=6.0 43 | if np.isnan(h): 44 | raise AssertionError('stepsize is NaN') 45 | if err <= 1.0: 46 | if err == 0.0: 47 | scale = maxscale 48 | else: 49 | scale = safe*np.power(err, -alpha)*np.power(err, beta) 50 | if scale < minscale: 51 | scale = minscale 52 | if scale > maxscale: 53 | scale = maxscale 54 | if self.reject: 55 | self.hnext = h*min(scale, 1.0) 56 | else: 57 | self.hnext = h*scale 58 | self.errold = max(err, 1.0e-4) 59 | self.reject = False 60 | if DEBUG: 61 | print ('Accept, ',h) 62 | return (True, h) 63 | else: 64 | 65 | scale = max(safe*np.power(err, -alpha), minscale) 66 | h *= scale 67 | if DEBUG: 68 | print ('Reject, ',h) 69 | self.reject = True 70 | return (False, h) -------------------------------------------------------------------------------- /src/pynlo/util/ode_solve/example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Jun 10 10:19:43 2015 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | @author: ycasg 19 | """ 20 | from __future__ import absolute_import 21 | from __future__ import division 22 | from __future__ import print_function 23 | 24 | import numpy as np 25 | from pynlo.util.ode_solve import dopr853 26 | from pynlo.util import ode_solve 27 | from matplotlib import pyplot as plt 28 | from mpl_toolkits.mplot3d import Axes3D 29 | 30 | class integrand: 31 | def deriv(self, x, y, dydx): 32 | sigma = 10.0 33 | R = 28.0 34 | b = 8.0/3.0 35 | dydx[0] = sigma * (y[1]- y[0]) 36 | dydx[1] = R * y[0] - y[1] - y[0]*y[2] 37 | dydx[2] = -b * y[2] + y[0] * y[1] 38 | 39 | ystart = np.ones((3,), dtype = np.complex128) 40 | ystart[:] = [10.0, 1.0, 1.0] 41 | dydxstart = np.ones((3,), dtype = np.complex128) 42 | 43 | xstart = np.zeros((1,)) 44 | 45 | rtol = 1.0e-9 46 | atol = 1.0e-9 47 | x0 = 0.0 48 | x1 = 250.0 49 | hmin = 0.0 50 | h1 = 0.01 51 | out = ode_solve.Output(5000) 52 | 53 | a = ode_solve.ODEint(ystart, x0, x1, atol, rtol, h1,hmin, out,\ 54 | dopr853.StepperDopr853, integrand()) 55 | a.integrate() 56 | 57 | fig = plt.figure() 58 | ax = fig.add_subplot(111, projection='3d') 59 | ax.plot(a.out.ysave[:a.out.count, 0], 60 | a.out.ysave[:a.out.count, 1], 61 | a.out.ysave[:a.out.count, 2]) 62 | plt.show() -------------------------------------------------------------------------------- /src/pynlo/util/ode_solve/test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Jun 10 10:19:43 2015 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | @author: ycasg 19 | """ 20 | from __future__ import absolute_import 21 | from __future__ import division 22 | from __future__ import print_function 23 | 24 | import unittest 25 | import numpy as np 26 | from pynlo.util.ode_solve import dopr853 27 | from pynlo.util import ode_solve 28 | 29 | class integrand: 30 | def deriv(self, x, y, dydx) : 31 | # A Bernoulli equation, see Handbook of Mathematical Formulas and Integrals 32 | # pp 349 33 | dydx[0] = (-6.0 * y[0] + 3 * x*np.power(y[0], 4./3.)) / x 34 | 35 | class IntegratorTest(unittest.TestCase): 36 | 37 | def test(self): 38 | # With initial condition y(1) = 2, solution is 39 | def exact(x): 40 | return np.power(x+x**2*(0.5*2**(2./3.)-1),-3) 41 | 42 | ystart = np.ones((1,)) 43 | ystart[:] = [2.0] 44 | 45 | rtol = 1.0e-13 46 | atol = 1.0e-13 47 | x0 = 1.0 48 | x1 = 4.0 49 | hmin = 0.0 50 | h1 = 0.01 51 | out = ode_solve.Output(1) 52 | 53 | a = ode_solve.ODEint(ystart, x0, x1, atol, rtol, h1,hmin, out,\ 54 | dopr853.StepperDopr853, integrand() ) 55 | a.integrate() 56 | y_calc = a.out.ysave[a.out.count-1] 57 | y_exct = exact(a.out.xsave[a.out.count-1]) 58 | self.assertAlmostEqual(y_calc, y_exct, delta = 2*atol*y_exct) 59 | if __name__ == '__main__': 60 | unittest.main() -------------------------------------------------------------------------------- /src/pynlo/util/pynlo_ffts.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Apr 19 12:08:09 2014 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | @author: Gabe-Local 19 | """ 20 | from __future__ import absolute_import 21 | from __future__ import division 22 | from __future__ import print_function 23 | 24 | from numpy import fft 25 | 26 | def FFT_t(A,ax=0): 27 | return fft.ifftshift(fft.ifft(fft.fftshift(A,axes=(ax,)),axis=ax),axes=(ax,)) 28 | def IFFT_t(A,ax=0): 29 | return fft.ifftshift(fft.fft(fft.fftshift(A,axes=(ax,)),axis=ax),axes=(ax,)) 30 | 31 | # these last two are defined in laserFOAM but never used 32 | def FFT_x(self,A): 33 | return fft.ifftshift(fft.fft(fft.fftshift(A))) 34 | def IFFT_x(self,A): 35 | return fft.ifftshift(fft.ifft(fft.fftshift(A))) 36 | -------------------------------------------------------------------------------- /src/validation/Old and Partial Tests/Agrawal_SSFM.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Apr 15 15:39:12 2014 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | @author: dim1 19 | """ 20 | 21 | import numpy as np 22 | import matplotlib.pyplot as plt 23 | from pynlo.interactions import FourWaveMixing 24 | from pynlo.media.fibers import fiber 25 | from pynlo.light.DerivedPulses import SechPulse 26 | 27 | plt.close('all') 28 | 29 | steps = 100 30 | 31 | centerwl = 1550.0 32 | gamma = 2e-3 33 | 34 | fiber_length = 2500.0 35 | P0 = 10.0 36 | T0 = 0.1 37 | nPoints = 2**13 38 | 39 | pulse = SechPulse(P0, T0, centerwl, NPTS = nPoints) 40 | 41 | fiber1 = fiber.FiberInstance() 42 | fiber1.generate_fiber(fiber_length,centerwl, [0,0], gamma, 0) 43 | 44 | evol = FourWaveMixing.SSFM.SSFM(disable_Raman = True, disable_self_steepening = True, 45 | local_error = 0.1, suppress_iteration = True) 46 | 47 | 48 | y = np.zeros(steps) 49 | AW = np.complex64(np.zeros((pulse.NPTS, steps))) 50 | AT = np.complex64(np.copy(AW)) 51 | 52 | 53 | y, AW, AT, pulse1, = evol.propagate(pulse_in = pulse, fiber = fiber1, 54 | n_steps = steps) 55 | 56 | m = 1 57 | T = pulse.T_ps 58 | Leff = fiber_length 59 | LNL = 1/(gamma * P0) 60 | dw_T = (2*m/(T0 * 1e-12)) * (Leff/LNL) * (T/T0)**(2*m-1) * np.exp(-(T/T0)**(2*m)) 61 | 62 | wl = 1e9 * 2 * np.pi * 3e8 / (pulse.W_THz * 1e12) 63 | 64 | loWL = 1200 65 | hiWL = 2000 66 | 67 | print wl 68 | 69 | iis = np.logical_and(wl>loWL,wl-1,pulse.T_ps<5) 71 | 72 | xW = wl[iis] 73 | xT = pulse.T_ps[iisT] 74 | zW_in = np.transpose(AW)[:,iis] 75 | zT_in = np.transpose(AT)[:,iisT] 76 | zW = 10*np.log10(np.abs(zW_in)**2) 77 | zT = 10*np.log10(np.abs(zT_in)**2) 78 | mlIW = np.max(zW) 79 | mlIT = np.max(zT) 80 | 81 | plt.figure() 82 | plt.plot(T/T0,dw_T) 83 | plt.xlim(-2,2) 84 | x = (pulse.W_THz - pulse.center_frequency_THz) / (2* np.pi) * T0 85 | 86 | plt.figure() 87 | plt.pcolormesh(x, y[0:-1] / LNL, 10 * np.log10(np.abs(np.transpose(AW))**2), vmin = mlIW - 40.0, vmax = mlIW, 88 | cmap = plt.cm.gray) 89 | plt.xlim(-8, 8) 90 | plt.xlabel(r'($\nu - \nu_0) \times T_0$') 91 | plt.ylabel(r'Distance ($z/L_{NL})$') 92 | 93 | 94 | plt.show() -------------------------------------------------------------------------------- /src/validation/Old and Partial Tests/Dudley_SSFM.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Apr 15 15:39:12 2014 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | @author: dim1 19 | """ 20 | 21 | import numpy as np 22 | import matplotlib.pyplot as plt 23 | from pynlo.interactions.FourWaveMixing import SSFM 24 | from pynlo.media.fibers import fiber 25 | from pynlo.light.DerivedPulses import SechPulse 26 | 27 | #plt.close('all') 28 | 29 | dz = 1e-3 30 | steps = 100 31 | range1 = np.arange(steps) 32 | 33 | centerwl = 835.0 34 | fiber_length = 0.04 35 | 36 | pump_power = 1.0e4 37 | pump_pulse_length = 28.4e-3 38 | npoints = 2**13 39 | 40 | 41 | init = SechPulse(pump_power, pump_pulse_length, centerwl, time_window = 10.0, 42 | GDD = 0, TOD = 0.0, NPTS = npoints, frep_MHz = 100, power_is_avg = False) 43 | 44 | fiber1 = fiber.FiberInstance() 45 | fiber1.load_from_db( fiber_length, 'dudley') 46 | 47 | evol = SSFM.SSFM(dz = 1e-6, local_error = 0.001, USE_SIMPLE_RAMAN = True) 48 | y = np.zeros(steps) 49 | AW = np.zeros((init.NPTS, steps)) 50 | AT = np.copy(AW) 51 | 52 | y, AW, AT, pulse1 = evol.propagate(pulse_in = init, fiber = fiber1, 53 | n_steps = steps) 54 | 55 | wl = init.wl_nm 56 | 57 | loWL = 400 58 | hiWL = 1400 59 | 60 | iis = np.logical_and(wl>loWL,wl-1,init.T_ps<5) 63 | 64 | xW = wl[iis] 65 | xT = init.T_ps[iisT] 66 | zW_in = np.transpose(AW)[:,iis] 67 | zT_in = np.transpose(AT)[:,iisT] 68 | zW = 10*np.log10(np.abs(zW_in)**2) 69 | zT = 10*np.log10(np.abs(zT_in)**2) 70 | mlIW = np.max(zW) 71 | mlIT = np.max(zT) 72 | 73 | D = fiber1.Beta2_to_D(init) 74 | beta = fiber1.Beta2(init) 75 | # 76 | #plt.figure() 77 | #plt.subplot(121) 78 | #plt.plot(wl,D,'x') 79 | #plt.xlim(400,1600) 80 | #plt.ylim(-400,300) 81 | #plt.xlabel('Wavelength (nm)') 82 | #plt.ylabel('D (ps/nm/km)') 83 | #plt.subplot(122) 84 | #plt.plot(wl,beta*1000,'x') 85 | #plt.xlim(400,1600) 86 | #plt.ylim(-350,200) 87 | #plt.xlabel('Wavelength (nm)') 88 | #plt.ylabel(r'$\beta_2$ (ps$^2$/km)') 89 | 90 | plt.figure() 91 | plt.subplot(121) 92 | plt.pcolormesh(xW, y, zW, vmin = mlIW - 40.0, vmax = mlIW) 93 | plt.autoscale(tight=True) 94 | plt.xlim([loWL, hiWL]) 95 | plt.xlabel('Wavelength (nm)') 96 | plt.ylabel('Distance (m)') 97 | 98 | plt.subplot(122) 99 | plt.pcolormesh(xT, y, zT, vmin = mlIT - 40.0, vmax = mlIT) 100 | plt.autoscale(tight=True) 101 | plt.xlabel('Delay (ps)') 102 | plt.ylabel('Distance (m)') 103 | 104 | plt.show() -------------------------------------------------------------------------------- /src/validation/Old and Partial Tests/Dudley_crosscheck.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Apr 30 11:31:34 2014 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | @author: dim1 19 | """ 20 | import numpy as np 21 | import matplotlib.pyplot as plt 22 | from SSFM import SSFM 23 | from fiber import Fiber 24 | from pulse import Pulse 25 | 26 | 27 | plt.close('all') 28 | 29 | dz = 1e-3 30 | steps = 500 31 | range1 = np.arange(steps) 32 | 33 | centerwl = 835.0 34 | fiber_length = 0.15 35 | 36 | init = Pulse(n = 2**13) 37 | init.gen_sech(1e4, 28.4e-3, centerwl) 38 | 39 | fiber1 = Fiber() 40 | fiber1.load_from_db( fiber_length, 'dudley') 41 | 42 | evoladv = SSFM(dz = 1e-6, local_error = 0.001, USE_SIMPLE_RAMAN = False) 43 | yadv = np.zeros(steps) 44 | AWadv = np.zeros((init.n, steps)) 45 | ATadv = np.copy(AWadv) 46 | 47 | yadv, AWadv, ATadv, pulse1adv = evoladv.propagate(pulse_in = init, 48 | fiber = fiber1, 49 | n_steps = steps) 50 | 51 | evolsimp = SSFM(dz = 1e-6, local_error = 0.001, USE_SIMPLE_RAMAN = True) 52 | ysimp = np.zeros(steps) 53 | AWsimp = np.zeros((init.n, steps)) 54 | ATsimp = np.copy(AWsimp) 55 | 56 | ysimp, AWsimp, ATsimp, pulse1simp = evolsimp.propagate(pulse_in = init, 57 | fiber = fiber1, 58 | n_steps = steps) 59 | 60 | ATmatload = np.genfromtxt('ATmat.csv',delimiter=',') 61 | AWmatload = np.genfromtxt('AWmat.csv',delimiter=',') 62 | Tmat = ATmatload[0,:] 63 | Wmat = AWmatload[0,:] 64 | ATmat = ATmatload[1,:] 65 | AWmat = AWmatload[1,:] 66 | ATmatmax = np.max(ATmat) 67 | AWmatmax = np.max(AWmat) 68 | 69 | ATpysimp = np.abs(ATsimp[:,-1]) 70 | AWpysimp = np.abs(AWsimp[:,-1]) 71 | ATpyadv = np.abs(ATadv[:,-1]) 72 | AWpyadv = np.abs(AWadv[:,-1]) 73 | 74 | ATpysimpmax = np.max(ATpysimp) 75 | AWpysimpmax = np.max(AWpysimp) 76 | ATpyadvmax = np.max(ATpyadv) 77 | AWpyadvmax = np.max(AWpyadv) 78 | 79 | Tpy = init.T 80 | Wpy = init.W 81 | wlpy = 2 * np.pi * init.c / Wpy 82 | wlmat = 2 * np.pi * init.c / Wmat 83 | 84 | plt.figure() 85 | plt.subplot(211) 86 | plt.plot(Tmat, ATmat**2 / ATmatmax**2, label = 'Dudley integrator') 87 | plt.plot(Tpy, ATpysimp**2 / ATpysimpmax**2, 'r', 88 | lw=2,alpha=0.5, label = 'Our SSFM, simple Raman') 89 | #plt.plot(Tpy, ATpyadv**2 / ATpyadvmax**2, label = 'Our SSFM, advanced Raman') 90 | plt.xlim(-1, 5) 91 | plt.xlabel('Time (ps)') 92 | plt.ylabel('Normalized Intensity') 93 | plt.legend() 94 | 95 | plt.subplot(212) 96 | plt.plot(wlmat, AWmat**2 / AWmatmax**2, label = 'Dudley integrator') 97 | plt.plot(wlpy, AWpysimp**2 / AWpysimpmax**2, 'r', 98 | lw=2,alpha=0.5, label = 'Our SSFM, simple Raman') 99 | #plt.plot(wlpy, AWpyadv**2 / AWpyadvmax**2, label = 'Our SSFM, advanced Raman') 100 | plt.xlim(400,1400) 101 | plt.xlabel('Wavelength (nm)') 102 | plt.ylabel('Normalized Spectral Intensity') 103 | plt.legend() 104 | 105 | plt.show() -------------------------------------------------------------------------------- /src/validation/Old and Partial Tests/Hult_SSFM.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Apr 21 12:42:30 2014 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | @author: dim1 19 | """ 20 | 21 | import numpy as np 22 | import matplotlib.pyplot as plt 23 | from SSFM import SSFM 24 | from fiber import Fiber 25 | from pulse import Pulse 26 | #from fftw_transforms import fftcomputer as fftw 27 | #from scipy import fftpack 28 | 29 | plt.close('all') 30 | 31 | steps = 100 32 | 33 | centerwl = 850.0 34 | gamma = 0.045 35 | fiber_length = 0.1 36 | P0 = 10e3 37 | T0 = 28.4e-3 38 | 39 | init = Pulse(n = 2**13) 40 | init.gen_sech(P0, T0, centerwl) 41 | 42 | fiber1 = Fiber() 43 | 44 | fiber1.generate_fiber(fiber_length ,centerwl, [-1.276e-2, 8.119e-5, -1.321e-7, 45 | 3.032e-10, -4.196e-13, 2.570e-16], gamma, 0) 46 | 47 | evol = SSFM(disable_Raman = False, disable_self_steepening = False, 48 | local_error = 0.1, suppress_iteration = True) 49 | 50 | y = np.zeros(steps) 51 | AW = np.complex64(np.zeros((init.n, steps))) 52 | AT = np.complex64(np.copy(AW)) 53 | 54 | y, AW, AT, pulse1, = evol.propagate(pulse_in = init, fiber = fiber1, 55 | n_steps = steps) 56 | 57 | wl = 2 * np.pi * init.c / (init.W) 58 | 59 | loWL = 500 60 | hiWL = 1200 61 | 62 | iis = np.logical_and(wl>loWL,wl-1,init.T<5) 65 | 66 | xW = wl[iis] 67 | xT = init.T[iisT] 68 | zW_in = np.transpose(AW)[:,iis] 69 | zT_in = np.transpose(AT)[:,iisT] 70 | zW = 10*np.log10(np.abs(zW_in)**2) 71 | zT = 10*np.log10(np.abs(zT_in)**2) 72 | mlIW = np.max(zW) 73 | mlIT = np.max(zT) 74 | 75 | D = fiber1.Beta2_to_D(init) 76 | beta = fiber1.Beta2(init) 77 | 78 | plt.figure() 79 | #plt.subplot(121) 80 | plt.pcolormesh(xW, y, zW, vmin = mlIW - 40.0, vmax = mlIW) 81 | plt.autoscale(tight=True) 82 | plt.xlim([loWL, hiWL]) 83 | plt.xlabel('Wavelength (nm)') 84 | plt.ylabel('Distance (m)') 85 | 86 | #plt.subplot(122) 87 | #plt.pcolormesh(xT, y, zT, vmin = mlIT - 40.0, vmax = mlIT) 88 | #plt.autoscale(tight=True) 89 | #plt.xlabel('Delay (ps)') 90 | #plt.ylabel('Distance (m)') 91 | 92 | plt.show() -------------------------------------------------------------------------------- /src/validation/Old and Partial Tests/PPLN_SHG.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Oct 23 15:54:36 2014 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | """ 19 | import numpy as np 20 | import matplotlib.pyplot as plt 21 | from mpl_toolkits.mplot3d import Axes3D 22 | 23 | from pynlo.light.pulseclass import Pulse 24 | from pynlo.media.crystals.PPLN import PPLN 25 | from pynlo.interactions.ThreeWaveMixing import dfg_problem 26 | from pynlo.util import ode_solve 27 | from pynlo.util.ode_solve import dopr853 28 | 29 | from gnlse_ffts import IFFT_t 30 | from mpl_toolkits.axes_grid1.inset_locator import inset_axes 31 | 32 | plt.close('all') 33 | 34 | npoints = 2**6 35 | crystallength = 0.46*1e-3 36 | crystal = PPLN(45, length = crystallength) 37 | 38 | n_saves = 500 39 | 40 | pump_wl = 1064. 41 | pump_power = 100.0 42 | 43 | twind = 25. 44 | beamwaist = 10e-6 45 | 46 | crystal.set_pp(crystal.calculate_poling_period(pump_wl*1e-9*0.5, pump_wl*1e-9, 0)) 47 | 48 | pump_in = Pulse(n = npoints, units = 'mks') 49 | pump_in.gen_CW(0.0, pump_wl/2.0, time_window = twind) 50 | 51 | sgnl_in = Pulse(n = npoints, units = 'mks') 52 | idlr_in = Pulse(n = npoints, units = 'mks') 53 | 54 | sgnl_in.gen_CW(pump_power/2.0, pump_wl , time_window = twind) 55 | idlr_in.gen_CW(pump_power/2.0, pump_wl , time_window = twind) 56 | 57 | integrand = dfg_problem(pump_in, sgnl_in, idlr_in, crystal, 58 | disable_SPM = True, waist = beamwaist) 59 | 60 | # Set up integrator 61 | rtol = 1.0e-6 62 | atol = 1.0e-6 63 | x0 = 0.0 64 | x1 = crystallength 65 | hmin = 0.0 66 | h1 = 0.00001 67 | out = ode_solve.Output(n_saves) 68 | 69 | a = ode_solve.ODEint(integrand.ystart, x0, x1, atol, rtol, h1,hmin, out,\ 70 | dopr853.StepperDopr853, integrand) 71 | a.integrate() 72 | 73 | print 'integrated!' 74 | 75 | pump_out = a.out.ysave[0:a.out.count, 0 : npoints].T 76 | sgnl_out = a.out.ysave[0:a.out.count, npoints : 2*npoints].T 77 | idlr_out = a.out.ysave[0:a.out.count, 2*npoints : 3*npoints].T 78 | z = a.out.xsave[0:a.out.count] 79 | 80 | pump_power_in = np.round(1e3 * np.trapz(abs(IFFT_t(pump_out[:,0]))**2, 81 | pump_in.T) * pump_in.frep, decimals = 4) 82 | signal_power_in = np.round(1e3 * np.trapz(abs(IFFT_t(sgnl_out[:,0]))**2, 83 | sgnl_in.T) * sgnl_in.frep, decimals = 4) 84 | idler_power_in = np.round(1e3 * np.trapz(abs(IFFT_t(idlr_out[:,0]))**2, 85 | idlr_in.T) * idlr_in.frep, decimals = 4) 86 | pump_power_out = np.round(1e3 * np.trapz(abs(IFFT_t(pump_out[:,-1]))**2, 87 | pump_in.T) * sgnl_in.frep, decimals = 4) 88 | signal_power_out = np.round(1e3 * np.trapz(abs(IFFT_t(sgnl_out[:,-1]))**2, 89 | sgnl_in.T) * sgnl_in.frep, decimals = 4) 90 | idler_power_out = np.round(1e3 * np.trapz(abs(IFFT_t(idlr_out[:,-1]))**2, 91 | idlr_in.T) * sgnl_in.frep, decimals = 4) 92 | 93 | print "pump power in: ", pump_power_in, "mW" 94 | print "signal power in: ", signal_power_in, "mW" 95 | print "idler power in: ", idler_power_in, "mW" 96 | print "pump power out: ", pump_power_out, "mW" 97 | print "signal power out: ", signal_power_out, "mW" 98 | print "idler power out: ", idler_power_out, "mW" 99 | 100 | plt.figure() 101 | for x in xrange(len(pump_out[:,0])): 102 | plt.plot(np.abs(sgnl_out[:, x])) 103 | plt.show() 104 | 105 | pump_intensity = np.sum(np.abs(pump_out)**2, axis=0) 106 | sgnl_intensity = np.sum(np.abs(sgnl_out)**2, axis=0) 107 | idlr_intensity = np.sum(np.abs(idlr_out)**2, axis=0) 108 | 109 | plt.plot(z*1000, pump_intensity , color = 'g', linewidth = 1) 110 | plt.plot(z*1000, (sgnl_intensity), color = 'r', linewidth = 1) 111 | plt.plot(z*1000, (idlr_intensity), color = 'k', linewidth = 1) 112 | plt.xlabel('Propagation length (mm)') 113 | plt.ylabel('Power (mW)') 114 | plt.show() -------------------------------------------------------------------------------- /src/validation/Old and Partial Tests/RIFS_SSFM.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Apr 21 13:29:08 2014 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | @author: dim1 19 | """ 20 | 21 | import numpy as np 22 | import matplotlib.pyplot as plt 23 | from SSFM import SSFM 24 | from fiber import Fiber 25 | from pulse import Pulse 26 | #from fftw_transforms import fftcomputer as fftw 27 | #from scipy import fftpack 28 | 29 | plt.close('all') 30 | 31 | fiber1 = Fiber() 32 | 33 | steps = 250 34 | 35 | centerwl = 1550.0 36 | gamma = 2e-3 37 | fiber_length = 100.0 38 | T0 = 50e-3 39 | D = 4 # ps / km / nm 40 | beta2 = -2 * np.pi * fiber1.c / centerwl**2 * D 41 | beta3 = 0.1 42 | betas = [beta2, beta3] 43 | print betas 44 | P0 = abs(betas[0] * 1e-3) / gamma / T0**2 45 | init = Pulse(n = 2**14) 46 | init.gen_sech(P0, T0, centerwl, time_window=25) 47 | fiber1.generate_fiber(fiber_length, centerwl, betas, gamma, 0, "ps^n/km") 48 | 49 | evol = SSFM(disable_Raman = False, disable_self_steepening = False, 50 | local_error = 0.001, suppress_iteration = True, USE_SIMPLE_RAMAN = True) 51 | 52 | y = np.zeros(steps) 53 | AW = np.complex64(np.zeros((init.n, steps))) 54 | AT = np.complex64(np.copy(AW)) 55 | wl = 2 * np.pi * init.c / (init.W) 56 | 57 | shift = np.zeros((len(y), 3)) 58 | fwhm = np.copy(shift) 59 | 60 | chirps = np.array([0.0])*-1 61 | j = 0 62 | 63 | for chirp in chirps: 64 | 65 | init.gen_sech(P0, T0, centerwl, chirp2 = chirp, time_window=25) 66 | 67 | y, AW, AT, pulse1 = evol.propagate(pulse_in = init, fiber = fiber1, 68 | n_steps = steps) 69 | 70 | for each in range(len(y) - 1): 71 | peak = np.argmax(abs(AW[:,each])) 72 | shift[each, j] = init.c/centerwl - init.W[peak] / (2 * np.pi) 73 | i = abs(AT[:,each])**2 > np.max(abs(AT[:,each]))**2/2 74 | fwhm[each, j] = init.dT * sum(i) * 1e3 / 1.76 75 | 76 | j += 1 77 | 78 | plt.figure() 79 | plt.subplot(121) 80 | for plots in range(len(chirps)): 81 | plt.plot(y[:-1], shift[:,plots]) 82 | plt.autoscale(tight=True) 83 | plt.xlabel("Distance (m)") 84 | plt.ylabel("RIFS (THz)") 85 | 86 | plt.subplot(122) 87 | for plots in range(len(chirps)): 88 | plt.plot(y[:-1], fwhm[:,plots]) 89 | plt.xlabel("Distance (m)") 90 | plt.ylabel("pulse width (fs)") 91 | 92 | loWL = 1500 93 | hiWL = 1600 94 | 95 | iis = np.logical_and(wl>loWL,wl-5,init.T<5) 98 | 99 | xW = wl[iis] 100 | xT = init.T[iisT] 101 | zW_in = np.transpose(AW)[:,iis] 102 | zT_in = np.transpose(AT)[:,iisT] 103 | if False: 104 | zW = 10*np.log10(np.abs(zW_in)**2) 105 | zT = 10*np.log10(np.abs(zT_in)**2) 106 | else: 107 | zW = np.abs(zW_in)**2 108 | zT = np.abs(zT_in)**2 109 | 110 | mlIW = np.max(zW) 111 | mlIT = np.max(zT) 112 | 113 | #D = fiber1.Beta2_to_D(init) 114 | #beta = fiber1.Beta2(init) 115 | # 116 | #x = (init.W - init.w0) / (2* np.pi) * T0 117 | #b2 = beta[0] # in ps^2 / m 118 | #LD = T0**2 / abs(b2) 119 | #ynew = y / LD 120 | 121 | #plt.figure() 122 | #plt.pcolormesh(x, ynew, 10*np.log10(np.abs(np.transpose(AW))**2), 123 | # vmin = mlIW - 20.0, vmax = mlIW, cmap = plt.cm.gray) 124 | #plt.autoscale(tight=True) 125 | #plt.xlim([-4, 4]) 126 | #plt.xlabel('(v - v0) T0') 127 | #plt.ylabel('z/LD') 128 | 129 | plt.figure() 130 | plt.subplot(221) 131 | plt.pcolormesh(xW, y, zW)#, vmin = mlIW - 40.0, vmax = mlIW) 132 | plt.autoscale(tight=True) 133 | plt.xlim([loWL, hiWL]) 134 | plt.xlabel('Wavelength (nm)') 135 | plt.ylabel('Distance (m)') 136 | 137 | plt.subplot(222) 138 | plt.pcolormesh(xT, y, zT)#, vmin = mlIT - 40.0, vmax = mlIT) 139 | plt.autoscale(tight=True) 140 | plt.xlabel('Delay (ps)') 141 | plt.ylabel('Distance (m)') 142 | 143 | 144 | plt.subplot(223) 145 | plt.plot(init.T, abs(AT[:,steps-1])**2) 146 | plt.plot(init.T, abs(AT[:,0])**2) 147 | #plt.xlim(-3,3) 148 | 149 | plt.subplot(224) 150 | plt.plot(init.wl, abs(AW[:,steps-1])**2) 151 | plt.plot(init.wl, abs(AW[:,0])**2) 152 | plt.xlim(1500,1630) 153 | plt.show() -------------------------------------------------------------------------------- /src/validation/Old and Partial Tests/devices_gratingcompressor_treacy.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jul 23 11:06:10 2015 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | @author: ycasg 19 | """ 20 | 21 | from pynlo.devices.grating_compressor import TreacyCompressor 22 | from pynlo.light.DerivedPulses import GaussianPulse 23 | import numpy as np 24 | from scipy import constants 25 | from matplotlib import pyplot as plt 26 | 27 | # Check against Tom Allison's calculation 28 | theta = 41.5 29 | ruling = 1250.0 30 | 31 | print "Allison's result: GDD = (-) 1.8 ps^2, TOD = (+) 7.793 10^6 fs^3" 32 | print 'TOD/GDD =', 1.e15*-7.793e6*1.e-45/(1.8e-24), 'fs' 33 | 34 | TC = TreacyCompressor(ruling, theta) 35 | 36 | wl = 1060.0 37 | sep = 11.5e-2 38 | 39 | gdd = TC.calc_compressor_gdd(wl, sep) 40 | print gdd * 1.0e24, 'ps^2' 41 | 42 | wls = np.linspace(1050, 1070) 43 | ws = 2.0*np.pi*299792458.0 / (wls*1.0e-9) 44 | w0 = 2.0*np.pi*299792458.0 / (wl*1.0e-9) 45 | 46 | phis = TC.calc_dphi_domega(ws, sep) 47 | fit = np.polyfit(ws, phis, 3) 48 | gddpoly = np.polyder(fit) 49 | gdd_from_fit = np.poly1d(gddpoly)(w0)*2.0 50 | print 'GDD from polyfit is ', gdd_from_fit 51 | todpoly = np.polyder(fit, m=2) 52 | tod_from_fit = np.poly1d(todpoly)(w0)*2.0 53 | print 'TOD from phase polyfit is ', tod_from_fit* 1.0e45*1.0e-6, ' 10^6 fs^3' 54 | 55 | betas = TC.calc_compressor_gdd(wls, sep) 56 | fit = np.polyfit(ws, betas, 3) 57 | todpoly = np.polyder(fit) 58 | tod_from_fit = np.poly1d(todpoly)(w0) 59 | print 'Fit of GDD derivative: ', tod_from_fit * 1.0e45*1.0e-6, ' 10^6 fs^3' 60 | 61 | 62 | tod = TC.calc_compressor_HOD(wl, sep, 3) 63 | print tod * 1.0e36, 'ps^3' 64 | print tod * 1.0e45*1.0e-6, ' 10^6 fs^3' 65 | print 'numerically calculated tod/gdd = ', tod/gdd*1.0e15 66 | 67 | 68 | gdd_deriv = TC.calc_compressor_dnphi_domega_n(wl, sep, 1) 69 | print gdd_deriv*-2.0 70 | 71 | p = GaussianPulse(0.100, 0.1, 1060., time_window = 10.0, power_is_avg = True) 72 | plt.plot(p.T_ps, abs(p.AT)**2) 73 | p.chirp_pulse_W(1.80262920634, -0.00780201762947) 74 | plt.plot(p.T_ps, abs(p.AT)**2) 75 | TC.apply_phase_to_pulse(sep, p) 76 | plt.plot(p.T_ps, abs(p.AT)**2,'ok') 77 | plt.show() -------------------------------------------------------------------------------- /src/validation/Old and Partial Tests/devices_gratingcompressor_treacy_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jul 23 11:06:10 2015 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | @author: ycasg 19 | """ 20 | import unittest 21 | from pynlo.devices.grating_compressor import TreacyCompressor 22 | import numpy as np 23 | 24 | class TreacyTest(unittest.TestCase): 25 | def setUp(self): 26 | pass 27 | def tearDown(self): 28 | pass 29 | def test_fn(self): 30 | # Check against Tom Allison's calculation 31 | theta = 41.5 32 | ruling = 1250.0 33 | 34 | gdd_check = -1.8e-24 35 | tod_check = 7.793e-39 36 | 37 | TC = TreacyCompressor(ruling, theta) 38 | 39 | wl = 1060.0 40 | sep = 11.5e-2 41 | 42 | gdd = TC.calc_compressor_gdd(wl, sep) 43 | tod = TC.calc_compressor_HOD(wl, sep, 3) 44 | 45 | self.assertLess(abs(gdd-gdd_check)/gdd_check, 0.05) 46 | self.assertLess(abs(tod-tod_check)/tod_check, 0.05) 47 | 48 | if __name__ == '__main__': 49 | unittest.main() -------------------------------------------------------------------------------- /src/validation/Old and Partial Tests/dfg_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Oct 23 15:54:36 2014 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | """ 19 | import numpy as np 20 | import matplotlib.pyplot as plt 21 | from mpl_toolkits.mplot3d import Axes3D 22 | from scipy.constants import speed_of_light 23 | from pynlo.light.DerivedPulses import CWPulse 24 | from pynlo.media import crystals 25 | from pynlo.interactions.ThreeWaveMixing import dfg_problem 26 | from pynlo.util import ode_solve 27 | from pynlo.util.ode_solve import dopr853 28 | from mpl_toolkits.axes_grid1.inset_locator import inset_axes 29 | 30 | plt.close('all') 31 | 32 | npoints = 2**6 33 | # Physical lengths are in meters 34 | crystallength = 0.10 35 | crystal = crystals.AgGaSe2() 36 | 37 | 38 | n_saves = 10 39 | 40 | # Wavelengths are in nanometers 41 | pump_wl_nm = 1560. 42 | sgnl_wl_nm = 2200. 43 | theta, idlr_wl_nm = crystal.phasematch(pump_wl_nm, sgnl_wl_nm, None, return_wavelength = True) 44 | crystal.set_theta(theta) 45 | 46 | 47 | pump_power = 25000.0 48 | sgnl_power = 0.10 49 | idlr_power = 0.000 50 | 51 | twind = 25. 52 | beamwaist = 10e-3 53 | 54 | pump_in = CWPulse(pump_power, pump_wl_nm, NPTS = npoints, time_window = twind, offset_from_center_THz = 0.100) 55 | sgnl_in = CWPulse(sgnl_power, sgnl_wl_nm, NPTS = npoints, time_window = twind) 56 | 57 | plt.plot(pump_in.wl_nm-pump_wl_nm, abs(pump_in.AW)) 58 | plt.xlim(pump_wl_nm-2,pump_wl_nm+2) 59 | plt.show() 60 | 61 | integrand = dfg_problem(pump_in, sgnl_in, crystal, 62 | disable_SPM = True, pump_waist = beamwaist) 63 | 64 | # Set up integrator 65 | rtol = 1.0e-8 66 | atol = 1.0e-8 67 | x0 = 0.0 68 | x1 = crystallength 69 | hmin = 0.0 70 | h1 = 0.0000001 71 | out = ode_solve.Output(n_saves) 72 | 73 | # From Boyd section 2.8, also my One Note 74 | omega = lambda l_nm : 2.0*np.pi*speed_of_light / (l_nm*1.0e-9) 75 | w_1 = omega(sgnl_wl_nm) 76 | w_2 = omega(idlr_wl_nm) 77 | k_1 = 2*np.pi*crystal.n(sgnl_wl_nm, 'o') / ( sgnl_wl_nm * 1.0e-9) 78 | k_2 = 2*np.pi*crystal.n(idlr_wl_nm, 'o') / ( idlr_wl_nm * 1.0e-9) 79 | 80 | n_1 = crystal.n(sgnl_wl_nm, 'o') 81 | n_2 = crystal.n(idlr_wl_nm, 'o') 82 | n_3 = crystal.n(pump_wl_nm, 'mix') 83 | 84 | 85 | A_3 = np.sqrt(pump_power) * integrand.pump_beam.rtP_to_a(crystal.n(pump_wl_nm, 'mix')) 86 | A_1 = np.sqrt(sgnl_power) * integrand.sgnl_beam.rtP_to_a(crystal.n(sgnl_wl_nm, 'o')) 87 | 88 | kappa = np.mean(np.sqrt( 4 * np.mean(crystal.deff)**2 * w_1**2 * w_2**2 / (k_1*k_2 * speed_of_light**4)) * A_3) 89 | 90 | a = ode_solve.ODEint(integrand.ystart, x0, x1, atol, rtol, h1,hmin, out,\ 91 | dopr853.StepperDopr853, integrand, dtype = np.complex128) 92 | print 'Running' 93 | a.integrate() 94 | print 'integrated!' 95 | print 'coupling length is ',1.0/kappa,'m' 96 | print 'calculated coupling coeff. is ',kappa 97 | 98 | print 'theoretical d idler/dz is ',A_1*A_3*(2*w_2**2*crystal.deff)/(k_2*speed_of_light**2) 99 | 100 | pump_out = a.out.ysave[0:a.out.count, 0 : npoints].T 101 | sgnl_out = a.out.ysave[0:a.out.count, npoints : 2*npoints].T 102 | idlr_out = a.out.ysave[0:a.out.count, 2*npoints : 3*npoints].T 103 | z = a.out.xsave[0:a.out.count] 104 | 105 | pump_power_in = np.sum(np.abs(pump_out[:,0]))**2 106 | sgnl_power_in = np.sum(np.abs(sgnl_out[:,0]))**2 107 | idlr_power_in = np.sum(np.abs(idlr_out[:,0]))**2 108 | 109 | pump_power_out = np.sum(np.abs(pump_out[:,-1]))**2 110 | sgnl_power_out = np.sum(np.abs(sgnl_out[:,-1]))**2 111 | idlr_power_out = np.sum(np.abs(idlr_out[:,-1]))**2 112 | 113 | 114 | 115 | print "pump power in: ", pump_power_in*1000, "mW" 116 | print "signal power in: ", sgnl_power_in*1000, "mW" 117 | print "idler power in: ", idlr_power_in*1000, "mW" 118 | print "pump power out: ", pump_power_out*1000, "mW" 119 | print "signal power out: ", sgnl_power_out*1000, "mW" 120 | print "idler power out: ", idlr_power_out*1000, "mW" 121 | 122 | plt.figure() 123 | plt.subplot(211) 124 | plt.plot(z, np.sum(np.abs(sgnl_out[:, :])**2, axis=0), label = 'Model') 125 | plt.plot(z, sgnl_power_in * np.cosh(kappa*z) **2, 'ok', label = 'Analytic') 126 | plt.xlabel('Distance (meters)') 127 | plt.ylabel('Signal power (W)') 128 | plt.legend(loc=2) 129 | plt.subplot(212) 130 | plt.plot(z, np.sum(np.abs(idlr_out[:, :])**2, axis=0)) 131 | plt.plot(z, n_1 * w_2 / (n_2*w_1) * sgnl_power_in * np.sinh(kappa*z) **2, 'ok') 132 | plt.xlabel('Distance (meters)') 133 | plt.ylabel('Idler power (W)') 134 | plt.show() 135 | 136 | # 137 | #pump_intensity = np.sum(np.abs(pump_out)**2, axis=0) 138 | #sgnl_intensity = np.sum(np.abs(sgnl_out)**2, axis=0) 139 | #idlr_intensity = np.sum(np.abs(idlr_out)**2, axis=0) 140 | # 141 | #plt.plot(z*1000, pump_intensity , color = 'g', linewidth = 1) 142 | #plt.plot(z*1000, (sgnl_intensity), color = 'r', linewidth = 1) 143 | #plt.plot(z*1000, (idlr_intensity), color = 'k', linewidth = 1) 144 | #plt.xlabel('Propagation length (mm)') 145 | #plt.ylabel('Power (mW)') 146 | #plt.show() -------------------------------------------------------------------------------- /src/validation/Old and Partial Tests/dfg_test_2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Oct 23 15:54:36 2014 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | """ 19 | import numpy as np 20 | import matplotlib.pyplot as plt 21 | from mpl_toolkits.mplot3d import Axes3D 22 | from scipy.constants import speed_of_light 23 | from pynlo.light.DerivedPulses import CWPulse 24 | from pynlo.media import crystals 25 | from pynlo.interactions.ThreeWaveMixing import dfg_problem 26 | from pynlo.util import ode_solve 27 | from pynlo.util.ode_solve import dopr853 28 | from mpl_toolkits.axes_grid1.inset_locator import inset_axes 29 | 30 | plt.close('all') 31 | 32 | # Test pump depetion in case where pump & signal photon counts are the same. 33 | # Idea from Brian Washburn 34 | 35 | npoints = 2**6 36 | # Physical lengths are in meters 37 | crystallength = 0.10 38 | crystal = crystals.AgGaSe2() 39 | 40 | 41 | n_saves = 1000 42 | 43 | # Wavelengths are in nanometers 44 | pump_wl_nm = 1560. 45 | sgnl_wl_nm = 2200. 46 | theta, idlr_wl_nm = crystal.phasematch(pump_wl_nm, sgnl_wl_nm, None, return_wavelength = True) 47 | crystal.set_theta(theta) 48 | 49 | 50 | pump_power = 2500000.0 51 | sgnl_power = pump_power*sgnl_wl_nm / pump_wl_nm 52 | idlr_power = 0.000 53 | 54 | twind = 25. 55 | beamwaist = 10e-3 56 | 57 | pump_in = CWPulse(pump_power, pump_wl_nm, NPTS = npoints, time_window = twind, offset_from_center_THz = 0.100) 58 | sgnl_in = CWPulse(sgnl_power, sgnl_wl_nm, NPTS = npoints, time_window = twind) 59 | 60 | plt.plot(pump_in.wl_nm-pump_wl_nm, abs(pump_in.AW)) 61 | plt.xlim(pump_wl_nm-2,pump_wl_nm+2) 62 | plt.show() 63 | 64 | integrand = dfg_problem(pump_in, sgnl_in, crystal, 65 | disable_SPM = True, pump_waist = beamwaist) 66 | 67 | # Set up integrator 68 | rtol = 1.0e-8 69 | atol = 1.0e-8 70 | x0 = 0.0 71 | x1 = crystallength 72 | hmin = 0.0 73 | h1 = 0.0000001 74 | out = ode_solve.Output(n_saves) 75 | 76 | # From Boyd section 2.8, also my One Note 77 | omega = lambda l_nm : 2.0*np.pi*speed_of_light / (l_nm*1.0e-9) 78 | w_1 = omega(sgnl_wl_nm) 79 | w_2 = omega(idlr_wl_nm) 80 | k_1 = 2*np.pi*crystal.n(sgnl_wl_nm, 'o') / ( sgnl_wl_nm * 1.0e-9) 81 | k_2 = 2*np.pi*crystal.n(idlr_wl_nm, 'o') / ( idlr_wl_nm * 1.0e-9) 82 | 83 | n_1 = crystal.n(sgnl_wl_nm, 'o') 84 | n_2 = crystal.n(idlr_wl_nm, 'o') 85 | n_3 = crystal.n(pump_wl_nm, 'mix') 86 | 87 | 88 | A_3 = np.sqrt(pump_power) * integrand.pump_beam.rtP_to_a(crystal.n(pump_wl_nm, 'mix')) 89 | A_1 = np.sqrt(sgnl_power) * integrand.sgnl_beam.rtP_to_a(crystal.n(sgnl_wl_nm, 'o')) 90 | 91 | kappa = np.mean(np.sqrt( 4 * np.mean(crystal.deff)**2 * w_1**2 * w_2**2 / (k_1*k_2 * speed_of_light**4)) * A_3) 92 | 93 | a = ode_solve.ODEint(integrand.ystart, x0, x1, atol, rtol, h1,hmin, out,\ 94 | dopr853.StepperDopr853, integrand, dtype = np.complex128) 95 | print 'Running' 96 | a.integrate() 97 | print 'integrated!' 98 | print 'coupling length is ',1.0/kappa,'m' 99 | print 'calculated coupling coeff. is ',kappa 100 | 101 | print 'theoretical d idler/dz is ',A_1*A_3*(2*w_2**2*crystal.deff)/(k_2*speed_of_light**2) 102 | 103 | pump_out = a.out.ysave[0:a.out.count, 0 : npoints].T 104 | sgnl_out = a.out.ysave[0:a.out.count, npoints : 2*npoints].T 105 | idlr_out = a.out.ysave[0:a.out.count, 2*npoints : 3*npoints].T 106 | z = a.out.xsave[0:a.out.count] 107 | 108 | pump_power_in = np.sum(np.abs(pump_out[:,0]))**2 109 | sgnl_power_in = np.sum(np.abs(sgnl_out[:,0]))**2 110 | idlr_power_in = np.sum(np.abs(idlr_out[:,0]))**2 111 | 112 | pump_power_out = np.sum(np.abs(pump_out[:,-1]))**2 113 | sgnl_power_out = np.sum(np.abs(sgnl_out[:,-1]))**2 114 | idlr_power_out = np.sum(np.abs(idlr_out[:,-1]))**2 115 | 116 | 117 | 118 | print "pump power in: ", pump_power_in*1000, "mW" 119 | print "signal power in: ", sgnl_power_in*1000, "mW" 120 | print "idler power in: ", idlr_power_in*1000, "mW" 121 | print "pump power out: ", pump_power_out*1000, "mW" 122 | print "signal power out: ", sgnl_power_out*1000, "mW" 123 | print "idler power out: ", idlr_power_out*1000, "mW" 124 | 125 | plt.figure() 126 | plt.plot(z, np.sum(np.abs(pump_out[:, :])**2, axis=0)) 127 | plt.plot(z, np.sum(np.abs(sgnl_out[:, :])**2, axis=0), label = 'Model') 128 | plt.plot(z, np.sum(np.abs(idlr_out[:, :])**2, axis=0)) 129 | plt.xlabel('Distance (meters)') 130 | plt.ylabel('Power (W)') 131 | plt.show() 132 | 133 | # 134 | #pump_intensity = np.sum(np.abs(pump_out)**2, axis=0) 135 | #sgnl_intensity = np.sum(np.abs(sgnl_out)**2, axis=0) 136 | #idlr_intensity = np.sum(np.abs(idlr_out)**2, axis=0) 137 | # 138 | #plt.plot(z*1000, pump_intensity , color = 'g', linewidth = 1) 139 | #plt.plot(z*1000, (sgnl_intensity), color = 'r', linewidth = 1) 140 | #plt.plot(z*1000, (idlr_intensity), color = 'k', linewidth = 1) 141 | #plt.xlabel('Propagation length (mm)') 142 | #plt.ylabel('Power (mW)') 143 | #plt.show() -------------------------------------------------------------------------------- /src/validation/Old and Partial Tests/ppln_generate_random_design.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Oct 23 15:54:36 2014 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | """ 19 | import numpy as np 20 | import matplotlib.pyplot as plt 21 | 22 | from pynlo.media.crystals.XTAL_PPLN import PPLN 23 | from scipy import integrate 24 | 25 | plt.close('all') 26 | 27 | npoints = 2**6 28 | crystallength = 40*1e-3 29 | crystal = PPLN(45, length = crystallength) 30 | 31 | 32 | pump_wl = 1064. 33 | crystal.set_pp(crystal.calculate_poling_period(pump_wl, 1540, None)) 34 | sgnl_stop_wl = 1700 35 | NPTS = 1000 36 | mix_bw = crystal.calculate_mix_phasematching_bw(1064, np.linspace(1300, sgnl_stop_wl,NPTS)) 37 | idler = 1.0/(1.0/1064 - 1.0/np.linspace(1300, sgnl_stop_wl,NPTS)) 38 | 39 | print crystal.invert_dfg_qpm_to_signal_wl(1064, 24e-6) 40 | 41 | # ODE for finding 'ideal' QPM structure 42 | # dLambda/dz = 1/phasematching BW 43 | # scale = 4.65e-9 # for propto BW 44 | #scale = 1.3e5 # for propto 1/BW 45 | scale = 7e-6 / (1e3*crystallength) # for linear chirp 10 um / crystal length 46 | def dLdz(L, z): 47 | signal = crystal.invert_dfg_qpm_to_signal_wl(pump_wl, L) 48 | bw = crystal.calculate_mix_phasematching_bw(pump_wl, signal) 49 | #return 1.0/(scale*bw) 50 | #return (scale*bw) 51 | return scale 52 | 53 | z = 0 54 | L = 32e-6 # perid to start at 55 | period_len = L 56 | print("Begin APPLN design") 57 | design = [ [z, L] ] 58 | z = period_len/2.0 59 | while z < 5e-3: 60 | signal = ( np.random.rand() * (5200-3000) + 3000 ) 61 | bw_invm_m = crystal.calculate_mix_phasematching_bw(pump_wl, signal) 62 | print ("Signal: %f nm"%(signal)) 63 | L = crystal.calculate_poling_period(pump_wl, signal, None)[0] 64 | z += L 65 | design.append([z+L,L]) 66 | 67 | design = np.array(design) 68 | print design 69 | 70 | grating_zs = design[:, 0] * 1e3 71 | grating_ps = design[:, 1] 72 | plt.plot(grating_zs, grating_ps) 73 | plt.show() 74 | 75 | np.savetxt('h:\\ppln_wg_apod.dat', np.vstack((grating_zs, grating_ps)).T) -------------------------------------------------------------------------------- /src/validation/Old and Partial Tests/ppln_generate_stepped_apodized_design.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Oct 23 15:54:36 2014 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | """ 19 | import numpy as np 20 | import matplotlib.pyplot as plt 21 | 22 | from pynlo.media.crystals.XTAL_PPLN import PPLN 23 | from scipy import integrate 24 | 25 | plt.close('all') 26 | 27 | npoints = 2**6 28 | crystallength = 40*1e-3 29 | crystal = PPLN(45, length = crystallength) 30 | 31 | 32 | pump_wl = 1064. 33 | crystal.set_pp(crystal.calculate_poling_period(pump_wl, 1540, None)) 34 | sgnl_stop_wl = 1700 35 | NPTS = 1000 36 | mix_bw = crystal.calculate_mix_phasematching_bw(1064, np.linspace(1300, sgnl_stop_wl,NPTS)) 37 | idler = 1.0/(1.0/1064 - 1.0/np.linspace(1300, sgnl_stop_wl,NPTS)) 38 | 39 | print crystal.invert_dfg_qpm_to_signal_wl(1064, 24e-6) 40 | 41 | # ODE for finding 'ideal' QPM structure 42 | # dLambda/dz = 1/phasematching BW 43 | # scale = 4.65e-9 # for propto BW 44 | #scale = 1.3e5 # for propto 1/BW 45 | scale = 7e-6 / (1e3*crystallength) # for linear chirp 10 um / crystal length 46 | def dLdz(L, z): 47 | signal = crystal.invert_dfg_qpm_to_signal_wl(pump_wl, L) 48 | bw = crystal.calculate_mix_phasematching_bw(pump_wl, signal) 49 | #return 1.0/(scale*bw) 50 | #return (scale*bw) 51 | return scale 52 | 53 | z = 0 54 | L = 32e-6 # perid to start at 55 | period_len = 1e-3*10.0/5.0 56 | print("Begin APPLN design") 57 | design = [ [z+period_len/2, L] ] 58 | while L > 24.5e-6: 59 | signal = crystal.invert_dfg_qpm_to_signal_wl(pump_wl, L) 60 | bw_invm_m = crystal.calculate_mix_phasematching_bw(pump_wl, signal) 61 | optical_bw = bw_invm_m / period_len 62 | print optical_bw 63 | z += period_len 64 | signal2 = 1.0e9/ ( 1/(signal*1e-9) + optical_bw) 65 | print "signal %f->%f"%(signal, signal2) 66 | L = crystal.calculate_poling_period(pump_wl, signal2, None)[0] 67 | print L 68 | design.append([z+period_len/2,L]) 69 | 70 | design = np.array(design) 71 | print design 72 | 73 | # Following Journal of the Optical Society of America B Vol. 26, Issue 12, pp. 2315-2322 (2009) 74 | # doi: 10.1364/JOSAB.26.002315 75 | 76 | 77 | # Use tanh apodization 78 | # f(z) = \frac{1}{2} tanh\left(\frac{2az}{L}\right), 0\leq z \leq L/2 79 | # f(z) = \frac{1}{2} tanh\left(\frac{2a(L-z)}{L}\right), L/2 < z \leq L 80 | 81 | # Generate apodization function for one unit cell (grating period,) 82 | # then concatenate together to form waveguide description 83 | apod_zs = np.linspace(0, period_len/2.0, 1024) 84 | apod_a = 7 85 | apod_fs = np.tanh(2*apod_a*apod_zs / period_len) 86 | grating_zs = [] 87 | grating_ps = [] 88 | for p in design: 89 | grating_zs.append(p[0] - apod_zs[::-1]) 90 | grating_ps.append(apod_fs * p[1]) 91 | grating_zs.append(p[0] + apod_zs) 92 | grating_ps.append(apod_fs[::-1] * p[1]) 93 | grating_zs = np.array(grating_zs).flatten() * 1e3 94 | grating_ps = np.array(grating_ps).flatten() 95 | grating_ps[grating_ps < 10*1e-6] = 10*1e-6 96 | plt.plot(grating_zs, grating_ps) 97 | plt.show() 98 | 99 | np.savetxt('h:\\ppln_wg_apod.dat', np.vstack((grating_zs, grating_ps)).T) -------------------------------------------------------------------------------- /src/validation/Old and Partial Tests/ppln_phasematching.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Oct 23 15:54:36 2014 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | """ 19 | import numpy as np 20 | import matplotlib.pyplot as plt 21 | 22 | from pynlo.media.crystals.XTAL_PPLN import PPLN 23 | from scipy import integrate 24 | 25 | plt.close('all') 26 | 27 | npoints = 2**6 28 | crystallength = 40*1e-3 29 | crystal = PPLN(45, length = crystallength) 30 | 31 | 32 | pump_wl = 1064. 33 | crystal.set_pp(crystal.calculate_poling_period(pump_wl, 1540, None)) 34 | sgnl_stop_wl = 1700 35 | NPTS = 1000 36 | mix_bw = crystal.calculate_mix_phasematching_bw(1064, np.linspace(1300, sgnl_stop_wl,NPTS)) 37 | idler = 1.0/(1.0/1064 - 1.0/np.linspace(1300, sgnl_stop_wl,NPTS)) 38 | 39 | print crystal.invert_dfg_qpm_to_signal_wl(1064, 24e-6) 40 | 41 | # ODE for finding 'ideal' QPM structure 42 | # dLambda/dz = 1/phasematching BW 43 | scale = 4.65e-9 # for propto BW 44 | #scale = 1.3e5 # for propto 1/BW 45 | #scale = 7e-6 / (1e3*crystallength) # for linear chirp 10 um / crystal length 46 | def dLdz(L, z): 47 | signal = crystal.invert_dfg_qpm_to_signal_wl(pump_wl, L) 48 | bw = crystal.calculate_mix_phasematching_bw(pump_wl, signal) 49 | #return 1.0/(scale*bw) 50 | return (scale*bw) 51 | #return scale 52 | 53 | zs = np.linspace(0, 40) 54 | Lambdas = integrate.odeint(dLdz, 24e-6, zs) 55 | Lambdas = np.reshape(Lambdas, len(Lambdas) ) 56 | 57 | 58 | design = np.vstack( (zs, Lambdas[::-1]) ) 59 | 60 | plt.plot(design[0,:], design[1,:] *1e6) 61 | plt.xlabel('Crystal position (mm)') 62 | plt.ylabel('$\Lambda$ (um)') 63 | 64 | np.savetxt('h:\\40mm_ppln_wg_inv.dat', design.T) 65 | plt.show() -------------------------------------------------------------------------------- /src/validation/Old and Partial Tests/pulse_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Jun 10 10:19:43 2015 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | @author: ycasg 19 | """ 20 | import unittest 21 | import numpy as np 22 | from pynlo.light.DerivedPulses import SechPulse, CWPulse 23 | 24 | 25 | class SechPowerTest(unittest.TestCase): 26 | TEST_PTS = 2**8 27 | power = 3.2 28 | fr = 2720. 29 | EPP = power / (fr*1e6) 30 | def setUp(self): 31 | self.sech = SechPulse(power = self.power, 32 | T0_ps = 0.0100, 33 | center_wavelength_nm = 1064, 34 | NPTS = self.TEST_PTS, 35 | time_window_ps = 12.345, 36 | frep_MHz = self.fr, 37 | power_is_avg = True) 38 | def tearDown(self): 39 | pass 40 | def test_wavelength_meters(self): 41 | self.assertAlmostEqual(self.sech.calc_epp(), self.EPP) 42 | class SechTest(unittest.TestCase): 43 | TEST_PTS = 2**8 44 | def setUp(self): 45 | self.sech = SechPulse(power = 2.727, 46 | T0_ps = 0.0100, 47 | center_wavelength_nm = 1064, 48 | NPTS = self.TEST_PTS, 49 | time_window_ps = 12.345) 50 | def tearDown(self): 51 | pass 52 | def test_wavelength_meters(self): 53 | self.assertAlmostEqual(self.sech.wl_mks[int(self.sech.NPTS/2)], 1064.*1e-9) 54 | def test_wavelength_nm(self): 55 | self.assertAlmostEqual(self.sech.wl_nm[int(self.sech.NPTS/2)], 1064.) 56 | def test_frequency_Hz(self): 57 | self.assertAlmostEqual(self.sech.W_mks[int(self.sech.NPTS/2)] /\ 58 | ( 2*np.pi*299792458/(1064.*1e-9)), 1.0) 59 | def test_frequency_THz(self): 60 | self.assertAlmostEqual(self.sech.W_THz[int(self.sech.NPTS/2)] /\ 61 | ( 1e-12*2*np.pi*299792458/(1064.*1e-9)), 1.0) 62 | def test_npts(self): 63 | self.assertEqual(self.sech.NPTS, self.TEST_PTS) 64 | self.assertEqual(self.sech._n, self.TEST_PTS) 65 | def test_timewindow(self): 66 | self.assertAlmostEqual(self.sech.time_window_ps, 12.345) 67 | self.assertAlmostEqual(self.sech.time_window_mks, 12.345e-12) 68 | def test_timeaxis(self): 69 | self.assertAlmostEqual(self.sech.T_ps[-1] - self.sech.T_ps[0], 12.345,1) 70 | self.assertAlmostEqual(self.sech.T_mks[-1] - self.sech.T_mks[0], 12.345e-12,1) 71 | 72 | def test_temporal_peak(self): 73 | self.assertAlmostEqual(np.max(np.abs(self.sech.AT))**2.0/ 2.727, 1, 2) 74 | 75 | def test_temporal_width(self): 76 | Tfwhm = 2.0*np.arccosh( np.sqrt(2.0)) * 0.0100 77 | half_max = 0.5*np.max(np.abs(self.sech.AT)**2) 78 | T1 = max(self.sech.T_ps[np.abs(self.sech.AT)**2 >= half_max]) 79 | T2 = min(self.sech.T_ps[np.abs(self.sech.AT)**2 >= half_max]) 80 | self.assertTrue( abs( (T1-T2) - Tfwhm) < 2*self.sech.dT_ps) 81 | 82 | class CWTest(unittest.TestCase): 83 | TEST_PTS = 2**8 84 | def setUp(self): 85 | self.cw = CWPulse(1, 1550, NPTS = self.TEST_PTS) 86 | def tearDown(self): 87 | pass 88 | def test_wavelength_meters(self): 89 | center_wl = self.cw.wl_nm[np.argmax(abs(self.cw.AW))] 90 | self.assertAlmostEqual(center_wl, 1550) 91 | if __name__ == '__main__': 92 | unittest.main() -------------------------------------------------------------------------------- /src/validation/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 08 11:57:31 2015 4 | 5 | @author: ycasg 6 | """ 7 | from . import * -------------------------------------------------------------------------------- /src/validation/devices_gratingcompressor_treacy_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jul 23 11:06:10 2015 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | @author: ycasg 19 | """ 20 | import unittest 21 | from pynlo.devices.grating_compressor import TreacyCompressor 22 | import numpy as np 23 | 24 | class TreacyTest(unittest.TestCase): 25 | def setUp(self): 26 | pass 27 | def tearDown(self): 28 | pass 29 | def test_fn(self): 30 | # Check against Tom Allison's calculation 31 | theta = 41.5 32 | ruling = 1250.0 33 | 34 | gdd_check = -1.8e-24 35 | tod_check = 7.793e-39 36 | 37 | TC = TreacyCompressor(ruling, theta) 38 | 39 | wl = 1060.0 40 | sep = 11.5e-2 41 | 42 | gdd = TC.calc_compressor_gdd(wl, sep) 43 | tod = TC.calc_compressor_HOD(wl, sep, 3) 44 | 45 | self.assertLess(abs(gdd-gdd_check)/gdd_check, 0.05) 46 | self.assertLess(abs(tod-tod_check)/tod_check, 0.05) 47 | 48 | if __name__ == '__main__': 49 | unittest.main() -------------------------------------------------------------------------------- /src/validation/pulse_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Jun 10 10:19:43 2015 4 | This file is part of pyNLO. 5 | 6 | pyNLO is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | pyNLO is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pyNLO. If not, see . 18 | @author: ycasg 19 | """ 20 | import unittest 21 | import numpy as np 22 | from pynlo.light.DerivedPulses import SechPulse, CWPulse 23 | 24 | 25 | class SechPowerTest(unittest.TestCase): 26 | TEST_PTS = 2**8 27 | power = 3.2 28 | fr = 2720. 29 | EPP = power / (fr*1e6) 30 | def setUp(self): 31 | self.sech = SechPulse(power = self.power, 32 | T0_ps = 0.0100, 33 | center_wavelength_nm = 1064, 34 | NPTS = self.TEST_PTS, 35 | time_window_ps = 12.345, 36 | frep_MHz = self.fr, 37 | power_is_avg = True) 38 | def tearDown(self): 39 | pass 40 | def test_wavelength_meters(self): 41 | self.assertAlmostEqual(self.sech.calc_epp(), self.EPP) 42 | class SechTest(unittest.TestCase): 43 | TEST_PTS = 2**8 44 | def setUp(self): 45 | self.sech = SechPulse(power = 2.727, 46 | T0_ps = 0.0100, 47 | center_wavelength_nm = 1064, 48 | NPTS = self.TEST_PTS, 49 | time_window_ps = 12.345) 50 | def tearDown(self): 51 | pass 52 | def test_wavelength_meters(self): 53 | self.assertAlmostEqual(self.sech.wl_mks[int(self.sech.NPTS/2)], 1064.*1e-9) 54 | def test_wavelength_nm(self): 55 | self.assertAlmostEqual(self.sech.wl_nm[int(self.sech.NPTS/2)], 1064.) 56 | def test_frequency_Hz(self): 57 | self.assertAlmostEqual(self.sech.W_mks[int(self.sech.NPTS/2)] /\ 58 | ( 2*np.pi*299792458/(1064.*1e-9)), 1.0) 59 | def test_frequency_THz(self): 60 | self.assertAlmostEqual(self.sech.W_THz[int(self.sech.NPTS/2)] /\ 61 | ( 1e-12*2*np.pi*299792458/(1064.*1e-9)), 1.0) 62 | def test_npts(self): 63 | self.assertEqual(self.sech.NPTS, self.TEST_PTS) 64 | self.assertEqual(self.sech._n, self.TEST_PTS) 65 | def test_timewindow(self): 66 | self.assertAlmostEqual(self.sech.time_window_ps, 12.345) 67 | self.assertAlmostEqual(self.sech.time_window_mks, 12.345e-12) 68 | def test_timeaxis(self): 69 | self.assertAlmostEqual(self.sech.T_ps[-1] - self.sech.T_ps[0], 12.345,1) 70 | self.assertAlmostEqual(self.sech.T_mks[-1] - self.sech.T_mks[0], 12.345e-12,1) 71 | 72 | def test_temporal_peak(self): 73 | self.assertAlmostEqual(np.max(np.abs(self.sech.AT))**2.0/ 2.727, 1, 2) 74 | 75 | def test_temporal_width(self): 76 | Tfwhm = 2.0*np.arccosh( np.sqrt(2.0)) * 0.0100 77 | half_max = 0.5*np.max(np.abs(self.sech.AT)**2) 78 | T1 = max(self.sech.T_ps[np.abs(self.sech.AT)**2 >= half_max]) 79 | T2 = min(self.sech.T_ps[np.abs(self.sech.AT)**2 >= half_max]) 80 | self.assertTrue( abs( (T1-T2) - Tfwhm) < 2*self.sech.dT_ps) 81 | 82 | class CWTest(unittest.TestCase): 83 | TEST_PTS = 2**8 84 | def setUp(self): 85 | self.cw = CWPulse(1, 1550, NPTS = self.TEST_PTS) 86 | def tearDown(self): 87 | pass 88 | def test_wavelength_meters(self): 89 | center_wl = self.cw.wl_nm[np.argmax(abs(self.cw.AW))] 90 | self.assertAlmostEqual(center_wl, 1550) 91 | if __name__ == '__main__': 92 | unittest.main() --------------------------------------------------------------------------------