Source code for simba.Modules.Fields.gdf

from typing import List
import numpy as np
import easygdf
from warnings import warn
from .FieldParameter import FieldParameter
from ..units import UnitValue
from ..constants import speed_of_light


[docs] def write_gdf_field_file(self) -> str: """ Generate the field data in a format that is suitable for GPT, based on the :class:`~simba.Modules.Fields.field` object provided. This is then written to a GDF file. The `field_type` parameter determines the format of the file. A warning is raised if the field type is not supported (perhaps elevate to a `NotImplementedError`? Parameters ---------- self: :class:`~simba.Modules.Fields.field` The field object Returns ------- str: The name of the GDF field file. """ gdf_file = self._output_filename(extension=".gdf") blocks = None zdata = self.z.value.val if self.field_type == "LongitudinalWake": wzdata = self.Wz.value.val blocks = union( [ {"name": "z", "value": zdata}, {"name": "Wz", "value": wzdata}, ] ) elif self.field_type == "TransverseWake": wxdata = self.Wx.value.val wydata = self.Wy.value.val blocks = [ {"name": "z", "value": zdata}, {"name": "Wx", "value": wxdata}, {"name": "Wy", "value": wydata}, ] elif self.field_type == "3DWake": wxdata = self.Wx.value.val wydata = self.Wy.value.val wzdata = self.Wz.value.val blocks = [ {"name": "z", "value": zdata}, {"name": "Wx", "value": wxdata}, {"name": "Wy", "value": wydata}, {"name": "Wz", "value": wzdata}, ] elif self.field_type == "1DMagnetoStatic": bzdata = self.Bz.value.val blocks = union( [ {"name": "z", "value": zdata}, {"name": "Bz", "value": bzdata}, ] ) elif self.field_type == "3DMagnetoStatic": xdata = self.x.value.val ydata = self.y.value.val bxdata = self.Bx.value.val bydata = self.By.value.val bzdata = self.Bz.value.val blocks = [ {"name": "x", "value": xdata}, {"name": "y", "value": ydata}, {"name": "z", "value": zdata}, {"name": "Bx", "value": bxdata}, {"name": "By", "value": bydata}, {"name": "Bz", "value": bzdata}, ] elif self.field_type == "1DElectroDynamic": ezdata = self.Ez.value.val fielddata = np.array([zdata, ezdata]).transpose() if self.cavity_type == "TravellingWave": startpos = list(zdata).index(self.start_cell_z) # stoppos = list(zdata).index(self.end_cell_z) halfcell1 = 1.0 * fielddata[:startpos] halfcell2 = 1.0 * halfcell1[::-1] halfcell1[:, 1] /= max(halfcell1[:, 1]) halfcell2[:, 0] = halfcell2[:, 0][::-1] halfcell2[:, 1] /= max(halfcell2[:, 1]) halfcell1end = halfcell1[-1, 0] zstep = zdata[1] - zdata[0] lambdaRF = speed_of_light / self.frequency ncells = (self.n_cells - 1) * self.mode_numerator / self.mode_denominator nsteps = int(np.floor((ncells) * lambdaRF / zstep)) middleRF = np.array( [ [ (x * zstep + halfcell1end + zstep), np.cos((2 * np.pi / lambdaRF) * (x * zstep)), ] for x in range(0, nsteps + 1) ] ) halfcell2[:, 0] += middleRF[-1, 0] + zstep zdata, ezdata = np.concatenate([halfcell1, middleRF, halfcell2]).transpose() blocks = union( [ {"name": "z", "value": zdata}, {"name": "Ez", "value": ezdata}, ] ) else: warn(f"Field type {self.field_type} not supported for GPT") if blocks is not None: # print(blocks) # print(union(blocks)) easygdf.save(gdf_file, blocks) return gdf_file
[docs] def union(blocks: List) -> List: """ Update the field data into a format compatible for easyGDF Parameters ---------- blocks: List[Dict] The field parameters, keyed by name Returns ------- List: A list of easyGDF-compatible dictionaries """ names = [b["name"] for b in blocks] if "z" in names: zidx = names.index("z") _, indices = np.unique( np.round(blocks[zidx]["value"], decimals=6), return_index=True ) blocks = [{"name": b["name"], "value": b["value"][indices]} for b in blocks] return blocks return blocks
[docs] def read_gdf_field_file( self, filename: str, field_type: str, cavity_type: str | None = None, frequency: float | None = None, normalize_b: bool = True ): """ Read a GDF field file and convert it into a :class:`simba.Modules.Fields.field` object Parameters ---------- self: :class:`~simba.Modules.Fields.field` The field object to be updated. filename: str The path to the GDF field file field_type: str The name of the field, see :attr:`~simba.Modules.Fields.allowed_fields` cavity_type: str, optional The type of RF cavity, see :attr:`~simba.Modules.Fields.allowed_cavities` frequency: float, optional The frequency of the RF cavity. normalize_b: bool, optional Normalize Bx and By with respect to Bz (True by default) Returns ------- None Raises ------ ValueError: if the cavity `field_type` contains the string `Electro` and `cavity_type` is not provided ValueError: if the cavity `field_type` contains the string `Electro` and `frequency` is not provided NotImplementedError: if a given `field_type` is not implemented """ self.reset_dicts() setattr(self, "field_type", field_type) if "Electro" in field_type: if cavity_type is None: raise ValueError(f"cavity_type must be provided for {field_type}") else: setattr(self, "cavity_type", cavity_type) if frequency is None: raise ValueError(f"frequency must be provided for {field_type}") else: setattr(self, "frequency", frequency) fdat = easygdf.load(filename)["blocks"] try: zval = [k["value"] for k in fdat if k["name"].lower() == "z"][0] except Exception: zval = [k["value"] * speed_of_light for k in fdat if k["name"].lower() == "t"][ 0 ] if field_type == "1DMagnetoStatic": bzval = [k["value"] for k in fdat if k["name"].lower().capitalize() == "Bz"][0] setattr(self, "z", FieldParameter(name="z", value=UnitValue(zval, units="m"))) setattr( self, "Bz", FieldParameter( name="Bz", value=UnitValue(bzval / np.max(bzval), units="T") ), ) elif field_type == "3DMagnetoStatic": xval = [k["value"] for k in fdat if k["name"].lower() == "x"][0] yval = [k["value"] for k in fdat if k["name"].lower() == "y"][0] bxval = [k["value"] for k in fdat if k["name"].lower().capitalize() == "Bx"][0] byval = [k["value"] for k in fdat if k["name"].lower().capitalize() == "By"][0] bzval = [k["value"] for k in fdat if k["name"].lower().capitalize() == "Bz"][0] # Normalise by the maximum *on-axis* Bz field if normalize_b: normBz = max( [abs(Bz) for x, y, Bz in zip(xval, yval, bzval) if x == 0.0 and y == 0.0] ) else: normBz = 1 setattr(self, "x", FieldParameter(name="x", value=UnitValue(xval, units="m"))) setattr(self, "y", FieldParameter(name="y", value=UnitValue(yval, units="m"))) setattr(self, "z", FieldParameter(name="z", value=UnitValue(zval, units="m"))) setattr( self, "Bx", FieldParameter(name="Bx", value=UnitValue(bxval / normBz, units="T")), ) setattr( self, "By", FieldParameter(name="By", value=UnitValue(byval / normBz, units="T")), ) setattr( self, "Bz", FieldParameter(name="Bz", value=UnitValue(bzval / normBz, units="T")), ) elif field_type == "1DElectroDynamic": if cavity_type == "StandingWave": ezval = [ k["value"] for k in fdat if k["name"].lower().capitalize() == "Ez" ][0] setattr( self, "z", FieldParameter(name="z", value=UnitValue(zval, units="m")) ) setattr( self, "Ez", FieldParameter( name="Ez", value=UnitValue(ezval / np.max(ezval), units="V/m") ), ) elif cavity_type == "TravellingWave": raise NotImplementedError(f"{cavity_type} not implemented for GDF files") elif field_type == "LongitudinalWake": wzval = [k["value"] for k in fdat if k["name"].lower().capitalize() == "Wz"][0] setattr(self, "z", FieldParameter(name="z", value=UnitValue(zval, units="m"))) setattr( self, "Wz", FieldParameter(name="Wz", value=UnitValue(wzval, units="V/C")) ) elif field_type == "TransverseWake": wxval = [k["value"] for k in fdat if k["name"].lower().capitalize() == "Wx"][0] wyval = [k["value"] for k in fdat if k["name"].lower().capitalize() == "Wy"][0] setattr(self, "z", FieldParameter(name="z", value=UnitValue(zval, units="m"))) setattr( self, "Wx", FieldParameter(name="Wx", value=UnitValue(wxval, units="V/C/m")) ) setattr( self, "Wy", FieldParameter(name="Wy", value=UnitValue(wyval, units="V/C/m")) ) elif field_type == "3DWake": wxval = [k["value"] for k in fdat if k["name"].lower().capitalize() == "Wx"][0] wyval = [k["value"] for k in fdat if k["name"].lower().capitalize() == "Wy"][0] wzval = [k["value"] for k in fdat if k["name"].lower().capitalize() == "Wz"][0] setattr(self, "z", FieldParameter(name="z", value=UnitValue(zval, units="m"))) setattr( self, "Wx", FieldParameter(name="Wx", value=UnitValue(wxval, units="V/C/m")) ) setattr( self, "Wy", FieldParameter(name="Wy", value=UnitValue(wyval, units="V/C/m")) ) setattr( self, "Wz", FieldParameter(name="Wz", value=UnitValue(wzval, units="V/C")) ) else: raise NotImplementedError(f"{field_type} loading not implemented for GDF files")