Source code for mewarpx.utils_store.pulsing

"""File to hold utility functions associated with setting up and plotting time
varying voltage functions."""

from collections import namedtuple
from functools import partial

import numpy as np

from mewarpx.mwxrun import mwxrun
from pywarpx import callbacks

# namedtuple is a convenient way of making a class with given properties
PulseFunction = namedtuple(
    'PulseFunction',
    ['get_voltage', 'V_off', 'V_on', 'pulse_period', 'pulse_length',
     't_rise', 't_fall', 'wait_time']
)


[docs]def linear_pulse_function(V_off, V_on, pulse_period, pulse_length, t_rise=25e-9, t_fall=25e-9, wait_time=5e-9, plot=True): r"""Function to construct an expression for a voltage pulse that can be parsed by the WarpX parser. This version uses linear segments to build a pulse with form ____ ____ / \ / \ ____/ \___________________________/ \_____ Arguments: V_off (float): Electrode bias in the off-phase in Volt. V_on (float): Electrode voltage at the peak of the pulse in Volt. pulse_period (float): Full period from pulse start to the subsequent pulse start in seconds. pulse_length (float): Time duration of on-pulse phase in seconds. Does not include t_rise and t_fall. t_rise (float): Time for pulse to rise to its on value in seconds. Defaults to 25 ns. t_fall (float): Time for pulse to decrease to its off value in seconds. Defaults to 25 ns. wait_time (float): Time in seconds to offset the pulse function back to allow the simulation time to equilibrate before starting the pulse. Defaults to 5 ns. plot (bool): Optional parameter, if True the pulse waveform will be plotted. Returns: String expression of voltage function that can be parsed by the WarpX parser. """ modT = f"fmod((t-{wait_time}),{pulse_period})" p1 = f"({modT}>=0 and {modT}<{t_rise})" p2 = f"({modT}>={t_rise} and {modT}<{t_rise+pulse_length})" p3 = f"({modT}>={t_rise+pulse_length} and {modT}<{t_rise+pulse_length+t_fall})" p4 = f"(if({p1} or {p2} or {p3}, 0, 1))" dV = V_on-V_off expr = ( f"({p1}*({V_off}+{dV/t_rise}*{modT})+{p2}*{V_on}+" f"{p3}*({V_on}-{dV/t_fall}*({modT}-{t_rise + pulse_length}))+" f"{p4}*{V_off})" ) if plot: def get_voltage(times): voltages = np.zeros_like(times) for ii, t in enumerate(times): voltages[ii] = mwxrun.eval_expression_t(expr, t) return voltages pulse_function = PulseFunction( get_voltage=get_voltage, V_off=V_off, V_on=V_on, pulse_period=pulse_period, pulse_length=pulse_length, t_rise=t_rise, t_fall=t_fall, wait_time=wait_time ) f = partial(plot_pulse, pulse_function) f.__name__ = 'pulse_plot' callbacks.installafterinit(f) return expr
[docs]def plot_pulse(pulse_function, save_dir='diags/circuit'): """Utility function that plots the pulse shape applied to the device with annotations showing pulse parameters. Arguments: pulse_function (PulseFunction): Contains pulse details as well as a callable function `get_voltage` to get the pulse voltage at a given time. save_dir (str): Directory where the pulse schematic should be saved. """ import os import matplotlib as ml import matplotlib.pyplot as plt from mewarpx.utils_store import util util.mkdir_p(save_dir) fig = plt.figure(figsize=(8, 7)) font = {'size': 13} ml.rc('font', **font) fig.suptitle( 'Schematic of pulse shape with V$_{on}$ = %.1f V, ' 'V$_{off}$ = %.1f V\nPulse period = %s, ' 'Pulse length = %.0f ns' % ( pulse_function.V_on, pulse_function.V_off, (r"$\infty$" if np.isinf(pulse_function.pulse_period) else r"%.0f $\mu$s" % (pulse_function.pulse_period*1e6)), pulse_function.pulse_length*1e9 ) ) plt.subplot(2, 1, 1) if hasattr(pulse_function, 'wait_time'): wait_time = pulse_function.wait_time else: wait_time = 0.0 arrow_style = dict(arrowstyle="<->", connectionstyle="arc3") text_style = { 'color': 'black', 'fontsize': 11, 'ha': 'center', 'va': 'center' } text_box_style = dict(boxstyle="round", fc="white", ec="black", pad=0.2) # Create top plot showing multiple pulse periods t_grid = np.linspace( max(-pulse_function.pulse_period*1.8, -10e-6), min(pulse_function.pulse_period*2.2, 50e-6), 50000 ) + wait_time plt.plot(t_grid*1e6, pulse_function.get_voltage(t_grid)) # Add arrow showing pulse period plt.annotate( "", xy=(wait_time*1e6, pulse_function.V_on*0.8), xytext=( min((pulse_function.pulse_period + wait_time), t_grid[-1])*1e6, pulse_function.V_on*0.8 ), arrowprops=arrow_style ) # Add text annotation to pulse period arrow plt.text( (wait_time + 0.5*min(pulse_function.pulse_period, t_grid[-1]))*1e6, pulse_function.V_on*0.8, 'pulse period', text_style, bbox=text_box_style ) # Add arrow showing pulse voltage plt.annotate( "", xy=((t_grid[0] - t_grid[0]*0.5)*1e6, pulse_function.V_off), xytext=((t_grid[0] - t_grid[0]/2.0)*1e6, pulse_function.V_on), arrowprops=arrow_style ) # Add text annotation to pulse voltage arrow plt.text( (t_grid[0] - t_grid[0]/2.0)*1e6, (pulse_function.V_on + pulse_function.V_off)*0.5, 'V$_{pulse}$', text_style, bbox=text_box_style ) plt.ylabel('Applied voltage (V)') plt.xlabel(r'Time ($\mu s$)') plt.grid() plt.subplot(2, 1, 2) # Create bottom plot showing pulse details t_grid = np.linspace( -0.01e-6, pulse_function.t_rise + pulse_function.pulse_length + pulse_function.t_fall + 5e-9, 500 ) plt.plot(t_grid*1e9, pulse_function.get_voltage(t_grid + wait_time)) # Add arrow showing rise time plt.annotate( "", xy=(0.0, pulse_function.V_on*0.5), xytext=( pulse_function.t_rise*1e9, pulse_function.V_on*0.5 ), arrowprops=arrow_style ) # Add text annotation to rise time arrow plt.text( (pulse_function.t_rise/2.)*1e9, pulse_function.V_on*0.5, 't$_{rise}$', text_style, bbox=text_box_style ) # Add arrow showing fall time plt.annotate( "", xy=( 1e9*(pulse_function.t_rise+pulse_function.pulse_length), pulse_function.V_on * 0.5 ), xytext=( (pulse_function.t_rise + pulse_function.pulse_length + pulse_function.t_fall)*1e9, pulse_function.V_on * 0.5 ), arrowprops=arrow_style ) # Add text annotation to fall time arrow plt.text( (pulse_function.t_rise + pulse_function.pulse_length + pulse_function.t_fall/2.)*1e9, pulse_function.V_on*0.5, 't$_{fall}$', text_style, bbox=text_box_style ) # Add arrow showing pulse length plt.annotate( "", xy=( pulse_function.t_rise*1e9, pulse_function.V_on*0.8 ), xytext=( (pulse_function.t_rise+pulse_function.pulse_length)*1e9, pulse_function.V_on*0.8 ), arrowprops=arrow_style ) # Add text annotation to pulse length arrow plt.text( (pulse_function.t_rise + pulse_function.pulse_length/2.)*1e9, pulse_function.V_on*0.8, 'pulse length', text_style, bbox=text_box_style ) plt.ylabel('Applied voltage (V)') plt.xlabel('t - %.1e s (ns)' % wait_time) plt.grid() plt.subplots_adjust(top=0.91, hspace=0.22) plt.savefig(os.path.join(save_dir, 'pulse_schematic.png'), dpi=600) plt.close(fig)