xcoll 0.5.11__py3-none-any.whl → 0.5.12__py3-none-any.whl
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.
Potentially problematic release.
This version of xcoll might be problematic. Click here for more details.
- xcoll/general.py +1 -1
- xcoll/headers/particle_states.h +2 -2
- xcoll/initial_distribution.py +10 -14
- xcoll/line_tools.py +0 -1
- xcoll/scattering_routines/engine.py +403 -0
- xcoll/scattering_routines/everest/properties.h +1 -1
- xcoll/xaux.py +117 -0
- {xcoll-0.5.11.dist-info → xcoll-0.5.12.dist-info}/METADATA +4 -4
- {xcoll-0.5.11.dist-info → xcoll-0.5.12.dist-info}/RECORD +12 -10
- {xcoll-0.5.11.dist-info → xcoll-0.5.12.dist-info}/LICENSE +0 -0
- {xcoll-0.5.11.dist-info → xcoll-0.5.12.dist-info}/NOTICE +0 -0
- {xcoll-0.5.11.dist-info → xcoll-0.5.12.dist-info}/WHEEL +0 -0
xcoll/general.py
CHANGED
xcoll/headers/particle_states.h
CHANGED
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
#define XC_LOST_ON_EVEREST_COLL -331
|
|
11
11
|
#define XC_LOST_ON_EVEREST_CRYSTAL -332
|
|
12
12
|
#define XC_LOST_ON_FLUKA_BLOCK -333
|
|
13
|
-
#define
|
|
13
|
+
#define XC_LOST_ON_FLUKA_COLL -334
|
|
14
14
|
#define XC_LOST_ON_FLUKA_CRYSTAL -335
|
|
15
15
|
#define XC_LOST_ON_GEANT4_BLOCK -336
|
|
16
|
-
#define
|
|
16
|
+
#define XC_LOST_ON_GEANT4_COLL -337
|
|
17
17
|
#define XC_LOST_ON_GEANT4_CRYSTAL -338
|
|
18
18
|
#define XC_LOST_ON_ABSORBER -340
|
|
19
19
|
|
xcoll/initial_distribution.py
CHANGED
|
@@ -187,25 +187,21 @@ def _generate_4D_pencil_one_jaw(line, name, num_particles, plane, side, impact_p
|
|
|
187
187
|
coll = line[name]
|
|
188
188
|
|
|
189
189
|
if side == '+':
|
|
190
|
-
if
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
else:
|
|
194
|
-
pencil_pos = coll.jaw_LU + impact_parameter
|
|
190
|
+
if isinstance(coll, EverestCrystal):
|
|
191
|
+
# A pencil on the crystal should always be upstream
|
|
192
|
+
pencil_pos = coll.jaw_U + impact_parameter
|
|
195
193
|
else:
|
|
196
|
-
if
|
|
197
|
-
pencil_pos = coll.
|
|
194
|
+
if is_converging:
|
|
195
|
+
pencil_pos = coll.jaw_LU + impact_parameter
|
|
198
196
|
else:
|
|
199
197
|
pencil_pos = coll.jaw_LD + impact_parameter
|
|
200
198
|
elif side == '-':
|
|
201
|
-
if
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
else:
|
|
205
|
-
pencil_pos = coll.jaw_RU - impact_parameter
|
|
199
|
+
if isinstance(coll, EverestCrystal):
|
|
200
|
+
# A pencil on the crystal should always be upstream
|
|
201
|
+
pencil_pos = coll.jaw_U - impact_parameter
|
|
206
202
|
else:
|
|
207
|
-
if
|
|
208
|
-
pencil_pos = coll.
|
|
203
|
+
if is_converging:
|
|
204
|
+
pencil_pos = coll.jaw_RU - impact_parameter
|
|
209
205
|
else:
|
|
210
206
|
pencil_pos = coll.jaw_RD - impact_parameter
|
|
211
207
|
|
xcoll/line_tools.py
CHANGED
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
# copyright ############################### #
|
|
2
|
+
# This file is part of the Xcoll Package. #
|
|
3
|
+
# Copyright (c) CERN, 2024. #
|
|
4
|
+
# ######################################### #
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import numpy as np
|
|
8
|
+
import shutil
|
|
9
|
+
|
|
10
|
+
import xobjects as xo
|
|
11
|
+
import xpart as xp
|
|
12
|
+
import xtrack as xt
|
|
13
|
+
try:
|
|
14
|
+
# TODO: once xaux is in Xsuite keep only this
|
|
15
|
+
from xaux import ClassProperty, ClassPropertyMeta, FsPath, singleton
|
|
16
|
+
except ImportError:
|
|
17
|
+
from ..xaux import ClassProperty, ClassPropertyMeta, FsPath, singleton
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BaseEngineMeta(xo.hybrid_class.MetaHybridClass, ClassPropertyMeta):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
@singleton
|
|
24
|
+
class BaseEngine(xo.HybridClass, metaclass=BaseEngineMeta):
|
|
25
|
+
_xofields = {
|
|
26
|
+
'_particle_ref': xp.Particles,
|
|
27
|
+
'_seed': xo.UInt64,
|
|
28
|
+
'_capacity': xo.Int64,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
_int32 = False
|
|
32
|
+
_element_classes = None
|
|
33
|
+
_only_protons = False
|
|
34
|
+
_uses_input_file = False
|
|
35
|
+
_uses_run_folder = False
|
|
36
|
+
|
|
37
|
+
def __init__(self, **kwargs):
|
|
38
|
+
if not self._initialised:
|
|
39
|
+
if np.any([key[0] != '_' for key in self._xofields.keys()]):
|
|
40
|
+
raise ValueError(f"All fields in `{self.__class__.__name__}._xofields` have "
|
|
41
|
+
+ f"to start with an underscore! This is to ensure to work "
|
|
42
|
+
+ f"correctly with `ClassProperty`.")
|
|
43
|
+
if '_xobject' not in kwargs:
|
|
44
|
+
# Initialise defaults
|
|
45
|
+
self._cwd = None
|
|
46
|
+
self._line = None
|
|
47
|
+
self._verbose = False
|
|
48
|
+
self._input_file = None
|
|
49
|
+
self._element_dict = {}
|
|
50
|
+
self._warning_given = False
|
|
51
|
+
self._tracking_initialised = False
|
|
52
|
+
kwargs.setdefault('_particle_ref', xp.Particles())
|
|
53
|
+
kwargs.setdefault('_seed', 0)
|
|
54
|
+
kwargs.setdefault('_capacity', 0)
|
|
55
|
+
filtered_kwargs = {}
|
|
56
|
+
remaining_kwargs = {}
|
|
57
|
+
for key, value in kwargs.items():
|
|
58
|
+
if key in self._xofields.keys() or key == '_xobject':
|
|
59
|
+
filtered_kwargs[key] = value
|
|
60
|
+
else:
|
|
61
|
+
remaining_kwargs[key] = value
|
|
62
|
+
super().__init__(**filtered_kwargs)
|
|
63
|
+
kwargs = remaining_kwargs
|
|
64
|
+
self._initialised = True
|
|
65
|
+
# Apply kwargs
|
|
66
|
+
for kk, vv in kwargs.items():
|
|
67
|
+
if not hasattr(self.__class__, kk):
|
|
68
|
+
raise ValueError(f"Invalid attribute {kk} for {self.__class__.__name__}!")
|
|
69
|
+
setattr(self, kk, vv)
|
|
70
|
+
|
|
71
|
+
def __del__(self, *args, **kwargs):
|
|
72
|
+
self.stop(warn=False)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _warn(self, error=None):
|
|
76
|
+
if not self._warning_given:
|
|
77
|
+
print(f"Warning: Failed to import {self.__class__.__name__} environment "
|
|
78
|
+
+ f" (did you compile?).\n{self.__class__.__name__.replace('Engine', '')} "
|
|
79
|
+
+ f"elements will be installed but are not trackable.\n", flush=True)
|
|
80
|
+
if error:
|
|
81
|
+
print(error, flush=True)
|
|
82
|
+
self._warning_given = True
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# ==================
|
|
86
|
+
# === Properties ===
|
|
87
|
+
# ==================
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@ClassProperty
|
|
91
|
+
def name(cls):
|
|
92
|
+
return cls.__name__.replace('Engine', '').lower()
|
|
93
|
+
|
|
94
|
+
@ClassProperty
|
|
95
|
+
def verbose(cls):
|
|
96
|
+
return cls.get_self()._verbose
|
|
97
|
+
|
|
98
|
+
@verbose.setter
|
|
99
|
+
def verbose(cls, val):
|
|
100
|
+
cls.get_self()._verbose = val
|
|
101
|
+
|
|
102
|
+
@ClassProperty
|
|
103
|
+
def line(cls):
|
|
104
|
+
return cls.get_self()._line
|
|
105
|
+
|
|
106
|
+
@line.setter
|
|
107
|
+
def line(cls, val):
|
|
108
|
+
if not val is None and not isinstance(val, xt.Line):
|
|
109
|
+
raise ValueError("`line` has to be an xt.Line object!")
|
|
110
|
+
cls.get_self()._line = val
|
|
111
|
+
|
|
112
|
+
@line.deleter
|
|
113
|
+
def line(cls):
|
|
114
|
+
cls.get_self()._line = None
|
|
115
|
+
|
|
116
|
+
@ClassProperty
|
|
117
|
+
def particle_ref(cls):
|
|
118
|
+
self = cls.get_self()
|
|
119
|
+
initial = xp.Particles().to_dict()
|
|
120
|
+
current = self._particle_ref.to_dict()
|
|
121
|
+
if xt.line._dicts_equal(initial, current):
|
|
122
|
+
return None
|
|
123
|
+
else:
|
|
124
|
+
return self._particle_ref
|
|
125
|
+
|
|
126
|
+
@particle_ref.setter
|
|
127
|
+
def particle_ref(cls, val):
|
|
128
|
+
self = cls.get_self()
|
|
129
|
+
if val is None:
|
|
130
|
+
self._particle_ref = xp.Particles()
|
|
131
|
+
else:
|
|
132
|
+
if not isinstance(val, xp.Particles):
|
|
133
|
+
raise ValueError("`particle_ref` has to be an xp.Particles object!")
|
|
134
|
+
if val._capacity > 1:
|
|
135
|
+
raise ValueError("`particle_ref` has to be a single particle!")
|
|
136
|
+
if val.pdg_id[0] == 0:
|
|
137
|
+
if cls._only_protons:
|
|
138
|
+
val.pdg_id[0] = xp.get_pdg_id_from_name('proton')
|
|
139
|
+
else:
|
|
140
|
+
raise ValueError("`particle_ref` needs to have a valid pdg_id")
|
|
141
|
+
elif cls._only_protons and val.pdg_id[0] != xp.get_pdg_id_from_name('proton'):
|
|
142
|
+
raise ValueError("{cls.__name__} only supports protons!")
|
|
143
|
+
self._particle_ref = val
|
|
144
|
+
|
|
145
|
+
@particle_ref.deleter
|
|
146
|
+
def particle_ref(cls):
|
|
147
|
+
cls.get_self()._particle_ref = xp.Particles()
|
|
148
|
+
|
|
149
|
+
@ClassProperty
|
|
150
|
+
def capacity(cls):
|
|
151
|
+
self = cls.get_self()
|
|
152
|
+
if self._capacity == 0:
|
|
153
|
+
return None
|
|
154
|
+
else:
|
|
155
|
+
return int(self._capacity)
|
|
156
|
+
|
|
157
|
+
@capacity.setter
|
|
158
|
+
def capacity(cls, val):
|
|
159
|
+
if val is None:
|
|
160
|
+
val = 0
|
|
161
|
+
cls.get_self()._capacity = int(val)
|
|
162
|
+
|
|
163
|
+
@capacity.deleter
|
|
164
|
+
def capacity(cls):
|
|
165
|
+
raise ValueError("Not allowed.")
|
|
166
|
+
|
|
167
|
+
@ClassProperty
|
|
168
|
+
def seed(cls):
|
|
169
|
+
self = cls.get_self()
|
|
170
|
+
if self._seed == 0:
|
|
171
|
+
return None
|
|
172
|
+
else:
|
|
173
|
+
return self._seed
|
|
174
|
+
|
|
175
|
+
@seed.setter
|
|
176
|
+
def seed(cls, val):
|
|
177
|
+
if val is None:
|
|
178
|
+
val = 0
|
|
179
|
+
val = int(val)
|
|
180
|
+
if cls._int32:
|
|
181
|
+
new_val = np.uint32(val)
|
|
182
|
+
else:
|
|
183
|
+
new_val = np.uint64(val)
|
|
184
|
+
if new_val != val:
|
|
185
|
+
print(f"Warning: type change for seed {val}. Using {new_val}.")
|
|
186
|
+
cls.get_self()._seed = new_val
|
|
187
|
+
|
|
188
|
+
@seed.deleter
|
|
189
|
+
def seed(cls):
|
|
190
|
+
cls.get_self()._seed = 0
|
|
191
|
+
|
|
192
|
+
@ClassProperty
|
|
193
|
+
def input_file(cls):
|
|
194
|
+
return cls.get_self()._input_file
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# ======================
|
|
198
|
+
# === Public Methods ===
|
|
199
|
+
# ======================
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@classmethod
|
|
203
|
+
def start(cls, *, line=None, elements=None, names=None, cwd=None, seed=None,
|
|
204
|
+
particle_ref=None, input_file=None, **kwargs):
|
|
205
|
+
self = cls.get_self(**kwargs)
|
|
206
|
+
if self.is_running() is None:
|
|
207
|
+
raise NotImplementedError(f"Need to implement `is_running` for {cls.__name__}!")
|
|
208
|
+
elif self.is_running() is True:
|
|
209
|
+
if self.verbose:
|
|
210
|
+
print("Engine already running.", flush=True)
|
|
211
|
+
return
|
|
212
|
+
|
|
213
|
+
self._starting_engine = True # We need this to allow changing the element settings which otherwise are locked
|
|
214
|
+
self._use_seed(seed)
|
|
215
|
+
self._use_line(line)
|
|
216
|
+
self._use_particle_ref(particle_ref)
|
|
217
|
+
self._sync_line_particle_ref()
|
|
218
|
+
self._get_elements(line=line, elements=elements, names=names)
|
|
219
|
+
self._set_cwd(cwd=cwd)
|
|
220
|
+
self._use_input_file(input_file=input_file, **kwargs)
|
|
221
|
+
self.clean_output_files()
|
|
222
|
+
self._starting_engine = False
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@classmethod
|
|
226
|
+
def stop(cls, clean=False, **kwargs):
|
|
227
|
+
self = cls.get_self(**kwargs)
|
|
228
|
+
if hasattr(self, '_old_seed'):
|
|
229
|
+
self.seed = self._old_seed
|
|
230
|
+
del self._old_seed
|
|
231
|
+
if hasattr(self, '_old_line'):
|
|
232
|
+
self.line = self._old_line
|
|
233
|
+
del self._old_line
|
|
234
|
+
if hasattr(self, '_old_particle_ref'):
|
|
235
|
+
self.particle_ref = self._old_particle_ref
|
|
236
|
+
del self._old_particle_ref
|
|
237
|
+
self._sync_line_particle_ref()
|
|
238
|
+
if hasattr(self, '_old_cwd') and self._old_cwd is not None:
|
|
239
|
+
os.chdir(self._old_cwd)
|
|
240
|
+
del self._old_cwd
|
|
241
|
+
if clean:
|
|
242
|
+
self.clean_output_files(clean_all=True)
|
|
243
|
+
self._cwd = None
|
|
244
|
+
self._input_file = None
|
|
245
|
+
self._element_dict = {}
|
|
246
|
+
self._warning_given = False
|
|
247
|
+
self._tracking_initialised = False
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
@classmethod
|
|
251
|
+
def assert_particle_ref(cls, **kwargs):
|
|
252
|
+
if cls.get_self(**kwargs).particle_ref is None:
|
|
253
|
+
raise ValueError(f"{cls.__name__} reference particle not set!")
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
# =======================
|
|
257
|
+
# === Private Methods ===
|
|
258
|
+
# =======================
|
|
259
|
+
|
|
260
|
+
# For all the following fields, they can either be set in advance on the engine,
|
|
261
|
+
# or they can be set when the engine is started. In the latter case, the values
|
|
262
|
+
# are temporary and the original will be restored when the engine is stopped.
|
|
263
|
+
|
|
264
|
+
def _use_line(self, line=None):
|
|
265
|
+
self._old_line = self.line
|
|
266
|
+
self.line = line
|
|
267
|
+
|
|
268
|
+
def _use_seed(self, seed=None):
|
|
269
|
+
self._old_seed = self.seed
|
|
270
|
+
if seed is not None:
|
|
271
|
+
self.seed = seed
|
|
272
|
+
else:
|
|
273
|
+
if self.seed is None:
|
|
274
|
+
if self._int32:
|
|
275
|
+
self.seed = np.random.randint(0, int(2**32))
|
|
276
|
+
else:
|
|
277
|
+
self.seed = np.random.randint(0, int(2**64))
|
|
278
|
+
if self.verbose:
|
|
279
|
+
print(f"Using seed {self.seed}.")
|
|
280
|
+
|
|
281
|
+
def _use_particle_ref(self, particle_ref=None):
|
|
282
|
+
# Prefer: provided particle_ref > existing particle_ref > particle_ref from line
|
|
283
|
+
self._old_particle_ref = self.particle_ref
|
|
284
|
+
if particle_ref is not None:
|
|
285
|
+
self.particle_ref = particle_ref
|
|
286
|
+
elif self.particle_ref is None:
|
|
287
|
+
if self.line is None or not hasattr(self.line, 'particle_ref') \
|
|
288
|
+
or self.line.particle_ref is None:
|
|
289
|
+
raise ValueError("Need to provide either a line with a reference "
|
|
290
|
+
+ "particle, or `particle_ref`.")
|
|
291
|
+
self.particle_ref = self.line.particle_ref
|
|
292
|
+
if self.verbose:
|
|
293
|
+
print(f"Using {xp.get_name_from_pdg_id(self.particle_ref.pdg_id[0])}.")
|
|
294
|
+
|
|
295
|
+
def _sync_line_particle_ref(self):
|
|
296
|
+
if self.line is None:
|
|
297
|
+
return
|
|
298
|
+
if self.line.particle_ref is not None \
|
|
299
|
+
and not xt.line._dicts_equal(self.line.particle_ref.to_dict(),
|
|
300
|
+
self.particle_ref.to_dict()):
|
|
301
|
+
overwrite_particle_ref_in_line = True
|
|
302
|
+
if overwrite_particle_ref_in_line:
|
|
303
|
+
print("Warning: Found different reference particle in line! Temporarily overwritten.")
|
|
304
|
+
self.line.particle_ref = self.particle_ref
|
|
305
|
+
|
|
306
|
+
def _get_elements(self, line=None, elements=None, names=None):
|
|
307
|
+
if self._element_classes is None:
|
|
308
|
+
raise NotImplementedError(f"{self.__class__.__name__} needs to define `_element_classes`!")
|
|
309
|
+
if line is None:
|
|
310
|
+
if elements is None:
|
|
311
|
+
raise ValueError("Need to provide either `line` or `elements`.")
|
|
312
|
+
if not hasattr(elements, '__iter__') or isinstance(elements, str):
|
|
313
|
+
elements = [elements]
|
|
314
|
+
if names is None:
|
|
315
|
+
names = [f"{self.__class__.name}_el_{i}" for i, _ in enumerate(elements)]
|
|
316
|
+
else:
|
|
317
|
+
if not hasattr(names, '__iter__') or isinstance(names, str):
|
|
318
|
+
names = [names]
|
|
319
|
+
if len(names) != len(elements):
|
|
320
|
+
raise ValueError("Length of `elements` and `names` doesn't match.")
|
|
321
|
+
else:
|
|
322
|
+
if elements is not None:
|
|
323
|
+
raise ValueError("Cannot provide both `line` and `elements`.")
|
|
324
|
+
if names is None:
|
|
325
|
+
elements, names = line.get_elements_of_type(self._element_classes)
|
|
326
|
+
else:
|
|
327
|
+
if not hasattr(names, '__iter__') or isinstance(names, str):
|
|
328
|
+
names = [names]
|
|
329
|
+
elements = [line[name] for name in names]
|
|
330
|
+
elements = [el for el in elements if el.active and el.jaw is not None]
|
|
331
|
+
names = [name for el, name in zip(elements,names) if el.active and el.jaw is not None]
|
|
332
|
+
for el in elements:
|
|
333
|
+
if not isinstance(el, self._element_classes):
|
|
334
|
+
raise ValueError(f"Element {el} is not a "
|
|
335
|
+
+ ", or a ".join([c.__name__ for c in self._element_classes])
|
|
336
|
+
+ ".")
|
|
337
|
+
if len(elements) == 0:
|
|
338
|
+
raise ValueError(f"No active {self.name} elements found!")
|
|
339
|
+
self._element_dict = dict(zip(names, elements))
|
|
340
|
+
|
|
341
|
+
def _set_cwd(self, cwd):
|
|
342
|
+
if self._uses_run_folder:
|
|
343
|
+
if cwd is not None:
|
|
344
|
+
cwd = FsPath(cwd).expanduser().resolve()
|
|
345
|
+
else:
|
|
346
|
+
# TODO: use xaux.ranID here
|
|
347
|
+
import base64
|
|
348
|
+
ran = base64.urlsafe_b64encode(os.urandom(8)).decode('utf-8')
|
|
349
|
+
ran_str = ''.join(c if c.isalnum() else 'X' for c in ran)
|
|
350
|
+
cwd = FsPath.cwd() / f'{self.name}_run_{ran_str}'
|
|
351
|
+
self._cwd = cwd
|
|
352
|
+
cwd.mkdir(parents=True, exist_ok=True)
|
|
353
|
+
self._old_cwd = FsPath.cwd()
|
|
354
|
+
os.chdir(cwd)
|
|
355
|
+
|
|
356
|
+
def _use_input_file(self, input_file=None, **kwargs):
|
|
357
|
+
if self._uses_input_file:
|
|
358
|
+
if input_file is None:
|
|
359
|
+
if not hasattr(self, 'generate_input_file'):
|
|
360
|
+
raise NotImplementedError("Need to implement `generate_input_file`"
|
|
361
|
+
"for {cls.__name__}!")
|
|
362
|
+
input_file = self.generate_input_file(**kwargs)
|
|
363
|
+
if not hasattr(input_file, '__iter__') or isinstance(input_file, str):
|
|
364
|
+
# Some engines might need multiple input files (like Fluka)
|
|
365
|
+
input_file = [input_file]
|
|
366
|
+
input_file = [FsPath(f).expanduser().resolve() for f in input_file]
|
|
367
|
+
new_files = []
|
|
368
|
+
for file in input_file:
|
|
369
|
+
if not file.exists():
|
|
370
|
+
raise ValueError(f"Input file {file.as_posix()} not found!")
|
|
371
|
+
if file.parent != FsPath.cwd():
|
|
372
|
+
shutil.copy(file, FsPath.cwd())
|
|
373
|
+
new_files.append(FsPath.cwd() / file.name)
|
|
374
|
+
else:
|
|
375
|
+
new_files.append(file)
|
|
376
|
+
self._input_file = new_files[0] if len(new_files)==1 else new_files
|
|
377
|
+
self._match_input_file()
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
# ===============================
|
|
381
|
+
# === Methods to be inherited ===
|
|
382
|
+
# ===============================
|
|
383
|
+
|
|
384
|
+
@classmethod
|
|
385
|
+
def is_running(cls, **kwargs):
|
|
386
|
+
self = cls.get_self(**kwargs)
|
|
387
|
+
if hasattr(self, '_starting_engine') and self._starting_engine:
|
|
388
|
+
# We need this to allow changing the element settings which otherwise are locked
|
|
389
|
+
return False
|
|
390
|
+
# If we get here, we cannot say if the engine is running or not and we need an
|
|
391
|
+
# implementation in the child class
|
|
392
|
+
return None
|
|
393
|
+
|
|
394
|
+
@classmethod
|
|
395
|
+
def clean_output_files(cls, **kwargs):
|
|
396
|
+
pass
|
|
397
|
+
|
|
398
|
+
def _match_input_file(self, **kwargs):
|
|
399
|
+
raise NotImplementedError("Need to implement `_match_input_file` for {cls.__name__}!")
|
|
400
|
+
|
|
401
|
+
@classmethod
|
|
402
|
+
def generate_input_file(cls, **kwargs):
|
|
403
|
+
raise NotImplementedError("Need to implement `generate_input_file` for {cls.__name__}!")
|
|
@@ -171,7 +171,7 @@ double calculate_dechanneling_length(EverestData restrict everest, double pc) {
|
|
|
171
171
|
double energy = sqrt(pow(momentum, 2.) + pow(XC_PROTON_MASS, 2.)); // [MeV]
|
|
172
172
|
double gammar = energy/XC_PROTON_MASS;
|
|
173
173
|
|
|
174
|
-
double const_dech = 256.0/(9.*pow(M_PI, 2.)) / (log(2.*XC_ELECTRON_MASS*gammar/exenergy) - 1.);
|
|
174
|
+
double const_dech = 256.0/(9.*pow(M_PI, 2.)) / (log(2.*XC_ELECTRON_MASS*gammar/exenergy/1000) - 1.);
|
|
175
175
|
const_dech *= (XC_SCREENING*XC_PLANE_DISTANCE)/(XC_CRADE*XC_ELECTRON_MASS)*1.0e3; // [m/GeV]
|
|
176
176
|
return const_dech;
|
|
177
177
|
}
|
xcoll/xaux.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# copyright ############################### #
|
|
2
|
+
# This file is part of the Xcoll Package. #
|
|
3
|
+
# Copyright (c) CERN, 2024. #
|
|
4
|
+
# ######################################### #
|
|
5
|
+
|
|
6
|
+
import functools
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
FsPath = Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def singleton(cls):
|
|
13
|
+
# Store the original __new__ method if it exists
|
|
14
|
+
original_new = cls.__new__ if hasattr(cls, '__new__') else None
|
|
15
|
+
|
|
16
|
+
# Define a new __new__ method for the singleton
|
|
17
|
+
def singleton_new(cls, *args, **kwargs):
|
|
18
|
+
if not hasattr(cls, 'instance'):
|
|
19
|
+
cls.instance = (original_new(cls, *args, **kwargs)
|
|
20
|
+
if original_new
|
|
21
|
+
else super(cls, cls).__new__(cls))
|
|
22
|
+
cls.instance._initialised = False
|
|
23
|
+
return cls.instance
|
|
24
|
+
cls.__new__ = singleton_new
|
|
25
|
+
|
|
26
|
+
# Define the get_self method
|
|
27
|
+
@classmethod
|
|
28
|
+
def get_self(cls, **kwargs):
|
|
29
|
+
# Filter kwargs to include only ClassProperty attributes
|
|
30
|
+
filtered_kwargs = {key: value for key, value in kwargs.items()
|
|
31
|
+
if key in ClassProperty.get_properties(cls) or
|
|
32
|
+
key in getattr(cls, '_xofields', {})}
|
|
33
|
+
return cls(**filtered_kwargs)
|
|
34
|
+
cls.get_self = get_self
|
|
35
|
+
|
|
36
|
+
return cls
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ClassPropertyMeta(type):
|
|
41
|
+
def __setattr__(cls, key, value):
|
|
42
|
+
# Check if the attribute is a ClassProperty
|
|
43
|
+
for parent in cls.__mro__:
|
|
44
|
+
if key in parent.__dict__ and isinstance(parent.__dict__[key], ClassProperty):
|
|
45
|
+
return parent.__dict__[key].__set__(cls, value)
|
|
46
|
+
return super(ClassPropertyMeta, cls).__setattr__(key, value)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ClassProperty:
|
|
50
|
+
_registry = {} # Registry to store ClassProperty names for each class
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def get_properties(cls, owner, parents=True):
|
|
54
|
+
if not parents:
|
|
55
|
+
return cls._registry.get(owner, [])
|
|
56
|
+
else:
|
|
57
|
+
return [prop for parent in owner.__mro__
|
|
58
|
+
for prop in cls._registry.get(parent, [])]
|
|
59
|
+
|
|
60
|
+
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
|
|
61
|
+
functools.update_wrapper(self, fget)
|
|
62
|
+
self.fget = fget
|
|
63
|
+
self.fset = fset
|
|
64
|
+
self.fdel = fdel
|
|
65
|
+
if doc is None and fget is not None:
|
|
66
|
+
doc = fget.__doc__
|
|
67
|
+
self.__doc__ = doc
|
|
68
|
+
|
|
69
|
+
def __set_name__(self, owner, name):
|
|
70
|
+
self.name = name
|
|
71
|
+
# Verify that the class is a subclass of ClassPropertyMeta
|
|
72
|
+
if ClassPropertyMeta not in type(owner).__mro__:
|
|
73
|
+
raise AttributeError(f"Class `{owner.__name__}` must be a subtype of ClassPropertyMeta!")
|
|
74
|
+
# Add the property name to the registry for the class
|
|
75
|
+
if owner not in ClassProperty._registry:
|
|
76
|
+
ClassProperty._registry[owner] = []
|
|
77
|
+
ClassProperty._registry[owner].append(name)
|
|
78
|
+
# Create default getter, setter, and deleter
|
|
79
|
+
if self.fget is None:
|
|
80
|
+
def _getter(*args, **kwargs):
|
|
81
|
+
raise AttributeError(f"Unreadable attribute `{name}` for class {owner.__name__}!")
|
|
82
|
+
self.fget = _getter
|
|
83
|
+
if self.fset is None:
|
|
84
|
+
def _setter(self, *args, **kwargs):
|
|
85
|
+
raise AttributeError(f"Can't set attribute `{name}` for class {owner.__name__}!")
|
|
86
|
+
self.fset = _setter
|
|
87
|
+
if self.fdel is None:
|
|
88
|
+
def _deleter(*args, **kwargs):
|
|
89
|
+
raise AttributeError(f"Can't delete attribute `{name}` for class {owner.__name__}!")
|
|
90
|
+
self.fdel = _deleter
|
|
91
|
+
|
|
92
|
+
def __get__(self, instance, owner):
|
|
93
|
+
if owner is None:
|
|
94
|
+
owner = type(instance)
|
|
95
|
+
try:
|
|
96
|
+
return self.fget(owner)
|
|
97
|
+
except ValueError:
|
|
98
|
+
# Return a fallback if initialization fails
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
def __set__(self, cls, value):
|
|
102
|
+
self.fset(cls, value)
|
|
103
|
+
|
|
104
|
+
def __delete__(self, instance):
|
|
105
|
+
self.fdel(instance.__class__)
|
|
106
|
+
|
|
107
|
+
def getter(self, fget):
|
|
108
|
+
self.fget = fget
|
|
109
|
+
return self
|
|
110
|
+
|
|
111
|
+
def setter(self, fset):
|
|
112
|
+
self.fset = fset
|
|
113
|
+
return self
|
|
114
|
+
|
|
115
|
+
def deleter(self, fdel):
|
|
116
|
+
self.fdel = fdel
|
|
117
|
+
return self
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: xcoll
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.12
|
|
4
4
|
Summary: Xsuite collimation package
|
|
5
5
|
Home-page: https://github.com/xsuite/xcoll
|
|
6
6
|
License: Apache 2.0
|
|
@@ -18,10 +18,10 @@ Provides-Extra: tests
|
|
|
18
18
|
Requires-Dist: numpy (>=1.0)
|
|
19
19
|
Requires-Dist: pandas (>=1.4)
|
|
20
20
|
Requires-Dist: ruamel-yaml (>=0.17.31,<0.18.0) ; extra == "tests"
|
|
21
|
-
Requires-Dist: xdeps (>=0.8.
|
|
22
|
-
Requires-Dist: xobjects (>=0.4.
|
|
21
|
+
Requires-Dist: xdeps (>=0.8.4)
|
|
22
|
+
Requires-Dist: xobjects (>=0.4.6)
|
|
23
23
|
Requires-Dist: xpart (>=0.19.3)
|
|
24
|
-
Requires-Dist: xtrack (>=0.
|
|
24
|
+
Requires-Dist: xtrack (>=0.72.2)
|
|
25
25
|
Project-URL: Repository, https://github.com/xsuite/xcoll
|
|
26
26
|
Description-Content-Type: text/markdown
|
|
27
27
|
|
|
@@ -16,18 +16,19 @@ xcoll/beam_elements/elements_src/everest_crystal.h,sha256=WwwNF6it7TuOimhpSXJa7U
|
|
|
16
16
|
xcoll/beam_elements/everest.py,sha256=PA_VWpnPrIuO1xN__eKyT_ejbGZK7p93QHDVi3re7cM,8541
|
|
17
17
|
xcoll/beam_elements/monitor.py,sha256=zzMdN3JMFSAs-30_ntRvd5qZGdsXfGtColhiFDuMcIk,16928
|
|
18
18
|
xcoll/colldb.py,sha256=hGScl4lU93KDemHrgHOKfu2OqVAufP1BtgKKHJ_THDc,30501
|
|
19
|
-
xcoll/general.py,sha256=
|
|
19
|
+
xcoll/general.py,sha256=VqLfCrl6mLRBUaBR8TC0bGG8MH271fZEGKITEw3UfYo,535
|
|
20
20
|
xcoll/headers/checks.h,sha256=qdXsOTBOK1MwW6bdFF93j4yE648mcDtEv5rGN1w9sfk,1582
|
|
21
|
-
xcoll/headers/particle_states.h,sha256=
|
|
22
|
-
xcoll/initial_distribution.py,sha256=
|
|
21
|
+
xcoll/headers/particle_states.h,sha256=9hzp5abC_pIV6QrHy0iNm0qblcvsymI-pJQMv6v1Rhc,840
|
|
22
|
+
xcoll/initial_distribution.py,sha256=yjjEk2PJjE1YB9xDMeVDa-qFVlPTvVWEf8yynHvgAow,9015
|
|
23
23
|
xcoll/install.py,sha256=SxEFQnhWXlsXyPBIo847q6wPgId_f5ZtFRR1awGbkjc,2108
|
|
24
24
|
xcoll/interaction_record/__init__.py,sha256=UFoLiKa-z2oX7YoszP-7Vgdt1nM6kT382v1CaIu8_u0,50
|
|
25
25
|
xcoll/interaction_record/interaction_record.py,sha256=ixsQtVZn71vVEuTAA27a2NWcZZZ8iAcWFOa58bcWEQU,13271
|
|
26
26
|
xcoll/interaction_record/interaction_record_src/interaction_record.h,sha256=0rNagnfSGc2i1jauOMIcDbj9QFic9dV_MOyqVx1kw5Q,6067
|
|
27
27
|
xcoll/interaction_record/interaction_types.py,sha256=Vh6oFYKdRNOx9hc_E0iT8auNZVKC0qYVp_p7oyClZ8o,2894
|
|
28
|
-
xcoll/line_tools.py,sha256=
|
|
28
|
+
xcoll/line_tools.py,sha256=ZVUNRP_UI9RP7PBYBUVegww4J_BmdSo2dn3BS4ZAohI,14298
|
|
29
29
|
xcoll/lossmap.py,sha256=W2EzOe4aKmJFd8kEpebeQaAn1818QF3ih_nhA_br-2I,8062
|
|
30
30
|
xcoll/rf_sweep.py,sha256=P2X1S9pGi4OpNYnzYfQVyblFt2p8aw9EWHsKDkAuYt0,8936
|
|
31
|
+
xcoll/scattering_routines/engine.py,sha256=ZE2a_RbjGvoRZEr6XNGa6avsB_faiIqPBqb7SVWZyng,15018
|
|
31
32
|
xcoll/scattering_routines/everest/__init__.py,sha256=7lkkeZ1liBjXVHCuRpgzZI6ohzHVMj5uJBO792147XY,286
|
|
32
33
|
xcoll/scattering_routines/everest/amorphous.h,sha256=0eSV8F7yb2xrhrEGPZGWu_Lgr3gjtU8RddaJElsZ-Tk,10362
|
|
33
34
|
xcoll/scattering_routines/everest/channeling.h,sha256=k7ohwPec3gk3WCvwm-0C8cB39F__CO2yLyvKCWCg714,11050
|
|
@@ -39,7 +40,7 @@ xcoll/scattering_routines/everest/jaw.h,sha256=BWpfrHNPLaMF8JjsNAhWwy6TDI0jsiOT3
|
|
|
39
40
|
xcoll/scattering_routines/everest/materials.py,sha256=nO0ZayGRgqgF7Eku-mtxDl0BPOls5YS8a0qPPG32qw0,10260
|
|
40
41
|
xcoll/scattering_routines/everest/multiple_coulomb_scattering.h,sha256=NgrUEX-zCaiS1mwJvMLhfmg8GPNcPSGzh6UQKYqphJ8,5432
|
|
41
42
|
xcoll/scattering_routines/everest/nuclear_interaction.h,sha256=jr49uo2cCk5SiebMZtb_If2WFHSxNLpYYKDJiQbhW1g,3110
|
|
42
|
-
xcoll/scattering_routines/everest/properties.h,sha256=
|
|
43
|
+
xcoll/scattering_routines/everest/properties.h,sha256=9kf-a8EYGTVVZPkIzI-YXLW3ZvKEAJvACB8eSpcD2dM,6779
|
|
43
44
|
xcoll/scattering_routines/fluka/flukaio/.git,sha256=5ZxurpwP1waJk2Zu2AstENGOp4rXU8-C-oU4CypmIVI,73
|
|
44
45
|
xcoll/scattering_routines/fluka/flukaio/.gitignore,sha256=bhrpWiAnKChkcwIaIPX_zKxyG2nCtNzNgQAHnIJa8Fw,12
|
|
45
46
|
xcoll/scattering_routines/fluka/flukaio/CMakeLists.txt,sha256=iWWqTC26UqcuQWQj_JCKtX6iiOb7arnhQ9kOo1fDVkE,591
|
|
@@ -406,8 +407,9 @@ xcoll/scattering_routines/geometry/objects.h,sha256=A5ktGvVlSkC4hUsI_PQFsE80CuDw
|
|
|
406
407
|
xcoll/scattering_routines/geometry/rotation.h,sha256=lO3RaQBys9r0ROMjR8T8Rr7UsIEm-H9_C_70Nwz4MXY,701
|
|
407
408
|
xcoll/scattering_routines/geometry/segments.h,sha256=7nKnnin2ByxkKyaYwGvFaqgLQg5uBba4CdLHL7L3iQs,7667
|
|
408
409
|
xcoll/scattering_routines/geometry/sort.h,sha256=b1MkFO2ddzv1fWGeQzsLuz46qo2pKyRSXHjoAEVU7Ts,5763
|
|
409
|
-
xcoll
|
|
410
|
-
xcoll-0.5.
|
|
411
|
-
xcoll-0.5.
|
|
412
|
-
xcoll-0.5.
|
|
413
|
-
xcoll-0.5.
|
|
410
|
+
xcoll/xaux.py,sha256=KME-8QXkZwQLqgtuC2q7KM27WCCBDLWHR9zq7uVaMlc,4085
|
|
411
|
+
xcoll-0.5.12.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
412
|
+
xcoll-0.5.12.dist-info/METADATA,sha256=Ag2SZen5Cuuvjz6ZoIZjvxluHWDkaDEgsxVC4XLU9Io,2676
|
|
413
|
+
xcoll-0.5.12.dist-info/NOTICE,sha256=6DO_E7WCdRKc42vUoVVBPGttvQi4mRt9fAcxj9u8zy8,74
|
|
414
|
+
xcoll-0.5.12.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
415
|
+
xcoll-0.5.12.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|