Skip to content

Commit f93d281

Browse files
committed
fixed some things
1 parent 61144f3 commit f93d281

File tree

3 files changed

+111
-30
lines changed

3 files changed

+111
-30
lines changed

pylhc/constants/kickgroups.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010

1111
# Columns ---
1212
KICKGROUP = "KICKGROUP"
13+
JSON_FILE = "JSON"
1314
SDDS = "SDDS"
15+
FILL = "FILL"
1416
TURNS = "TURNS"
1517
BUNCH = "BUNCH"
1618
UTCTIME = "UTC"
@@ -29,6 +31,6 @@
2931
BEAMPROCESS = "BEAMPROCESS"
3032
BEAM = "BEAM"
3133

32-
KICK_COLUMNS = [UTCTIME, LOCALTIME, TUNEX, TUNEY, DRIVEN_TUNEX, DRIVEN_TUNEY, DRIVEN_TUNEZ, AMPX, AMPY, AMPZ, TURNS, BUNCH, SDDS, BEAM, OPTICS, OPTICS_URI, BEAMPROCESS]
33-
COLUMNS_TO_HEADERS = [BEAM, BUNCH, TURNS, BEAMPROCESS, OPTICS, OPTICS_URI]
34+
KICK_COLUMNS = [UTCTIME, LOCALTIME, TUNEX, TUNEY, DRIVEN_TUNEX, DRIVEN_TUNEY, DRIVEN_TUNEZ, AMPX, AMPY, AMPZ, TURNS, BUNCH, SDDS, JSON_FILE, BEAM, FILL, OPTICS, OPTICS_URI, BEAMPROCESS]
35+
COLUMNS_TO_HEADERS = [BEAM, FILL, BUNCH, TURNS, BEAMPROCESS, OPTICS, OPTICS_URI]
3436
KICK_GROUP_COLUMNS = [UTCTIME, LOCALTIME, KICKGROUP, TIMESTAMP]

pylhc/data_extract/lsa.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,9 @@ def find_beamprocess_history(
159159
return fills
160160

161161
def get_trim_history(
162-
self, beamprocess: str, knobs: list,
163-
start_time: AccDatetime = None, end_time: AccDatetime = None,
164-
accelerator: str = "lhc"
162+
self, beamprocess: str, knobs: list,
163+
start_time: AccDatetime = None, end_time: AccDatetime = None,
164+
accelerator: str = "lhc"
165165
) -> dict:
166166
"""
167167
Get trim history for knobs between specified times.
@@ -187,10 +187,10 @@ def get_trim_history(
187187
raise ValueError("None of the given knobs exist!")
188188

189189
if start_time is not None:
190-
start_time = start_time.timestamp()
191-
190+
start_time = start_time.timestamp()
191+
192192
if end_time is not None:
193-
end_time = end_time.timestamp()
193+
end_time = end_time.timestamp()
194194

195195
LOG.debug(f"Getting trims for {len(knobs)} knobs.")
196196
try:
@@ -259,7 +259,7 @@ def get_knob_circuits(self, knob_name: str, optics: str) -> tfs.TfsDataFrame:
259259
"""
260260
Get a dataframe of the structure of the knob. Similar to online model extractor
261261
(KnobExtractor.getKnobHiercarchy)
262-
262+
263263
Args:
264264
knob_name: name of the knob.
265265
optics: name of the optics.

pylhc/kickgroups.py

Lines changed: 100 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@
6161

6262
from datetime import datetime
6363
from pathlib import Path
64-
from typing import List, Union
6564

6665
import numpy as np
6766
import pandas as pd
@@ -83,6 +82,8 @@
8382
DRIVEN_TUNEX,
8483
DRIVEN_TUNEY,
8584
DRIVEN_TUNEZ,
85+
FILL,
86+
JSON_FILE,
8687
KICK_COLUMNS,
8788
KICK_GROUP_COLUMNS,
8889
KICKGROUP,
@@ -105,7 +106,7 @@
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

Comments
 (0)