55Functions to estimate coupling from twiss dataframes and different methods to calculate the closest tune
66approach from the calculated coupling RDTs.
77"""
8+
9+ from __future__ import annotations
10+
811import logging
912from contextlib import suppress
10- from typing import Sequence , Tuple
13+ from typing import TYPE_CHECKING
1114
1215import numpy as np
1316from pandas import DataFrame , Series
1417from tfs import TfsDataFrame
1518
16- from optics_functions .constants import (ALPHA , BETA , GAMMA , X , Y , TUNE , DELTA ,
17- MINIMUM , PI2 , PHASE_ADV , S , LENGTH ,
18- IMAG , REAL , F1010 , F1001 )
19+ from optics_functions .constants import (
20+ ALPHA ,
21+ BETA ,
22+ DELTA ,
23+ F1001 ,
24+ F1010 ,
25+ GAMMA ,
26+ IMAG ,
27+ LENGTH ,
28+ MINIMUM ,
29+ PHASE_ADV ,
30+ PI2 ,
31+ REAL ,
32+ TUNE ,
33+ S ,
34+ X ,
35+ Y ,
36+ )
1937from optics_functions .rdt import calculate_rdts
2038from optics_functions .utils import split_complex_columns , timeit
2139
40+ if TYPE_CHECKING :
41+ from collections .abc import Sequence
42+
2243COUPLING_RDTS = [F1001 , F1010 ]
2344LOG = logging .getLogger (__name__ )
2445
@@ -59,8 +80,11 @@ def coupling_via_rdts(df: TfsDataFrame, complex_columns: bool = True, **kwargs)
5980 return df_res
6081
6182
62- def coupling_via_cmatrix (df : DataFrame , complex_columns : bool = True ,
63- output : Sequence [str ] = ("rdts" , "gamma" , "cmatrix" )) -> DataFrame :
83+ def coupling_via_cmatrix (
84+ df : DataFrame ,
85+ complex_columns : bool = True ,
86+ output : Sequence [str ] = ("rdts" , "gamma" , "cmatrix" ),
87+ ) -> DataFrame :
6488 """Calculates C matrix then Coupling and Gamma from it.
6589 See [CalagaBetatronCoupling2005]_ .
6690
@@ -173,10 +197,14 @@ def rmatrix_from_coupling(df: DataFrame, complex_columns: bool = True) -> DataFr
173197
174198 # Eq. (15)
175199 if complex_columns :
176- abs_squared_diff = df [F1001 ].abs ()** 2 - df [F1010 ].abs ()** 2
200+ abs_squared_diff = df [F1001 ].abs () ** 2 - df [F1010 ].abs () ** 2
177201 else :
178- abs_squared_diff = (df [f"{ F1001 } { REAL } " ]** 2 + df [f"{ F1001 } { IMAG } " ]** 2 -
179- df [f"{ F1010 } { REAL } " ]** 2 - df [f"{ F1010 } { IMAG } " ]** 2 )
202+ abs_squared_diff = (
203+ df [f"{ F1001 } { REAL } " ] ** 2
204+ + df [f"{ F1001 } { IMAG } " ] ** 2
205+ - df [f"{ F1010 } { REAL } " ] ** 2
206+ - df [f"{ F1010 } { IMAG } " ] ** 2
207+ )
180208
181209 gamma = np .sqrt (1.0 / (1.0 + 4.0 * abs_squared_diff ))
182210
@@ -209,6 +237,7 @@ def rmatrix_from_coupling(df: DataFrame, complex_columns: bool = True) -> DataFr
209237
210238# Closest Tune Approach --------------------------------------------------------
211239
240+
212241def closest_tune_approach (
213242 df : TfsDataFrame , qx : float = None , qy : float = None , method : str = "teapot"
214243) -> TfsDataFrame :
@@ -249,7 +278,9 @@ def closest_tune_approach(
249278 of the mean of this column.
250279 """
251280 if F1001 not in df .columns :
252- raise KeyError (f"'{ F1001 } ' column not in dataframe. Needed to calculated closest tune approach." )
281+ raise KeyError (
282+ f"'{ F1001 } ' column not in dataframe. Needed to calculated closest tune approach."
283+ )
253284
254285 method_map = {
255286 "teapot" : _cta_teapot , # as named in [HoydalsvikEvaluationOfTheClosestTuneApproach2021]_
@@ -279,7 +310,7 @@ def closest_tune_approach(
279310
280311
281312def _cta_franchi (df : TfsDataFrame , qx_frac : float , qy_frac : float ) -> Series :
282- """ Closest tune approach calculated by Eq. (1) in [PerssonImprovedControlCoupling2014]_ . """
313+ """Closest tune approach calculated by Eq. (1) in [PerssonImprovedControlCoupling2014]_ ."""
283314 return 4 * (qx_frac - qy_frac ) * df [F1001 ].abs ()
284315
285316
@@ -294,21 +325,21 @@ def _cta_persson_alt(df: TfsDataFrame, qx_frac: float, qy_frac: float) -> Series
294325
295326
296327def _cta_persson (df : TfsDataFrame , qx_frac : float , qy_frac : float ) -> Series :
297- """ Closest tune approach calculated by Eq. (2) in [PerssonImprovedControlCoupling2014]_ . """
328+ """Closest tune approach calculated by Eq. (2) in [PerssonImprovedControlCoupling2014]_ ."""
298329 deltaq = qx_frac - qy_frac # fractional tune split
299330 location_term = np .exp (1j * PI2 * (deltaq * df [S ] / (df .headers [LENGTH ] / PI2 )))
300331 return _cta_persson_alt (df , qx_frac , qy_frac ) * location_term
301332
302333
303334def _cta_hoydalsvik (df : TfsDataFrame , qx_frac : float , qy_frac : float ) -> Series :
304- """ Closest tune approach calculated by Eq. (14) in
335+ """Closest tune approach calculated by Eq. (14) in
305336 [HoydalsvikEvaluationOfTheClosestTuneApproach2021]_ .
306337 This is like the persson estimate but divided by 1 + 4|F1001|^2 ."""
307338 return _cta_persson (df , qx_frac , qy_frac ) / (1 + 4 * df [F1001 ].abs () ** 2 )
308339
309340
310341def _cta_hoydalsvik_alt (df : TfsDataFrame , qx_frac : float , qy_frac : float ) -> Series :
311- """ Closest tune approach calculated by Eq. (14) without the s-term in
342+ """Closest tune approach calculated by Eq. (14) without the s-term in
312343 [HoydalsvikEvaluationOfTheClosestTuneApproach2021]_ .
313344 This is like the persson_alt estimate but divided by 1 + 4|F1001|^2 ."""
314345 return _cta_persson_alt (df , qx_frac , qy_frac ) / (1 + 4 * df [F1001 ].abs () ** 2 )
@@ -332,20 +363,19 @@ def _cta_teapot(df: TfsDataFrame, qx_frac: float, qy_frac: float) -> Series:
332363def _cta_teapot_franchi (df : TfsDataFrame , qx_frac : float , qy_frac : float ) -> Series :
333364 """Closest tune approach calculated by Eq. (12) in
334365 [HoydalsvikEvaluationOfTheClosestTuneApproach2021]_ .
335- This is the teapot approach with the Franchi approximation. """
366+ This is the teapot approach with the Franchi approximation."""
336367 return 4 * (qx_frac - qy_frac ) * df [F1001 ].abs () / (1 + 4 * df [F1001 ].abs () ** 2 )
337368
338369
339- def _get_weights_from_lengths (df : TfsDataFrame ) -> Tuple [float , np .array ]:
340- """Coefficients for the `persson` method. """
370+ def _get_weights_from_lengths (df : TfsDataFrame ) -> tuple [float , np .ndarray ]:
371+ """Coefficients for the `persson` method."""
341372 # approximate length of each element (ds in integral)
342373 s_periodic = np .zeros (len (df ) + 1 )
343374 s_periodic [1 :] = df [S ].to_numpy ()
344375 s_periodic [0 ] = df [S ].to_numpy ()[- 1 ] - df .headers [LENGTH ]
345376
346377 # weight ds/(2*pi*R) * N (as we take the mean afterwards)
347- weights = np .diff (s_periodic ) / df .headers [LENGTH ] * len (df )
348- return weights
378+ return np .diff (s_periodic ) / df .headers [LENGTH ] * len (df )
349379
350380
351381def check_resonance_relation (df : DataFrame , to_nan : bool = False ) -> DataFrame :
@@ -368,11 +398,15 @@ def check_resonance_relation(df: DataFrame, to_nan: bool = False) -> DataFrame:
368398 LOG .debug ("Sum-resonance not in df, skipping resonance relation check." )
369399 return df
370400
371- condition_not_fulfilled = df [F1001 ].abs () < df [F1010 ].abs () # comparison with NaN always yields False
401+ condition_not_fulfilled = (
402+ df [F1001 ].abs () < df [F1010 ].abs ()
403+ ) # comparison with NaN always yields False
372404 if any (condition_not_fulfilled ):
373- LOG .warning (f"In { sum (condition_not_fulfilled ) / len (df .index ) * 100 } % "
374- "of the data points |F1001| < |F1010|. Your closest tune "
375- "approach estimates might not be accurate." )
405+ LOG .warning (
406+ f"In { sum (condition_not_fulfilled ) / len (df .index ) * 100 } % "
407+ "of the data points |F1001| < |F1010|. Your closest tune "
408+ "approach estimates might not be accurate."
409+ )
376410 if to_nan :
377411 df .loc [condition_not_fulfilled , COUPLING_RDTS ] = np .nan
378412 return df
0 commit comments