xtrack 0.41.2__tar.gz → 0.42.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. {xtrack-0.41.2/xtrack.egg-info → xtrack-0.42.0}/PKG-INFO +1 -1
  2. xtrack-0.42.0/xtrack/_version.py +1 -0
  3. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements.py +108 -3
  4. xtrack-0.42.0/xtrack/beam_elements/elements_src/solenoid.h +151 -0
  5. xtrack-0.42.0/xtrack/footprint.py +382 -0
  6. xtrack-0.42.0/xtrack/headers/atomicadd.h +41 -0
  7. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/line.py +32 -12
  8. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/mad_loader.py +15 -1
  9. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/match.py +2 -2
  10. xtrack-0.42.0/xtrack/monitors/__init__.py +6 -0
  11. xtrack-0.42.0/xtrack/monitors/beam_position_monitor.h +67 -0
  12. xtrack-0.42.0/xtrack/monitors/beam_position_monitor.py +138 -0
  13. xtrack-0.42.0/xtrack/monitors/beam_profile_monitor.h +88 -0
  14. xtrack-0.42.0/xtrack/monitors/beam_profile_monitor.py +209 -0
  15. xtrack-0.42.0/xtrack/monitors/beam_size_monitor.h +69 -0
  16. xtrack-0.42.0/xtrack/monitors/beam_size_monitor.py +151 -0
  17. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/slicing.py +33 -12
  18. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/survey.py +5 -5
  19. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/tracker.py +17 -11
  20. {xtrack-0.41.2 → xtrack-0.42.0/xtrack.egg-info}/PKG-INFO +1 -1
  21. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack.egg-info/SOURCES.txt +8 -0
  22. xtrack-0.41.2/xtrack/_version.py +0 -1
  23. xtrack-0.41.2/xtrack/footprint.py +0 -197
  24. xtrack-0.41.2/xtrack/monitors/__init__.py +0 -3
  25. {xtrack-0.41.2 → xtrack-0.42.0}/LICENSE +0 -0
  26. {xtrack-0.41.2 → xtrack-0.42.0}/MANIFEST.in +0 -0
  27. {xtrack-0.41.2 → xtrack-0.42.0}/README.md +0 -0
  28. {xtrack-0.41.2 → xtrack-0.42.0}/ducktrack/__init__.py +0 -0
  29. {xtrack-0.41.2 → xtrack-0.42.0}/ducktrack/base_classes.py +0 -0
  30. {xtrack-0.41.2 → xtrack-0.42.0}/ducktrack/be_beamfields/BB6D.py +0 -0
  31. {xtrack-0.41.2 → xtrack-0.42.0}/ducktrack/be_beamfields/BB6Ddata.py +0 -0
  32. {xtrack-0.41.2 → xtrack-0.42.0}/ducktrack/be_beamfields/__init__.py +0 -0
  33. {xtrack-0.41.2 → xtrack-0.42.0}/ducktrack/be_beamfields/beambeam.py +0 -0
  34. {xtrack-0.41.2 → xtrack-0.42.0}/ducktrack/be_beamfields/boost.py +0 -0
  35. {xtrack-0.41.2 → xtrack-0.42.0}/ducktrack/be_beamfields/gaussian_fields.py +0 -0
  36. {xtrack-0.41.2 → xtrack-0.42.0}/ducktrack/be_beamfields/propagate_sigma_matrix.py +0 -0
  37. {xtrack-0.41.2 → xtrack-0.42.0}/ducktrack/be_beamfields/qgauss.py +0 -0
  38. {xtrack-0.41.2 → xtrack-0.42.0}/ducktrack/be_beamfields/slicing.py +0 -0
  39. {xtrack-0.41.2 → xtrack-0.42.0}/ducktrack/be_beamfields/spacecharge.py +0 -0
  40. {xtrack-0.41.2 → xtrack-0.42.0}/ducktrack/elements.py +0 -0
  41. {xtrack-0.41.2 → xtrack-0.42.0}/ducktrack/line.py +0 -0
  42. {xtrack-0.41.2 → xtrack-0.42.0}/ducktrack/mathlibs.py +0 -0
  43. {xtrack-0.41.2 → xtrack-0.42.0}/ducktrack/particles.py +0 -0
  44. {xtrack-0.41.2 → xtrack-0.42.0}/ducktrack/temp_pyparticles.py +0 -0
  45. {xtrack-0.41.2 → xtrack-0.42.0}/pyproject.toml +0 -0
  46. {xtrack-0.41.2 → xtrack-0.42.0}/setup.cfg +0 -0
  47. {xtrack-0.41.2 → xtrack-0.42.0}/setup.py +0 -0
  48. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/__init__.py +0 -0
  49. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/_temp/__init__.py +0 -0
  50. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/_temp/lhc_match/__init__.py +0 -0
  51. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/_temp/lhc_match/gen_madx_optics_file.py +0 -0
  52. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/_temp/lhc_match/lhc_match.py +0 -0
  53. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/_temp/lhc_match/var_limits.py +0 -0
  54. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/base_element.py +0 -0
  55. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/__init__.py +0 -0
  56. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/apertures.py +0 -0
  57. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/apertures_src/limitellipse.h +0 -0
  58. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/apertures_src/limitpolygon.h +0 -0
  59. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/apertures_src/limitracetrack.h +0 -0
  60. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/apertures_src/limitrect.h +0 -0
  61. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/apertures_src/limitrectellipse.h +0 -0
  62. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/apertures_src/longitudinallimitrect.h +0 -0
  63. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/beam_interaction.py +0 -0
  64. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/bend.h +0 -0
  65. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/cavity.h +0 -0
  66. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/combinedfunctionmagnet.h +0 -0
  67. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/dipoleedge.h +0 -0
  68. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/drift.h +0 -0
  69. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/drift_elem.h +0 -0
  70. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/elens.h +0 -0
  71. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/exciter.h +0 -0
  72. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/firstordertaylormap.h +0 -0
  73. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/fringe.h +0 -0
  74. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/fringe_track.h +0 -0
  75. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/lineartransfermatrix.h +0 -0
  76. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/linesegmentmap.h +0 -0
  77. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/multipolar_kick.h +0 -0
  78. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/multipole.h +0 -0
  79. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/nonlinearlens.h +0 -0
  80. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/quadrupole.h +0 -0
  81. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/referenceenergyincrease.h +0 -0
  82. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/rfmultipole.h +0 -0
  83. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/sextupole.h +0 -0
  84. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/simplethinbend.h +0 -0
  85. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/simplethinquadrupole.h +0 -0
  86. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/srotation.h +0 -0
  87. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/track_thick_bend.h +0 -0
  88. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/track_thick_cfd.h +0 -0
  89. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/track_yrotation.h +0 -0
  90. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/wedge.h +0 -0
  91. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/wedge_track.h +0 -0
  92. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/wire.h +0 -0
  93. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/xrotation.h +0 -0
  94. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/xyshift.h +0 -0
  95. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/yrotation.h +0 -0
  96. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/elements_src/zetashift.h +0 -0
  97. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/beam_elements/exciter.py +0 -0
  98. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/compounds.py +0 -0
  99. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/general.py +0 -0
  100. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/headers/checks.h +0 -0
  101. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/headers/constants.h +0 -0
  102. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/headers/particle_states.h +0 -0
  103. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/headers/synrad_spectrum.h +0 -0
  104. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/internal_record.py +0 -0
  105. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/linear_normal_form.py +0 -0
  106. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/loss_location_refinement/__init__.py +0 -0
  107. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/loss_location_refinement/loss_location_refinement.py +0 -0
  108. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/lumi.py +0 -0
  109. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/monitors/last_turns_monitor.h +0 -0
  110. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/monitors/last_turns_monitor.py +0 -0
  111. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/monitors/particles_monitor.h +0 -0
  112. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/monitors/particles_monitor.py +0 -0
  113. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/multiline/__init__.py +0 -0
  114. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/multiline/multiline.py +0 -0
  115. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/multiline/shared_knobs.py +0 -0
  116. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/multisetter/__init__.py +0 -0
  117. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/multisetter/multisetter.py +0 -0
  118. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/pipeline/__init__.py +0 -0
  119. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/pipeline/core.py +0 -0
  120. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/pipeline/manager.py +0 -0
  121. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/pipeline/multitracker.py +0 -0
  122. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/prebuild_kernels.py +0 -0
  123. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/prebuilt_kernels/__init__.py +0 -0
  124. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/prebuilt_kernels/kernel_definitions.py +0 -0
  125. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/random/__init__.py +0 -0
  126. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/random/random_generators.py +0 -0
  127. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/random/random_src/exponential.h +0 -0
  128. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/random/random_src/exponential_integral_Ei.h +0 -0
  129. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/random/random_src/normal.h +0 -0
  130. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/random/random_src/rutherford.h +0 -0
  131. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/random/random_src/uniform.h +0 -0
  132. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/tapering.py +0 -0
  133. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/targets.py +0 -0
  134. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/tracker_data.py +0 -0
  135. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/tracker_src/tracker.h +0 -0
  136. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack/twiss.py +0 -0
  137. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack.egg-info/dependency_links.txt +0 -0
  138. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack.egg-info/requires.txt +0 -0
  139. {xtrack-0.41.2 → xtrack-0.42.0}/xtrack.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: xtrack
3
- Version: 0.41.2
3
+ Version: 0.42.0
4
4
  Summary: Tracking library for particle accelerators
5
5
  Home-page: https://xsuite.readthedocs.io/
6
6
  Download-URL: https://pypi.python.org/pypi/xtrack
@@ -0,0 +1 @@
1
+ __version__ = '0.42.0'
@@ -78,9 +78,6 @@ class Drift(BeamElement):
78
78
  _pkg_root.joinpath('beam_elements/elements_src/drift_elem.h'),
79
79
  ]
80
80
 
81
- def make_slice(self, weight):
82
- return Drift(length=self.length * weight)
83
-
84
81
  @staticmethod
85
82
  def add_slice(weight, container, thick_name, slice_name, _buffer=None):
86
83
  container[slice_name] = Drift(_buffer=_buffer)
@@ -873,6 +870,27 @@ class CombinedFunctionMagnet(BeamElement):
873
870
  ref.length = _get_expr(self_or_ref.length) * weight
874
871
  ref.order = order
875
872
 
873
+ @classmethod
874
+ def add_thick_slice(cls, weight, container, name, slice_name, _buffer=None):
875
+ self_or_ref = container[name]
876
+ container[slice_name] = cls(
877
+ length=self_or_ref.length * weight,
878
+ num_multipole_kicks=self_or_ref.num_multipole_kicks,
879
+ order=self_or_ref.order,
880
+ _buffer=_buffer,
881
+ )
882
+ ref = container[slice_name]
883
+
884
+ ref.k0 = _get_expr(self_or_ref.k0)
885
+ ref.k1 = _get_expr(self_or_ref.k1)
886
+ ref.h = _get_expr(self_or_ref.h)
887
+
888
+ for ii in range(len(self_or_ref.knl)):
889
+ ref.knl[ii] = _get_expr(self_or_ref.knl[ii]) * weight
890
+
891
+ for ii in range(len(self_or_ref.ksl)):
892
+ ref.ksl[ii] = _get_expr(self_or_ref.ksl[ii]) * weight
893
+
876
894
  @staticmethod
877
895
  def delete_element_ref(ref):
878
896
  # Remove the array fields
@@ -1048,6 +1066,25 @@ class Quadrupole(BeamElement):
1048
1066
  ref.length = _get_expr(self_or_ref.length) * weight
1049
1067
  ref.order = order
1050
1068
 
1069
+ @classmethod
1070
+ def add_thick_slice(cls, weight, container, name, slice_name, _buffer=None):
1071
+ self_or_ref = container[name]
1072
+ container[slice_name] = cls(
1073
+ length=self_or_ref.length * weight,
1074
+ num_multipole_kicks=self_or_ref.num_multipole_kicks,
1075
+ order=self_or_ref.order,
1076
+ _buffer=_buffer,
1077
+ )
1078
+ ref = container[slice_name]
1079
+
1080
+ ref.k1 = _get_expr(self_or_ref.k1)
1081
+
1082
+ for ii in range(len(self_or_ref.knl)):
1083
+ ref.knl[ii] = _get_expr(self_or_ref.knl[ii]) * weight
1084
+
1085
+ for ii in range(len(self_or_ref.ksl)):
1086
+ ref.ksl[ii] = _get_expr(self_or_ref.ksl[ii]) * weight
1087
+
1051
1088
  @staticmethod
1052
1089
  def delete_element_ref(ref):
1053
1090
  # Remove the array fields
@@ -1065,6 +1102,53 @@ class Quadrupole(BeamElement):
1065
1102
  _unregister_if_preset(ref)
1066
1103
 
1067
1104
 
1105
+ class Solenoid(BeamElement):
1106
+ isthick = True
1107
+
1108
+ _xofields = {
1109
+ 'length': xo.Float64,
1110
+ 'ks': xo.Float64,
1111
+ 'ksi': xo.Float64,
1112
+ }
1113
+
1114
+ _extra_c_sources = [
1115
+ _pkg_root.joinpath('beam_elements/elements_src/drift.h'),
1116
+ _pkg_root.joinpath('beam_elements/elements_src/solenoid.h'),
1117
+ ]
1118
+
1119
+ def __init__(self, length=0, ks=0, ksi=0, **kwargs):
1120
+ """
1121
+ Solenoid element.
1122
+
1123
+ Parameters
1124
+ ----------
1125
+ length : float
1126
+ Length of the element in meters.
1127
+ ks : float
1128
+ Strength of the solenoid component in rad / m. Only to be specified
1129
+ when the element is thin, i.e. when `length` == 0.
1130
+ ksi : float
1131
+ Integrated strength of the solenoid component in rad.
1132
+ """
1133
+
1134
+ if '_xobject' in kwargs.keys() and kwargs['_xobject'] is not None:
1135
+ self.xoinitialize(**kwargs)
1136
+ return
1137
+
1138
+ if length == 0:
1139
+ # Fail when trying to create a thin solenoid, as these are not
1140
+ # tested yet
1141
+ raise NotImplementedError('Thin solenoids are not implemented yet.')
1142
+ # self.isthick = False
1143
+
1144
+ if ksi and length:
1145
+ raise ValueError(
1146
+ "The parameter `ksi` can only be specified when `length` == 0."
1147
+ )
1148
+
1149
+ self.xoinitialize(length=length, ks=ks, ksi=ksi, **kwargs)
1150
+
1151
+
1068
1152
  class Bend(BeamElement):
1069
1153
  isthick = True
1070
1154
  has_backtrack = True
@@ -1204,6 +1288,27 @@ class Bend(BeamElement):
1204
1288
  ref.length = _get_expr(self_or_ref.length) * weight
1205
1289
  ref.order = order
1206
1290
 
1291
+ @classmethod
1292
+ def add_thick_slice(cls, weight, container, name, slice_name, _buffer=None):
1293
+ self_or_ref = container[name]
1294
+ container[slice_name] = cls(
1295
+ length=self_or_ref.length * weight,
1296
+ num_multipole_kicks=self_or_ref.num_multipole_kicks,
1297
+ order=self_or_ref.order,
1298
+ _buffer=_buffer,
1299
+ )
1300
+ ref = container[slice_name]
1301
+
1302
+ ref.k0 = _get_expr(self_or_ref.k0)
1303
+ ref.h = _get_expr(self_or_ref.h)
1304
+ ref.length = _get_expr(self_or_ref.length) * weight
1305
+
1306
+ for ii in range(len(self_or_ref.knl)):
1307
+ ref.knl[ii] = _get_expr(self_or_ref.knl[ii]) * weight
1308
+
1309
+ for ii in range(len(self_or_ref.ksl)):
1310
+ ref.ksl[ii] = _get_expr(self_or_ref.ksl[ii]) * weight
1311
+
1207
1312
  @staticmethod
1208
1313
  def delete_element_ref(ref):
1209
1314
  # Remove the array fields
@@ -0,0 +1,151 @@
1
+ // copyright ############################### //
2
+ // This file is part of the Xtrack Package. //
3
+ // Copyright (c) CERN, 2023. //
4
+ // ######################################### //
5
+
6
+ #ifndef XTRACK_SOLENOID_H
7
+ #define XTRACK_SOLENOID_H
8
+
9
+ #define IS_ZERO(X) (fabs(X) < 1e-9)
10
+
11
+ /*gpufun*/
12
+ void Solenoid_thin_track_single_particle(LocalParticle*, double, double, double);
13
+
14
+ /*gpufun*/
15
+ void Solenoid_thick_track_single_particle(LocalParticle*, double, double);
16
+
17
+
18
+ /*gpufun*/
19
+ void Solenoid_track_local_particle(SolenoidData el, LocalParticle* part0) {
20
+ // Parameters
21
+ const double length = SolenoidData_get_length(el);
22
+ const double ks = SolenoidData_get_ks(el);
23
+ const double ksi = SolenoidData_get_ksi(el);
24
+
25
+ if (IS_ZERO(length)) {
26
+ //start_per_particle_block (part0->part)
27
+ Solenoid_thin_track_single_particle(part, length, ks, ksi);
28
+ //end_per_particle_block
29
+ }
30
+ else {
31
+ //start_per_particle_block (part0->part)
32
+ Solenoid_thick_track_single_particle(part, length, ks);
33
+ //end_per_particle_block
34
+ }
35
+ }
36
+
37
+
38
+ /*gpufun*/
39
+ void Solenoid_thin_track_single_particle(
40
+ LocalParticle* part,
41
+ double length,
42
+ double ks,
43
+ double ksi
44
+ ) {
45
+ const double sk = ks / 2; // todo?: flip sign to change beam direction
46
+ const double skl = ksi / 2;
47
+ const double beta0 = LocalParticle_get_beta0(part);
48
+
49
+ // Particle coordinates
50
+ const double x = LocalParticle_get_x(part);
51
+ const double px = LocalParticle_get_px(part);
52
+ const double y = LocalParticle_get_y(part);
53
+ const double py = LocalParticle_get_py(part);
54
+ const double t = LocalParticle_get_zeta(part) / beta0;
55
+ const double pt = LocalParticle_get_ptau(part);
56
+ const double delta = LocalParticle_get_delta(part);
57
+
58
+ // Useful quantities
59
+ const double psigf = pt / beta0;
60
+ const double betas = beta0;
61
+
62
+ const double onedp = 1 + delta;
63
+ const double fppsig = (1 + (betas * betas) * psigf) / onedp;
64
+
65
+ // Set up C, S, Q, R, Z
66
+ const double cosTh = cos(skl / onedp);
67
+ const double sinTh = sin(skl / onedp);
68
+ const double Q = -skl * sk / onedp;
69
+ const double Z = fppsig / (onedp * onedp) * skl;
70
+ const double R = Z * sk;
71
+
72
+ const double pxf = px + x * Q;
73
+ const double pyf = py + y * Q;
74
+ const double sigf = t * betas - 0.5 * (x * x + y * y) * R;
75
+
76
+ // Final angles after solenoid
77
+ const double pxf_ = pxf * cosTh + pyf * sinTh;
78
+ const double pyf_ = -pxf * sinTh + pyf * cosTh;
79
+
80
+ // Calculate new coordinates
81
+ const double new_x = x * cosTh + y * sinTh;
82
+ const double new_px = pxf_;
83
+ const double new_y = -x * sinTh + y * cosTh;
84
+ const double new_py = pyf_;
85
+ const double new_zeta = sigf + (x * new_py - y * new_px) * Z;
86
+
87
+ LocalParticle_set_x(part, new_x);
88
+ LocalParticle_set_px(part, new_px);
89
+ LocalParticle_set_y(part, new_y);
90
+ LocalParticle_set_py(part, new_py);
91
+ LocalParticle_set_zeta(part, new_zeta);
92
+ }
93
+
94
+
95
+ /*gpufun*/
96
+ void Solenoid_thick_track_single_particle(
97
+ LocalParticle* part,
98
+ double length,
99
+ double ks
100
+ ) {
101
+ const double sk = ks / 2; // todo?: flip sign to change beam direction
102
+
103
+ if (IS_ZERO(sk)) {
104
+ Drift_single_particle(part, length);
105
+ return;
106
+ }
107
+
108
+ const double skl = sk * length;
109
+
110
+ // Particle coordinates
111
+ const double x = LocalParticle_get_x(part);
112
+ const double px = LocalParticle_get_px(part);
113
+ const double y = LocalParticle_get_y(part);
114
+ const double py = LocalParticle_get_py(part);
115
+ const double delta = LocalParticle_get_delta(part);
116
+ const double rvv = LocalParticle_get_rvv(part);
117
+
118
+ // set up constants
119
+ const double pk1 = px + sk * y;
120
+ const double pk2 = py - sk * x;
121
+ const double ptr2 = pk1 * pk1 + pk2 * pk2;
122
+ const double one_plus_delta = 1 + delta;
123
+ const double one_plus_delta_sq = one_plus_delta * one_plus_delta;
124
+ const double pz = sqrt(one_plus_delta_sq - ptr2);
125
+
126
+ // set up constants
127
+ const double cosTh = cos(skl / pz);
128
+ const double sinTh = sin(skl / pz);
129
+
130
+ const double si = sin(skl / pz) / sk;
131
+ const double rps[4] = {
132
+ cosTh * x + sinTh * y,
133
+ cosTh * px + sinTh * py,
134
+ cosTh * y - sinTh * x,
135
+ cosTh * py - sinTh * px
136
+ };
137
+ const double new_x = cosTh * rps[0] + si * rps[1];
138
+ const double new_px = cosTh * rps[1] - sk * sinTh * rps[0];
139
+ const double new_y = cosTh * rps[2] + si * rps[3];
140
+ const double new_py = cosTh * rps[3] - sk * sinTh * rps[2];
141
+ const double add_to_zeta = length * (1 - one_plus_delta / (pz * rvv));
142
+
143
+ LocalParticle_set_x(part, new_x);
144
+ LocalParticle_set_px(part, new_px);
145
+ LocalParticle_set_y(part, new_y);
146
+ LocalParticle_set_py(part, new_py);
147
+ LocalParticle_add_to_zeta(part, add_to_zeta);
148
+ LocalParticle_add_to_s(part, length);
149
+ }
150
+
151
+ #endif // XTRACK_SOLENOID_H
@@ -0,0 +1,382 @@
1
+ import numpy as np
2
+ import xobjects as xo
3
+ import xtrack as xt
4
+
5
+ class LinearRescale():
6
+
7
+ def __init__(self, knob_name, v0, dv):
8
+ self.knob_name = knob_name
9
+ self.v0 = v0
10
+ self.dv = dv
11
+
12
+ def _footprint_with_linear_rescale(linear_rescale_on_knobs, line,
13
+ freeze_longitudinal=False,
14
+ delta0=None, zeta0=None,
15
+ kwargs={}):
16
+
17
+ if isinstance (linear_rescale_on_knobs, LinearRescale):
18
+ linear_rescale_on_knobs = [linear_rescale_on_knobs]
19
+
20
+ assert len(linear_rescale_on_knobs) == 1, (
21
+ 'Only one linear rescale is supported for now')
22
+
23
+ knobs_0 = {}
24
+ for rr in linear_rescale_on_knobs:
25
+ nn = rr.knob_name
26
+ v0 = rr.v0
27
+ knobs_0[nn] = v0
28
+
29
+ with xt._temp_knobs(line, knobs_0):
30
+ fp = line.get_footprint(
31
+ freeze_longitudinal=freeze_longitudinal,
32
+ delta0=delta0, zeta0=zeta0, **kwargs)
33
+
34
+ qx0 = fp.qx
35
+ qy0 = fp.qy
36
+
37
+ for rr in linear_rescale_on_knobs:
38
+ nn = rr.knob_name
39
+ v0 = rr.v0
40
+ dv = rr.dv
41
+
42
+ knobs_1 = knobs_0.copy()
43
+ knobs_1[nn] = v0 + dv
44
+
45
+ with xt._temp_knobs(line, knobs_1):
46
+ fp1 = line.get_footprint(freeze_longitudinal=freeze_longitudinal,
47
+ delta0=delta0, zeta0=zeta0, **kwargs)
48
+ delta_qx = (fp1.qx - qx0) / dv * (line.vars[nn]._value - v0)
49
+ delta_qy = (fp1.qy - qy0) / dv * (line.vars[nn]._value - v0)
50
+
51
+ fp.qx += delta_qx
52
+ fp.qy += delta_qy
53
+
54
+ return fp
55
+
56
+ class Footprint():
57
+
58
+ def __init__(self, nemitt_x=None, nemitt_y=None, n_turns=256, n_fft=2**18,
59
+ mode='polar', r_range=None, theta_range=None, n_r=None, n_theta=None,
60
+ x_norm_range=None, y_norm_range=None, n_x_norm=None, n_y_norm=None,
61
+ keep_fft=False, keep_tracking_data=False,
62
+ auto_to_numpy=True,fft_chunk_size=200
63
+ ):
64
+
65
+ assert nemitt_x is not None and nemitt_y is not None, (
66
+ 'nemitt_x and nemitt_y must be provided')
67
+ self.mode = mode
68
+ self.auto_to_numpy = auto_to_numpy
69
+ self.n_turns = n_turns
70
+ self.n_fft = n_fft
71
+ self.fft_chunk_size = fft_chunk_size
72
+ self.keep_fft = keep_fft
73
+ self.keep_tracking_data = keep_tracking_data
74
+
75
+ self.nemitt_x = nemitt_x
76
+ self.nemitt_y = nemitt_y
77
+
78
+ assert mode in ['polar', 'uniform_action_grid'], (
79
+ 'mode must be either polar or uniform_action_grid')
80
+
81
+ if mode == 'polar':
82
+
83
+ assert x_norm_range is None and y_norm_range is None, (
84
+ 'x_norm_range and y_norm_range must be None for mode polar')
85
+ assert n_x_norm is None and n_y_norm is None, (
86
+ 'n_x_norm and n_y_norm must be None for mode polar')
87
+
88
+ if r_range is None:
89
+ r_range = (0.1, 6)
90
+ if theta_range is None:
91
+ theta_range = (0.05, np.pi/2-0.05)
92
+ if n_r is None:
93
+ n_r = 10
94
+ if n_theta is None:
95
+ n_theta = 10
96
+
97
+ self.r_range = r_range
98
+ self.theta_range = theta_range
99
+ self.n_r = n_r
100
+ self.n_theta = n_theta
101
+
102
+ self.r_grid = np.linspace(*r_range, n_r)
103
+ self.theta_grid = np.linspace(*theta_range, n_theta)
104
+ self.R_2d, self.Theta_2d = np.meshgrid(self.r_grid, self.theta_grid)
105
+
106
+ self.x_norm_2d = self.R_2d * np.cos(self.Theta_2d)
107
+ self.y_norm_2d = self.R_2d * np.sin(self.Theta_2d)
108
+
109
+ elif mode == 'uniform_action_grid':
110
+
111
+ assert r_range is None and theta_range is None, (
112
+ 'r_range and theta_range must be None for mode uniform_action_grid')
113
+ assert n_r is None and n_theta is None, (
114
+ 'n_r and n_theta must be None for mode uniform_action_grid')
115
+
116
+ if x_norm_range is None:
117
+ x_norm_range = (0.1, 6)
118
+ if y_norm_range is None:
119
+ y_norm_range = (0.1, 6)
120
+ if n_x_norm is None:
121
+ n_x_norm = 10
122
+ if n_y_norm is None:
123
+ n_y_norm = 10
124
+
125
+ Jx_min = nemitt_x * x_norm_range[0]**2 / 2
126
+ Jx_max = nemitt_x * x_norm_range[1]**2 / 2
127
+ Jy_min = nemitt_y * y_norm_range[0]**2 / 2
128
+ Jy_max = nemitt_y * y_norm_range[1]**2 / 2
129
+
130
+ self.Jx_grid = np.linspace(Jx_min, Jx_max, n_x_norm)
131
+ self.Jy_grid = np.linspace(Jy_min, Jy_max, n_y_norm)
132
+
133
+ self.Jx_2d, self.Jy_2d = np.meshgrid(self.Jx_grid, self.Jy_grid)
134
+
135
+ self.x_norm_2d = np.sqrt(2 * self.Jx_2d / nemitt_x)
136
+ self.y_norm_2d = np.sqrt(2 * self.Jy_2d / nemitt_y)
137
+
138
+ def _compute_footprint(self, line, freeze_longitudinal=False,
139
+ delta0=None, zeta0=None):
140
+
141
+ if freeze_longitudinal is None:
142
+ # In future we could detect if the line has frozen longitudinal plane
143
+ freeze_longitudinal = False
144
+
145
+ nplike_lib = line._context.nplike_lib
146
+
147
+ particles = line.build_particles(
148
+ x_norm=self.x_norm_2d.flatten(), y_norm=self.y_norm_2d.flatten(),
149
+ nemitt_x=self.nemitt_x, nemitt_y=self.nemitt_y,
150
+ zeta=zeta0, delta=delta0,
151
+ freeze_longitudinal=freeze_longitudinal,
152
+ method={True: '4d', False: '6d'}[freeze_longitudinal]
153
+ )
154
+
155
+ print('Tracking particles for footprint...')
156
+ line.track(particles, num_turns=self.n_turns, turn_by_turn_monitor=True,
157
+ freeze_longitudinal=freeze_longitudinal)
158
+ print('Done tracking.')
159
+
160
+ ctx2np = line._context.nparray_from_context_array
161
+ assert np.all(ctx2np(particles.state == 1)), (
162
+ 'Some particles were lost during tracking')
163
+ mon = line.record_last_track
164
+ mon.auto_to_numpy = False
165
+
166
+ if isinstance(line._context, xo.ContextPyopencl):
167
+ raise NotImplementedError(
168
+ 'Footprint calculation with Pyopencl not supported yet. '
169
+ 'Let us know if you need this feature.')
170
+ # Could be implemented using xobject fft
171
+
172
+ x_noCO = mon.x - nplike_lib.atleast_2d(mon.x.mean(axis=1)).T
173
+ y_noCO = mon.y - nplike_lib.atleast_2d(mon.y.mean(axis=1)).T
174
+
175
+ freq_axis = nplike_lib.fft.rfftfreq(self.n_fft)
176
+
177
+ npart = nplike_lib.shape(x_noCO)[0]
178
+ self.qx = nplike_lib.zeros(npart,dtype=float)
179
+ self.qy = nplike_lib.zeros(npart,dtype=float)
180
+
181
+ if self.keep_fft:
182
+ self.fft_x = nplike_lib.zeros((npart,len(freq_axis)),dtype=complex)
183
+ self.fft_y = nplike_lib.zeros((npart,len(freq_axis)),dtype=complex)
184
+
185
+ # Compute in chunks
186
+ iStart = 0
187
+ while iStart < npart:
188
+ iEnd = iStart + self.fft_chunk_size
189
+ if iEnd > npart:
190
+ iEnd = npart
191
+ fft_x = nplike_lib.fft.rfft(x_noCO[iStart:iEnd,:], n=self.n_fft)
192
+ fft_y = nplike_lib.fft.rfft(y_noCO[iStart:iEnd,:], n=self.n_fft)
193
+ if self.keep_fft:
194
+ self.fft_x[iStart:iEnd,:] = fft_x
195
+ self.fft_y[iStart:iEnd,:] = fft_y
196
+ qx = freq_axis[nplike_lib.argmax(nplike_lib.abs(fft_x), axis=1)]
197
+ qy = freq_axis[nplike_lib.argmax(nplike_lib.abs(fft_y), axis=1)]
198
+ self.qx[iStart:iEnd] = qx
199
+ self.qy[iStart:iEnd] = qy
200
+ iStart += self.fft_chunk_size
201
+
202
+ self.qx = nplike_lib.reshape(self.qx, self.x_norm_2d.shape)
203
+ self.qy = nplike_lib.reshape(self.qy, self.y_norm_2d.shape)
204
+
205
+ if self.auto_to_numpy:
206
+ ctx2np = line._context.nparray_from_context_array
207
+ self.qx = ctx2np(self.qx)
208
+ self.qy = ctx2np(self.qy)
209
+ if self.keep_fft:
210
+ self.fft_x = ctx2np(self.fft_x)
211
+ self.fft_y = ctx2np(self.fft_y)
212
+
213
+ if self.keep_tracking_data:
214
+ self.tracking_data = mon
215
+
216
+ print ('Done computing footprint.')
217
+
218
+ def _compute_tune_shift(self,_context,J1_2d,J1_grid,J2_2d,J2_grid,q,coherent_tune,epsilon):
219
+ nplike_lib = _context.nplike_lib
220
+ ctx2np = _context.nparray_from_context_array
221
+ np2ctx = _context.nparray_to_context_array
222
+
223
+ integrand = -J1_2d*nplike_lib.exp(-J1_2d-J2_2d) / (coherent_tune - q + epsilon*1j)
224
+ tune_shift = ctx2np(-1.0/nplike_lib.trapz(J2_grid,nplike_lib.trapz(J1_grid,integrand,1),0))
225
+ return tune_shift
226
+
227
+ def _compute_tune_shift_adaptive_epsilon(self,_context,J1_2d,J1_grid,J2_2d,J2_grid,q,coherent_tune,
228
+ epsilon0,epsilon_factor,epsilon_rel_tol,max_iter,min_epsilon):
229
+ tune_shift = self._compute_tune_shift(_context,J1_2d,J1_grid,J2_2d,J2_grid,q,coherent_tune,epsilon0)
230
+ if epsilon_factor > 0.0:
231
+ epsilon_ref = epsilon0
232
+ epsilon = np.abs(np.imag(tune_shift)*epsilon_factor)
233
+ if epsilon < min_epsilon:
234
+ epsilon = min_epsilon
235
+ count = 0
236
+ while np.abs(1-epsilon/epsilon_ref) > epsilon_rel_tol and count < max_iter and epsilon >= min_epsilon:
237
+ tune_shift = self._compute_tune_shift(_context,J1_2d,J1_grid,J2_2d,J2_grid,q,coherent_tune,epsilon)
238
+ epsilon_ref = epsilon
239
+ epsilon = np.abs(np.imag(tune_shift)*epsilon_factor)
240
+ count += 1
241
+ return tune_shift
242
+
243
+ def get_stability_diagram(
244
+ self,
245
+ _context=None,
246
+ n_points_stabiliy_diagram=100,
247
+ epsilon0=1e-5,
248
+ epsilon_factor=0.1,
249
+ epsilon_rel_tol=0.1,
250
+ max_iter=10,
251
+ min_epsilon=1e-6,
252
+ n_points_interpolate=1000,
253
+ ):
254
+ """
255
+ Compute the stability diagram by evaluating the dispersion integral from [1]
256
+ numerically for a set of complex tune shifts with vanishing imaginary part.
257
+ By convention the imaginary part is positive.
258
+
259
+ Parameters
260
+ ----------
261
+ _context:
262
+ n_points_stabiliy_diagram: scalar(int)
263
+ Number of times that the dispersion integral will be solved,
264
+ each yielding a point on the output stability diagram
265
+ epsilon0: scalar(float)
266
+ vanishing imaginary part of the tune shift
267
+ epsilon_factor: scalar(float)
268
+ if larger than 0, an adaptive algorithm will be used to adjust
269
+ epsilon between epsilon0 and epsilon_min using relative varitions
270
+ in the order of the epsilon_factor
271
+ epsilon_rel_tol: scalar(float)
272
+ Stop the iterative algorithm if the relative change of
273
+ epilson is smaller than epsilon_rel_tol
274
+ max_iter: scalar(int)
275
+ Stop the iterative algorithm if the the number of iterations
276
+ reached max_iter
277
+ min_epsilon: scalar(float)
278
+ Stop the iterative algorithm if the epsilon is smaller than
279
+ min_epsilon
280
+ n_points_interpolate: scalar(int)
281
+ Perform the numerical integration on a grid finer than the footprint
282
+ using linear interpolation
283
+
284
+ Returns
285
+ -------
286
+ tune_shifts_x: array_like(complex)
287
+ Horizontal stability diagram
288
+ tune_shifts_y: array_like(complex)
289
+ Vertical stability diagram
290
+
291
+ References
292
+ ----------
293
+ [1] https://cds.cern.ch/record/318826
294
+ [2] https://doi.org/10.1103/PhysRevSTAB.17.111002
295
+ """
296
+ if _context == None:
297
+ _context = xo.ContextCpu()
298
+ nplike_lib = _context.nplike_lib
299
+ splike_lib = _context.splike_lib
300
+ ctx2np = _context.nparray_from_context_array
301
+ np2ctx = _context.nparray_to_context_array
302
+
303
+ Jx_2d = np2ctx(self.Jx_2d / self.nemitt_x)
304
+ Jx_grid = np2ctx(self.Jx_grid / self.nemitt_x)
305
+ Jy_2d = np2ctx(self.Jy_2d / self.nemitt_y)
306
+ Jy_grid = np2ctx(self.Jy_grid / self.nemitt_y)
307
+ qx = np2ctx(self.qx)
308
+ qy = np2ctx(self.qy)
309
+
310
+ if n_points_interpolate > len(Jx_grid) or n_points_interpolate > len(Jy_grid):
311
+ interpolator_x = splike_lib.interpolate.RegularGridInterpolator(
312
+ points=[Jy_grid, Jx_grid], values=qx, bounds_error=True, fill_value=None
313
+ )
314
+ interpolator_y = splike_lib.interpolate.RegularGridInterpolator(
315
+ points=[Jy_grid, Jx_grid], values=qy, bounds_error=True, fill_value=None
316
+ )
317
+ Jx_grid = nplike_lib.linspace(Jx_grid[0], Jx_grid[-1], n_points_interpolate)
318
+ Jy_grid = nplike_lib.linspace(Jy_grid[0], Jy_grid[-1], n_points_interpolate)
319
+ Jx_2d, Jy_2d = nplike_lib.meshgrid(Jx_grid, Jy_grid)
320
+ qx = interpolator_x((Jy_2d, Jx_2d))
321
+ qy = interpolator_y((Jy_2d, Jx_2d))
322
+
323
+ coherent_tunes_x = np.linspace(
324
+ np.min(self.qx), np.max(self.qx), n_points_stabiliy_diagram
325
+ )
326
+ coherent_tunes_y = np.linspace(
327
+ np.min(self.qy), np.max(self.qy), n_points_stabiliy_diagram
328
+ )
329
+ tune_shifts_x = np.zeros_like(coherent_tunes_x, dtype=complex)
330
+ tune_shifts_y = np.zeros_like(coherent_tunes_y, dtype=complex)
331
+ for i in range(n_points_stabiliy_diagram):
332
+ tune_shifts_x[i] = self._compute_tune_shift_adaptive_epsilon(
333
+ _context=_context,
334
+ J1_2d=Jx_2d,
335
+ J1_grid=Jx_grid,
336
+ J2_2d=Jy_2d,
337
+ J2_grid=Jy_grid,
338
+ q=qx,
339
+ coherent_tune=coherent_tunes_x[i],
340
+ epsilon0=epsilon0,
341
+ epsilon_factor=epsilon_factor,
342
+ epsilon_rel_tol=epsilon_rel_tol,
343
+ max_iter=max_iter,
344
+ min_epsilon=min_epsilon,
345
+ )
346
+ tune_shifts_y[i] = self._compute_tune_shift_adaptive_epsilon(
347
+ _context=_context,
348
+ J1_2d=Jy_2d,
349
+ J1_grid=Jy_grid,
350
+ J2_2d=Jx_2d,
351
+ J2_grid=Jx_grid,
352
+ q=qy,
353
+ coherent_tune=coherent_tunes_y[i],
354
+ epsilon0=epsilon0,
355
+ epsilon_factor=epsilon_factor,
356
+ epsilon_rel_tol=epsilon_rel_tol,
357
+ max_iter=max_iter,
358
+ min_epsilon=min_epsilon,
359
+ )
360
+ return tune_shifts_x, tune_shifts_y
361
+
362
+ def plot(self, ax=None, **kwargs):
363
+ import matplotlib.pyplot as plt
364
+
365
+ if ax is None:
366
+ ax = plt.gca()
367
+
368
+ if 'color' not in kwargs:
369
+ kwargs['color'] = 'k'
370
+
371
+ labels = [None] * self.qx.shape[1]
372
+
373
+ if 'label' in kwargs:
374
+ label_str = kwargs['label']
375
+ kwargs.pop('label')
376
+ labels[0] = label_str
377
+
378
+ ax.plot(self.qx, self.qy, label=labels, **kwargs)
379
+ ax.plot(self.qx.T, self.qy.T, **kwargs)
380
+
381
+ ax.set_xlabel(r'$q_x$')
382
+ ax.set_ylabel(r'$q_y$')