Source code for simba.Modules.Beams.astra

import sys
import os
import numpy as np
import csv
from .. import constants
from ..units import UnitValue


[docs] def read_csv_file(self, filename, delimiter=" "): with open(filename, "r") as f: data = np.array( [ line for line in csv.reader( f, delimiter=delimiter, quoting=csv.QUOTE_NONNUMERIC, skipinitialspace=True, ) ] ) return data
[docs] def write_csv_file(self, filename, data): if sys.version_info[0] > 2: with open(filename, "w", newline="") as f: writer = csv.writer( f, delimiter=" ", quoting=csv.QUOTE_NONNUMERIC, skipinitialspace=True ) [writer.writerow(line) for line in data] else: with open(filename, "wb") as f: writer = csv.writer( f, delimiter=" ", quoting=csv.QUOTE_NONNUMERIC, skipinitialspace=True ) [writer.writerow(line) for line in data]
[docs] def read_astra_beam_file(self, filename, normaliseZ=False, keepLost=False): self.reset_dicts() data = read_csv_file(self, filename) self.filename = filename interpret_astra_data(self, data, normaliseZ=normaliseZ, keepLost=keepLost)
[docs] def interpret_astra_data(self, data, normaliseZ=False, keepLost=False): if not keepLost: data = [d for d in data if d[-1] >= 0] x, y, z, cpx, cpy, cpz, clock, charge, index, status = np.transpose(data) zref = z[0] # s-position of the beam is the z-position of the reference-particle (!) self._beam.s = UnitValue(zref, units="m") self.code = "ASTRA" self.reference_particle = data[0] self.reference_particle_index = 0 self._beam.toffset = UnitValue(1e-9 * data[0][6], units="z") self.longitudinal_reference = "z" z = self.normalise_to_ref_particle(z, subtractmean=False) cpz = self.normalise_to_ref_particle(cpz, subtractmean=False) clock = self.normalise_to_ref_particle(clock, subtractmean=True) self._beam.px = UnitValue(cpx * self.q_over_c, units="kg*m/s") self._beam.py = UnitValue(cpy * self.q_over_c, units="kg*m/s") self._beam.pz = UnitValue(cpz * self.q_over_c, units="kg*m/s") self._beam.clock = UnitValue(1.0e-9 * clock, units="s") self._beam.charge = UnitValue(1.0e-9 * charge, units="C") self._beam.status = UnitValue(status) self._beam.z = UnitValue(z, units="m") self._beam.particle_mass = UnitValue( [self.mass_index[i] for i in index], units="kg" ) self._beam.particle_rest_energy = UnitValue( self._beam.particle_mass * constants.speed_of_light**2, units="J" ) self._beam.particle_rest_energy_eV = UnitValue( self._beam.particle_rest_energy / constants.elementary_charge, units="eV/c" ) self._beam.particle_charge = UnitValue( [constants.elementary_charge * self.charge_sign_index[i] for i in index], units="C", ) # print self.Bz self._beam.t = UnitValue( [ ( clock if status == -1 else ((z - zref) / (-1 * Bz * constants.speed_of_light)) ) for status, z, Bz, clock in zip( self._beam.status, z, self.Bz, self._beam.clock ) ], units="s", ) # self._beam['t'] = self.z / (1 * self.Bz * constants.speed_of_light)#[time if status is -1 else 0 for time, status in zip(clock, status)]# self._beam.x = UnitValue(x, units="m") # - self.xp * (self.t - np.mean(self.t)) self._beam.y = UnitValue(y, units="m") # - self.yp * (self.t - np.mean(self.t)) self._beam.total_charge = UnitValue(np.sum(1.0e-9 * charge), units="C") self._beam.nmacro = UnitValue( np.array(np.array(self._beam.charge) / self._beam.particle_charge) )
[docs] def read_csrtrack_beam_file(self, filename): self.reset_dicts() data = self.read_csv_file(filename) self.code = "CSRTrack" self.reference_particle = data[0] self.reference_particle_index = 0 self.longitudinal_reference = "z" z, x, y, cpz, cpx, cpy, charge = np.transpose(data[1:]) z = self.normalise_to_ref_particle(z, subtractmean=False) cpz = self.normalise_to_ref_particle(cpz, subtractmean=False) self._beam.x = UnitValue(x, units="m") self._beam.y = UnitValue(y, units="m") self._beam.z = UnitValue(z, units="m") self._beam.px = UnitValue(cpx * self.q_over_c, units="kg*m/s") self._beam.py = UnitValue(cpy * self.q_over_c, units="kg*m/s") self._beam.pz = UnitValue(cpz * self.q_over_c, units="kg*m/s") self._beam.clock = UnitValue(np.full(len(self.x), 0), units="s") self._beam.clock[0] = UnitValue(data[0, 0] * 1e-9, units="s") self._beam.status = UnitValue(np.full(len(self.x), 5)) self._beam.particle_mass = UnitValue([constants.m_e], units="kg") self._beam.particle_rest_energy = UnitValue( self._beam.particle_mass * constants.speed_of_light**2, units="J" ) self._beam.particle_rest_energy_eV = UnitValue( self._beam.particle_rest_energy / constants.elementary_charge, units="eV/c", ) self._beam.particle_charge = UnitValue([constants.elementary_charge], units="C") self._beam.t = UnitValue( self.z / (-1 * self.Bz * constants.speed_of_light), units="s" ) # [time if status is -1 else 0 for time, status in zip(clock, self._beam['status'])] self._beam.charge = UnitValue(charge, units="C") self._beam.total_charge = UnitValue(np.sum(self._beam.charge), units="C")
[docs] def read_pacey_beam_file(self, filename, charge=250e-12): self.reset_dicts() data = self.read_csv_file(filename, delimiter="\t") self.filename = filename self.code = "TPaceyASTRA" self.longitudinal_reference = "z" x, y, z, cpx, cpy, cpz = np.transpose(data) # cp = np.sqrt(cpx**2 + cpy**2 + cpz**2) self._beam.x = UnitValue(x, units="m") self._beam.y = UnitValue(y, units="m") self._beam.z = UnitValue(z, units="m") self._beam.px = UnitValue(cpx * self.q_over_c, units="kg*m/s") self._beam.py = UnitValue(cpy * self.q_over_c, units="kg*m/s") self._beam.pz = UnitValue(cpz * self.q_over_c, units="kg*m/s") self._beam.t = UnitValue( (self.z / (-1 * self.Bz * constants.speed_of_light)), units="s", ) # self._beam['t'] = self.z / (1 * self.Bz * constants.speed_of_light)#[time if status is -1 else 0 for time, status in zip(clock, status)]# self._beam.total_charge = UnitValue(charge, units="C") self._beam.charge = UnitValue(np.full(len(x), charge / len(x)), units="C")
[docs] def convert_csrtrackfile_to_astrafile(self, csrtrackfile, astrafile): data = read_csv_file(self, csrtrackfile) z, x, y, cpz, cpx, cpy, charge = np.transpose(data[1:]) charge = -charge * 1e9 clock0 = (data[0, 0] / constants.speed_of_light) * 1e9 clock = np.full(len(x), 0) clock[0] = clock0 index = np.full(len(x), 1) status = np.full(len(x), 5) array = np.array([x, y, z, cpx, cpy, cpz, clock, charge, index, status]).transpose() write_csv_file(self, astrafile, array)
[docs] def cdist(a, b): return np.sqrt(np.sum((a - b) ** 2, axis=1))
[docs] def find_nearest_vector(self, nodes, node): return cdist([node], nodes).argmin()
[docs] def rms(self, x, axis=None): return np.sqrt(np.mean(x**2, axis=axis))
[docs] def create_ref_particle(self, array, index=0, subtractmean=False): array[1:] = array[0] + array[1:] if subtractmean: array = array - np.mean(array) return array
[docs] def write_astra_beam_file( self, filename: str = None, index: int = None, status: int = 5, normaliseZ: bool = False, zoffset: float = 0.0 ): if filename is None: fn = os.path.splitext(self.filename) filename = fn[0].strip(".ocelot").strip(".openpmd") + ".astra" if not isinstance(index, (list, tuple, np.ndarray)): if len(self._beam.charge) == len(self._beam.x): chargevector = 1e9 * self._beam.charge else: chargevector = np.full( len(self._beam.x), 1e9 * self._beam.total_charge / len(self._beam.x) ) if index is not None: indexvector = np.full(len(self._beam.x), index) else: # print('write_astra_beam_file: index not found') indexvector = self._beam.particle_index # print('write_astra_beam_file: index =', indexvector) # exit() statusvector = ( self._beam.status if hasattr(self._beam, "status") and self._beam.status is not None else ( status if isinstance(status, (list, tuple, np.ndarray)) else np.full(len(self._beam.x), 5) ) ) # print("write_astra", self._beam.status, statusvector) """ if a particle is emitting from the cathode it's z value is 0 and it's clock value is finite, otherwise z is finite and clock is irrelevant (thus zero) """ if self.longitudinal_reference == "t": zvector = np.array([ 0 if status == -1 and t == 0 else z for status, z, t in zip(statusvector, self._beam.z, self._beam.t) ]) else: zvector = self._beam.z """ if the clock value is finite, we calculate it from the z value, using Betaz """ # clockvector = [1e9*z / (1 * Bz * constants.speed_of_light) if status == -1 and t == 0 else 1.0e9*t for status, z, t, Bz in zip(statusvector, self.z, self.t, self.Bz)] clockvector = 1.0e9 * self._beam.t """ this is the ASTRA array in all it's glory """ array = np.array( [ self._beam.x, self._beam.y, zvector, self._beam.cpx, self._beam.cpy, self._beam.cpz, clockvector, chargevector, indexvector, statusvector, ] ).transpose() if self.reference_particle is not None: ref_particle = self.reference_particle # print(f'ASTRA Write we have a reference particle! idx = {self.reference_particle_index} pz = {ref_particle[5]}') # # np.insert(array, 0, ref_particle, axis=0) else: """ Offset the z position to the nominal starting position """ array[:, 2] += zoffset """take the rms - if the rms is 0 set it to 1, so we don't get a divide by error""" rms_vector = [a if abs(a) > 0 else 1 for a in rms(self, array, axis=0)] """ normalise the array """ norm_array = array / rms_vector """ take the meen of the normalised array """ mean_vector = np.mean(norm_array, axis=0) """ find the index of the vector that is closest to the mean - if you read in an ASTRA file, this should actually return the reference particle! """ nearest_idx = find_nearest_vector(self, norm_array, mean_vector) ref_particle = array[nearest_idx] self.reference_particle = ref_particle self.reference_particle_index = nearest_idx """ set the closest mean vector to be in position 0 in the array """ array = np.roll(array, -1 * nearest_idx, axis=0) """ normalise Z to the reference particle """ array[1:, 2] = array[1:, 2] - ref_particle[2] """ should we leave Z as the reference value, set it to 0, or set it to be some offset? """ if normaliseZ is not False: array[0, 2] = 0 if isinstance(normaliseZ, (int, float)): # print('Setting z offset', normaliseZ) array[0, 2] += normaliseZ """ normalise pz and the clock """ # print('Mean pz = ', np.mean(array[:,5])) array[1:, 5] = array[1:, 5] - ref_particle[5] array[0, 6] = array[0, 6] + ref_particle[6] np.savetxt( filename, array, fmt=( "%.12e", "%.12e", "%.12e", "%.12e", "%.12e", "%.12e", "%.12e", "%.12e", "%d", "%d", ), )