Source code for lasy.utils.plotting

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1 import make_axes_locatable
from scipy.constants import c, epsilon_0

from .laser_utils import field_to_vector_potential, get_duration, get_w0


[docs] def show_laser( grid, dim, envelope_type="field", t_shift=0, show_lineout=True, show_max=False, omega0=None, udict={}, **kw, ): r""" Show a 2D image of the laser represented on the grid. Parameters ---------- grid : Grid The Grid object to be plotted dim : string Dimensionality of the array. Options are: - ``'xyt'``: The laser pulse is represented on a 3D grid: Cartesian (x,y) transversely, and temporal (t) longitudinally. - ``'rt'`` : The laser pulse is represented on a 2D grid: Cylindrical (r) transversely, and temporal (t) longitudinally. envelope_type : string, default: "field" Options are: - ``'field'``: Show the envelope of the laser field. - ``'intensity'``: Show the intensity of the laser field. - ``'vector_potential'``: Show the vector potential of the laser field. t_shift : float, default: 0 Shift the temporal axis by `t_shift` seconds. It also can be a string with `"left"`, `"right"` or `"center"`, to shift the temporal axis such that the t=0 lies at the left, right or center of the x-axis. show_lineout : bool, default: True Show the lineout of the laser field. show_max : bool, default: False Print the maximum intensity of the laser field. omega0 : scalar Angular frequency at which the envelope is defined. Needed if `envelope_type == "vector_potential"`. udict : dict, default: {} Dictionary with the information of the unit scales of the axes, e.g. ``{'t': {'value': 1e-15, 'label': 'fs'}, 'x': {'value': 1e-6, 'label': r'\mu m'}}`` Override the default unit scales. **kw : additional arguments to be passed to matplotlib's imshow command """ if "cmap" in kw.keys(): pass else: kw["cmap"] = "Reds" # Set default colormap if envelope_type == "intensity": F = epsilon_0 * c / 2 * np.abs(grid.get_temporal_field()) ** 2 / 1e4 cbar_label = r"I (W/cm$^2$)" elif envelope_type == "vector_potential": assert omega0 is not None, ( "omega0 must be provided if envelope_type == 'vector_potential'" ) F = np.abs(field_to_vector_potential(grid=grid, omega0=omega0)) cbar_label = r"$|a|$" elif envelope_type == "field": F = np.abs(grid.get_temporal_field()) cbar_label = r"$|E|$ (V/m)" else: raise ValueError( "Invalid value for envelope_type.\n" "It should be one of 'field', 'intensity' or 'vector_potential'.\n" ) # Set default unit scales for the axes units = { "t": {"value": 1e-15, "label": "fs"}, "x": {"value": 1e-6, "label": r"\mu m"}, } # Calculate spatial scales for the axes if grid.hi[0] > 1: # scale is meters units["x"]["value"] = 1 units["x"]["label"] = "m" elif grid.hi[0] > 1e-3: # scale is millimeters units["x"]["value"] = 1e-3 units["x"]["label"] = "mm" # Calculate temporal scales for the axes if grid.hi[-1] - grid.lo[-1] > 1e-9: # scale is nanoseconds units["t"]["value"] = 1e-9 units["t"]["label"] = "ns" elif grid.hi[-1] - grid.lo[-1] > 1e-12: # scale is picoseconds units["t"]["value"] = 1e-12 units["t"]["label"] = "ps" # Override default units for k in udict.keys(): units[k] = udict[k] # Shift the temporal axis if t_shift == "left": t_shift = grid.lo[-1] elif t_shift == "right": t_shift = grid.hi[-1] elif t_shift == "center": t_shift = 0.5 * (grid.hi[-1] + grid.lo[-1]) elif not isinstance(t_shift, (float, int)): raise ValueError( "Invalid value for t_shift.\n" "It should be one of 'left', 'right', 'center', or a number.\n" ) if dim == "rt": # Show field in the plane y=0, above and below axis, with proper sign for each mode F_plot = [ np.concatenate(((-1.0) ** m * F[m, ::-1], F[m])) for m in grid.azimuthal_modes ] F_plot = sum(F_plot) # Sum all the modes extent = [ (grid.lo[-1] - t_shift) / units["t"]["value"], (grid.hi[-1] - t_shift) / units["t"]["value"], -grid.hi[0] / units["x"]["value"], grid.hi[0] / units["x"]["value"], ] else: # In 3D show an image in the xt plane i_slice = int(F.shape[1] // 2) F_plot = F[:, i_slice, :] extent = [ (grid.lo[-1] - t_shift) / units["t"]["value"], (grid.hi[-1] - t_shift) / units["t"]["value"], grid.lo[0] / units["x"]["value"], grid.hi[0] / units["x"]["value"], ] fig, ax = plt.subplots() divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="3%", pad=0.075) im = ax.imshow(F_plot, extent=extent, aspect="auto", origin="lower", **kw) cb = fig.colorbar(im, cax=cax) cb.set_label(cbar_label) ax.set_xlabel(r"t " + r"($%s$)" % units["t"]["label"]) ax.set_ylabel(r"x " + r"($%s$)" % units["x"]["label"]) position = grid.position vpos = 0.98 ax.text( 0.025, vpos, r"Position = %.3f mm" % (position / 1e-3), transform=ax.transAxes, fontsize="x-small", ha="left", va="top", ) if t_shift != 0: vpos = vpos - 0.05 ax.text( 0.025, vpos, r"Time shift = %.1f fs" % (-t_shift / 1e-15), transform=ax.transAxes, fontsize="x-small", ha="left", va="top", ) if show_lineout: # Create projected lineouts along time and space temporal_lineout = np.sum(F_plot, axis=0) / np.sum(F_plot, axis=0).max() ax.plot( (grid.axes[-1] - t_shift) / units["t"]["value"], 0.15 * temporal_lineout * (extent[3] - extent[2]) + extent[2], c=(0.3, 0.3, 0.3), ) spatial_lineout = np.sum(F_plot, axis=1) / np.sum(F_plot, axis=1).max() ax.plot( 0.15 * spatial_lineout * (extent[1] - extent[0]) + extent[0], np.linspace(extent[2], extent[3], F_plot.shape[0]), c=(0.3, 0.3, 0.3), ) field_max = np.max(F_plot) vpos = 0.98 if envelope_type == "intensity": field_max_label = r"$I_{max}$ = %.2e $W/cm^2$" % (field_max) # Get the pulse duration tau = 2 * get_duration(grid, dim) / units["t"]["value"] ax.text( 0.975, vpos, r"Pulse duration = %.2f " % (tau) + r"$%s$" % units["t"]["label"], transform=ax.transAxes, fontsize="x-small", ha="right", va="top", ) vpos = vpos - 0.05 # Get the spot size w0 = get_w0(grid, dim) / units["x"]["value"] ax.text( 0.975, vpos, r"Spot size = %.2f " % (w0) + r"$%s$" % units["x"]["label"], transform=ax.transAxes, fontsize="x-small", ha="right", va="top", ) vpos = vpos - 0.05 elif envelope_type == "vector_potential": field_max_label = r"$|a_{max}|$ = %.3f" % (field_max) elif envelope_type == "field": field_max_label = r"$|E_{max}|$ = %.2e V/m" % (field_max) else: field_max_label = r"Max = %.2e" % (field_max) if show_max: ax.text( 0.975, vpos, field_max_label, transform=ax.transAxes, fontsize="x-small", ha="right", va="top", )