xcoll 0.3.5__py3-none-any.whl → 0.4.0__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.
- xcoll/__init__.py +12 -4
- xcoll/beam_elements/__init__.py +7 -5
- xcoll/beam_elements/absorber.py +41 -7
- xcoll/beam_elements/base.py +1161 -244
- xcoll/beam_elements/collimators_src/black_absorber.h +118 -0
- xcoll/beam_elements/collimators_src/black_crystal.h +111 -0
- xcoll/beam_elements/collimators_src/everest_block.h +40 -28
- xcoll/beam_elements/collimators_src/everest_collimator.h +129 -50
- xcoll/beam_elements/collimators_src/everest_crystal.h +217 -73
- xcoll/beam_elements/everest.py +60 -113
- xcoll/colldb.py +250 -750
- xcoll/general.py +2 -2
- xcoll/headers/checks.h +1 -1
- xcoll/headers/particle_states.h +2 -2
- xcoll/initial_distribution.py +195 -0
- xcoll/install.py +177 -0
- xcoll/interaction_record/__init__.py +1 -0
- xcoll/interaction_record/interaction_record.py +252 -0
- xcoll/interaction_record/interaction_record_src/interaction_record.h +98 -0
- xcoll/{impacts → interaction_record}/interaction_types.py +11 -4
- xcoll/line_tools.py +83 -0
- xcoll/lossmap.py +209 -0
- xcoll/manager.py +2 -937
- xcoll/rf_sweep.py +1 -1
- xcoll/scattering_routines/everest/amorphous.h +239 -0
- xcoll/scattering_routines/everest/channeling.h +245 -0
- xcoll/scattering_routines/everest/crystal_parameters.h +137 -0
- xcoll/scattering_routines/everest/everest.h +8 -30
- xcoll/scattering_routines/everest/everest.py +13 -10
- xcoll/scattering_routines/everest/jaw.h +27 -197
- xcoll/scattering_routines/everest/materials.py +2 -0
- xcoll/scattering_routines/everest/multiple_coulomb_scattering.h +31 -10
- xcoll/scattering_routines/everest/nuclear_interaction.h +86 -0
- xcoll/scattering_routines/geometry/__init__.py +6 -0
- xcoll/scattering_routines/geometry/collimator_geometry.h +219 -0
- xcoll/scattering_routines/geometry/crystal_geometry.h +150 -0
- xcoll/scattering_routines/geometry/geometry.py +26 -0
- xcoll/scattering_routines/geometry/get_s.h +92 -0
- xcoll/scattering_routines/geometry/methods.h +111 -0
- xcoll/scattering_routines/geometry/objects.h +154 -0
- xcoll/scattering_routines/geometry/rotation.h +23 -0
- xcoll/scattering_routines/geometry/segments.h +226 -0
- xcoll/scattering_routines/geometry/sort.h +184 -0
- {xcoll-0.3.5.dist-info → xcoll-0.4.0.dist-info}/METADATA +1 -1
- {xcoll-0.3.5.dist-info → xcoll-0.4.0.dist-info}/RECORD +48 -33
- xcoll/beam_elements/collimators_src/absorber.h +0 -141
- xcoll/collimator_settings.py +0 -457
- xcoll/impacts/__init__.py +0 -1
- xcoll/impacts/impacts.py +0 -102
- xcoll/impacts/impacts_src/impacts.h +0 -99
- xcoll/scattering_routines/everest/crystal.h +0 -1302
- xcoll/scattering_routines/everest/scatter.h +0 -169
- xcoll/scattering_routines/everest/scatter_crystal.h +0 -260
- {xcoll-0.3.5.dist-info → xcoll-0.4.0.dist-info}/LICENSE +0 -0
- {xcoll-0.3.5.dist-info → xcoll-0.4.0.dist-info}/NOTICE +0 -0
- {xcoll-0.3.5.dist-info → xcoll-0.4.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// copyright ############################### #
|
|
2
|
+
// This file is part of the Xcoll Package. #
|
|
3
|
+
// Copyright (c) CERN, 2024. #
|
|
4
|
+
// ######################################### #
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
#ifndef XCOLL_IMPACTS_H
|
|
8
|
+
#define XCOLL_IMPACTS_H
|
|
9
|
+
|
|
10
|
+
// TODO: do we need to pass RecordIndex?
|
|
11
|
+
// probably can do RecordIndex record_index = InteractionRecordData_getp__index(record); ?
|
|
12
|
+
/*gpufun*/
|
|
13
|
+
int64_t InteractionRecordData_log(InteractionRecordData record, RecordIndex record_index, LocalParticle* parent,
|
|
14
|
+
int64_t interaction){
|
|
15
|
+
// This can be used for a point-like interaction where there is no child (or because it's equal to the parent)
|
|
16
|
+
// or to log the parent first, to be followed up with InteractionRecordData_log_child on the same slot
|
|
17
|
+
|
|
18
|
+
int64_t i_slot = -1;
|
|
19
|
+
if (record){
|
|
20
|
+
// Get a slot in the record (this is thread safe)
|
|
21
|
+
i_slot = RecordIndex_get_slot(record_index);
|
|
22
|
+
// The returned slot id is negative if record is NULL or if record is full
|
|
23
|
+
|
|
24
|
+
if (i_slot>=0){
|
|
25
|
+
InteractionRecordData_set_at_element(record, i_slot, LocalParticle_get_at_element(parent));
|
|
26
|
+
InteractionRecordData_set_at_turn(record, i_slot, LocalParticle_get_at_turn(parent));
|
|
27
|
+
InteractionRecordData_set_ds(record, i_slot, 0);
|
|
28
|
+
InteractionRecordData_set__inter(record, i_slot, interaction);
|
|
29
|
+
|
|
30
|
+
double charge_ratio = LocalParticle_get_charge_ratio(parent);
|
|
31
|
+
double mass_ratio = charge_ratio / LocalParticle_get_chi(parent);
|
|
32
|
+
double energy = ( LocalParticle_get_ptau(parent) + 1 / LocalParticle_get_beta0(parent)
|
|
33
|
+
) * mass_ratio * LocalParticle_get_p0c(parent);
|
|
34
|
+
// All fields have to be written, or the arrays will not have the same length
|
|
35
|
+
// TODO: maybe this is not true, as we are setting by slot index? Don't the arrays come pre-initialised?
|
|
36
|
+
InteractionRecordData_set_parent_id(record, i_slot, LocalParticle_get_particle_id(parent));
|
|
37
|
+
InteractionRecordData_set_parent_x(record, i_slot, LocalParticle_get_x(parent));
|
|
38
|
+
InteractionRecordData_set_parent_px(record, i_slot, LocalParticle_get_px(parent));
|
|
39
|
+
InteractionRecordData_set_parent_y(record, i_slot, LocalParticle_get_y(parent));
|
|
40
|
+
InteractionRecordData_set_parent_py(record, i_slot, LocalParticle_get_py(parent));
|
|
41
|
+
InteractionRecordData_set_parent_zeta(record, i_slot, LocalParticle_get_zeta(parent));
|
|
42
|
+
InteractionRecordData_set_parent_delta(record, i_slot, LocalParticle_get_delta(parent));
|
|
43
|
+
InteractionRecordData_set_parent_energy(record, i_slot, energy);
|
|
44
|
+
InteractionRecordData_set_parent_mass(record, i_slot, mass_ratio*LocalParticle_get_mass0(parent));
|
|
45
|
+
InteractionRecordData_set_parent_charge(record, i_slot, charge_ratio*LocalParticle_get_q0(parent));
|
|
46
|
+
// TODO: particle info
|
|
47
|
+
InteractionRecordData_set_parent_z(record, i_slot, -1);
|
|
48
|
+
InteractionRecordData_set_parent_a(record, i_slot, -1);
|
|
49
|
+
InteractionRecordData_set_parent_pdgid(record, i_slot, -1);
|
|
50
|
+
|
|
51
|
+
// TODO: maybe this is not needed
|
|
52
|
+
InteractionRecordData_set_child_id(record, i_slot, -1);
|
|
53
|
+
InteractionRecordData_set_child_x(record, i_slot, -1);
|
|
54
|
+
InteractionRecordData_set_child_px(record, i_slot, -1);
|
|
55
|
+
InteractionRecordData_set_child_y(record, i_slot, -1);
|
|
56
|
+
InteractionRecordData_set_child_py(record, i_slot, -1);
|
|
57
|
+
InteractionRecordData_set_child_zeta(record, i_slot, -1);
|
|
58
|
+
InteractionRecordData_set_child_delta(record, i_slot, -1);
|
|
59
|
+
InteractionRecordData_set_child_energy(record, i_slot, -1);
|
|
60
|
+
InteractionRecordData_set_child_mass(record, i_slot, -1);
|
|
61
|
+
InteractionRecordData_set_child_charge(record, i_slot, -1);
|
|
62
|
+
InteractionRecordData_set_child_z(record, i_slot, -1);
|
|
63
|
+
InteractionRecordData_set_child_a(record, i_slot, -1);
|
|
64
|
+
InteractionRecordData_set_child_pdgid(record, i_slot, -1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// printf("Logging %i in slot %i\n", interaction, i_slot);
|
|
68
|
+
return i_slot;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/*gpufun*/
|
|
72
|
+
void InteractionRecordData_log_child(InteractionRecordData record, int64_t i_slot, LocalParticle* child, double ds){
|
|
73
|
+
if (record && i_slot>=0){
|
|
74
|
+
InteractionRecordData_set_ds(record, i_slot, ds);
|
|
75
|
+
|
|
76
|
+
double charge_ratio = LocalParticle_get_charge_ratio(child);
|
|
77
|
+
double mass_ratio = charge_ratio / LocalParticle_get_chi(child);
|
|
78
|
+
double energy = ( LocalParticle_get_ptau(child) + 1 / LocalParticle_get_beta0(child)
|
|
79
|
+
) * mass_ratio * LocalParticle_get_p0c(child);
|
|
80
|
+
InteractionRecordData_set_child_id(record, i_slot, LocalParticle_get_particle_id(child));
|
|
81
|
+
InteractionRecordData_set_child_x(record, i_slot, LocalParticle_get_x(child));
|
|
82
|
+
InteractionRecordData_set_child_px(record, i_slot, LocalParticle_get_px(child));
|
|
83
|
+
InteractionRecordData_set_child_y(record, i_slot, LocalParticle_get_y(child));
|
|
84
|
+
InteractionRecordData_set_child_py(record, i_slot, LocalParticle_get_py(child));
|
|
85
|
+
InteractionRecordData_set_child_zeta(record, i_slot, LocalParticle_get_zeta(child));
|
|
86
|
+
InteractionRecordData_set_child_delta(record, i_slot, LocalParticle_get_delta(child));
|
|
87
|
+
InteractionRecordData_set_child_energy(record, i_slot, energy);
|
|
88
|
+
InteractionRecordData_set_child_mass(record, i_slot, mass_ratio*LocalParticle_get_mass0(child));
|
|
89
|
+
InteractionRecordData_set_child_charge(record, i_slot, charge_ratio*LocalParticle_get_q0(child));
|
|
90
|
+
// TODO: particle info
|
|
91
|
+
InteractionRecordData_set_child_z(record, i_slot, -1);
|
|
92
|
+
InteractionRecordData_set_child_a(record, i_slot, -1);
|
|
93
|
+
InteractionRecordData_set_child_pdgid(record, i_slot, -1);
|
|
94
|
+
// printf("Slot %i: length %f\n", i_slot, ds);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
#endif /* XCOLL_IMPACTS_H */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# copyright ############################### #
|
|
2
2
|
# This file is part of the Xcoll Package. #
|
|
3
|
-
# Copyright (c) CERN,
|
|
3
|
+
# Copyright (c) CERN, 2024. #
|
|
4
4
|
# ######################################### #
|
|
5
5
|
|
|
6
6
|
|
|
@@ -10,15 +10,16 @@ source = r'''
|
|
|
10
10
|
|
|
11
11
|
#define XC_UNITIALISED 0 // NAN // Do not use
|
|
12
12
|
|
|
13
|
-
#define
|
|
14
|
-
#define
|
|
13
|
+
#define XC_ENTER_JAW_L -1 // JI // point (no children) Set ds > 0 if entering later
|
|
14
|
+
#define XC_ENTER_JAW_R -2 // JI // point (no children) Set ds > 0 if entering later
|
|
15
|
+
#define XC_EXIT_JAW -3 // JO // point (no children)
|
|
16
|
+
#define XC_ENTER_JAW -4 // JI // point (no children) Set ds > 0 if entering later still here for compatibility
|
|
15
17
|
#define XC_ABSORBED 1 // A // point (no children) Don't use 0 (is default for unitialised)
|
|
16
18
|
#define XC_MULTIPLE_COULOMB_SCATTERING 13 // MCS // continuous
|
|
17
19
|
#define XC_PN_ELASTIC 14 // PN // point (no children)
|
|
18
20
|
#define XC_PP_ELASTIC 15 // PP // point (no children)
|
|
19
21
|
#define XC_SINGLE_DIFFRACTIVE 16 // SD // point (no children)
|
|
20
22
|
#define XC_COULOMB 17 // C // point (no children)
|
|
21
|
-
#define XC_RUTHERFORD 18 // RU // point (no children)
|
|
22
23
|
#define XC_CHANNELING 100 // CH // continuous
|
|
23
24
|
#define XC_DECHANNELING 101 // DCH // point (no children)
|
|
24
25
|
#define XC_VOLUME_REFLECTION_TRANS_CH 102 // VRCH // point (no children) Transition region around +-xpcrit
|
|
@@ -43,3 +44,9 @@ shortcuts = {
|
|
|
43
44
|
for line in source.split('\n')
|
|
44
45
|
if len(line.split()) > 1 and line.split()[1][:3] == 'XC_' # select the source lines with the definitions
|
|
45
46
|
}
|
|
47
|
+
|
|
48
|
+
is_point = {
|
|
49
|
+
int(line.split()[2]): line.split()[6].lower() == 'point'
|
|
50
|
+
for line in source.split('\n')
|
|
51
|
+
if len(line.split()) > 1 and line.split()[1][:3] == 'XC_' # select the source lines with the definitions
|
|
52
|
+
}
|
xcoll/line_tools.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# copyright ############################### #
|
|
2
|
+
# This file is part of the Xcoll Package. #
|
|
3
|
+
# Copyright (c) CERN, 2024. #
|
|
4
|
+
# ######################################### #
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import xtrack as xt
|
|
8
|
+
|
|
9
|
+
from .beam_elements import element_classes, collimator_classes
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def assign_optics_to_collimators(line, nemitt_x=None, nemitt_y=None, twiss=None):
|
|
13
|
+
if not line._has_valid_tracker():
|
|
14
|
+
raise Exception("Please build tracker before setting the openings!")
|
|
15
|
+
names = line.get_elements_of_type(collimator_classes)[1]
|
|
16
|
+
tw_upstream, tw_downstream = get_optics_at(names, twiss=twiss, line=line)
|
|
17
|
+
beta_gamma_rel = line.particle_ref._xobject.gamma0[0]*line.particle_ref._xobject.beta0[0]
|
|
18
|
+
for coll in names:
|
|
19
|
+
print(coll)
|
|
20
|
+
line[coll].assign_optics(name=coll, nemitt_x=nemitt_x, nemitt_y=nemitt_x, twiss_upstream=tw_upstream,
|
|
21
|
+
twiss_downstream=tw_downstream, beta_gamma_rel=beta_gamma_rel)
|
|
22
|
+
|
|
23
|
+
def get_optics_at(names, *, twiss=None, line=None):
|
|
24
|
+
if twiss is None:
|
|
25
|
+
if not line._has_valid_tracker():
|
|
26
|
+
raise Exception("Please pass a line and build tracker before computing the optics for the openings!")
|
|
27
|
+
twiss = line.twiss()
|
|
28
|
+
if not hasattr(names, '__iter__') and not isinstance(names, str):
|
|
29
|
+
names = [names]
|
|
30
|
+
coll_entry_mask = twiss.mask[names]
|
|
31
|
+
tw_entry = twiss.rows[coll_entry_mask]
|
|
32
|
+
tw_exit = twiss.rows[coll_entry_mask+1]
|
|
33
|
+
tw_exit.name = tw_entry.name
|
|
34
|
+
return tw_entry, tw_exit
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def open_collimators(line, names=None):
|
|
38
|
+
if names is None:
|
|
39
|
+
names = line.get_elements_of_type(collimator_classes)[1]
|
|
40
|
+
if len(names) == 0:
|
|
41
|
+
print("No collimators found in line.")
|
|
42
|
+
else:
|
|
43
|
+
for coll in names:
|
|
44
|
+
line[coll].open_jaws(keep_tilts=False)
|
|
45
|
+
line[coll].gap = None
|
|
46
|
+
|
|
47
|
+
def send_to_parking(line, names=None):
|
|
48
|
+
if names is None:
|
|
49
|
+
names = line.get_elements_of_type(collimator_classes)[1]
|
|
50
|
+
if len(names) == 0:
|
|
51
|
+
print("No collimators found in line.")
|
|
52
|
+
else:
|
|
53
|
+
raise NotImplementedError("Need to move this to new type manager or so.")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def enable_scattering(line):
|
|
57
|
+
elements = line.get_elements_of_type(element_classes)[0]
|
|
58
|
+
if len(elements) == 0:
|
|
59
|
+
print("No xcoll elements found in line.")
|
|
60
|
+
else:
|
|
61
|
+
nemitt_x = None
|
|
62
|
+
nemitt_y = None
|
|
63
|
+
for el in elements:
|
|
64
|
+
if hasattr(el, 'optics') and el.optics is not None:
|
|
65
|
+
if nemitt_x is None:
|
|
66
|
+
nemitt_x = el.nemitt_x
|
|
67
|
+
if nemitt_y is None:
|
|
68
|
+
nemitt_y = el.nemitt_y
|
|
69
|
+
if not np.isclose(el.nemitt_x, nemitt_x) \
|
|
70
|
+
or not np.isclose(el.nemitt_x, nemitt_x):
|
|
71
|
+
raise ValueError("Not all collimators have the same "
|
|
72
|
+
+ "emittance. This is not supported.")
|
|
73
|
+
el.enable_scattering()
|
|
74
|
+
# self.line.tracker.io_buffer = self._io_buffer
|
|
75
|
+
# self._set_record_interaction_record()
|
|
76
|
+
|
|
77
|
+
def disable_scattering(line):
|
|
78
|
+
elements = line.get_elements_of_type(element_classes)[0]
|
|
79
|
+
if len(elements) == 0:
|
|
80
|
+
print("No xcoll elements found in line.")
|
|
81
|
+
else:
|
|
82
|
+
for el in elements:
|
|
83
|
+
el.disable_scattering()
|
xcoll/lossmap.py
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# copyright ############################### #
|
|
2
|
+
# This file is part of the Xcoll Package. #
|
|
3
|
+
# Copyright (c) CERN, 2024. #
|
|
4
|
+
# ######################################### #
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
import json
|
|
10
|
+
|
|
11
|
+
import xtrack as xt
|
|
12
|
+
import xpart as xp
|
|
13
|
+
import xobjects as xo
|
|
14
|
+
|
|
15
|
+
from .beam_elements import collimator_classes
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class LossMap:
|
|
19
|
+
|
|
20
|
+
def __init__(self, line, part, *, line_is_reversed, interpolation=0.1,
|
|
21
|
+
weights=None, weight_function=None):
|
|
22
|
+
self._line = line
|
|
23
|
+
self._line_is_reversed = line_is_reversed
|
|
24
|
+
self._machine_length = line.get_length()
|
|
25
|
+
self._part = part
|
|
26
|
+
self._interpolation = interpolation
|
|
27
|
+
if weights is None:
|
|
28
|
+
if weight_function is None:
|
|
29
|
+
self._weights = np.ones(len(part.x))
|
|
30
|
+
else:
|
|
31
|
+
self._weights = _create_weights_from_initial_state(part, weight_function)
|
|
32
|
+
else:
|
|
33
|
+
if weight_function is not None:
|
|
34
|
+
raise ValueError("Use either 'weights' or 'weight_function', not both!")
|
|
35
|
+
self._weights = part.sort(interleave_lost_particles=True)
|
|
36
|
+
|
|
37
|
+
# loss location refinement
|
|
38
|
+
if interpolation is not None:
|
|
39
|
+
self._interpolate()
|
|
40
|
+
|
|
41
|
+
self._make_coll_summary()
|
|
42
|
+
coll_summary = self._summary[self._summary.nabs > 0].to_dict('list')
|
|
43
|
+
aper_s, aper_names, aper_nabs = self._get_aperture_losses()
|
|
44
|
+
|
|
45
|
+
self._lossmap = {
|
|
46
|
+
'collimator': {
|
|
47
|
+
's': coll_summary['s'],
|
|
48
|
+
'name': coll_summary['collname'],
|
|
49
|
+
'length': coll_summary['length'],
|
|
50
|
+
'n': coll_summary['nabs']
|
|
51
|
+
}
|
|
52
|
+
,
|
|
53
|
+
'aperture': {
|
|
54
|
+
's': aper_s,
|
|
55
|
+
'name': aper_names,
|
|
56
|
+
'n': aper_nabs
|
|
57
|
+
}
|
|
58
|
+
,
|
|
59
|
+
'machine_length': self._machine_length
|
|
60
|
+
,
|
|
61
|
+
'interpolation': interpolation
|
|
62
|
+
,
|
|
63
|
+
'reversed': self._line_is_reversed
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def to_json(self, file):
|
|
68
|
+
with open(Path(file), 'w') as fid:
|
|
69
|
+
json.dump(self._lossmap, fid, indent=True, cls=xo.JEncoder)
|
|
70
|
+
|
|
71
|
+
def save_summary(self, file):
|
|
72
|
+
with open(Path(file), 'w') as fid:
|
|
73
|
+
fid.write(self._summary.__repr__())
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def lossmap(self):
|
|
78
|
+
return self._lossmap
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def summary(self):
|
|
82
|
+
return self._summary
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def line(self):
|
|
86
|
+
return self._line
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def line_is_reversed(self):
|
|
90
|
+
return self._line_is_reversed
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def machine_length(self):
|
|
94
|
+
return self._machine_length
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def part(self):
|
|
98
|
+
return self._part
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def interpolation(self):
|
|
102
|
+
return self._interpolation
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def weights(self):
|
|
106
|
+
return self._weights
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _interpolate(self):
|
|
110
|
+
new_state = self._part.state.copy()
|
|
111
|
+
new_elem = self._part.at_element.copy()
|
|
112
|
+
|
|
113
|
+
# Correct particles that are at an aperture directly after a collimator
|
|
114
|
+
for idx, elem in enumerate(self._part.at_element):
|
|
115
|
+
if (self._part.state[idx] == 0 and elem > 0
|
|
116
|
+
and self._line.element_names[elem-1] in
|
|
117
|
+
self._line.get_elements_of_type(collimator_classes)[1]):
|
|
118
|
+
print(f"Found at {self._line.element_names[elem]}, "
|
|
119
|
+
+ f"should be {self._line.element_names[elem-1]}")
|
|
120
|
+
new_elem[idx] = elem - 1
|
|
121
|
+
what_type = self._line[elem-1].__class__.__name__
|
|
122
|
+
if what_type == 'EverestCollimator':
|
|
123
|
+
new_state[idx] = -331
|
|
124
|
+
elif what_type == 'EverestCrystal':
|
|
125
|
+
new_state[idx] = -332
|
|
126
|
+
elif what_type == 'FlukaCollimator':
|
|
127
|
+
new_state[idx] = -334 # TODO: what if crystal?
|
|
128
|
+
elif what_type == 'Geant4Collimator':
|
|
129
|
+
new_state[idx] = -337 # TODO: what if crystal?
|
|
130
|
+
elif what_type == 'BlackAbsorber':
|
|
131
|
+
new_state[idx] = -340
|
|
132
|
+
else:
|
|
133
|
+
raise ValueError(f"Unknown collimator type {what_type}")
|
|
134
|
+
self._part.state = new_state
|
|
135
|
+
self._part.at_element = new_elem
|
|
136
|
+
|
|
137
|
+
# do the interpolation
|
|
138
|
+
aper_s = list(self._part.s[self._part.state==0])
|
|
139
|
+
if len(aper_s) > 0:
|
|
140
|
+
print("Performing the aperture losses refinement.")
|
|
141
|
+
loss_loc_refinement = xt.LossLocationRefinement(
|
|
142
|
+
self._line,
|
|
143
|
+
n_theta = 360, # Angular resolution
|
|
144
|
+
r_max = 0.5, # Maximum transverse aperture [m]
|
|
145
|
+
dr = 50e-6, # Transverse accuracy [m]
|
|
146
|
+
ds = self.interpolation # Longitudinal accuracy [m]
|
|
147
|
+
)
|
|
148
|
+
loss_loc_refinement.refine_loss_location(self._part)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _make_coll_summary(self):
|
|
152
|
+
collimator_names = self._line.get_elements_of_type(collimator_classes)[1]
|
|
153
|
+
coll_mask = (self._part.state <= -330) & (self._part.state >= -340)
|
|
154
|
+
coll_losses = np.array([self._line.element_names[i]
|
|
155
|
+
for i in self._part.at_element[coll_mask]])
|
|
156
|
+
coll_lengths = [self._line[j].length for j in collimator_names]
|
|
157
|
+
coll_pos = [(self._line.get_s_position(i) + self._line[i].length/2)
|
|
158
|
+
for i in collimator_names]
|
|
159
|
+
|
|
160
|
+
if self._line is reversed:
|
|
161
|
+
coll_pos = [self._machine_length - s for s in coll_pos]
|
|
162
|
+
|
|
163
|
+
coll_types = [self._line[i].__class__.__name__ for i in collimator_names]
|
|
164
|
+
coll_weights = self._weights[coll_mask]
|
|
165
|
+
nabs = [coll_weights[coll_losses == j].sum() for j in collimator_names]
|
|
166
|
+
|
|
167
|
+
self._summary = pd.DataFrame({
|
|
168
|
+
'collname': collimator_names,
|
|
169
|
+
'nabs': nabs, # of particles lost on collimators
|
|
170
|
+
'length': coll_lengths,
|
|
171
|
+
's': coll_pos,
|
|
172
|
+
'type': coll_types
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _get_aperture_losses(self):
|
|
177
|
+
# Get s position per particle (lost on aperture)
|
|
178
|
+
aper_mask = self._part.state == 0
|
|
179
|
+
aper_s = list(self._part.s[aper_mask])
|
|
180
|
+
|
|
181
|
+
if len(aper_s) == 0:
|
|
182
|
+
return [], [], []
|
|
183
|
+
if self._line_is_reversed:
|
|
184
|
+
aper_s = [ self._machine_length - s for s in aper_s ]
|
|
185
|
+
|
|
186
|
+
# Store names of aperture markers
|
|
187
|
+
aper_names = [self._line.element_names[i] for i in self._part.at_element[aper_mask]]
|
|
188
|
+
name_dict = dict(zip(aper_s, aper_names)) # TODO: not floating-point-safe and slow
|
|
189
|
+
|
|
190
|
+
# Create output arrays
|
|
191
|
+
aper_pos = np.unique(aper_s)
|
|
192
|
+
aper_weights = self._weights[aper_mask]
|
|
193
|
+
aper_nabs = [aper_weights[aper_s == j].sum() for j in aper_pos]
|
|
194
|
+
aper_names = [name_dict[ss] for ss in aper_pos]
|
|
195
|
+
|
|
196
|
+
return aper_pos, aper_names, aper_nabs
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _create_weights_from_initial_state(part, function):
|
|
200
|
+
if len(function) == 4:
|
|
201
|
+
return function[0](part.x)*function[1](part.px)*\
|
|
202
|
+
function[2](part.y)*function[3](part.py)
|
|
203
|
+
elif len(function) == 6:
|
|
204
|
+
return function[0](part.x)*function[1](part.px)*\
|
|
205
|
+
function[2](part.y)*function[3](part.py)*\
|
|
206
|
+
function[4](part.zeta)*function[5](part.delta)
|
|
207
|
+
else:
|
|
208
|
+
raise NotImplementedError
|
|
209
|
+
|