The SASE Source Module

XFEL pulses seldom come alone - in the following section, we demonstrate how the ‘phenom.source’ module can be used to generate large ensembles of XFEL pulses.

To do so, we begin by importing the SASE source module

[1]:
from phenom.source import SASE_Source

Store a Pulse

The SASE source class provides a mechanism of generating multiple pulses and storing them to individual .h5 files. In the simplest case, it takes as its inputs identical parameters to the sase_source function defined in the previous section.

[2]:
import numpy as np

## define the spatial/temporal mesh
x = y = np.linspace(-250e-06, 250e-06, 500)
t = np.linspace(-25e-15, 25e-15, 250)

## define the pulse
src = SASE_Source(x = x,
                  y = y,
                  t = t,
                  photon_energy = 10e3,
                  pulse_energy = 1e-03,
                  pulse_duration = 15e-15,
                  bandwidth = 1e-12,
                  sigma = 50e-06,
                  div = 2.5e-03,
                  x0 = 0.0,
                  y0 = 0.0,
                  t0 = 0.0,
                  theta_x = 0.0,
                  theta_y = 0.0,
                  domain = 'time'
                  )

which we can within a .h5 file:

[3]:
import os

save_loc = "./sase_field.h5"
src.generate_pulses(save_loc)

Load a Pulse from Memory

The SASE_Source module stores pulses within a master .h5 file

[4]:
import h5py as h5


with h5.File(save_loc, mode = 'r') as hf:

    print("Pulses in the master file:")
    print((hf.keys()), "\n")

    print("Contents of each pulse:")
    print(hf['pulse000'].keys(), "\n")

    print("Pulse Mesh Fields:")
    print(hf['pulse000']['mesh'].keys(), "\n")

    print("Pulse Parameters:")
    print(hf['pulse000']['params'].keys(), "\n")

    print("Pulse Data:")
    print("Type: ", hf['pulse000']['data'][()].dtype)
    print("Shape: ", hf['pulse000']['data'][()].shape)
Pulses in the master file:
<KeysViewHDF5 ['pulse000']>

Contents of each pulse:
<KeysViewHDF5 ['data', 'mesh', 'params']>

Pulse Mesh Fields:
<KeysViewHDF5 ['t', 'x', 'y']>

Pulse Parameters:
<KeysViewHDF5 ['bandwidth', 'div', 'file', 'photon_energy', 'pulse_duration', 'pulse_energy', 'sigma', 't0', 'theta_x', 'theta_y', 'x0', 'y0']>

Pulse Data:
Type:  complex128
Shape:  (500, 500, 250)

To complete this example, lets load the electric field and mesh of the first pulse, and plot its intensity as demonstrated eariler

[5]:
with h5.File(save_loc, mode = 'r') as hf:

    x = sase_pulse = hf['pulse000']['mesh']['x'][()]
    y = sase_pulse = hf['pulse000']['mesh']['y'][()]
    t = sase_pulse = hf['pulse000']['mesh']['t'][()]

    sase_pulse = hf['pulse000']['data'][()]
[6]:
from matplotlib import pyplot as plt

fig, [ax1, ax2] = plt.subplots(1,2, figsize = (9,3))

### spatial intensity
im = ax1.imshow((abs(sase_pulse)**2).sum(axis = 2),
                extent = [x.min() * 1e06,
                          x.max() * 1e06,
                          y.min() * 1e06,
                          y.max() * 1e06],
                cmap = 'bone'
                )

plt.colorbar(im, label = "Intensity (W/mm$^2$)", ax = ax1)

ax1.set_xlabel(r'x ($\mu$m)')
ax1.set_ylabel(r'y ($\mu$m)')


ax2.plot(t*1e15, (abs(sase_pulse)**2).sum(axis = (0,1)))
ax2.set_xlabel("Time (fs)")
[6]:
Text(0.5, 0, 'Time (fs)')
../_images/notebooks_sase_model_pt2_12_1.png

Generating Multiple Pulses

The advantadge that the SASE pulse class brings over the sase_pulse function is the capacity to generate ensembles of pulses in a pythonised manner. Each of the non-mesh variables of the SASE pulse class can take the form of a list, numpy.ndarray or python ‘lambda’.

The source module will generate and save N pulses to the SASE master .h5, where N is the length of the longest non-integer input of the function.

Let’s consider a few examples

Pulses from Lists

Suppose we want to consider a set of 5 pulses in the energy range 1-5 keV. If these pulses have identical properties, other than photon energy, we can generate them as follows:

[7]:
energies = [1000,2000,3000,4000,5000] ### list of energies in eV

## using the properties defined above
src = SASE_Source(x = x,
                  y = y,
                  t = t,
                  photon_energy = energies,
                  pulse_energy = 1e-04,
                  pulse_duration = 15e-15,
                  bandwidth = 1e-12,
                  sigma = 50e-06,
                  div = 2.5e-03,
                  x0 = 0.0,
                  y0 = 0.0,
                  t0 = 0.0,
                  theta_x = 0.0,
                  theta_y = 0.0
                  )

## storing these avefronts in the following location
save_loc = "./sase_field.h5"
src.generate_pulses(save_loc)

The .h5 master now contains 5 SASE pulses. We can check their paramters

[8]:
import h5py as h5


with h5.File(save_loc, mode = 'r') as hf:

    print("Pulses in the master file:")
    print((hf.keys()), "\n")

    for key in hf.keys():

        print("Pulse Parameters:")
        print(hf[key]['params']['photon_energy'][()],"eV", "\n")

Pulses in the master file:
<KeysViewHDF5 ['pulse000', 'pulse001', 'pulse002', 'pulse003', 'pulse004']>

Pulse Parameters:
1000 eV

Pulse Parameters:
2000 eV

Pulse Parameters:
3000 eV

Pulse Parameters:
4000 eV

Pulse Parameters:
5000 eV

We can compare their spectrums:

[9]:
fig, ax = plt.subplots(figsize = (6,2))

with h5.File(save_loc, mode = 'r') as hf:

    for key in hf.keys():
        sase_pulse = hf[key]['data'][()]
        t = hf[key]['mesh']['t'][()]
        photon_energy = hf[key]['params']['photon_energy'][()]

        ax.plot(t*1e15, (abs(sase_pulse)**2).sum(axis = (0,1)),
               label = '{:.2e} keV'.format(photon_energy*1e3))

    ax.legend()

    ax.set_ylabel("Intensity (W/mm$^2$)")
    ax.set_xlabel("Time (fs)")
../_images/notebooks_sase_model_pt2_19_0.png

Pulses from Arrays

Let’s now consider the case of a pulse with a fluctuating center-of-mass in the transverse-plane. If this process is determininstic, we can define a set of input parameters from an arbitrary function.

[10]:
x0 = np.linspace(-100e-06,100e-06,5)
y0 = np.linspace(-100e-06,100e-06,5)

Using these values as the input to the SASE_Source function yields:

[11]:
## using the properties defined above
src = SASE_Source(x = x,
                  y = y,
                  t = t,
                  photon_energy = 10e03,
                  pulse_energy = 1e-04,
                  pulse_duration = 15e-15,
                  bandwidth = 1e-12,
                  sigma = 50e-06,
                  div = 2.5e-03,
                  x0 = x0, ### provide coordinates here
                  y0 = y0, ### and here
                  t0 = 0.0,
                  theta_x = 0.0,
                  theta_y = 0.0
                  )

## storing these avefronts in the following location
save_loc = "./sase_field.h5"
src.generate_pulses(save_loc)

which we can check from the stored pulse intensites:

[12]:
fig, ax = plt.subplots(1,5, figsize = (10,2))
ax = ax.flatten()

with h5.File(save_loc, mode = 'r') as hf:

    for itr, key in enumerate(hf.keys()):

        ax[itr].set_xticks([])
        ax[itr].set_yticks([])

        sase_pulse = hf[key]['data'][()]
        ax[itr].imshow((abs(sase_pulse)**2).sum(axis = 2),
                        cmap = 'bone'
                    )

        ax[itr].set_title(key)
../_images/notebooks_sase_model_pt2_25_0.png

Note that we can combine this method with the previous changes in photon energy if the length of the inputs are equivalent

[13]:
energies = [1000,2000,3000,4000,5000] ### list of energies in eV

## using the properties defined above
src = SASE_Source(x = x,
                  y = y,
                  t = t,
                  photon_energy = energies,
                  pulse_energy = 1e-04,
                  pulse_duration = 15e-15,
                  bandwidth = 1e-12,
                  sigma = 50e-06,
                  div = 2.5e-03,
                  x0 = x0, ### provide coordinates here
                  y0 = y0, ### and here
                  t0 = 0.0,
                  theta_x = 0.0,
                  theta_y = 0.0
                  )

## storing these avefronts in the following location
save_loc = "./sase_field.h5"
src.generate_pulses(save_loc)
[14]:
fig, ax = plt.subplots(1,5, figsize = (10,2))
ax = ax.flatten()

with h5.File(save_loc, mode = 'r') as hf:

    for itr, key in enumerate(hf.keys()):

        ax[itr].set_xticks([])
        ax[itr].set_yticks([])

        sase_pulse = hf[key]['data'][()]
        ax[itr].imshow((abs(sase_pulse)**2).sum(axis = 2),
                        cmap = 'bone'
                    )
        photon_energy = hf[key]['params']['photon_energy'][()]
        ax[itr].set_title("Photon Energy:\n{:.2e} eV".format(photon_energy*1e3))
../_images/notebooks_sase_model_pt2_28_0.png

Pulses from Lambdas

The SASE pulse model is inherently stochastic due to the partially random SASE spectral distribution. Using python lambdas, we can create a source that with properties that fluctuate in time.

[15]:
energies = [1000,2000,3000,4000,5000] ### list of energies in eV

## using the properties defined above
src = SASE_Source(x = x,
                  y = y,
                  t = t,
                  photon_energy = energies,
                  pulse_energy = 1e-04,
                  pulse_duration = 15e-15,
                  bandwidth = 1e-12,
                  sigma = lambda: np.random.rand()*250e-06,
                  div = 2.5e-03,
                  x0 = x0, ### provide coordinates here
                  y0 = y0, ### and here
                  t0 = 0.0,
                  theta_x = 0.0,
                  theta_y = 0.0
                  )

## storing these avefronts in the following location
save_loc = "./sase_field.h5"
src.generate_pulses(save_loc)
[16]:
fig, ax = plt.subplots(1,5, figsize = (10,2))
ax = ax.flatten()

with h5.File(save_loc, mode = 'r') as hf:

    for itr, key in enumerate(hf.keys()):

        ax[itr].set_xticks([])
        ax[itr].set_yticks([])

        sase_pulse = hf[key]['data'][()]
        ax[itr].imshow((abs(sase_pulse)**2).sum(axis = 2),
                        cmap = 'bone'
                    )
        photon_energy = hf[key]['params']['photon_energy'][()]
        ax[itr].set_title("Photon Energy:\n{:.2e} eV".format(photon_energy*1e3))
../_images/notebooks_sase_model_pt2_31_0.png

In the following example - we demonstrate how these properties can be used to make a custom represenation of your SASE source