11import numpy as np
22from scipy .special import exp1
3- from scipy .optimize import bisect
3+ from scipy .optimize import bisect , newton
44
55from typing import Sequence , Tuple
66
@@ -62,7 +62,7 @@ def dispersion_integral_2d(tune_shift: np.ndarray, b_direct: float, b_cross: flo
6262def find_detuning_coeffs_threshold (tune_shift : complex , q_s : float , reference_b_direct : float , reference_b_cross : float ,
6363 added_b_direct : float = 0 , added_b_cross : float = 0 ,
6464 fraction_of_qs_allowed_on_positive_side : float = 0.05 ,
65- distribution : str = 'gaussian' , tolerance = 1e-10 ):
65+ distribution : str = 'gaussian' , algorithm : str = 'newton' , tolerance = 1e-10 ):
6666 """
6767 Compute the detuning coefficients (multiplied by sigma) corresponding to stability diagram threshold for a complex
6868 tune shift.
@@ -80,10 +80,11 @@ def find_detuning_coeffs_threshold(tune_shift: complex, q_s: float, reference_b_
8080 :param distribution: the transverse distribution of the beam. It can be 'gaussian' or 'parabolic'
8181 :param fraction_of_qs_allowed_on_positive_side: to determine azimuthal mode number l_mode (around which is drawn the
8282 stability diagram), one can consider positive tune shift up to this fraction of q_s (default=5%)
83- :param tolerance: tolerance on difference w.r.t stability diagram, for Newton's root finding
83+ :param algorithm: root-solving algorithm ('bisect' or 'newton')
84+ :param tolerance: tolerance on difference w.r.t stability diagram, for root finding
8485 and for the final check that the roots are actually proper roots.
8586 :return: the detuning coefficients corresponding to the stability diagram threshold if the corresponding mode is
86- unstable, 0 if the corresponding mode is stable or np.nan if the threshold cannot be found (failure of Newton's
87+ unstable, 0 if the corresponding mode is stable or np.nan if the threshold cannot be found (failure of root-solving
8788 algorithm).
8889 """
8990 # evaluate azimuthal mode number
@@ -107,13 +108,20 @@ def f(b_direct):
107108 return tune_shift .imag - np .interp (tune_shift .real , np .real (stab )[::int (np .sign (b_direct_i ))],
108109 np .imag (stab )[::int (np .sign (b_direct_i ))])
109110
110- # Newton root finding
111+ # root finding
111112 try :
112- # we use 1e-15 as a bound instead of 0 because 0 would cause a divsion by 0 in dispersion_integral_2d
113- if reference_b_direct > 0 :
114- b_direct_new = bisect (f , 1e-15 , 100 * reference_b_direct )
113+ if algorithm == 'bisect' :
114+ # we use 1e-15 as a bound instead of 0 because 0 would cause a divsion by 0 in dispersion_integral_2d
115+ if reference_b_direct > 0 :
116+ b_direct_new = bisect (f , 1e-15 , 100 * reference_b_direct )
117+ else :
118+ b_direct_new = bisect (f , 100 * reference_b_direct , - 1e-15 )
119+
120+ elif algorithm == 'newton' :
121+ b_direct_new = newton (f , reference_b_direct , tol = tolerance )
122+
115123 else :
116- b_direct_new = bisect ( f , 100 * reference_b_direct , - 1e-15 )
124+ raise ValueError ( "algorithm must be either 'newton' or 'bisect'" )
117125
118126 except RuntimeError :
119127 b_direct_new = np .nan
@@ -138,7 +146,7 @@ def find_detuning_coeffs_threshold_many_tune_shifts(tune_shifts: Sequence[comple
138146 added_b_direct : float = 0 , added_b_cross : float = 0 ,
139147 distribution : str = 'gaussian' ,
140148 fraction_of_qs_allowed_on_positive_side : float = 0.05 ,
141- tolerance = 1e-10 ):
149+ algorithm : str = 'newton' , tolerance = 1e-10 ):
142150 """
143151 Compute the detuning coefficients corresponding to the most stringent stability diagram threshold for a sequence of
144152 complex tune shifts. It keeps fixed the ratio between reference_b_direct and reference_b_cross.
@@ -155,17 +163,18 @@ def find_detuning_coeffs_threshold_many_tune_shifts(tune_shifts: Sequence[comple
155163 :param distribution: the transverse distribution of the beam. It can be 'gaussian' or 'parabolic'
156164 :param fraction_of_qs_allowed_on_positive_side: to determine azimuthal mode number l_mode (around which is drawn the
157165 stability diagram), one can consider positive tuneshift up to this fraction of q_s (default=5%)
158- :param tolerance: tolerance on difference w.r.t stability diagram, for Newton's root finding
166+ :param algorithm: root-solving algorithm ('bisect' or 'newton')
167+ :param tolerance: tolerance on difference w.r.t stability diagram, for root finding
159168 and for the final check that the roots are actually proper roots.
160169 :return: the detuning coefficients corresponding to the most stringent stability diagram threshold for all the
161170 given tune shifts if the corresponding mode is unstable, 0 if all modes are stable or np.nan if the
162- no threshold can be found (failure of Newton's algorithm).
171+ no threshold can be found (failure of root-solving algorithm).
163172 """
164173 # find max octupole current required from a list of modes, given their tuneshifts
165174 b_coefficients = np .array ([find_detuning_coeffs_threshold (
166175 tune_shift = tune_shift , q_s = q_s , reference_b_direct = reference_b_direct ,
167176 reference_b_cross = reference_b_cross , added_b_direct = added_b_direct , added_b_cross = added_b_cross , distribution = distribution ,
168177 fraction_of_qs_allowed_on_positive_side = fraction_of_qs_allowed_on_positive_side ,
169- tolerance = tolerance ) for tune_shift in tune_shifts if tune_shift is not np .nan ])
178+ algorithm = algorithm , tolerance = tolerance ) for tune_shift in tune_shifts if tune_shift is not np .nan ])
170179
171180 return max (b_coefficients , key = abs_first_item_or_nan )
0 commit comments