.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_tutorials/basics/plot_07_time_window.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_07_time_window.py: Is my time window big enough? ============================= The time window is periodic: light that walks past one edge comes back at the other. Every run therefore carries a soft absorbing **collar** just outside the window you ask for (``trange``), and a **boundary monitor** that watches the outer 5 % of the window at each edge, the **guards**, and the energy the collar removes. This tutorial uses both to size a window, on a short fibre where a Raman soliton decelerates towards the window's late edge. The fibre: 40 cm of a solid-core fibre at 835 nm (``beta2 = -11.83 ps^2/km``, a little third-order dispersion, ``gamma = 0.11 /W/m``), pumped by a 50 fs sech pulse of soliton order 1.7 with Raman on. The pulse settles into a soliton, which shifts to the red and slows down, and sheds dispersive radiation near 700 nm, which travels slower still. The runs below switch the input shot noise off (``noise=None``), so that windows of different sizes see the same input; with noise, each grid draws its own realisation. The :doc:`boundaries guide ` describes the collar, the monitor and their settings. .. GENERATED FROM PYTHON SOURCE LINES 29-33 A window that is too small -------------------------- One function builds the fibre on a requested window ``trange``, optionally with the pulse launched at ``t0`` and with the monitor's thresholds changed. .. GENERATED FROM PYTHON SOURCE LINES 33-71 .. code-block:: Python import time import warnings import matplotlib.pyplot as plt import numpy as np from solprop.driver import Simulation from solprop.exceptions import BoundaryError, BoundaryWarning from solprop.monitor import DEFAULT_BOUNDARY_CHECK, BoundaryCheck from solprop.processing import get_power from solprop.pulses import SechPulse LENGTH = 0.4 BETAS = [0.0, 0.0, -11.83e-27, 8.1e-41] WIDTH = 28e-15 """sech half-width, s (50 fs full width at half maximum).""" PEAK = 3 * abs(BETAS[2]) / (0.11 * WIDTH**2) """Peak power, W: three times the fundamental soliton's, a soliton order of 1.7.""" def fibre(trange, *, t0=0.0, check=DEFAULT_BOUNDARY_CHECK): """Return the fibre on a requested window `trange`, s.""" return Simulation.from_gnlse( 0.11, LENGTH, BETAS, wavelength=835e-9, wavelength_limits=(600e-9, 1600e-9), trange=trange, raman=True, n_save=21, noise=None, pulses=[SechPulse(wavelength=835e-9, width=WIDTH, peak_power=PEAK, t0=t0)], boundary_check=check, ) .. GENERATED FROM PYTHON SOURCE LINES 72-75 On a 0.5 ps window the soliton reaches the late edge before the end of the fibre. The monitor warns once the field in a guard passes its budget (a field error of 1e-3, so a millionth of the energy), and stops the run at ten times the budget: .. GENERATED FROM PYTHON SOURCE LINES 75-81 .. code-block:: Python try: fibre(0.5e-12).run(progress="none") except BoundaryError as error: print(f"stopped: {error}") .. rst-class:: sphx-glr-script-out .. code-block:: none BoundaryWarning: at z = 1.442264e-01 m the square root of the time window's guard occupancy is 1.01e-03, above the 1e-03 budget: light is reaching the window's edge; widen trange or move the launch stopped: at z = 3.641578e-01 m the field puts 1.02e-04 of its energy in the time window's 0.05 guards, whose square root 1.01e-02 is above 10 times the 1e-03 budget: light is reaching the window's edge and will wrap around or be absorbed; widen trange, move the launch, or relax the check (settings.boundary_check) .. GENERATED FROM PYTHON SOURCE LINES 82-89 Reading what the monitor saw ---------------------------- A relaxed check lets the run finish so that its record can be read. The record sits in the run's execution record, with a row for every saved plane: the collar's occupancy there, each guard's largest occupancy since the previous plane, and the fraction of the launch energy the collar has removed so far. (The occupancies are counted above the shot-noise floor, which these runs do not carry.) .. GENERATED FROM PYTHON SOURCE LINES 89-122 .. code-block:: Python relaxed = BoundaryCheck(budget=1.0, stop_factor=1.0) def watched(trange, **keywords): """Run the fibre with a relaxed check; return the run, its record and wall time.""" simulation = fibre(trange, check=relaxed, **keywords) started = time.perf_counter() with warnings.catch_warnings(): warnings.simplefilter("ignore", BoundaryWarning) run = simulation.run(progress="none") wall = time.perf_counter() - started return run, run.outcome.execution["boundary_monitor"], wall, simulation.grid small, small_record, _, _ = watched(0.5e-12) modest, modest_record, _, _ = watched(1e-12) fig, (guards, removed) = plt.subplots(1, 2, figsize=(9, 3.4), layout="constrained") for record, label in ((small_record, "0.5 ps"), (modest_record, "1 ps")): rows = record["planes"] z = 100 * np.asarray(rows["z"]) guards.semilogy( z, np.maximum(rows["guard_high"], 1e-16), label=f"{label}, late guard" ) removed.semilogy(z, np.maximum(rows["removed_fraction"], 1e-16), label=label) guards.axhline(1e-6, color="0.5", ls=":", label="budget (field error 1e-3)") guards.set(xlabel="z (cm)", ylabel="guard occupancy", ylim=(1e-14, 1)) removed.set(xlabel="z (cm)", ylabel="fraction removed by the collar", ylim=(1e-8, 1)) guards.legend(fontsize=8) removed.legend(fontsize=8) plt.show() .. image-sg:: /auto_tutorials/basics/images/sphx_glr_plot_07_time_window_001.png :alt: plot 07 time window :srcset: /auto_tutorials/basics/images/sphx_glr_plot_07_time_window_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 123-128 On 0.5 ps the soliton fills the late guard and the collar takes most of the energy. On 1 ps the guards stay far below the budget, yet the collar still removes about a quarter of a per cent of the energy: light that is not the soliton walks out. The monitor reports that at the end of the run as a warning, since its square root is above the budget; whether it matters depends on what you measure. .. GENERATED FROM PYTHON SOURCE LINES 128-134 .. code-block:: Python print( f"1 ps window: guards peaked at {modest_record['guard_high_max']:.1e}, " f"the collar removed {modest_record['removed_fraction']:.1e} of the launch energy" ) .. rst-class:: sphx-glr-script-out .. code-block:: none 1 ps window: guards peaked at 1.3e-07, the collar removed 2.5e-03 of the launch energy .. GENERATED FROM PYTHON SOURCE LINES 135-140 What is walking out ------------------- A wide window answers that. On 8 ps nothing reaches the collar, and the exit power shows the soliton at about +0.34 ps and a weak tail far later: the dispersive radiation shed at fission, near 700 nm, which a 1 ps window cannot hold. .. GENERATED FROM PYTHON SOURCE LINES 140-160 .. code-block:: Python wide, wide_record, _, _ = watched(8e-12) t, power = get_power(wide, plane=-1) total = power.sum() print(f"8 ps window: the collar removed {wide_record['removed_fraction']:.1e}") for edge in (0.25e-12, 0.5e-12, 1e-12): print( f"energy later than {1e12 * edge:.2f} ps: {power[t > edge].sum() / total:.1e}, " f"earlier than {-1e12 * edge:.2f} ps: {power[t < -edge].sum() / total:.1e}" ) fig, ax = plt.subplots(figsize=(7, 3.4), layout="constrained") ax.semilogy(1e12 * t, power / power.max(), lw=0.8, color="k") for edge, colour in ((0.25e-12, "C3"), (0.5e-12, "C1"), (1.5e-12, "C2")): ax.axvspan(-edge, edge, color=colour, alpha=0.08) ax.axvline(edge, color=colour, lw=0.8, label=f"{2e12 * edge:g} ps request") ax.set(xlabel="time (ps)", ylabel="power (normalised)", xlim=(-1, 3), ylim=(1e-12, 2)) ax.legend(fontsize=8) plt.show() .. image-sg:: /auto_tutorials/basics/images/sphx_glr_plot_07_time_window_002.png :alt: plot 07 time window :srcset: /auto_tutorials/basics/images/sphx_glr_plot_07_time_window_002.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none 8 ps window: the collar removed 8.7e-15 energy later than 0.25 ps: 9.1e-01, earlier than -0.25 ps: 3.1e-05 energy later than 0.50 ps: 2.8e-03, earlier than -0.50 ps: 5.3e-16 energy later than 1.00 ps: 2.5e-03, earlier than -1.00 ps: 1.1e-23 .. GENERATED FROM PYTHON SOURCE LINES 161-167 Cost against what is kept ------------------------- Widening the window costs transform points and time. The fibre loses energy to the Raman response itself, so what the window costs is read against the wide run's exit energy. The soliton is the same on every window that holds it; the radiation needs 3 ps. .. GENERATED FROM PYTHON SOURCE LINES 167-184 .. code-block:: Python def kept(run): """Return the exit energy over the launch energy.""" energy = np.asarray(run["stats/energy"]).reshape(len(run["z"]), -1).sum(axis=1) return energy[-1] / energy[0] reference = kept(wide) print(" window points wall (s) removed exit energy against 8 ps") for trange in (0.5e-12, 1e-12, 2e-12, 3e-12, 4e-12): run, record, wall, grid = watched(trange) print( f"{1e12 * trange:5.1f} ps {grid.n_t:6d} {wall:8.2f} " f"{record['removed_fraction']:.1e} {kept(run) / reference - 1:+.1e}" ) .. rst-class:: sphx-glr-script-out .. code-block:: none window points wall (s) removed exit energy against 8 ps 0.5 ps 384 0.12 8.4e-01 -8.7e-01 1.0 ps 768 0.16 2.5e-03 -2.6e-03 2.0 ps 1536 0.35 1.6e-03 -1.6e-03 3.0 ps 2048 0.36 1.5e-07 -1.6e-07 4.0 ps 3072 0.52 3.5e-12 -3.6e-12 .. GENERATED FROM PYTHON SOURCE LINES 185-193 Placing the launch ------------------ The soliton walks one way, to later times, so launching the pulse early moves it away from the late edge. On 0.75 ps, a launch 0.2 ps early brings the soliton's exit from near the edge back towards the middle. The guard and the collar barely notice: what they report is the radiation, which walks far further than the soliton and crosses the late guard whatever the launch. Placing the launch helps when the light you measure is the only light that walks; here only a wider window keeps it all. .. GENERATED FROM PYTHON SOURCE LINES 193-204 .. code-block:: Python for t0 in (0.0, -0.2e-12): run, record, _, _ = watched(0.75e-12, t0=t0) t, power = get_power(run, plane=-1) guard, removed = record["guard_high_max"], record["removed_fraction"] print( f"0.75 ps, launched at {1e12 * t0:+.2f} ps: soliton at " f"{1e12 * t[power.argmax()]:+.2f} ps, late guard {guard:.1e}, " f"collar removed {removed:.1e}" ) .. rst-class:: sphx-glr-script-out .. code-block:: none 0.75 ps, launched at +0.00 ps: soliton at +0.34 ps, late guard 2.6e-05, collar removed 3.4e-03 0.75 ps, launched at -0.20 ps: soliton at +0.14 ps, late guard 2.0e-05, collar removed 2.7e-03 .. GENERATED FROM PYTHON SOURCE LINES 205-210 **In short.** A monitor that stays quiet means no light reached the guards; a removed fraction near zero means none left through the collar either. When either is not, run the same case on a wider window, see what walks out, and decide whether it is part of what you measure. ``boundary_check`` relaxes or switches off the check, ``absorber=False`` the collar. .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 3.565 seconds) .. _sphx_glr_download_auto_tutorials_basics_plot_07_time_window.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_07_time_window.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_07_time_window.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_07_time_window.zip `