@@ -111,6 +111,16 @@ def __call__(self, ring, data):
111
111
return data if index is None else data [self .index ]
112
112
113
113
114
+ class _MuAccess (_RecordAccess ):
115
+ """Access to selected items in a record array"""
116
+
117
+ def __init__ (self , index ):
118
+ super ().__init__ ("mu" , index )
119
+
120
+ def __call__ (self , ring , data ):
121
+ return super ().__call__ (ring , data ) % (2.0 * np .pi )
122
+
123
+
114
124
def _all_rows (index : Optional [RefIndex ]):
115
125
"""Prepends "all rows" (":") to an index tuple"""
116
126
if index is None :
@@ -173,12 +183,6 @@ class Need(Enum):
173
183
#: Specify geometry computation and provide the full data at evaluation
174
184
#: points
175
185
GEOMETRY = 9
176
- #: Specify whether the modulo of the phase has to be used, necessary when
177
- #: matching the fractional part only
178
- MODULO = 10
179
- #: Specify whether the phase is expressed in units of tune units, in
180
- #: case phase = phase/(2*pi)
181
- TUNEUNIT = 11
182
186
183
187
184
188
class Observable (object ):
@@ -486,9 +490,12 @@ def _setup(self, ring: Lattice):
486
490
487
491
488
492
class GeometryObservable (_ElementObservable ):
489
- """Observe the geometrical parameters of the reference trajectory"""
493
+ """Observe the geometrical parameters of the reference trajectory.
494
+
495
+ Process the result of calling :py:func:`.get_geometry`.
496
+ """
490
497
491
- field_list = {"x" , "y" , "angle" }
498
+ _field_list = {"x" , "y" , "angle" }
492
499
493
500
def __init__ (
494
501
self , refpts : Refpts , param : str , name : Optional [str ] = None , ** kwargs
@@ -522,16 +529,19 @@ def __init__(
522
529
523
530
Observe x coordinate of monitors
524
531
"""
525
- if param not in self .field_list :
526
- raise ValueError (f"Expected { param !r} to be one of { self .field_list !r} " )
532
+ if param not in self ._field_list :
533
+ raise ValueError (f"Expected { param !r} to be one of { self ._field_list !r} " )
527
534
name = self ._set_name (name , "geometry" , param )
528
535
fun = _RecordAccess (param , None )
529
536
needs = {Need .GEOMETRY }
530
537
super ().__init__ (fun , refpts , needs = needs , name = name , ** kwargs )
531
538
532
539
533
540
class OrbitObservable (_ElementObservable ):
534
- """Observes the transfer matrix at selected locations"""
541
+ """Observe the transfer matrix at selected locations.
542
+
543
+ Process the result of calling :py:func:`.find_orbit`.
544
+ """
535
545
536
546
def __init__ (
537
547
self , refpts : Refpts , axis : AxisDef = None , name : Optional [str ] = None , ** kwargs
@@ -573,7 +583,11 @@ def __init__(
573
583
574
584
575
585
class MatrixObservable (_ElementObservable ):
576
- """Observes the closed orbit at selected locations"""
586
+ """Observe the closed orbit at selected locations.
587
+
588
+ Processs the result of calling :py:func:`.find_m44` or :py:func:`.find_m44`
589
+ depending of :py:meth:`~.Lattice.is_6d`.
590
+ """
577
591
578
592
def __init__ (
579
593
self ,
@@ -660,7 +674,10 @@ def __init__(
660
674
661
675
662
676
class LocalOpticsObservable (_ElementObservable ):
663
- """Observe a local optics parameter at selected locations"""
677
+ """Observe a local optics parameter at selected locations.
678
+
679
+ Process the local output of :py:func:`.get_optics`.
680
+ """
664
681
665
682
def __init__ (
666
683
self ,
@@ -685,6 +702,12 @@ def __init__(
685
702
use_integer: For the *'mu'* parameter, compute the
686
703
phase advance at all points to avoid discontinuities (slower)
687
704
705
+ .. Attention::
706
+
707
+ if *use_integer* is :py:obj:`False` (default value), all phase advance
708
+ values are folded into the :math:`[0, 2\pi]` interval to avoid
709
+ unpredictible jumps.
710
+
688
711
Keyword Args:
689
712
summary: Set to :py:obj:`True` if the user-defined
690
713
evaluation function returns a single item (see below)
@@ -708,7 +731,8 @@ def __init__(
708
731
709
732
:pycode:`value = fun(ring, elemdata)`
710
733
711
- *elemdata* if the output of :py:func:`.get_optics`.
734
+ *elemdata* if the output of :py:func:`.get_optics`, evaluated at the *refpts*
735
+ of the observable.
712
736
713
737
*value* is the value of the Observable and must have one line per
714
738
refpoint. Alternatively, it may be a single line, but then the
@@ -746,25 +770,19 @@ def __init__(
746
770
if callable (param ):
747
771
fun = param
748
772
needs .add (Need .CHROMATICITY )
773
+ elif param == "mu" and not use_integer :
774
+ # values and target are taken modulo 2*pi
775
+ fun = _MuAccess (_all_rows (ax_ (plane , "index" )))
749
776
else :
750
777
fun = _RecordAccess (param , _all_rows (ax_ (plane , "index" )))
751
778
if use_integer :
752
779
needs .add (Need .ALL_POINTS )
753
- else :
754
- needs .add (Need .MODULO )
755
- target = kwargs .get ("target" , None )
756
- if target is not None and param == "mu" :
757
- kwargs ["target" ] = target % (2.0 * np .pi )
758
- elif target is not None and param == "mun" :
759
- kwargs ["target" ] = target % 1.0
760
- needs .add (Need .TUNEUNIT )
761
- fun = _RecordAccess ("mu" , _all_rows (ax_ (plane , "index" )))
762
780
763
781
super ().__init__ (fun , refpts , needs = needs , name = name , ** kwargs )
764
782
765
783
766
784
class LatticeObservable (_ElementObservable ):
767
- """Observe an attribute of selected lattice elements"""
785
+ """Observe an attribute of selected lattice elements. """
768
786
769
787
def __init__ (
770
788
self ,
@@ -841,15 +859,18 @@ def __init__(
841
859
842
860
843
861
class EmittanceObservable (Observable ):
844
- """Observe emittance-related parameters"""
862
+ """Observe emittance-related parameters.
863
+
864
+ Process the output of :py:func:`.envelope_parameters`.
865
+ """
845
866
846
867
def __init__ (
847
868
self , param : str , plane : AxisDef = None , name : Optional [str ] = None , ** kwargs
848
869
):
849
870
r"""
850
871
Args:
851
- param: Parameter name (see
852
- :py:func:`.envelope_parameters`)
872
+ param: Parameter name (see :py:func:`.envelope_parameters`) or
873
+ :ref:`user-defined evaluation function <emittance_eval>`
853
874
plane: One out of {0, 'x', 'h', 'H'} for horizontal plane,
854
875
one out of {1, 'y', 'v', 'V'} for vertival plane or one out of
855
876
{2, 'z', 'l', 'L'} for longitudinal plane
@@ -867,14 +888,29 @@ def __init__(
867
888
is constrained in the interval
868
889
[*target*\ +\ *low_bound* *target*\ +\ *up_bound*]
869
890
891
+ .. _emittance_eval:
892
+ .. rubric:: User-defined evaluation function
893
+
894
+ It is called as:
895
+
896
+ :pycode:`value = fun(ring, paramdata)`
897
+
898
+ *paramdata* if the :py:class:`.RingParameters` object returned by
899
+ :py:func:`.envelope_parameters`.
900
+
901
+ *value* is the value of the Observable.
902
+
870
903
Example:
871
904
872
905
>>> EmittanceObservable('emittances', plane='h')
873
906
874
907
Observe the horizontal emittance
875
908
"""
876
909
name = self ._set_name (name , param , plane_ (plane , "code" ))
877
- fun = _RecordAccess (param , plane_ (plane , "index" ))
910
+ if callable (param ):
911
+ fun = param
912
+ else :
913
+ fun = _RecordAccess (param , plane_ (plane , "index" ))
878
914
needs = {Need .EMITTANCE }
879
915
super ().__init__ (fun , needs = needs , name = name , ** kwargs )
880
916
@@ -888,7 +924,9 @@ def GlobalOpticsObservable(
888
924
** kwargs ,
889
925
):
890
926
# noinspection PyUnresolvedReferences
891
- r"""Observe a global optics parameter
927
+ r"""Observe a global optics parameter.
928
+
929
+ Process the global output of :py:func:`.get_optics`.
892
930
893
931
Args:
894
932
param: Optics parameter name (see :py:func:`.get_optics`)
0 commit comments