.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_tutorials/basics/plot_03_analysing_a_run.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_auto_tutorials_basics_plot_03_analysing_a_run.py: Analysing a saved run ===================== Everything a propagation leaves behind is read through one protocol, and everything drawn from it comes from one set of accessors. This example makes a run without a solver -- a Gaussian carried along z by the closed-form phase of pure group-velocity dispersion, which supplies an analytic reference for this example -- saves it, reads it back, and draws the standard figures. Swap the synthetic planes for a real output and nothing else changes. .. GENERATED FROM PYTHON SOURCE LINES 18-21 Build the run. A saved plane is a position, a field in the internal FFT convention, and whatever statistics were taken there; a handler stores them and records the grid, the modes and the configuration beside them. .. GENERATED FROM PYTHON SOURCE LINES 21-71 .. code-block:: Python import matplotlib.pyplot as plt import numpy as np from solprop.grid import EnvelopeGrid from solprop.output import MemoryOutput, open_file from solprop.output.schema import GridRecord, RunMetadata, SavedPlane, StepperRecord from solprop.plotting import energy, prop_2d, spec_1d, stats, time_1d from solprop.processing import energy as pulse_energy from solprop.processing import ( fwhm_t, get_power, get_spectral_density, grid_of, time_bandwidth, ) from solprop.pulses import GaussianPulse from solprop.stats import collect, default_spec LENGTH = 0.2 FWHM = 30e-15 grid = EnvelopeGrid.from_limits(LENGTH, 800e-9, (400e-9, 2e-6), 1e-12) seed = GaussianPulse(wavelength=800e-9, fwhm=FWHM, energy=1e-6).build(grid) # Chosen so that the pulse ends four times longer than it started: the closed form is # tau(z) = tau0 sqrt(1 + (beta2 z / tau0^2)^2) with tau0 the 1/e half-width of the # field. tau0 = FWHM / (2 * np.sqrt(np.log(2))) beta2 = np.sqrt(15) * tau0**2 / LENGTH metadata = RunMetadata( grid=GridRecord.from_grid(grid), modes=(), simulation_kind="mode_averaged", configuration={"beta2": beta2}, stepper=StepperRecord(tableau="DOPRI5", controller={"rtol": 1e-8}), ) spec = default_spec("mode_averaged") run = MemoryOutput() run.open(metadata) for z in np.linspace(0.0, LENGTH, 25): # The internal linear operator of a purely dispersive co-moving frame. field = seed * np.exp(-0.5j * beta2 * (grid.omega - grid.omega0) ** 2 * z) plane = SavedPlane(z=float(z), field=field) run.save(SavedPlane(z=float(z), field=field, stats=collect(spec, grid, plane))) print(f"{run.n_saved} planes on a {grid.kind} grid of {grid.n_omega} bins") .. rst-class:: sphx-glr-script-out .. code-block:: none 25 planes on a envelope grid of 1024 bins .. GENERATED FROM PYTHON SOURCE LINES 72-75 Read it back. The accessors take the handler, not the grid: they rebuild the axes from the metadata, return the active band on an ascending axis, and squeeze the mode and polarisation axes a mode-averaged run does not need. .. GENERATED FROM PYTHON SOURCE LINES 75-84 .. code-block:: Python wavelength, density = get_spectral_density(run, "wavelength", plane=-1) t, power = get_power(run, plane=-1, oversampling=4) print(f"axes: {wavelength.shape} wavelengths, {t.shape} time samples") print(f"energy {float(pulse_energy(run, plane=-1)) * 1e6:.4f} uJ") print(f"duration {float(fwhm_t(run, plane=-1)) * 1e15:.1f} fs") print(f"time-bandwidth product {float(time_bandwidth(run, plane=-1)):.3f}") .. rst-class:: sphx-glr-script-out .. code-block:: none axes: (853,) wavelengths, (4096,) time samples energy 1.0000 uJ duration 120.0 fs time-bandwidth product 1.766 .. GENERATED FROM PYTHON SOURCE LINES 85-89 The metrics against the closed form. Pure dispersion conserves the energy and the spectrum exactly and stretches the pulse by a known factor, so the measured duration is a check on the whole chain: the stored convention, the rebuilt axes, the transform and the half-maximum crossing. .. GENERATED FROM PYTHON SOURCE LINES 89-110 .. code-block:: Python z = run["z"] measured = fwhm_t(run) expected = FWHM * np.sqrt(1.0 + (beta2 * z / tau0**2) ** 2) fig, ax = plt.subplots(figsize=(5.6, 3.2)) ax.plot(z * 100, measured * 1e15, lw=3, alpha=0.4, label="measured") ax.plot( z * 100, expected * 1e15, "k--", lw=1, label=r"$\tau_0\sqrt{1+(\beta_2z/\tau_0^2)^2}$", ) ax.set_xlabel("distance (cm)") ax.set_ylabel("duration (fs)") ax.legend(frameon=False) fig.tight_layout() print(f"largest relative error {np.max(np.abs(measured / expected - 1)):.2e}") .. image-sg:: /auto_tutorials/basics/images/sphx_glr_plot_03_analysing_a_run_001.png :alt: plot 03 analysing a run :srcset: /auto_tutorials/basics/images/sphx_glr_plot_03_analysing_a_run_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none largest relative error 2.66e-04 .. GENERATED FROM PYTHON SOURCE LINES 111-114 The propagation maps. One figure per mode and one for the modal sum; here there is one mode, so there is one figure. The spectral panel is in decibels below the peak and the temporal one is a linear power in an auto-chosen unit. .. GENERATED FROM PYTHON SOURCE LINES 114-121 .. code-block:: Python figures = prop_2d( run, "wavelength", wavelength_range=(650e-9, 1000e-9), trange=(-150e-15, 150e-15) ) figures[0].set_size_inches(9.5, 3.4) figures[0].tight_layout() .. image-sg:: /auto_tutorials/basics/images/sphx_glr_plot_03_analysing_a_run_002.png :alt: Propagation (mode 1) :srcset: /auto_tutorials/basics/images/sphx_glr_plot_03_analysing_a_run_002.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 122-125 Slices at three positions. A colour per position; the temporal legend carries each trace's width at half maximum, and the spectral axis is logarithmic over six decades by default. .. GENERATED FROM PYTHON SOURCE LINES 125-131 .. code-block:: Python fig, (left, right) = plt.subplots(1, 2, figsize=(9.5, 3.4)) time_1d(run, [0.0, 0.1, 0.2], trange=(-150e-15, 150e-15), ax=left) spec_1d(run, [0.0, 0.1, 0.2], wavelength_range=(650e-9, 1000e-9), ax=right) fig.tight_layout() .. image-sg:: /auto_tutorials/basics/images/sphx_glr_plot_03_analysing_a_run_003.png :alt: plot 03 analysing a run :srcset: /auto_tutorials/basics/images/sphx_glr_plot_03_analysing_a_run_003.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 132-135 The energy along the fibre, with the conversion efficiency on the right-hand axis. Pure dispersion moves neither, which is the point: a plot that shows nothing happening is how you find out that nothing happened. .. GENERATED FROM PYTHON SOURCE LINES 135-140 .. code-block:: Python figure = energy(run) figure.set_size_inches(6.0, 3.4) figure.tight_layout() .. image-sg:: /auto_tutorials/basics/images/sphx_glr_plot_03_analysing_a_run_004.png :alt: plot 03 analysing a run :srcset: /auto_tutorials/basics/images/sphx_glr_plot_03_analysing_a_run_004.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 141-143 The statistics the run recorded. Only the groups with something in them are drawn, so a run that recorded no waveguide statistics gets one figure rather than an error. .. GENERATED FROM PYTHON SOURCE LINES 143-148 .. code-block:: Python figure = stats(run)[0] figure.set_size_inches(8.0, 5.6) figure.tight_layout() .. image-sg:: /auto_tutorials/basics/images/sphx_glr_plot_03_analysing_a_run_005.png :alt: Pulse statistics :srcset: /auto_tutorials/basics/images/sphx_glr_plot_03_analysing_a_run_005.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 149-154 The same run from disk. A memory handler writes exactly the file the HDF5 handler would have written, ``open_file`` opens it again read-only through the same protocol, and an accessor reading either returns the same bits -- which is what makes it safe to develop an analysis against a small in-memory run and then point it at a large file. .. GENERATED FROM PYTHON SOURCE LINES 154-167 .. code-block:: Python import tempfile from pathlib import Path with tempfile.TemporaryDirectory() as directory: path = Path(directory) / "run.h5" run.to_file(path, compression="lzf") with open_file(path) as from_disk: same_grid = grid_of(from_disk).n_omega == grid.n_omega _, from_file = get_spectral_density(from_disk, "wavelength", plane=-1) print(f"grid rebuilt from the file: {same_grid}") print(f"densities identical: {np.array_equal(density, from_file)}") .. rst-class:: sphx-glr-script-out .. code-block:: none grid rebuilt from the file: True densities identical: True .. GENERATED FROM PYTHON SOURCE LINES 168-174 Related reading --------------- Continue with :doc:`/guide/results/processing` for the model, options and limits, and :doc:`/guide/numerics/accuracy` for convergence checks. For a paced propagation lesson, return to :doc:`/introduction/lessons/plot_01_rdw`. .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.994 seconds) .. _sphx_glr_download_auto_tutorials_basics_plot_03_analysing_a_run.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_03_analysing_a_run.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_03_analysing_a_run.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_03_analysing_a_run.zip `