xcoll 0.5.8__py3-none-any.whl → 0.5.9__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.

@@ -0,0 +1,32 @@
1
+ # copyright ############################### #
2
+ # This file is part of the Xcoll package. #
3
+ # Copyright (c) CERN, 2024. #
4
+ # ######################################### #
5
+
6
+ from .base import BaseBlock, BaseCollimator, BaseCrystal
7
+ from .absorber import BlackAbsorber, BlackCrystal
8
+ from .everest import EverestBlock, EverestCollimator, EverestCrystal
9
+ from .blowup import BlowUp
10
+ from .monitor import EmittanceMonitor
11
+
12
+ block_classes = tuple(v for v in globals().values()
13
+ if isinstance(v, type) and issubclass(v, BaseBlock) and v != BaseBlock
14
+ and v != BaseCollimator and v != BaseCrystal)
15
+ # Includes crystals
16
+ collimator_classes = tuple(v for v in globals().values()
17
+ if isinstance(v, type) and (issubclass(v, BaseCollimator) or issubclass(v, BaseCrystal))
18
+ and v != BaseCollimator and v != BaseCrystal)
19
+ crystal_classes = tuple(v for v in globals().values()
20
+ if isinstance(v, type) and issubclass(v, BaseCrystal) and v != BaseCrystal)
21
+
22
+ element_classes = block_classes + (BlowUp, EmittanceMonitor)
23
+ <<<<<<< HEAD
24
+
25
+ # These should not go into any of the classes lists
26
+ # but are added for compatibility with _K2
27
+ from .k2 import _K2Collimator, _K2Crystal
28
+ _all_block_classes = block_classes + (_K2Collimator, _K2Crystal)
29
+ _all_collimator_classes = collimator_classes + (_K2Collimator, _K2Crystal)
30
+ _all_crystal_classes = crystal_classes + (_K2Crystal,)
31
+ =======
32
+ >>>>>>> release/v0.6.0
xcoll/colldb.py.orig ADDED
@@ -0,0 +1,654 @@
1
+ # copyright ############################### #
2
+ # This file is part of the Xcoll package. #
3
+ # Copyright (c) CERN, 2024. #
4
+ # ######################################### #
5
+
6
+ import io
7
+ import re
8
+ import json
9
+ import numpy as np
10
+ import pandas as pd
11
+ from pathlib import Path
12
+
13
+ import xtrack as xt
14
+
15
+ from .beam_elements import BlackAbsorber, BlackCrystal, EverestCollimator, EverestCrystal, \
16
+ <<<<<<< HEAD
17
+ FlukaCollimator, BaseCollimator, BaseCrystal, _all_collimator_classes
18
+ =======
19
+ BaseCollimator, BaseCrystal, collimator_classes
20
+ >>>>>>> release/v0.6.0
21
+ from .scattering_routines.everest.materials import SixTrack_to_xcoll
22
+ from .scattering_routines.fluka import FlukaEngine
23
+
24
+
25
+ def _initialise_None(dct):
26
+ fields = {'gap': None, 'angle': 0, 'offset': 0, 'parking': 1, 'jaw': None, 'family': None}
27
+ fields.update({'overwritten_keys': [], 'side': 'both', 'material': None, 'stage': None})
28
+ fields.update({'length': 0, 'collimator_type': None, 'active': True, 'crystal': None, 'tilt': 0})
29
+ fields.update({'bending_radius': None, 'bending_angle': None, 'width': 0, 'height': 0, 'miscut': 0})
30
+ for f, val in fields.items():
31
+ if f not in dct.keys():
32
+ dct[f] = val
33
+ for key in dct.keys():
34
+ if key not in fields.keys():
35
+ raise ValueError(f"Illegal setting {key} in collimator!")
36
+
37
+
38
+ def _dict_keys_to_lower(dct):
39
+ if isinstance(dct, dict):
40
+ return {k.lower(): _dict_keys_to_lower(v) for k,v in dct.items()}
41
+ else:
42
+ return dct
43
+
44
+
45
+ def _get_coll_dct_by_beam(coll, beam):
46
+ # The dictionary can be a CollimatorDatabase for a single beam (beam=None)
47
+ # or for both beams (beam='b1' or beam='b2)
48
+ if beam is not None:
49
+ if isinstance(beam, int) or len(beam) == 1:
50
+ beam = f'b{beam}'
51
+ beam = beam.lower()
52
+ beam_in_db = list(coll.keys())
53
+
54
+ if beam_in_db == ['b1','b2']:
55
+ if beam is None:
56
+ raise ValueError("Need to specify a beam, because the given dict is for both beams!")
57
+ return coll[beam]
58
+
59
+ elif len(beam_in_db) == 1:
60
+ if beam is None:
61
+ beam = beam_in_db[0].lower()
62
+ elif beam != beam_in_db[0].lower():
63
+ raise ValueError("Variable 'beam' does not match beam specified in CollimatorDatabase!")
64
+ return coll[beam]
65
+
66
+ elif beam is not None:
67
+ print("Warning: Specified a beam, but the CollimatorDatabase is for a single beam only!")
68
+ return coll
69
+
70
+
71
+ class CollimatorDatabase:
72
+
73
+ def __init__(self, **kwargs):
74
+ # Get all arguments
75
+ for key in ['collimator_dict', 'nemitt_x', 'nemitt_y']:
76
+ if key not in kwargs.keys():
77
+ raise ValueError(f"CollimatorDatabase is missing required argument '{key}'!")
78
+
79
+ self._parse_dict(kwargs['collimator_dict'],
80
+ kwargs.get('family_dict', {}),
81
+ kwargs.get('beam', None),
82
+ kwargs.get('_yaml_merged', False),
83
+ kwargs.get('ignore_crystals', True))
84
+ self.nemitt_x = kwargs['nemitt_x']
85
+ self.nemitt_y = kwargs['nemitt_y']
86
+ self._elements = {}
87
+
88
+
89
+ def _parse_dict(self, coll, fam, beam=None, _yaml_merged=False, ignore_crystals=True):
90
+ # We make all keys case-insensitive to avoid confusion between different conventions
91
+ coll = _dict_keys_to_lower(coll)
92
+ fam = _dict_keys_to_lower(fam)
93
+
94
+ # The dictionary can be a CollimatorDatabase for a single beam (beam=None)
95
+ # or for both beams (beam='b1' or beam='b2)
96
+ coll = _get_coll_dct_by_beam(coll, beam)
97
+
98
+ # Apply family settings
99
+ crystals = []
100
+ for thiscoll, settings in coll.items():
101
+ settings = {k.lower(): v for k,v in settings.items()}
102
+ if 'family' in settings.keys() and settings['family'] is not None:
103
+ settings['family'] = settings['family'].lower()
104
+ thisfam = settings['family']
105
+ if thisfam not in fam.keys():
106
+ raise ValueError(f"Collimator {thiscoll} depends on family {thisfam}, "
107
+ + f"but the latter is not defined!")
108
+
109
+ # Check if some family settings are overwritten for this collimator
110
+ # Only do this check if we didn't do a YAML merge earlier (because then it
111
+ # is already taken care of)
112
+ if not _yaml_merged:
113
+ overwritten_keys = [key.lower() for key in settings.keys() if key in fam[thisfam]]
114
+ if len(overwritten_keys) > 0:
115
+ settings['overwritten_keys'] = overwritten_keys
116
+
117
+ # Load family settings, potentially overwriting settings for this collimator
118
+ settings = {**fam[thisfam], **settings}
119
+
120
+ else:
121
+ settings['family'] = None
122
+ coll[thiscoll] = settings
123
+
124
+ # Save list of crystals
125
+ if 'crystal' in settings:
126
+ if settings['crystal'] != 0.0:
127
+ crystals += [thiscoll]
128
+ else:
129
+ settings['crystal'] = None
130
+
131
+ # Remove crystals from colldb
132
+ if ignore_crystals:
133
+ for thiscoll in crystals:
134
+ del coll[thiscoll]
135
+
136
+ # Check that all collimators have gap settings
137
+ if not np.all(['gap' in val.keys() or 'jaw' in val.keys() for val in coll.values()]):
138
+ raise ValueError("Ill-defined CollimatorDatabase: Not all collimators have a gap or "
139
+ + "jaw setting, (or the keys / structure of the dictionary is wrong)!")
140
+
141
+ # Update collimators with default values for missing keys
142
+ for name, collimator in coll.items():
143
+ # Change all values to lower case
144
+ for key, val in collimator.items():
145
+ collimator[key] = val.lower() if isinstance(val, str) else val
146
+ _initialise_None(collimator)
147
+
148
+ self._collimator_dict = coll
149
+ self._family_dict = fam
150
+
151
+ # =======================================
152
+ # ====== Loading/dumping functions ======
153
+ # =======================================
154
+
155
+ @classmethod
156
+ def from_yaml(cls, file, **kwargs):
157
+ # Only do the import here, as to not force people to install
158
+ # ruamel if they don't load CollimatorDatabase yaml's
159
+ from ruamel.yaml import YAML
160
+ yaml = YAML(typ='safe')
161
+ if isinstance(file, io.IOBase):
162
+ dct = yaml.load(file)
163
+ else:
164
+ file = Path(file).resolve()
165
+ with file.open('r') as fid:
166
+ dct = yaml.load(fid)
167
+ dct = _dict_keys_to_lower(dct)
168
+
169
+ # If the CollimatorDatabase uses YAML merging, we need a bit of hackery to get the
170
+ # family names from the tags (anchors/aliases)
171
+ _yaml_merged = False
172
+ if 'families' in dct.keys() and not isinstance(dct['families'], dict):
173
+ _yaml_merged = True
174
+
175
+ # First we load a round-trip yaml
176
+ yaml = YAML()
177
+ if isinstance(file, io.IOBase):
178
+ full_dct = yaml.load(file)
179
+ else:
180
+ with open(file, 'r') as fid:
181
+ full_dct = yaml.load(fid)
182
+ famkey = [key for key in full_dct.keys() if key.lower() == 'families'][0]
183
+ collkey = [key for key in full_dct.keys() if key.lower() == 'collimators'][0]
184
+ families = {}
185
+
186
+ # We loop over family names to get the name tag ('anchor') of each family
187
+ for fam, full_fam in zip(dct['families'], full_dct[famkey]):
188
+ if full_fam.anchor.value is None:
189
+ raise ValueError("Missing name tag / anchor in "
190
+ + "CollimatorDatabase['families']!")
191
+ # We get the anchor from the rt yaml, and use it as key in the families dict
192
+ families[full_fam.anchor.value.lower()] = {f.lower(): v for f, v in fam.items()}
193
+ dct['families'] = families
194
+
195
+ # Now we need to loop over each collimator, and verify which family was used
196
+ beam = kwargs.get('beam', None)
197
+ coll_dct = _get_coll_dct_by_beam(dct['collimators'], beam)
198
+ full_coll_dct = _get_coll_dct_by_beam(full_dct[collkey], beam)
199
+ for coll, full_coll in zip(coll_dct.values(), full_coll_dct.values()):
200
+ if not isinstance(coll['gap'], (int,float)):
201
+ coll['gap'] = None
202
+ if 'family' in coll.keys():
203
+ raise ValueError(f"Error in {coll}: Cannot use merging for families "
204
+ + "and manually specify family as well!")
205
+ elif len(full_coll.merge) > 0:
206
+ coll['family'] = full_coll.merge[0][1].anchor.value.lower()
207
+ # Check if some family settings are overwritten for this collimator
208
+ overwritten_keys = [key.lower() for key in full_coll.keys()
209
+ if full_coll._unmerged_contains(key)
210
+ and key.lower() in families[coll['family']].keys()]
211
+ if len(overwritten_keys) > 0:
212
+ coll['overwritten_keys'] = overwritten_keys
213
+ else:
214
+ coll['family'] = None
215
+
216
+ return cls.from_dict(dct, _yaml_merged=_yaml_merged, **kwargs)
217
+
218
+
219
+ @classmethod
220
+ def from_json(cls, file, **kwargs):
221
+ if isinstance(file, io.IOBase):
222
+ dct = json.load(file)
223
+ else:
224
+ file = Path(file).resolve()
225
+ with file.open('r') as fid:
226
+ dct = json.load(fid)
227
+ dct = _dict_keys_to_lower(dct)
228
+ return cls.from_dict(dct, **kwargs)
229
+
230
+
231
+ @classmethod
232
+ def from_dict(cls, dct, beam=None, _yaml_merged=False, nemitt_x=None, nemitt_y=None, ignore_crystals=True):
233
+ # We make all keys case-insensitive to avoid confusion between different conventions
234
+ # The families are optional
235
+ fam = {}
236
+ dct = _dict_keys_to_lower(dct)
237
+
238
+ # Get the emittance
239
+ if nemitt_x is None and nemitt_y is None:
240
+ if 'emittance' not in dct.keys():
241
+ raise ValueError("Missing emittance info! Add 'emittance' as a key to "
242
+ + "the CollimatorDatabase file, or specify it as 'nemitt_x' "
243
+ + "and 'nemitt_y' to the loader!")
244
+ nemitt_x = dct['emittance']['x']
245
+ nemitt_y = dct['emittance']['y']
246
+ elif nemitt_x is None or nemitt_y is None:
247
+ raise ValueError("Need to provide both 'nemitt_x' and 'nemitt_y'!")
248
+ elif 'emittance' in dct.keys():
249
+ if dct['emittance']['x'] != nemitt_x or dct['emittance']['y'] != nemitt_y:
250
+ raise ValueError("Emittance in CollimatorDatabase file different from "
251
+ + "'nemitt_x' and 'nemitt_y'!")
252
+
253
+ # Get family and collimator dicts
254
+ if 'families' in dct.keys():
255
+ if not 'collimators' in dct.keys():
256
+ raise ValueError("Could not find 'collimators' dict in CollimatorDatabase!")
257
+ fam = dct['families']
258
+ coll = dct['collimators']
259
+ elif 'collimators' in dct.keys():
260
+ coll = dct['collimators']
261
+ else:
262
+ coll = dct
263
+
264
+ return cls(collimator_dict=coll, family_dict=fam, nemitt_x=nemitt_x, nemitt_y=nemitt_y,
265
+ beam=beam, _yaml_merged=_yaml_merged, ignore_crystals=ignore_crystals)
266
+
267
+
268
+ @classmethod
269
+ def from_SixTrack(cls, file, ignore_crystals=True, **kwargs):
270
+ file = Path(file).resolve()
271
+ with file.open('r') as fp:
272
+ coll_data_string = ''
273
+ family_settings = {}
274
+ family_types = {}
275
+ side = {}
276
+ cry_fields = ['bending_radius', 'width', 'height', 'thick', 'tilt', 'miscut', 'crystal']
277
+ cry = {}
278
+
279
+ for line in fp:
280
+ if line.startswith('#'):
281
+ continue # Comment
282
+ sline = line.split()
283
+ if len(sline) > 0:
284
+ if sline[0].lower() == 'nsig_fam':
285
+ family_settings[sline[1]] = float(sline[2])
286
+ family_types[sline[1]] = sline[3]
287
+ elif sline[0].lower() == 'onesided':
288
+ side[sline[1]] = int(sline[2])
289
+ elif sline[0].lower() == "crystal":
290
+ cry[sline[1]] = {}
291
+ for i, key in enumerate(cry_fields):
292
+ idx = i+2
293
+ if i < 6:
294
+ cry[sline[1]][key] = float(sline[idx])
295
+ else:
296
+ cry[sline[1]][key] = int(sline[idx])
297
+ elif sline[0].lower() == 'settings':
298
+ pass # Acknowledge and ignore this line
299
+ elif len(sline) == 6:
300
+ # Standard collimator definition
301
+ coll_data_string += line
302
+ else:
303
+ print(f"Unknown setting {line}")
304
+
305
+ defaults = {}
306
+ _initialise_None(defaults)
307
+ defaults['thick'] = 0
308
+
309
+ famdct = {key: {'gap': None if family_settings[key] > 900 else family_settings[key],
310
+ 'stage': family_types[key]} for key in family_settings}
311
+ names = ['name', 'gap', 'material', 'length', 'angle', 'offset']
312
+
313
+ df = pd.read_csv(io.StringIO(coll_data_string), sep=r'\s+', index_col=False, names=names)
314
+ df['family'] = df['gap'].copy()
315
+ df['family'] = df['family'].apply(lambda s: None if re.match(r'^-?\d+(\.\d+)?$', str(s)) else s)
316
+ df.insert(5,'stage', df['gap'].apply(lambda s: None if s in family_types else 'UNKNOWN'))
317
+
318
+ df['gap'] = df['gap'].apply(lambda s: None if not isinstance(s, str) and s > 900 else s)
319
+ df['gap'] = df['gap'].apply(lambda s: None if isinstance(s, str) else s)
320
+
321
+ # TODO this breaks code if a key has upper case, e.g. gap_L
322
+ df['name'] = df['name'].str.lower() # Make the names lowercase for easy processing
323
+ df['parking'] = 0.025
324
+ if ignore_crystals:
325
+ df = df[~df.name.isin(list(cry.keys()))]
326
+ else:
327
+ for key in cry_fields:
328
+ df[key] = [cry[name][key] if name in cry else defaults[key]
329
+ for name in df['name']]
330
+ if not np.allclose(np.unique(df.thick.values), 0):
331
+ print("Warning: Keyword 'thick' is currently not supported in xcoll! Ignoring.")
332
+ df = df.drop('thick', axis=1)
333
+ df['crystal'] = ['strip' if s==1 else s for s in df['crystal']]
334
+ df['crystal'] = ['quasi-mosaic' if s==2 else s for s in df['crystal']]
335
+ df['side'] = [side[name] if name in side else defaults['side']
336
+ for name in df['name']]
337
+ df['side'] = ['both' if s==0 else s for s in df['side']]
338
+ df['side'] = ['left' if s==1 else s for s in df['side']]
339
+ df['side'] = ['right' if s==2 else s for s in df['side']]
340
+ if not np.allclose(np.unique(df.offset.values), 0):
341
+ print("Warning: Keyword 'offset' is currently not supported in xcoll! Ignoring.")
342
+ df = df.drop('offset', axis=1)
343
+ df = df.set_index('name')
344
+ df = df.replace(np.nan, None)
345
+
346
+ colldict = df.transpose().to_dict()
347
+ # Remove Nonetype families
348
+ colldict = {coll: {kk: vv for kk, vv in coll_settings.items()
349
+ if kk != 'family' or vv is not None}
350
+ for coll, coll_settings in colldict.items()}
351
+ # Remove None gaps and stages if the collimator is assigned a family (they will be set in _parse_dict)
352
+ colldict = {coll: {kk: vv for kk, vv in coll_settings.items()
353
+ if kk != 'gap' or vv is not None or 'family' not in coll_settings}
354
+ for coll, coll_settings in colldict.items()}
355
+ colldict = {coll: {kk: vv for kk, vv in coll_settings.items()
356
+ if kk != 'stage' or vv is not None or 'family' not in coll_settings}
357
+ for coll, coll_settings in colldict.items()}
358
+
359
+ return cls.from_dict({'collimators': colldict, 'families': famdct}, \
360
+ ignore_crystals=ignore_crystals, **kwargs)
361
+
362
+ def to_pandas(self):
363
+ return pd.DataFrame(self._collimator_dict).transpose()
364
+
365
+ def to_yaml(self, out, lhc_style=True):
366
+ """
367
+ Writes a colldb in memory to disk in the yaml format.
368
+
369
+ > colldb_object.write_to_yaml(<path+name>, lhc_style=Bool)
370
+
371
+ if lhc_style == True, it will add comments assuming that the collimators are named
372
+ as in the lhc.
373
+
374
+ The function can dump b1, b2 and a general bx, however multi-beam functionality is not yet
375
+ added to the collmanager. TODO
376
+
377
+ If any of the dumped keys contains capital letters (e.g. gap_L), it will not be possible
378
+ to load it back into xcoll, since all keys are set to lowercase when importing TODO
379
+ """
380
+ # Dumps collimator database to a YAML file with optional LHC style formatting
381
+ import re
382
+
383
+ # Local helper functions
384
+ def _format_dict_entry(key, value, spacing='', mapping=False, key_width=15):
385
+ # Formats a dictionary entry into a string for YAML output
386
+ formatted_values = ', '.join(f"{k}: {v}" for k, v in value.items())
387
+ formatted_values = re.sub(r'none', 'null', formatted_values, flags=re.IGNORECASE)
388
+ # Ensure key has a fixed width for alignment
389
+ if mapping:
390
+ formatted_key = f'{key}'.ljust(key_width)
391
+ else:
392
+ formatted_key = f'{key}:'.ljust(key_width)
393
+ #formatted_values = formatted_values.ljust(key_width)
394
+ return f"{spacing}{formatted_key} {{ {formatted_values} }}\n"
395
+
396
+ def _print_values(keys, dct, file, spacing='', mapping=False):
397
+ # Writes formatted dictionary entries to a file
398
+ for key in keys:
399
+ file.write(_format_dict_entry(key, dct[key], spacing=spacing, mapping=mapping))
400
+
401
+ def _print_colls(colls, dcts, beam, file):
402
+ # Filters and formats collimator data, then writes to a file
403
+ coll_items_to_print = ['<<','gap','angle','material','active','length','side']
404
+ file.write(f' {beam}:\n')
405
+ for coll in colls:
406
+ coll_dict = dcts.to_pandas().transpose().to_dict()[coll]
407
+ fam = coll_dict['family']
408
+ fam_keys = []
409
+ if fam is not None:
410
+ fam_keys = dcts._family_dict[fam].keys()
411
+ coll_dict = {**{'<<': '*'+fam}, **coll_dict}
412
+ temp_items_to_print = []
413
+ if coll_dict['crystal'] and str(coll_dict['crystal'])!='nan':
414
+ temp_items_to_print = ['bending_radius','width','height','miscut','crystal']
415
+ # if 'angle_L' in coll_dict and coll_dict['angle_L'] == coll_dict['angle_R']:
416
+ # coll_dict.update({'angle': coll_dict['angle_L']})
417
+ # else:
418
+ # temp_items_to_print = temp_items_to_print + ['angle_L','angle_R']
419
+ # if coll_dict['gap_L'] == coll_dict['gap_R']:
420
+ # coll_dict.update({'gap': coll_dict['gap_L']})
421
+ # elif coll_dict['gap_L'] is None and coll_dict['gap_R'] is not None:
422
+ # coll_dict.update({'gap': coll_dict['gap_R']})
423
+ # elif coll_dict['gap_L'] is not None and coll_dict['gap_R'] is None:
424
+ # coll_dict.update({'gap': coll_dict['gap_L']})
425
+ # else:
426
+ # temp_items_to_print = temp_items_to_print + ['gap_L','gap_R']
427
+ value = {}
428
+ overwritten_keys = coll_dict['overwritten_keys']
429
+ for key, val in coll_dict.items():
430
+ if key == 'active_length':
431
+ key = 'length'
432
+ if (key in coll_items_to_print+temp_items_to_print) and (key not in (set(fam_keys)-set(overwritten_keys))) and (val != 'both'):
433
+ value.update({key: val})
434
+ file.write(_format_dict_entry(coll, value, spacing=' '))
435
+ file.write('\n')
436
+
437
+ LHC_families = ['tcp3', 'tcsg3', 'tcsm3', 'tcla3', 'tcp7', 'tcsg7', 'tcsm7', 'tcla7', 'tcli', 'tdi', 'tcdq', 'tcstcdq', 'tcth1', 'tcth2', 'tcth5', 'tcth8', 'tctv1', 'tctv2', 'tctv5', 'tctv8', 'tclp', 'tcxrp', 'tcryo', 'tcl4', 'tcl5', 'tcl6', 'tct15', 'tct2', 'tct8', 'tcsp', 'tcld']
438
+ with open(f'{out}.yaml', 'w') as file:
439
+ if '_family_dict' in self.__dict__.keys():
440
+ file.write('families:\n')
441
+ if lhc_style:
442
+ printed_families = []
443
+ fams_in_dict = self._family_dict.keys()
444
+
445
+ # Momentum cleaning
446
+ file.write(' # Momentum cleaning\n')
447
+ sel_fam = [fam for fam in LHC_families if re.match('.*3', fam) and (fam in fams_in_dict)]
448
+ printed_families += sel_fam
449
+ _print_values(sel_fam, self._family_dict, file, spacing=' - &', mapping=True)
450
+
451
+ # Betatron cleaning
452
+ file.write(' # Betatron cleaning\n')
453
+ sel_fam = [fam for fam in LHC_families if re.match('.*7', fam) and (fam in fams_in_dict)]
454
+ printed_families += sel_fam
455
+ _print_values(sel_fam, self._family_dict, file, spacing=' - &', mapping=True)
456
+
457
+ # Injection protection
458
+ file.write(' # Injection protection\n')
459
+ sel_fam = [fam for fam in LHC_families if (fam in ['tcli', 'tdi']) and (fam in fams_in_dict)]
460
+ printed_families += sel_fam
461
+ _print_values(sel_fam, self._family_dict, file, spacing=' - &', mapping=True)
462
+
463
+ # Dump protection
464
+ file.write(' # Dump protection\n')
465
+ sel_fam = [fam for fam in LHC_families if (fam in ['tcdq', 'tcsp', 'tcstcdq']) and (fam in fams_in_dict)]
466
+ printed_families += sel_fam
467
+ _print_values(sel_fam, self._family_dict, file, spacing=' - &', mapping=True)
468
+
469
+ # Physics background / debris
470
+ file.write(' # Physics background / debris\n')
471
+ sel_fam = [fam for fam in LHC_families if ((re.match('tc[lt][0-9dp].*', fam)) or (fam in ['tcryo', 'tcxrp'])) and (fam in fams_in_dict)]
472
+ printed_families += sel_fam
473
+ _print_values(sel_fam, self._family_dict, file, spacing=' - &', mapping=True)
474
+
475
+ # Other families
476
+ if set(printed_families) != set(fams_in_dict):
477
+ file.write(' # Other families\n')
478
+ _print_values(set(fams_in_dict) - set(printed_families), self._family_dict, file, spacing=' - &', mapping=True)
479
+ else:
480
+ file.write(' # Families\n')
481
+ _print_values(self._family_dict.keys(), self._family_dict, file, spacing=' - &', mapping=True)
482
+
483
+ # Emittance section
484
+ ex = self.nemitt_x
485
+ ey = self.nemitt_y
486
+ file.write(f'\nemittance:\n x: {ex}\n y: {ey}\n')
487
+
488
+ # Collimators section
489
+ file.write('\ncollimators:\n')
490
+ b1_colls, b2_colls, bx_colls = [], [], []
491
+ for coll in self.to_pandas().index:
492
+ if coll == 'tclia.4r2' or coll == 'tclia.4l8': # TODO: hardcoded!!!
493
+ b1_colls.append(coll)
494
+ b2_colls.append(coll)
495
+ elif coll[-2:] == 'b1':
496
+ b1_colls.append(coll)
497
+ elif coll[-2:] == 'b2':
498
+ b2_colls.append(coll)
499
+ else:
500
+ bx_colls.append(coll)
501
+
502
+ # Handle special cases for collimators
503
+ if (('tclia.4r2' in b1_colls) or ('tclia.4l8' in b1_colls)) and (len(b1_colls) <= 2):
504
+ b1_colls = []
505
+ if (('tclia.4r2' in b2_colls) or ('tclia.4l8' in b2_colls)) and (len(b2_colls) <= 2):
506
+ b2_colls = []
507
+
508
+ # Print collimators for each beam
509
+ if len(b1_colls) > 0:
510
+ _print_colls(b1_colls, self, 'b1', file)
511
+ if len(b2_colls) > 0:
512
+ _print_colls(b2_colls, self, 'b2', file)
513
+ if len(bx_colls) > 0:
514
+ _print_colls(bx_colls, self, 'bx', file)
515
+ print('WARNING -- some collimators could not be assigned to b1 or b2. Tracking might not work with those collimators. Please manually change the output file if necessary.')
516
+
517
+
518
+ # ====================================
519
+ # ====== Installing collimators ======
520
+ # ====================================
521
+
522
+ def _get_names_from_line(self, line, names, families):
523
+ if names is None and families is None:
524
+ names = self.collimator_names
525
+ elif names is None:
526
+ names = self.get_collimators_from_family(families)
527
+ elif families is not None:
528
+ names.append(self.get_collimators_from_family(families))
529
+ return list(set(names)) # Remove duplicates
530
+
531
+ def _check_installed(self, line, name, collimator_class):
532
+ # Check that collimator is not installed as different type
533
+ # TODO: automatically replace collimator type and print warning
534
+ if isinstance(line[name], collimator_classes):
535
+ raise ValueError(f"Trying to install {name} as {collimator_class.__name__}, "
536
+ + f"but it is already installed as {line[name].__class__.__name__}!\n"
537
+ + f"Please reconstruct the line.")
538
+ # TODO: only allow Marker elements, no Drifts!!
539
+ # How to do this with importing a line for MAD-X or SixTrack...?
540
+ # Maybe we want a DriftCollimator type in Xtrack as a general placeholder
541
+ elif not isinstance(line[name], (xt.Marker, xt.Drift)):
542
+ raise ValueError(f"Trying to install {name} as {collimator_class.__name__}, "
543
+ + f"but the line element to replace is not an xtrack.Marker "
544
+ + f"(or xtrack.Drift)!\nPlease check the name, or correct the "
545
+ + f"element.")
546
+
547
+ def _create_collimator(self, line, collimator_class, name, **kwargs):
548
+ assert issubclass(collimator_class, BaseCollimator)
549
+ self._check_installed(line, name, collimator_class)
550
+ if kwargs.pop('verbose', False):
551
+ print(f"Installing {name:20} as {collimator_class.__name__}")
552
+ el = collimator_class(gap=self[name]['gap'], angle=self[name]['angle'],
553
+ length=self[name]['length'], side=self[name]['side'],
554
+ _tracking=False, **kwargs)
555
+ el.emittance = [self.nemitt_x, self.nemitt_y]
556
+ self._elements[name] = el
557
+
558
+ def _create_crystal(self, line, crystal_class, name, **kwargs):
559
+ assert issubclass(crystal_class, BaseCrystal)
560
+ self._check_installed(line, name, crystal_class)
561
+ if kwargs.pop('verbose', False):
562
+ print(f"Installing {name:20} as {crystal_class.__name__}")
563
+ el = crystal_class(gap=self[name]['gap'], angle=self[name]['angle'],
564
+ length=self[name]['length'], side=self[name]['side'],
565
+ bending_radius=self[name]['bending_radius'],
566
+ width=self[name]['width'], height=self[name]['height'],
567
+ _tracking=False, **kwargs)
568
+ el.emittance = [self.nemitt_x, self.nemitt_y]
569
+ self._elements[name] = el
570
+
571
+ def install_black_absorbers(self, line, *, names=None, families=None, verbose=False, need_apertures=True):
572
+ names = self._get_names_from_line(line, names, families)
573
+ for name in names:
574
+ if self[name]['bending_radius'] is None:
575
+ self._create_collimator(line, BlackAbsorber, name, verbose=verbose)
576
+ else:
577
+ self._create_crystal(line, BlackCrystal, name, verbose=verbose)
578
+ elements = [self._elements[name] for name in names]
579
+ line.collimators.install(names, elements, need_apertures=need_apertures)
580
+
581
+ def install_everest_collimators(self, line, *, names=None, families=None, verbose=False, need_apertures=True):
582
+ names = self._get_names_from_line(line, names, families)
583
+ for name in names:
584
+ mat = SixTrack_to_xcoll(self[name]['material'])
585
+ if self[name]['bending_radius'] is None:
586
+ self._create_collimator(line, EverestCollimator, name, material=mat[0],
587
+ verbose=verbose)
588
+ else:
589
+ self._create_crystal(line, EverestCrystal, name, material=mat[1],
590
+ lattice=self[name]['crystal'], verbose=verbose,
591
+ miscut=self[name]['miscut'])
592
+ elements = [self._elements[name] for name in names]
593
+ line.collimators.install(names, elements, need_apertures=need_apertures)
594
+
595
+ def install_fluka_collimators(self, line, *, names=None, families=None, verbose=False, need_apertures=True,
596
+ fluka_input_file=None, remove_missing=True):
597
+ # Check server
598
+ if FlukaEngine.is_running():
599
+ print("Warning: FLUKA server is already running. Stopping server to install collimators.")
600
+ FlukaEngine.stop_server(fluka_input_file)
601
+ names = self._get_names_from_line(line, names, families)
602
+ for name in names:
603
+ self._create_collimator(line, FlukaCollimator, name, verbose=verbose)
604
+ elements = [self._elements[name] for name in names]
605
+ line.collimators.install(names, elements, need_apertures=need_apertures)
606
+
607
+ # ==================================
608
+ # ====== Accessing attributes ======
609
+ # ==================================
610
+
611
+ @property
612
+ def collimator_names(self):
613
+ return list(self._collimator_dict.keys())
614
+
615
+ @property
616
+ def collimator_families(self):
617
+ families = {fam: [] for fam in self._family_dict.keys()}
618
+ families["no family"] = []
619
+ for name in self.collimator_names:
620
+ if 'family' not in self[name] or self[name]['family'].lower() == 'unknown':
621
+ families["no family"].append(name)
622
+ else:
623
+ families[self[name]['family']].append(name)
624
+ return families
625
+
626
+ def get_collimators_from_family(self, family):
627
+ if not hasattr(family, '__iter__') and not isinstance(family, str):
628
+ family = [family]
629
+ result = []
630
+ for fam in family:
631
+ if fam not in self.collimator_families:
632
+ raise ValueError(f"Family '{fam}' not found in CollimatorDatabase!")
633
+ result += self.collimator_families[fam]
634
+ return result
635
+
636
+ @property
637
+ def properties(self):
638
+ return {attr for d in self._collimator_dict.values() for attr in d.keys()}
639
+
640
+ def __getattr__(self, attr):
641
+ if attr in self.properties:
642
+ # TODO: include families
643
+ return {kk: vv.get(attr, None) for kk, vv in self._collimator_dict.items()}
644
+ else:
645
+ raise ValueError(f"Property `{attr}` not present in CollimatorDatabase!")
646
+
647
+ def __getitem__(self, name):
648
+ if name in self._family_dict:
649
+ return self._family_dict[name]
650
+ elif name in self._collimator_dict:
651
+ return self._collimator_dict[name]
652
+ else:
653
+ raise ValueError(f"Family nor collimator `{name}` found in CollimatorDatabase!")
654
+
xcoll/general.py CHANGED
@@ -12,5 +12,5 @@ citation = "F.F. Van der Veken, et al.: Recent Developments with the New Tools f
12
12
  # ======================
13
13
  # Do not change
14
14
  # ======================
15
- __version__ = '0.5.8'
15
+ __version__ = '0.5.9'
16
16
  # ======================
@@ -0,0 +1,224 @@
1
+ # copyright ############################### #
2
+ # This file is part of the Xcoll package. #
3
+ # Copyright (c) CERN, 2024. #
4
+ # ######################################### #
5
+
6
+ import numpy as np
7
+ from warnings import warn
8
+
9
+ import xtrack as xt
10
+ import xobjects as xo
11
+ import xpart as xp
12
+
13
+ <<<<<<< HEAD
14
+ from .beam_elements import _all_collimator_classes, EverestCrystal, FlukaCollimator
15
+ =======
16
+ from .beam_elements import collimator_classes, EverestCrystal
17
+ >>>>>>> release/v0.6.0
18
+
19
+
20
+ def generate_pencil_on_collimator(line, name, num_particles, *, side='+-', pencil_spread=1e-6,
21
+ impact_parameter=0, sigma_z=7.61e-2, twiss=None, longitudinal=None,
22
+ longitudinal_betatron_cut=None, tw=None, **kwargs):
23
+ """
24
+ Generate a pencil beam on a collimator.
25
+ """
26
+
27
+ if not line._has_valid_tracker():
28
+ raise ValueError("Please build tracker before generating pencil distribution!")
29
+
30
+ coll = line[name]
31
+
32
+ if not isinstance(coll, tuple(collimator_classes)):
33
+ raise ValueError("Need to provide a valid collimator!")
34
+
35
+ if coll.optics is None:
36
+ raise ValueError("Need to assign optics to collimators before generating pencil distribution!")
37
+
38
+ num_particles = int(num_particles)
39
+ if len(line.get_elements_of_type(FlukaCollimator)[0]) > 0:
40
+ kwargs.setdefault('_capacity', 2*num_particles)
41
+
42
+ if coll.side == 'left':
43
+ side = '+'
44
+ if coll.side == 'right':
45
+ side = '-'
46
+
47
+ # Define the plane
48
+ angle = coll.angle
49
+ if abs(np.mod(angle-90,180)-90) < 1e-6:
50
+ plane = 'x'
51
+ transv_plane = 'y'
52
+ elif abs(np.mod(angle,180)-90) < 1e-6:
53
+ plane = 'y'
54
+ transv_plane = 'x'
55
+ else:
56
+ raise NotImplementedError("Pencil beam on a skew collimator not yet supported!")
57
+
58
+ if tw is not None:
59
+ warn("The argument tw is deprecated. Please use twiss instead.", FutureWarning)
60
+ if twiss is None:
61
+ twiss = tw
62
+
63
+ if twiss is None:
64
+ twiss = line.twiss()
65
+
66
+ # Is it converging or diverging? # TODO: This might change with a tilt!!!!!!
67
+ is_converging = twiss[f'alf{plane}', name] > 0
68
+ print(f"Collimator {name} is {'con' if is_converging else 'di'}verging.")
69
+
70
+ beam_sizes = twiss.get_beam_covariance(nemitt_x=coll.nemitt_x, nemitt_y=coll.nemitt_y)
71
+ if is_converging:
72
+ # pencil at front of jaw
73
+ sigma = beam_sizes.rows[name:f'{name}>>1'][f'sigma_{plane}'][0]
74
+ sigma_transv = beam_sizes.rows[name:f'{name}>>1'][f'sigma_{transv_plane}'][0]
75
+ tw_at_s = twiss.rows[name]
76
+ at_element = name
77
+ else:
78
+ # pencil at back of jaw
79
+ sigma = beam_sizes.rows[name:f'{name}>>1'][f'sigma_{plane}'][1]
80
+ sigma_transv = beam_sizes.rows[name:f'{name}>>1'][f'sigma_{transv_plane}'][1]
81
+ tw_at_s = twiss.rows[f'{name}>>1']
82
+ at_element = line.element_names[line.element_names.index(name)+1]
83
+
84
+ dr_sigmas = pencil_spread/sigma
85
+
86
+ # Generate 4D coordinates
87
+ # TODO: there is some looping in the calculation here and in xpart. Can it be improved?
88
+ if side == '+-':
89
+ num_plus = int(num_particles/2)
90
+ num_min = int(num_particles - num_plus)
91
+ coords_plus = _generate_4D_pencil_one_jaw(line, name, num_plus, plane, '+', impact_parameter, dr_sigmas, at_element, is_converging, tw_at_s)
92
+ coords_min = _generate_4D_pencil_one_jaw(line, name, num_min, plane, '-', impact_parameter, dr_sigmas, at_element, is_converging, tw_at_s)
93
+ coords = [ [*c_plus, *c_min] for c_plus, c_min in zip(coords_plus, coords_min)]
94
+ else:
95
+ coords = _generate_4D_pencil_one_jaw(line, name, num_particles, plane, side, impact_parameter, dr_sigmas, at_element, is_converging, tw_at_s)
96
+ pencil = coords[0]
97
+ p_pencil = coords[1]
98
+ transverse_norm = coords[2]
99
+ p_transverse_norm = coords[3]
100
+
101
+ # Longitudinal plane
102
+ # TODO: make this more general, make this better
103
+ if longitudinal is None:
104
+ delta = 0
105
+ zeta = 0
106
+ elif longitudinal == 'matched_dispersion':
107
+ raise NotImplementedError
108
+ # if longitudinal_betatron_cut is None:
109
+ # cut = 0
110
+ # else:
111
+ # cut = np.random.uniform(-longitudinal_betatron_cut, longitudinal_betatron_cut,
112
+ # num_particles)
113
+ # delta = generate_delta_from_dispersion(line, name, plane=plane, position_mm=pencil,
114
+ # nemitt_x=nemitt_x, nemitt_y=nemitt_y, twiss=tw,
115
+ # betatron_cut=cut, match_at_front=is_converging)
116
+ # zeta = 0
117
+ elif longitudinal == 'bucket':
118
+ zeta, delta = xp.generate_longitudinal_coordinates(
119
+ num_particles=num_particles, distribution='gaussian', sigma_z=sigma_z, line=line
120
+ )
121
+ elif not hasattr(longitudinal, '__iter__'):
122
+ raise ValueError
123
+ elif len(longitudinal) != 2:
124
+ raise ValueError
125
+ elif isinstance(longitudinal, str):
126
+ raise ValueError
127
+ elif isinstance(longitudinal, dict):
128
+ zeta = longitudinal['zeta']
129
+ delta = longitudinal['delta']
130
+ else:
131
+ zeta = longitudinal[0]
132
+ delta = longitudinal[1]
133
+
134
+ # Build the particles
135
+ if plane == 'x':
136
+ part = xp.build_particles(
137
+ x=pencil, px=p_pencil, y_norm=transverse_norm, py_norm=p_transverse_norm,
138
+ zeta=zeta, delta=delta, nemitt_x=coll.nemitt_x, nemitt_y=coll.nemitt_y,
139
+ line=line, at_element=at_element, _context=coll._buffer.context, **kwargs
140
+ )
141
+ else:
142
+ part = xp.build_particles(
143
+ x_norm=transverse_norm, px_norm=p_transverse_norm, y=pencil, py=p_pencil,
144
+ zeta=zeta, delta=delta, nemitt_x=coll.nemitt_x, nemitt_y=coll.nemitt_y,
145
+ line=line, at_element=at_element, _context=coll._buffer.context, **kwargs
146
+ )
147
+
148
+ part._init_random_number_generator()
149
+
150
+ if not is_converging:
151
+ dri = xt.Drift(length=-coll.length)
152
+ dri.track(part)
153
+ part.start_tracking_at_element -= 1
154
+ part.at_element -= 1
155
+
156
+ return part
157
+
158
+
159
+ def generate_delta_from_dispersion(line, at_element, *, plane, position_mm, nemitt_x, nemitt_y,
160
+ twiss=None, betatron_cut=0, match_at_front=True):
161
+ if line.tracker is None:
162
+ raise ValueError("Need to build tracker first!")
163
+ if not hasattr(betatron_cut, '__iter__'):
164
+ if hasattr(position_mm, '__iter__'):
165
+ betatron_cut = np.full_like(position_mm, betatron_cut)
166
+ elif not hasattr(position_mm, '__iter__'):
167
+ position_mm = np.full_like(betatron_cut, position_mm)
168
+ elif len(position_mm) != len(betatron_cut):
169
+ raise ValueError
170
+ if plane not in ['x', 'y']:
171
+ raise ValueError("The variable 'plane' needs to be either 'x' or 'y'!")
172
+
173
+ if twiss is None:
174
+ twiss = line.twiss()
175
+
176
+ beam_sizes = twiss.get_beam_covariance(nemitt_x=nemitt_x, nemitt_y=nemitt_y)
177
+ beam_sizes = beam_sizes.rows[at_element:f'{at_element}>>1'][f'sigma_{plane}']
178
+ sigma = beam_sizes[0] if match_at_front else beam_sizes[1]
179
+ delta = (position_mm - betatron_cut*sigma - twiss.rows[at_element][plane])
180
+ delta /= twiss.rows[at_element][f'd{plane}']
181
+
182
+ return delta
183
+
184
+
185
+ def _generate_4D_pencil_one_jaw(line, name, num_particles, plane, side, impact_parameter,
186
+ dr_sigmas, at_element, is_converging, tw_at_s=None):
187
+ coll = line[name]
188
+
189
+ if side == '+':
190
+ if is_converging:
191
+ if isinstance(coll, EverestCrystal):
192
+ pencil_pos = coll.jaw_U + impact_parameter
193
+ else:
194
+ pencil_pos = coll.jaw_LU + impact_parameter
195
+ else:
196
+ if isinstance(coll, EverestCrystal):
197
+ pencil_pos = coll.jaw_D - impact_parameter
198
+ else:
199
+ pencil_pos = coll.jaw_LD + impact_parameter
200
+ elif side == '-':
201
+ if is_converging:
202
+ if isinstance(coll, EverestCrystal):
203
+ pencil_pos = coll.jaw_U - impact_parameter
204
+ else:
205
+ pencil_pos = coll.jaw_RU - impact_parameter
206
+ else:
207
+ if isinstance(coll, EverestCrystal):
208
+ pencil_pos = coll.jaw_D + impact_parameter
209
+ else:
210
+ pencil_pos = coll.jaw_RD - impact_parameter
211
+
212
+ # Collimator plane: generate pencil distribution
213
+ pencil, p_pencil = xp.generate_2D_pencil_with_absolute_cut(
214
+ num_particles, plane=plane, absolute_cut=pencil_pos, line=line,
215
+ dr_sigmas=dr_sigmas, nemitt_x=coll.nemitt_x, nemitt_y=coll.nemitt_y,
216
+ at_element=at_element, side=side, twiss=tw_at_s
217
+ )
218
+
219
+ # Other plane: generate gaussian distribution in normalized coordinates
220
+ transverse_norm = np.random.normal(size=num_particles)
221
+ p_transverse_norm = np.random.normal(size=num_particles)
222
+
223
+ return [pencil, p_pencil, transverse_norm, p_transverse_norm]
224
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: xcoll
3
- Version: 0.5.8
3
+ Version: 0.5.9
4
4
  Summary: Xsuite collimation package
5
5
  Home-page: https://github.com/xsuite/xcoll
6
6
  License: Apache 2.0
@@ -18,11 +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.1.1)
22
- Requires-Dist: xfields (>=0.12.1)
23
- Requires-Dist: xobjects (>=0.2.6)
24
- Requires-Dist: xpart (>=0.15.0)
25
- Requires-Dist: xtrack (>=0.36.5)
21
+ Requires-Dist: xdeps (>=0.7.3)
22
+ Requires-Dist: xobjects (>=0.4.5)
23
+ Requires-Dist: xpart (>=0.19.1)
24
+ Requires-Dist: xtrack (>=0.69.7)
26
25
  Project-URL: Repository, https://github.com/xsuite/xcoll
27
26
  Description-Content-Type: text/markdown
28
27
 
@@ -3,6 +3,7 @@ NOTICE,sha256=6DO_E7WCdRKc42vUoVVBPGttvQi4mRt9fAcxj9u8zy8,74
3
3
  xcoll/__init__.py,sha256=b_61vh5irhf5fPmqTFJlyhNSt4rmftXg9uXPIEpgVB4,1612
4
4
  xcoll/_manager.py,sha256=9NQKaNxZR2I1ChMVBeKQc0A8h6W8gVgRRg72a5NgbXU,808
5
5
  xcoll/beam_elements/__init__.py,sha256=06bU8rzvlUPhcvwpaUippddm5IChpcCHBvpmvXJQU74,1122
6
+ xcoll/beam_elements/__init__.py.orig,sha256=XZMbC-0IzLmKN4LXXT5zm2zOXzRWvkpuElCBGE3VWtw,1507
6
7
  xcoll/beam_elements/absorber.py,sha256=efK6gyUgD4x_FnBLpMR7-5_HCdp_753nkYikcdn6ulw,2502
7
8
  xcoll/beam_elements/base.py,sha256=6scwhofls5AHPJcUyEbTJOJ8U86EQU4S7BB7NGoE_j0,52728
8
9
  xcoll/beam_elements/blowup.py,sha256=gBXdlISvoDiMjXVpA77ls5QdAU3H9krwvFt2bSW_NII,8029
@@ -16,10 +17,12 @@ xcoll/beam_elements/elements_src/everest_crystal.h,sha256=WwwNF6it7TuOimhpSXJa7U
16
17
  xcoll/beam_elements/everest.py,sha256=PA_VWpnPrIuO1xN__eKyT_ejbGZK7p93QHDVi3re7cM,8541
17
18
  xcoll/beam_elements/monitor.py,sha256=zzMdN3JMFSAs-30_ntRvd5qZGdsXfGtColhiFDuMcIk,16928
18
19
  xcoll/colldb.py,sha256=lEpkDRAT54szT9i7-jbTMRvkuW_0W2piZCdDaungcIs,30983
19
- xcoll/general.py,sha256=tYbVvHS7A8CxAZYyCBXWq0RaD9mPy8wBP8aLcV17TKU,534
20
+ xcoll/colldb.py.orig,sha256=v4AXbY44o_mlealtFXzMzDNLVt-Hkt_ZkjKs5QUJlec,31898
21
+ xcoll/general.py,sha256=-xLa-F2jd_1I3hIhlntMYsz_B-DZw_bj0qqh1SR1Cno,534
20
22
  xcoll/headers/checks.h,sha256=qdXsOTBOK1MwW6bdFF93j4yE648mcDtEv5rGN1w9sfk,1582
21
23
  xcoll/headers/particle_states.h,sha256=DZa_ZSaJrjnA8aHFriZKfRCkArQ8nK1t445MRwevDtA,840
22
24
  xcoll/initial_distribution.py,sha256=x5G4LTXn4boEg5jBFrQCk_l759h91XiAUhDTdcUvLkc,8779
25
+ xcoll/initial_distribution.py.orig,sha256=NOtDtAoNYx8p5l-hUO9ueafl3jtbIzGU0z8-xHtTX_E,9026
23
26
  xcoll/install.py,sha256=SxEFQnhWXlsXyPBIo847q6wPgId_f5ZtFRR1awGbkjc,2108
24
27
  xcoll/interaction_record/__init__.py,sha256=UFoLiKa-z2oX7YoszP-7Vgdt1nM6kT382v1CaIu8_u0,50
25
28
  xcoll/interaction_record/interaction_record.py,sha256=ixsQtVZn71vVEuTAA27a2NWcZZZ8iAcWFOa58bcWEQU,13271
@@ -406,8 +409,8 @@ xcoll/scattering_routines/geometry/objects.h,sha256=A5ktGvVlSkC4hUsI_PQFsE80CuDw
406
409
  xcoll/scattering_routines/geometry/rotation.h,sha256=lO3RaQBys9r0ROMjR8T8Rr7UsIEm-H9_C_70Nwz4MXY,701
407
410
  xcoll/scattering_routines/geometry/segments.h,sha256=7nKnnin2ByxkKyaYwGvFaqgLQg5uBba4CdLHL7L3iQs,7667
408
411
  xcoll/scattering_routines/geometry/sort.h,sha256=b1MkFO2ddzv1fWGeQzsLuz46qo2pKyRSXHjoAEVU7Ts,5763
409
- xcoll-0.5.8.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
410
- xcoll-0.5.8.dist-info/METADATA,sha256=ikWtL-ecHne0cta39XCx61xI5-P_DwuJY2jmHTPLmSo,2709
411
- xcoll-0.5.8.dist-info/NOTICE,sha256=6DO_E7WCdRKc42vUoVVBPGttvQi4mRt9fAcxj9u8zy8,74
412
- xcoll-0.5.8.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
413
- xcoll-0.5.8.dist-info/RECORD,,
412
+ xcoll-0.5.9.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
413
+ xcoll-0.5.9.dist-info/METADATA,sha256=ZBJuoHNhd-lFobMiSbj1SmSZ8yyie9OC_QTUo2Af4PI,2675
414
+ xcoll-0.5.9.dist-info/NOTICE,sha256=6DO_E7WCdRKc42vUoVVBPGttvQi4mRt9fAcxj9u8zy8,74
415
+ xcoll-0.5.9.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
416
+ xcoll-0.5.9.dist-info/RECORD,,
File without changes
File without changes
File without changes