6161
6262from datetime import datetime
6363from pathlib import Path
64- from typing import List , Union
6564
6665import numpy as np
6766import pandas as pd
8382 DRIVEN_TUNEX ,
8483 DRIVEN_TUNEY ,
8584 DRIVEN_TUNEZ ,
85+ FILL ,
86+ JSON_FILE ,
8687 KICK_COLUMNS ,
8788 KICK_GROUP_COLUMNS ,
8889 KICKGROUP ,
105106# List Kickgroups --------------------------------------------------------------
106107
107108
108- def list_available_kickgroups (by : str = TIMESTAMP , root : Union [ Path , str ] = KICKGROUPS_ROOT , printout : bool = True ) -> DataFrame :
109+ def list_available_kickgroups (by : str = TIMESTAMP , root : Path | str = KICKGROUPS_ROOT , printout : bool = True ) -> DataFrame :
109110 """
110111 List all available KickGroups in `root` with optional sorting..
111112
@@ -139,11 +140,11 @@ def list_available_kickgroups(by: str = TIMESTAMP, root: Union[Path, str] = KICK
139140 return df_info
140141
141142
142- def get_folder_json_files (root : Union [ Path , str ] = KICKGROUPS_ROOT ) -> List [Path ]:
143+ def get_folder_json_files (root : Path | str = KICKGROUPS_ROOT ) -> list [Path ]:
143144 """Returns a list of all **.json** files in the folder.
144145
145146 Args:
146- root (Union[ Path, str] )): the path to the folder. (Defaults
147+ root (Path | str)): the path to the folder. (Defaults
147148 to the ``NFS`` path of our kickgroups).
148149
149150 Returns:
@@ -157,7 +158,7 @@ def get_folder_json_files(root: Union[Path, str] = KICKGROUPS_ROOT) -> List[Path
157158# Kickgroup Info ---------------------------------------------------------------
158159
159160
160- def get_kickgroup_info (kick_group : str , root : Union [ Path , str ] = KICKGROUPS_ROOT ) -> TfsDataFrame :
161+ def get_kickgroup_info (kick_group : str , root : Path | str = KICKGROUPS_ROOT ) -> TfsDataFrame :
161162 """
162163 Gather all important info about the KickGroup into a `~tfs.TfsDataFrame`.
163164
@@ -176,7 +177,7 @@ def get_kickgroup_info(kick_group: str, root: Union[Path, str] = KICKGROUPS_ROOT
176177 df_info = TfsDataFrame (index = range (len (kicks_files )), columns = KICK_COLUMNS , headers = {KICKGROUP : kick_group })
177178
178179 if not len (kicks_files ):
179- raise FileNotFoundError (f"KickGroup { kick_group } contains no kicks." )
180+ raise ValueError (f"KickGroup { kick_group } contains no kicks." )
180181
181182 for idx , kf in enumerate (kicks_files ):
182183 df_info .loc [idx , :] = load_kickfile (kf )
@@ -187,24 +188,34 @@ def get_kickgroup_info(kick_group: str, root: Union[Path, str] = KICKGROUPS_ROOT
187188 return df_info
188189
189190
190- def load_kickfile (kickfile : Union [ Path , str ] ) -> pd .Series :
191+ def load_kickfile (kickfile : Path | str ) -> pd .Series :
191192 """
192193 Load the important data from a **json** kickfile into a `~pandas.Series`.
193194
194195 Args:
195- kickfile (Union[ Path, str] ): the path to the kickfile to load data from.
196+ kickfile (Path | str): the path to the kickfile to load data from.
196197
197198 Returns:
198199 A `~pandas.Series` with the relevant information loaded from the provided
199200 *kickfile*. The various entries in the Series are defined in `pylhc.constants.kickgroups`
200201 as ``KICK_COLUMNS``.
201202 """
202203 LOG .debug (f"Loading kick information from Kickfile at '{ Path (kickfile ).absolute ()} '" )
204+ kickfile = _find_existing_file_path (kickfile )
203205 kick = _load_json (kickfile )
206+
204207 data = pd .Series (index = KICK_COLUMNS , dtype = object )
208+ data [JSON_FILE ] = _find_existing_file_path (kickfile )
205209 data [LOCALTIME ] = _jsontime_to_datetime (kick ["acquisitionTime" ])
206210 data [UTCTIME ] = _local_to_utc (data [LOCALTIME ])
207- data [SDDS ] = kick ["sddsFile" ]
211+
212+ try :
213+ data [SDDS ] = _find_existing_file_path (kick ["sddsFile" ])
214+ except FileNotFoundError as e :
215+ LOG .warning (str (e ))
216+ data [SDDS ] = None
217+
218+ data [FILL ] = _get_fill_from_path (kick ["sddsFile" ]) # TODO: Ask OP to include in json?
208219 data [BEAM ] = kick ["measurementEnvironment" ]["lhcBeam" ]["beamName" ]
209220 data [BEAMPROCESS ] = kick ["measurementEnvironment" ]["environmentContext" ]["name" ]
210221 data [TURNS ] = kick ["acqSettings" ]["capturedTurns" ]
@@ -229,27 +240,85 @@ def load_kickfile(kickfile: Union[Path, str]) -> pd.Series:
229240 data [AMPZ ] = kick ["excitationSettings" ][0 ]["longitudinalRfSettings" ]["excitationAmplitude" ]
230241 else :
231242 LOG .debug ("Kick is 2D Excitation, longitudinal settings will be set as NaNs" )
232- idx = _get_plane_index (kick ["excitationSettings" ], "X" )
233- idy = _get_plane_index (kick ["excitationSettings" ], "Y" )
243+ entry_map = {"X" : (TUNEX , DRIVEN_TUNEX , AMPX ), "Y" : (TUNEY , DRIVEN_TUNEY , AMPY )}
244+ for plane in ["X" , "Y" ]:
245+ tune , driven_tune , amp = entry_map [plane ]
246+
247+ data [tune ] = np .NaN
248+ data [driven_tune ] = np .NaN
249+ data [amp ] = np .NaN
250+
251+ try :
252+ idx = _get_plane_index (kick ["excitationSettings" ], plane )
253+ except ValueError as e :
254+ LOG .warning (f"{ str (e )} in { kickfile } " )
255+ continue
256+
257+ if "measuredTune" not in kick ["excitationSettings" ][idx ]: # Happens in very early files in 2022
258+ LOG .warning (f"No measured tune { plane } in the kick file: { kickfile } " )
259+ continue
260+
261+ data [tune ] = kick ["excitationSettings" ][idx ]["measuredTune" ]
262+ data [driven_tune ] = data [tune ] + _get_delta_tune (kick , idx )
263+ data [amp ] = kick ["excitationSettings" ][idx ]["amplitude" ]
234264
235- data [TUNEX ] = kick ["excitationSettings" ][idx ]["measuredTune" ]
236- data [TUNEY ] = kick ["excitationSettings" ][idy ]["measuredTune" ]
237- data [DRIVEN_TUNEX ] = data [TUNEX ] + kick ["excitationSettings" ][idx ]["deltaTuneStart" ]
238- data [DRIVEN_TUNEY ] = data [TUNEY ] + kick ["excitationSettings" ][idy ]["deltaTuneStart" ]
239265 data [DRIVEN_TUNEZ ] = np .NaN
240- data [AMPX ] = kick ["excitationSettings" ][idx ]["amplitude" ]
241- data [AMPY ] = kick ["excitationSettings" ][idy ]["amplitude" ]
242266 data [AMPZ ] = np .NaN
243267
244268 return data
245269
270+ def _get_delta_tune (kick : dict , idx_plane : int ) -> float :
271+ """ Return the delta from the tune for the kicks.
272+ For some reason, there are multiple different keys where this can be stored. """
273+
274+ # Default key for ACDipole ---
275+ # There is also "deltaTuneEnd", but we usually don't change the delta during kick
276+ try :
277+ return kick ["excitationSettings" ][idx_plane ]["deltaTuneStart" ]
278+ except KeyError :
279+ pass
280+
281+ # Key for ADTACDipole ---
282+ try :
283+ return kick ["excitationSettings" ][idx_plane ]["deltaTune" ]
284+ except KeyError :
285+ pass
286+
287+ # Another key for ADTACDipole (unclear to me why) ---
288+ try :
289+ return kick ["excitationSettings" ][idx_plane ]["deltaTuneOffset" ]
290+ except KeyError :
291+ pass
292+
293+ raise KeyError (f"Could not find delta tune for plane-entry { idx_plane } " )
294+
295+
296+ def _find_existing_file_path (path : str | Path ) -> Path :
297+ """ Find the existing kick file for the kick group. """
298+ path = Path (path )
299+ if path .is_file ():
300+ return path
301+
302+ fill_data = "FILL_DATA"
303+ all_fill_data = "ALL_FILL_DATA"
304+
305+ if fill_data in path .parts :
306+ # Fills are moved at the end of year
307+ idx = path .parts .index (fill_data )+ 1
308+ new_path = Path (* path .parts [:idx ], all_fill_data , * path .parts [idx :])
309+ if new_path .exists ():
310+ return new_path
311+
312+ raise FileNotFoundError (f"Could not find kick file at { path } " )
313+
314+
246315
247316# Functions with console output ---
248317
249318# Full Info -
250319
251320
252- def show_kickgroup_info (kick_group : str , root : Union [ Path , str ] = KICKGROUPS_ROOT ) -> None :
321+ def show_kickgroup_info (kick_group : str , root : Path | str = KICKGROUPS_ROOT ) -> None :
253322 """
254323 Wrapper around `~pylhc.kickgroups.get_kickgroup_info`, gathering the relevant
255324 information from the kick files in the group and printing it to console.
@@ -284,7 +353,7 @@ def _print_kickgroup_info(kicks_info: TfsDataFrame) -> None:
284353# Files only -
285354
286355
287- def show_kickgroup_files (kick_group : str , nfiles : int = None , root : Union [ Path , str ] = KICKGROUPS_ROOT ) -> None :
356+ def show_kickgroup_files (kick_group : str , nfiles : int = None , root : Path | str = KICKGROUPS_ROOT ) -> None :
288357 """
289358 Wrapper around `pylhc.kickgroups.get_kickgroup_info`, gathering the relevant
290359 information from all kickfiles in the KickGroup and printing only the sdds-filepaths
@@ -337,7 +406,7 @@ def _print_kickgroup_files(kicks_info: TfsDataFrame, nfiles: int = None) -> None
337406# IO ---
338407
339408
340- def _load_json (jsonfile : Union [ Path , str ] ) -> dict :
409+ def _load_json (jsonfile : Path | str ) -> dict :
341410 return json .loads (Path (jsonfile ).read_text ())
342411
343412
@@ -371,7 +440,7 @@ def _local_to_utc(dt: datetime):
371440# Other ---
372441
373442
374- def _get_plane_index (data : List [dict ], plane : str ) -> str :
443+ def _get_plane_index (data : list [dict ], plane : str ) -> str :
375444 """
376445 Find the index for the given plane in the data list.
377446 This is necessary as they are not always in X,Y order.
@@ -384,6 +453,16 @@ def _get_plane_index(data: List[dict], plane: str) -> str:
384453 raise ValueError (f"Plane '{ plane } ' not found in data." )
385454
386455
456+ def _get_fill_from_path (sdds_path : str | Path ) -> str :
457+ """ Get the fill number from the path to the sdds file.
458+ Note: Not sure why the fill is not saved automatically into the .json file.
459+ Maybe we should ask OP to include this.
460+ """
461+ parts = Path (sdds_path ).parts
462+ idx_parent = parts .index ("FILL_DATA" )
463+ return int (parts [idx_parent + 1 ])
464+
465+
387466# Script Mode ------------------------------------------------------------------
388467
389468
0 commit comments