xcoll 0.3.6__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.6.dist-info → xcoll-0.4.0.dist-info}/METADATA +1 -1
- {xcoll-0.3.6.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.6.dist-info → xcoll-0.4.0.dist-info}/LICENSE +0 -0
- {xcoll-0.3.6.dist-info → xcoll-0.4.0.dist-info}/NOTICE +0 -0
- {xcoll-0.3.6.dist-info → xcoll-0.4.0.dist-info}/WHEEL +0 -0
|
@@ -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
|
#ifndef XCOLL_EVEREST_CRYSTAL_H
|
|
@@ -9,106 +9,250 @@
|
|
|
9
9
|
#include <stdio.h>
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
/*gpufun*/
|
|
13
12
|
void EverestCrystal_set_material(EverestCrystalData el){
|
|
14
13
|
CrystalMaterialData material = EverestCrystalData_getp__material(el);
|
|
15
14
|
RandomRutherfordData rng = EverestCrystalData_getp_rutherford_rng(el);
|
|
16
15
|
RandomRutherford_set_by_xcoll_material(rng, (GeneralMaterialData) material);
|
|
17
16
|
}
|
|
18
17
|
|
|
18
|
+
|
|
19
19
|
/*gpufun*/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
CrystalGeometry EverestCrystal_init_geometry(EverestCrystalData el, LocalParticle* part0, int8_t active){
|
|
21
|
+
CrystalGeometry cg = (CrystalGeometry) malloc(sizeof(CrystalGeometry_));
|
|
22
|
+
if (active){ // This is needed in order to avoid that the initialisation is called during a twiss!
|
|
23
|
+
cg->length = EverestCrystalData_get_length(el);
|
|
24
|
+
cg->side = EverestCrystalData_get__side(el);
|
|
25
|
+
if (cg->side == 0){
|
|
26
|
+
kill_all_particles(part0, XC_ERR_INVALID_XOFIELD);
|
|
27
|
+
return cg;
|
|
28
|
+
}
|
|
29
|
+
double R = EverestCrystalData_get__bending_radius(el);
|
|
30
|
+
double t_R = EverestCrystalData_get__bending_angle(el);
|
|
31
|
+
cg->bending_radius = R;
|
|
32
|
+
cg->bending_angle = t_R;
|
|
33
|
+
cg->miscut_angle = EverestCrystalData_get_miscut(el);
|
|
34
|
+
cg->width = EverestCrystalData_get_width(el);
|
|
35
|
+
cg->height = EverestCrystalData_get_height(el);
|
|
36
|
+
cg->jaw_U = EverestCrystalData_get__jaw_U(el);
|
|
37
|
+
cg->sin_z = EverestCrystalData_get__sin_z(el);
|
|
38
|
+
cg->cos_z = EverestCrystalData_get__cos_z(el);
|
|
39
|
+
cg->sin_y = EverestCrystalData_get__sin_y(el);
|
|
40
|
+
cg->cos_y = EverestCrystalData_get__cos_y(el);
|
|
41
|
+
// Segments
|
|
42
|
+
if (cg->side == 1){
|
|
43
|
+
cg->segments = create_crystal(cg->bending_radius, cg->width, cg->length, cg->jaw_U, \
|
|
44
|
+
cg->sin_y, cg->cos_y);
|
|
45
|
+
} else if (cg->side == -1){
|
|
46
|
+
// jaw_U is the inner corner (shifted if right-sided crystal)
|
|
47
|
+
cg->segments = create_crystal(cg->bending_radius, cg->width, cg->length, cg->jaw_U - cg->width, \
|
|
48
|
+
cg->sin_y, cg->cos_y);
|
|
49
|
+
}
|
|
50
|
+
// Bend centre
|
|
51
|
+
cg->s_B = 0;
|
|
52
|
+
cg->x_B = cg->bending_radius;
|
|
53
|
+
// Miscut centre
|
|
54
|
+
cg->s_P = -R*sin(cg->miscut_angle);
|
|
55
|
+
cg->x_P = R*cos(cg->miscut_angle);
|
|
56
|
+
// Mirror the crystal geometry
|
|
57
|
+
if (cg->side == -1){
|
|
58
|
+
cg->bending_radius = -cg->bending_radius;
|
|
59
|
+
cg->bending_angle = -cg->bending_angle;
|
|
60
|
+
cg->miscut_angle = -cg->miscut_angle;
|
|
61
|
+
cg->x_P = -cg->x_P;
|
|
62
|
+
cg->x_B = -cg->x_B;
|
|
63
|
+
}
|
|
64
|
+
if (R < 0){
|
|
65
|
+
// If R<0, a left-sided crystal bends towards the beam
|
|
66
|
+
cg->x_P = cg->x_P + cg->width;
|
|
67
|
+
cg->x_B = cg->x_B + cg->width;
|
|
68
|
+
}
|
|
69
|
+
// From here on, crystal geometry parameters can always be treated as left-sided.
|
|
70
|
+
// Note that the segments are not mirrored, which is fine as get_s_of_first_crossing_with_vlimit
|
|
71
|
+
// is absolute (not in the jaw reference frame). It is only after a hit is registered, that we
|
|
72
|
+
// need to transform the particle to the jaw reference frame.
|
|
73
|
+
double Rb;
|
|
74
|
+
if (cg->miscut_angle > 0){
|
|
75
|
+
Rb = R - cg->width;
|
|
76
|
+
} else {
|
|
77
|
+
Rb = R;
|
|
78
|
+
}
|
|
79
|
+
cg->t_VImax = atan( (Rb*sin(t_R) - cg->s_P) / (R - Rb*cos(t_R) - cg->x_P) );
|
|
80
|
+
// Impact table
|
|
81
|
+
cg->record = EverestCrystalData_getp_internal_record(el, part0);
|
|
82
|
+
cg->record_index = NULL;
|
|
83
|
+
cg->record_touches = 0;
|
|
84
|
+
if (cg->record){
|
|
85
|
+
cg->record_index = InteractionRecordData_getp__index(cg->record);
|
|
86
|
+
cg->record_touches = EverestCrystalData_get_record_touches(el);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
26
89
|
|
|
27
|
-
|
|
28
|
-
|
|
90
|
+
return cg;
|
|
91
|
+
}
|
|
29
92
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// TODO: use xtrack C-code for rotation element
|
|
35
|
-
// TODO: we are ignoring the angle of the right jaw
|
|
36
|
-
// TODO: is a crystal always one-sided...?
|
|
37
|
-
double const sin_zL = EverestCrystalData_get_sin_zL(el);
|
|
38
|
-
double const cos_zL = EverestCrystalData_get_cos_zL(el);
|
|
39
|
-
double const sin_zR = EverestCrystalData_get_sin_zR(el);
|
|
40
|
-
double const cos_zR = EverestCrystalData_get_cos_zR(el);
|
|
41
|
-
if (fabs(sin_zL-sin_zR) > 1.e-10 || fabs(cos_zL-cos_zR) > 1.e-10 ){
|
|
42
|
-
kill_all_particles(part0, XC_ERR_NOT_IMPLEMENTED);
|
|
43
|
-
};
|
|
44
|
-
double const c_aperture = EverestCrystalData_get_jaw_L(el) - EverestCrystalData_get_jaw_R(el);
|
|
45
|
-
double const c_offset = ( EverestCrystalData_get_jaw_L(el) + EverestCrystalData_get_jaw_R(el) ) /2;
|
|
46
|
-
double const c_tilt0 = asin(EverestCrystalData_get_sin_yL(el));
|
|
47
|
-
double const c_tilt1 = asin(EverestCrystalData_get_sin_yR(el));
|
|
48
|
-
if (fabs(c_tilt1) > 1.e-10){
|
|
49
|
-
kill_all_particles(part0, XC_ERR_INVALID_XOFIELD);
|
|
50
|
-
};
|
|
51
|
-
int const side = EverestCrystalData_get__side(el);
|
|
52
|
-
double const bend = EverestCrystalData_get__bending_radius(el);
|
|
53
|
-
// TODO: cry_tilt should be given by jaw positions...?
|
|
54
|
-
double const cry_tilt = EverestCrystalData_get_align_angle(el) + c_tilt0;
|
|
55
|
-
double const bend_ang = length/bend; // temporary value
|
|
56
|
-
if (cry_tilt >= -bend_ang) {
|
|
57
|
-
length = bend*(sin(bend_ang + cry_tilt) - sin(cry_tilt));
|
|
58
|
-
} else {
|
|
59
|
-
length = bend*(sin(bend_ang - cry_tilt) + sin(cry_tilt));
|
|
93
|
+
/*gpufun*/
|
|
94
|
+
void EverestCrystal_free(CrystalGeometry restrict cg, int8_t active){
|
|
95
|
+
if (active){
|
|
96
|
+
destroy_crystal(cg->segments);
|
|
60
97
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
98
|
+
free(cg);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
// TODO: it would be great if we could set EverestData as an xofield, because then we could
|
|
103
|
+
// run this function at creation of the collimator instead of every turn
|
|
104
|
+
/*gpufun*/
|
|
105
|
+
EverestCollData EverestCrystal_init(EverestCrystalData el, LocalParticle* part0, int8_t active){
|
|
106
|
+
EverestCollData coll = (EverestCollData) malloc(sizeof(EverestCollData_));
|
|
107
|
+
if (active){ // This is needed in order to avoid that the initialisation is called during a twiss!
|
|
108
|
+
// Random generator and material
|
|
109
|
+
coll->rng = EverestCrystalData_getp_rutherford_rng(el);
|
|
110
|
+
CrystalMaterialData material = EverestCrystalData_getp__material(el);
|
|
111
|
+
coll->exenergy = CrystalMaterialData_get_excitation_energy(material)*1.0e3; // MeV
|
|
112
|
+
coll->rho = CrystalMaterialData_get_density(material);
|
|
113
|
+
coll->anuc = CrystalMaterialData_get_A(material);
|
|
114
|
+
coll->zatom = CrystalMaterialData_get_Z(material);
|
|
115
|
+
coll->bnref = CrystalMaterialData_get_nuclear_elastic_slope(material);
|
|
116
|
+
coll->csref[0] = CrystalMaterialData_get_cross_section(material, 0);
|
|
117
|
+
coll->csref[1] = CrystalMaterialData_get_cross_section(material, 1);
|
|
118
|
+
coll->csref[5] = CrystalMaterialData_get_cross_section(material, 5);
|
|
119
|
+
coll->dlri = CrystalMaterialData_get_crystal_radiation_length(material);
|
|
120
|
+
coll->dlyi = CrystalMaterialData_get_crystal_nuclear_length(material);
|
|
121
|
+
coll->ai = CrystalMaterialData_get_crystal_plane_distance(material);
|
|
122
|
+
coll->eum = CrystalMaterialData_get_crystal_potential(material);
|
|
123
|
+
coll->collnt = CrystalMaterialData_get_nuclear_collision_length(material);
|
|
124
|
+
coll->eta = 0.9; // Hard-coded channeling saturation factor
|
|
125
|
+
coll->orient = EverestCrystalData_get__orient(el);
|
|
126
|
+
// Impact table
|
|
127
|
+
coll->record = EverestCrystalData_getp_internal_record(el, part0);
|
|
128
|
+
coll->record_index = NULL;
|
|
129
|
+
if (coll->record){
|
|
130
|
+
coll->record_index = InteractionRecordData_getp__index(coll->record);
|
|
131
|
+
coll->record_scatterings = EverestCrystalData_get_record_scatterings(el);
|
|
132
|
+
coll->record_touches = EverestCrystalData_get_record_touches(el);
|
|
133
|
+
}
|
|
74
134
|
}
|
|
135
|
+
return coll;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
/*gpufun*/
|
|
140
|
+
EverestData EverestCrystal_init_data(LocalParticle* part, EverestCollData restrict coll, CrystalGeometry restrict cg){
|
|
141
|
+
EverestData everest = (EverestData) malloc(sizeof(EverestData_));
|
|
142
|
+
everest->coll = coll;
|
|
143
|
+
everest->rescale_scattering = 1;
|
|
144
|
+
#ifndef XCOLL_REFINE_ENERGY
|
|
145
|
+
// Preinitialise scattering parameters
|
|
146
|
+
double charge_ratio = LocalParticle_get_charge_ratio(part);
|
|
147
|
+
double mass_ratio = charge_ratio / LocalParticle_get_chi(part);
|
|
148
|
+
double energy = ( LocalParticle_get_ptau(part) + 1 / LocalParticle_get_beta0(part)
|
|
149
|
+
) * mass_ratio * LocalParticle_get_p0c(part) / 1e9; // energy in GeV
|
|
150
|
+
calculate_scattering(everest, energy);
|
|
151
|
+
calculate_ionisation_properties(everest, energy);
|
|
152
|
+
calculate_critical_angle(everest, part, cg, energy);
|
|
153
|
+
calculate_VI_parameters(everest, part, energy);
|
|
154
|
+
#endif
|
|
155
|
+
return everest;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
/*gpufun*/
|
|
160
|
+
void EverestCrystal_track_local_particle(EverestCrystalData el, LocalParticle* part0) {
|
|
161
|
+
int8_t active = EverestCrystalData_get_active(el);
|
|
162
|
+
active *= EverestCrystalData_get__tracking(el);
|
|
163
|
+
double length = EverestCrystalData_get_length(el);
|
|
164
|
+
|
|
165
|
+
// Initialise collimator data
|
|
166
|
+
// TODO: we want this to happen before tracking (instead of every turn), as a separate kernel
|
|
167
|
+
EverestCollData coll = EverestCrystal_init(el, part0, active);
|
|
168
|
+
CrystalGeometry cg = EverestCrystal_init_geometry(el, part0, active);
|
|
169
|
+
|
|
170
|
+
double t_c = 0;
|
|
75
171
|
|
|
76
172
|
//start_per_particle_block (part0->part)
|
|
77
173
|
if (!active){
|
|
78
174
|
// Drift full length
|
|
79
|
-
Drift_single_particle(part,
|
|
175
|
+
Drift_single_particle(part, length);
|
|
80
176
|
|
|
81
177
|
} else {
|
|
82
178
|
// Check collimator initialisation
|
|
83
|
-
int8_t
|
|
84
|
-
int8_t rng_is_set = assert_rng_set(part, RNG_ERR_SEEDS_NOT_SET);
|
|
85
|
-
int8_t ruth_is_set = assert_rutherford_set(rng, part, RNG_ERR_RUTH_NOT_SET);
|
|
179
|
+
int8_t is_valid = xcoll_check_particle_init(coll->rng, part);
|
|
86
180
|
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
|
|
181
|
+
if (is_valid) {
|
|
182
|
+
double const s_coll = LocalParticle_get_s(part);
|
|
183
|
+
LocalParticle_set_s(part, 0);
|
|
90
184
|
|
|
91
|
-
//
|
|
185
|
+
// Store initial coordinates for updating later
|
|
186
|
+
double const e0 = LocalParticle_get_energy0(part);
|
|
187
|
+
double const p0 = LocalParticle_get_p0c(part);
|
|
188
|
+
double const ptau_in = LocalParticle_get_ptau(part);
|
|
189
|
+
double const rvv_in = LocalParticle_get_rvv(part);
|
|
190
|
+
#ifdef XCOLL_USE_EXACT
|
|
191
|
+
double const xp_in = LocalParticle_get_exact_xp(part);
|
|
192
|
+
double const yp_in = LocalParticle_get_exact_yp(part);
|
|
193
|
+
#else
|
|
194
|
+
double const xp_in = LocalParticle_get_xp(part);
|
|
195
|
+
double const yp_in = LocalParticle_get_yp(part);
|
|
196
|
+
#endif
|
|
197
|
+
double const zeta_in = LocalParticle_get_zeta(part);
|
|
198
|
+
double const mass_ratio = LocalParticle_get_charge_ratio(part) / LocalParticle_get_chi(part); // m/m0
|
|
199
|
+
double energy = (p0*ptau_in + e0) * mass_ratio;
|
|
92
200
|
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
LocalParticle_add_to_y(part, -co_y);
|
|
201
|
+
// Check if hit on jaws
|
|
202
|
+
int8_t is_hit = hit_crystal_check_and_transform(part, cg);
|
|
96
203
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
204
|
+
if (is_hit != 0) {
|
|
205
|
+
// Hit one of the jaws, so scatter
|
|
206
|
+
double remaining_length = length - LocalParticle_get_s(part);
|
|
207
|
+
// Scatter
|
|
208
|
+
EverestData everest = EverestCrystal_init_data(part0, coll, cg);
|
|
209
|
+
calculate_initial_angle(everest, part, cg);
|
|
210
|
+
#ifdef XCOLL_USE_EXACT
|
|
211
|
+
double const xp = LocalParticle_get_exact_xp(part);
|
|
212
|
+
#else
|
|
213
|
+
double const xp = LocalParticle_get_xp(part);
|
|
214
|
+
#endif
|
|
215
|
+
if (fabs(xp - everest->t_I) < everest->t_c) {
|
|
216
|
+
energy = Channel(everest, part, cg, energy/1.e9, remaining_length)*1.e9;
|
|
217
|
+
} else {
|
|
218
|
+
energy = Amorphous(everest, part, cg, energy/1.e9, remaining_length)*1.e9;
|
|
219
|
+
}
|
|
220
|
+
// Temporary workaround to store the critical angle for use later
|
|
221
|
+
calculate_critical_angle(everest, part, cg, e0/1.e9);
|
|
222
|
+
t_c = everest->t_c;
|
|
223
|
+
free(everest);
|
|
224
|
+
}
|
|
100
225
|
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
|
|
226
|
+
// Transform back to the lab frame
|
|
227
|
+
hit_crystal_transform_back(is_hit, part, cg);
|
|
228
|
+
LocalParticle_add_to_s(part, s_coll);
|
|
104
229
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
230
|
+
LocalParticle_set_zeta(part, zeta_in);
|
|
231
|
+
// Hit and survived particles need correcting:
|
|
232
|
+
if (is_hit!=0 && LocalParticle_get_state(part)>0){
|
|
233
|
+
// Update energy
|
|
234
|
+
double ptau_out = (energy/mass_ratio - e0) / p0;
|
|
235
|
+
LocalParticle_update_ptau(part, ptau_out);
|
|
236
|
+
// Update zeta
|
|
237
|
+
#ifdef XCOLL_USE_EXACT
|
|
238
|
+
double xp = LocalParticle_get_exact_xp(part);
|
|
239
|
+
double yp = LocalParticle_get_exact_yp(part);
|
|
240
|
+
#else
|
|
241
|
+
double xp = LocalParticle_get_xp(part);
|
|
242
|
+
double yp = LocalParticle_get_yp(part);
|
|
243
|
+
#endif
|
|
244
|
+
double rvv = LocalParticle_get_rvv(part);
|
|
245
|
+
// First we drift half the length with the old angles:
|
|
246
|
+
LocalParticle_add_to_zeta(part, drift_zeta_single(rvv_in, xp_in, yp_in, length/2) );
|
|
247
|
+
// then half the length with the new angles:
|
|
248
|
+
LocalParticle_add_to_zeta(part, drift_zeta_single(rvv, xp, yp, length/2) );
|
|
108
249
|
}
|
|
109
250
|
}
|
|
110
251
|
}
|
|
111
252
|
//end_per_particle_block
|
|
253
|
+
EverestCrystalData_set__critical_angle(el, t_c);
|
|
254
|
+
EverestCrystal_free(cg, active);
|
|
255
|
+
free(coll);
|
|
112
256
|
}
|
|
113
257
|
|
|
114
258
|
|
xcoll/beam_elements/everest.py
CHANGED
|
@@ -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
|
import numpy as np
|
|
@@ -9,8 +9,8 @@ import xobjects as xo
|
|
|
9
9
|
import xpart as xp
|
|
10
10
|
import xtrack as xt
|
|
11
11
|
|
|
12
|
-
from .base import BaseBlock, BaseCollimator, InvalidXcoll
|
|
13
|
-
from ..scattering_routines.everest import
|
|
12
|
+
from .base import BaseBlock, BaseCollimator, BaseCrystal, InvalidXcoll
|
|
13
|
+
from ..scattering_routines.everest import Material, CrystalMaterial, EverestEngine
|
|
14
14
|
from ..general import _pkg_root
|
|
15
15
|
|
|
16
16
|
|
|
@@ -21,10 +21,9 @@ from ..general import _pkg_root
|
|
|
21
21
|
# only activated around the track command. Furthermore, because of 'iscollective = False' we need to specify
|
|
22
22
|
# get_backtrack_element. We want it nicer..
|
|
23
23
|
|
|
24
|
-
# TODO: _per_particle_kernels should be a normal kernel (such that we don't need to pass a dummy Particles() )
|
|
25
24
|
|
|
26
25
|
class EverestBlock(BaseBlock):
|
|
27
|
-
_xofields = {
|
|
26
|
+
_xofields = {**BaseBlock._xofields,
|
|
28
27
|
'_material': Material,
|
|
29
28
|
'rutherford_rng': xt.RandomRutherford,
|
|
30
29
|
'_tracking': xo.Int8
|
|
@@ -55,21 +54,15 @@ class EverestBlock(BaseBlock):
|
|
|
55
54
|
|
|
56
55
|
|
|
57
56
|
def __init__(self, **kwargs):
|
|
57
|
+
to_assign = {}
|
|
58
58
|
if '_xobject' not in kwargs:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
raise ValueError("Need to provide a material to the block!")
|
|
62
|
-
if not isinstance(mat, Material):
|
|
63
|
-
if not isinstance(mat, dict) \
|
|
64
|
-
or mat['__class__'] != "Material":
|
|
65
|
-
raise ValueError("Invalid material!")
|
|
66
|
-
kwargs['_material'] = mat
|
|
59
|
+
to_assign['material'] = kwargs.pop('material', None)
|
|
60
|
+
kwargs['_material'] = Material()
|
|
67
61
|
kwargs.setdefault('rutherford_rng', xt.RandomRutherford())
|
|
68
62
|
kwargs.setdefault('_tracking', True)
|
|
69
|
-
use_prebuilt_kernels = kwargs.pop('use_prebuilt_kernels', True)
|
|
70
63
|
super().__init__(**kwargs)
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
for key, val in to_assign.items():
|
|
65
|
+
setattr(self, key, val)
|
|
73
66
|
|
|
74
67
|
|
|
75
68
|
@property
|
|
@@ -78,9 +71,12 @@ class EverestBlock(BaseBlock):
|
|
|
78
71
|
|
|
79
72
|
@material.setter
|
|
80
73
|
def material(self, material):
|
|
74
|
+
if material is None:
|
|
75
|
+
material = Material()
|
|
76
|
+
if isinstance(material, dict):
|
|
77
|
+
material = Material.from_dict(material)
|
|
81
78
|
if not isinstance(material, Material):
|
|
82
|
-
|
|
83
|
-
raise ValueError("Invalid material!")
|
|
79
|
+
raise ValueError("Invalid material!")
|
|
84
80
|
if not xt.line._dicts_equal(self.material.to_dict(), material.to_dict()):
|
|
85
81
|
self._material = material
|
|
86
82
|
self.EverestBlock_set_material(el=self)
|
|
@@ -91,7 +87,7 @@ class EverestBlock(BaseBlock):
|
|
|
91
87
|
|
|
92
88
|
|
|
93
89
|
class EverestCollimator(BaseCollimator):
|
|
94
|
-
_xofields = {
|
|
90
|
+
_xofields = {**BaseCollimator._xofields,
|
|
95
91
|
'_material': Material,
|
|
96
92
|
'rutherford_rng': xt.RandomRutherford,
|
|
97
93
|
'_tracking': xo.Int8
|
|
@@ -122,20 +118,15 @@ class EverestCollimator(BaseCollimator):
|
|
|
122
118
|
|
|
123
119
|
|
|
124
120
|
def __init__(self, **kwargs):
|
|
121
|
+
to_assign = {}
|
|
125
122
|
if '_xobject' not in kwargs:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if not isinstance(kwargs['material'], Material):
|
|
129
|
-
if not isinstance(kwargs['material'], dict) \
|
|
130
|
-
or kwargs['material']['__class__'] != "Material":
|
|
131
|
-
raise ValueError("Invalid material!")
|
|
132
|
-
kwargs['_material'] = kwargs.pop('material')
|
|
123
|
+
to_assign['material'] = kwargs.pop('material', None)
|
|
124
|
+
kwargs['_material'] = Material()
|
|
133
125
|
kwargs.setdefault('rutherford_rng', xt.RandomRutherford())
|
|
134
126
|
kwargs.setdefault('_tracking', True)
|
|
135
|
-
use_prebuilt_kernels = kwargs.pop('use_prebuilt_kernels', True)
|
|
136
127
|
super().__init__(**kwargs)
|
|
137
|
-
|
|
138
|
-
|
|
128
|
+
for key, val in to_assign.items():
|
|
129
|
+
setattr(self, key, val)
|
|
139
130
|
|
|
140
131
|
@property
|
|
141
132
|
def material(self):
|
|
@@ -143,9 +134,12 @@ class EverestCollimator(BaseCollimator):
|
|
|
143
134
|
|
|
144
135
|
@material.setter
|
|
145
136
|
def material(self, material):
|
|
137
|
+
if material is None:
|
|
138
|
+
material = Material()
|
|
139
|
+
if isinstance(material, dict):
|
|
140
|
+
material = Material.from_dict(material)
|
|
146
141
|
if not isinstance(material, Material):
|
|
147
|
-
|
|
148
|
-
raise ValueError("Invalid material!")
|
|
142
|
+
raise ValueError("Invalid material!")
|
|
149
143
|
if not xt.line._dicts_equal(self.material.to_dict(), material.to_dict()):
|
|
150
144
|
self._material = material
|
|
151
145
|
self.EverestCollimator_set_material(el=self)
|
|
@@ -156,17 +150,11 @@ class EverestCollimator(BaseCollimator):
|
|
|
156
150
|
|
|
157
151
|
|
|
158
152
|
|
|
159
|
-
class EverestCrystal(
|
|
160
|
-
_xofields = {
|
|
161
|
-
'align_angle': xo.Float64, # = - sqrt(eps/beta)*alpha*nsigma
|
|
162
|
-
'_bending_radius': xo.Float64,
|
|
163
|
-
'_bending_angle': xo.Float64,
|
|
164
|
-
'_critical_angle': xo.Float64,
|
|
165
|
-
'xdim': xo.Float64,
|
|
166
|
-
'ydim': xo.Float64,
|
|
167
|
-
'thick': xo.Float64,
|
|
153
|
+
class EverestCrystal(BaseCrystal):
|
|
154
|
+
_xofields = {**BaseCrystal._xofields,
|
|
168
155
|
'miscut': xo.Float64,
|
|
169
156
|
'_orient': xo.Int8,
|
|
157
|
+
'_critical_angle': xo.Float64,
|
|
170
158
|
'_material': CrystalMaterial,
|
|
171
159
|
'rutherford_rng': xt.RandomRutherford,
|
|
172
160
|
'_tracking': xo.Int8
|
|
@@ -178,12 +166,11 @@ class EverestCrystal(BaseCollimator):
|
|
|
178
166
|
behaves_like_drift = True
|
|
179
167
|
skip_in_loss_location_refinement = True
|
|
180
168
|
|
|
181
|
-
_skip_in_to_dict = [*
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
_internal_record_class = BaseCollimator._internal_record_class
|
|
169
|
+
_skip_in_to_dict = [*BaseCrystal._skip_in_to_dict, '_orient', '_material']
|
|
170
|
+
_store_in_to_dict = [*BaseCrystal._store_in_to_dict, 'lattice', 'material']
|
|
171
|
+
_internal_record_class = BaseCrystal._internal_record_class
|
|
185
172
|
|
|
186
|
-
_depends_on = [
|
|
173
|
+
_depends_on = [BaseCrystal, EverestEngine]
|
|
187
174
|
|
|
188
175
|
_extra_c_sources = [
|
|
189
176
|
_pkg_root.joinpath('beam_elements','collimators_src','everest_crystal.h')
|
|
@@ -198,62 +185,38 @@ class EverestCrystal(BaseCollimator):
|
|
|
198
185
|
|
|
199
186
|
|
|
200
187
|
def __init__(self, **kwargs):
|
|
188
|
+
to_assign = {}
|
|
201
189
|
if '_xobject' not in kwargs:
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if not isinstance(kwargs['material'], dict) \
|
|
206
|
-
or kwargs['material']['__class__'] != "CrystalMaterial":
|
|
207
|
-
raise ValueError("Invalid material!")
|
|
208
|
-
kwargs['_material'] = kwargs.pop('material')
|
|
209
|
-
bending_radius = False
|
|
210
|
-
bending_angle = False
|
|
211
|
-
if 'bending_radius' in kwargs:
|
|
212
|
-
if 'bending_angle' in kwargs:
|
|
213
|
-
raise ValueError("Need to choose between 'bending_radius' and 'bending_angle'!")
|
|
214
|
-
bending_radius = kwargs['bending_radius']
|
|
215
|
-
elif 'bending_angle' in kwargs:
|
|
216
|
-
bending_angle = kwargs['bending_angle']
|
|
217
|
-
kwargs['_bending_radius'] = kwargs.pop('bending_radius',0)
|
|
218
|
-
kwargs['_bending_angle'] = kwargs.pop('bending_angle', 0)
|
|
219
|
-
kwargs.setdefault('xdim', 0)
|
|
220
|
-
kwargs.setdefault('ydim', 0)
|
|
221
|
-
kwargs.setdefault('thick', 0)
|
|
190
|
+
to_assign['material'] = kwargs.pop('material', None)
|
|
191
|
+
kwargs['_material'] = CrystalMaterial()
|
|
192
|
+
to_assign['lattice'] = kwargs.pop('lattice', 'strip')
|
|
222
193
|
kwargs.setdefault('miscut', 0)
|
|
223
|
-
kwargs['_orient'] = _lattice_setter(kwargs.pop('lattice', 'strip'))
|
|
224
194
|
kwargs.setdefault('rutherford_rng', xt.RandomRutherford())
|
|
225
195
|
kwargs.setdefault('_tracking', True)
|
|
226
|
-
use_prebuilt_kernels = kwargs.pop('use_prebuilt_kernels', True)
|
|
227
196
|
super().__init__(**kwargs)
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
self._bending_angle = np.arcsin(self.active_length/bending_radius)
|
|
231
|
-
if bending_angle:
|
|
232
|
-
self._bending_radius = self.active_length / np.sin(bending_angle)
|
|
233
|
-
self.EverestCrystal_set_material(el=self)
|
|
197
|
+
for key, val in to_assign.items():
|
|
198
|
+
setattr(self, key, val)
|
|
234
199
|
|
|
235
200
|
|
|
236
201
|
@property
|
|
237
|
-
def
|
|
238
|
-
return self.
|
|
239
|
-
|
|
240
|
-
@property
|
|
241
|
-
def bending_radius(self):
|
|
242
|
-
return self._bending_radius
|
|
202
|
+
def material(self):
|
|
203
|
+
return self._material
|
|
243
204
|
|
|
244
|
-
@
|
|
245
|
-
def
|
|
246
|
-
|
|
247
|
-
|
|
205
|
+
@material.setter
|
|
206
|
+
def material(self, material):
|
|
207
|
+
if material is None:
|
|
208
|
+
material = CrystalMaterial()
|
|
209
|
+
if isinstance(material, dict):
|
|
210
|
+
material = CrystalMaterial.from_dict(material)
|
|
211
|
+
if not isinstance(material, CrystalMaterial):
|
|
212
|
+
raise ValueError("Invalid material!")
|
|
213
|
+
if not xt.line._dicts_equal(self.material.to_dict(), material.to_dict()):
|
|
214
|
+
self._material = material
|
|
215
|
+
self.EverestCrystal_set_material(el=self)
|
|
248
216
|
|
|
249
217
|
@property
|
|
250
|
-
def
|
|
251
|
-
return self.
|
|
252
|
-
|
|
253
|
-
@bending_angle.setter
|
|
254
|
-
def bending_angle(self, bending_angle):
|
|
255
|
-
self._bending_angle = bending_angle
|
|
256
|
-
self._bending_radius = self.active_length / np.sin(bending_angle)
|
|
218
|
+
def critical_angle(self):
|
|
219
|
+
return self._critical_angle if abs(self._critical_angle) > 1.e-10 else None
|
|
257
220
|
|
|
258
221
|
@property
|
|
259
222
|
def lattice(self):
|
|
@@ -266,20 +229,13 @@ class EverestCrystal(BaseCollimator):
|
|
|
266
229
|
|
|
267
230
|
@lattice.setter
|
|
268
231
|
def lattice(self, lattice):
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
def material(self, material):
|
|
277
|
-
if not isinstance(material, CrystalMaterial):
|
|
278
|
-
if not isinstance(material, dict) or material['__class__'] != "CrystalMaterial":
|
|
279
|
-
raise ValueError("Invalid material!")
|
|
280
|
-
if not xt.line._dicts_equal(self.material.to_dict(), material.to_dict()):
|
|
281
|
-
self._material = material
|
|
282
|
-
self.EverestCrystal_set_material(el=self)
|
|
232
|
+
if lattice == 'strip' or lattice == '110' or lattice == 110:
|
|
233
|
+
self._orient = 1
|
|
234
|
+
elif lattice == 'quasi-mosaic' or lattice == '111' or lattice == 111:
|
|
235
|
+
self._orient = 2
|
|
236
|
+
else:
|
|
237
|
+
raise ValueError(f"Illegal value {lattice} for 'lattice'! "
|
|
238
|
+
+ "Only use 'strip' (110) or 'quasi-mosaic' (111).")
|
|
283
239
|
|
|
284
240
|
|
|
285
241
|
def get_backtrack_element(self, _context=None, _buffer=None, _offset=None):
|
|
@@ -287,12 +243,3 @@ class EverestCrystal(BaseCollimator):
|
|
|
287
243
|
_buffer=_buffer, _offset=_offset)
|
|
288
244
|
|
|
289
245
|
|
|
290
|
-
def _lattice_setter(lattice):
|
|
291
|
-
if lattice == 'strip' or lattice == '110' or lattice == 110:
|
|
292
|
-
return 1
|
|
293
|
-
elif lattice == 'quasi-mosaic' or lattice == '111' or lattice == 111:
|
|
294
|
-
return 2
|
|
295
|
-
else:
|
|
296
|
-
raise ValueError(f"Illegal value {lattice} for 'lattice'! "
|
|
297
|
-
+ "Only use 'strip' (110) or 'quasi-mosaic' (111).")
|
|
298
|
-
|