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
xcoll/rf_sweep.py
CHANGED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
// copyright ############################### #
|
|
2
|
+
// This file is part of the Xcoll Package. #
|
|
3
|
+
// Copyright (c) CERN, 2023. #
|
|
4
|
+
// ######################################### #
|
|
5
|
+
|
|
6
|
+
#ifndef XCOLL_EVEREST_AMORPHOUS_H
|
|
7
|
+
#define XCOLL_EVEREST_AMORPHOUS_H
|
|
8
|
+
#include <math.h>
|
|
9
|
+
#include <stdio.h>
|
|
10
|
+
#include <stdlib.h>
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/*gpufun*/
|
|
14
|
+
void volume_reflection(EverestData restrict everest, LocalParticle* part, int8_t transition) {
|
|
15
|
+
|
|
16
|
+
InteractionRecordData record = everest->coll->record;
|
|
17
|
+
RecordIndex record_index = everest->coll->record_index;
|
|
18
|
+
int8_t sc = everest->coll->record_scatterings;
|
|
19
|
+
int64_t i_slot;
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
double Ang_avr = everest->Ang_avr;
|
|
23
|
+
double Ang_rms = everest->Ang_rms;
|
|
24
|
+
|
|
25
|
+
if (transition == XC_VOLUME_REFLECTION_TRANS_CH){
|
|
26
|
+
// We are in transition from CH to VR
|
|
27
|
+
double xp = LocalParticle_get_xp(part);
|
|
28
|
+
// TODO: we believe the original 0.45 comes from the 0.9 saturation factor, so we changed it to 0.5
|
|
29
|
+
Ang_avr *= 0.5*((xp - everest->t_I)/everest->t_c + 1);
|
|
30
|
+
Ang_rms = 0; // TODO: why does transition to CH not use any spread?
|
|
31
|
+
if (sc) i_slot = InteractionRecordData_log(record, record_index, part, XC_VOLUME_REFLECTION_TRANS_CH);
|
|
32
|
+
|
|
33
|
+
} else if (transition == XC_VOLUME_REFLECTION_TRANS_MCS){
|
|
34
|
+
// We are in transition from VR to MCS
|
|
35
|
+
// double t_c = everest->t_c;
|
|
36
|
+
// double t_P = everest->t_P;
|
|
37
|
+
// double xp_rel = LocalParticle_get_xp(part) - everest->t_I;
|
|
38
|
+
// // TODO: where does 3 come from
|
|
39
|
+
// Ang_rms *= -3.*(xp_rel-t_P)/(2.*t_c); // TODO: why no random number?
|
|
40
|
+
Ang_rms *= RandomNormal_generate(part);
|
|
41
|
+
if (sc) i_slot = InteractionRecordData_log(record, record_index, part, XC_VOLUME_REFLECTION_TRANS_MCS);
|
|
42
|
+
|
|
43
|
+
} else {
|
|
44
|
+
Ang_rms *= RandomNormal_generate(part);
|
|
45
|
+
if (sc) i_slot = InteractionRecordData_log(record, record_index, part, XC_VOLUME_REFLECTION);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
double t_VR = Ang_avr + Ang_rms;
|
|
49
|
+
LocalParticle_add_to_xp(part, t_VR);
|
|
50
|
+
if (sc) InteractionRecordData_log_child(record, i_slot, part, 0);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
// Amorphous transport is just Multiple Coulomb scattering
|
|
55
|
+
/*gpufun*/
|
|
56
|
+
double amorphous_transport(EverestData restrict everest, LocalParticle* part, double pc, double length, int8_t transition) {
|
|
57
|
+
|
|
58
|
+
InteractionRecordData record = everest->coll->record;
|
|
59
|
+
RecordIndex record_index = everest->coll->record_index;
|
|
60
|
+
int8_t sc = everest->coll->record_scatterings;
|
|
61
|
+
int64_t i_slot;
|
|
62
|
+
|
|
63
|
+
// Accumulated effect of mcs on the angles (with initial energy)
|
|
64
|
+
// TODO: pc is energy
|
|
65
|
+
// TODO: missing factor (1 + 0.038*log( L / dlri) ) ( *Z if not protons)
|
|
66
|
+
double dya = (13.6/pc)*sqrt(length/everest->coll->dlri)*1.0e-3; // RMS of coloumb scattering MCS (rad)
|
|
67
|
+
double kxmcs, kymcs;
|
|
68
|
+
|
|
69
|
+
if (transition == XC_MULTIPLE_COULOMB_TRANS_VR){
|
|
70
|
+
// Transition MCS
|
|
71
|
+
if (sc) i_slot = InteractionRecordData_log(record, record_index, part, XC_MULTIPLE_COULOMB_TRANS_VR);
|
|
72
|
+
// double xp_rel = LocalParticle_get_xp(part) - everest->t_I;
|
|
73
|
+
// double t_P = everest->t_P;
|
|
74
|
+
// double t_c = everest->t_c;
|
|
75
|
+
// dya *= 1 - (xp_rel-t_P)/(2.*t_c);
|
|
76
|
+
} else {
|
|
77
|
+
// Normal MCS
|
|
78
|
+
if (sc) i_slot = InteractionRecordData_log(record, record_index, part, XC_MULTIPLE_COULOMB_SCATTERING);
|
|
79
|
+
}
|
|
80
|
+
kxmcs = dya*RandomNormal_generate(part);
|
|
81
|
+
kymcs = dya*RandomNormal_generate(part);
|
|
82
|
+
|
|
83
|
+
// Transport of Multiple Coulomb Scattering
|
|
84
|
+
Drift_single_particle_4d(part, length);
|
|
85
|
+
|
|
86
|
+
// Energy lost because of ionisation process[GeV]
|
|
87
|
+
double energy_loss = calcionloss(everest, part, length);
|
|
88
|
+
pc = pc - energy_loss*length;
|
|
89
|
+
|
|
90
|
+
// Store new angles
|
|
91
|
+
LocalParticle_add_to_xp_yp(part, kxmcs, kymcs);
|
|
92
|
+
|
|
93
|
+
// Finally log particle at end of mcs
|
|
94
|
+
if (sc) InteractionRecordData_log_child(record, i_slot, part, length);
|
|
95
|
+
|
|
96
|
+
return pc;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
double Channel(EverestData restrict everest, LocalParticle* part, CrystalGeometry restrict cg, double pc, double length);
|
|
100
|
+
|
|
101
|
+
/*gpufun*/
|
|
102
|
+
double Amorphous(EverestData restrict everest, LocalParticle* part, CrystalGeometry restrict cg, double pc, double length) {
|
|
103
|
+
|
|
104
|
+
if (LocalParticle_get_state(part) < 1){
|
|
105
|
+
// Do nothing if already absorbed
|
|
106
|
+
return pc;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
InteractionRecordData record = everest->coll->record;
|
|
110
|
+
RecordIndex record_index = everest->coll->record_index;
|
|
111
|
+
int8_t sc = everest->coll->record_scatterings;
|
|
112
|
+
|
|
113
|
+
calculate_initial_angle(everest, part, cg);
|
|
114
|
+
|
|
115
|
+
// -----------------------------------------------
|
|
116
|
+
// Calculate longitudinal position where we go out
|
|
117
|
+
// -----------------------------------------------
|
|
118
|
+
// Assumption: crystal bends away from beam (this does not work when crystal bends towards beam)
|
|
119
|
+
// All s are absolute in the collimator frame
|
|
120
|
+
double R = cg->bending_radius;
|
|
121
|
+
double t = cg->bending_angle;
|
|
122
|
+
double d = cg->width;
|
|
123
|
+
double s = LocalParticle_get_s(part);
|
|
124
|
+
double x = LocalParticle_get_x(part);
|
|
125
|
+
double xp = LocalParticle_get_xp(part);
|
|
126
|
+
// We either exit the crystal at the exit face
|
|
127
|
+
double s1 = (R - x + s*xp) / (xp + cos(t)/sin(t) );
|
|
128
|
+
if (s1 < s) {s1 = 1e10;}
|
|
129
|
+
// or the larger bend
|
|
130
|
+
double dd = (R - x + s*xp) / (1 + xp*xp);
|
|
131
|
+
double s2 = dd*xp + sqrt(R*R / (1 + xp*xp) * dd*dd);
|
|
132
|
+
if (s2 < s) {s2 = 1e10;}
|
|
133
|
+
// or the smaller bend (which potentially has two solutions, in case the particle enters again before the exit face)
|
|
134
|
+
double s3 = dd*xp - sqrt( (R-d)*(R-d) / (1 + xp*xp) * dd*dd);
|
|
135
|
+
if (s3 < s) {s3 = 1e10;}
|
|
136
|
+
// whichever comes first after the current position.
|
|
137
|
+
// This gives us an "exit length" \with respect to the current position
|
|
138
|
+
double exit_point = fmin(fmin(s1, s2), s3);
|
|
139
|
+
double length_exit = fmin(exit_point - s, length);
|
|
140
|
+
|
|
141
|
+
// ----------------------------------------------------
|
|
142
|
+
// Calculate longitudinal length of nuclear interaction
|
|
143
|
+
// ----------------------------------------------------
|
|
144
|
+
double length_nucl = everest->coll->collnt*RandomExponential_generate(part);
|
|
145
|
+
|
|
146
|
+
// --------------------------------------------------------------
|
|
147
|
+
// Calculate longitudinal length of volume interaction (VR or VC)
|
|
148
|
+
// --------------------------------------------------------------
|
|
149
|
+
// This happens when the particle is tangential to the crystal planes.
|
|
150
|
+
// The (straight) trajectory until the point of reflection is r sin(xp - t_I),
|
|
151
|
+
// hence the longitudinal length is r sin(xp - miscut) cos xp
|
|
152
|
+
double length_VI = everest->r * sin(xp - everest->t_I) * cos(xp);
|
|
153
|
+
// If length_VI is negative, VI is not possible, so set to a big value
|
|
154
|
+
if (length_VI < 0) {length_VI = 1e10;}
|
|
155
|
+
// Calculate extra length to transition region between VR and AM
|
|
156
|
+
double length_VR_trans = everest->r * sin(xp - everest->t_I - 2.*everest->t_c) * cos(xp - 2.*everest->t_c);
|
|
157
|
+
if (length_VR_trans < 0) {length_VR_trans = 1e10;}
|
|
158
|
+
|
|
159
|
+
// ------------------------------------------------------------------------
|
|
160
|
+
// Compare the 3 lengths: the first one encountered is what will be applied
|
|
161
|
+
// ------------------------------------------------------------------------
|
|
162
|
+
if (length_VI <= fmin(length_nucl, length_exit)){
|
|
163
|
+
// MCS to volume interaction
|
|
164
|
+
pc = amorphous_transport(everest, part, pc, length_VI, 0);
|
|
165
|
+
#ifdef XCOLL_REFINE_ENERGY
|
|
166
|
+
calculate_VI_parameters(everest, part, pc);
|
|
167
|
+
#endif
|
|
168
|
+
// Are we reflecting or captured?
|
|
169
|
+
if (RandomUniform_generate(part) > everest->Vcapt) {
|
|
170
|
+
// Volume Reflection
|
|
171
|
+
volume_reflection(everest, part, 0);
|
|
172
|
+
// We call the main Amorphous function for the leftover
|
|
173
|
+
pc = Amorphous(everest, part, cg, pc, length - length_VI);
|
|
174
|
+
|
|
175
|
+
} else {
|
|
176
|
+
// Volume Capture
|
|
177
|
+
if (sc) InteractionRecordData_log(record, record_index, part, XC_VOLUME_CAPTURE);
|
|
178
|
+
// We call the main Channel function for the leftover
|
|
179
|
+
pc = Channel(everest, part, cg, pc, length - length_VI);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
#ifdef XCOLL_TRANSITION
|
|
183
|
+
} else if (length_VR_trans <= fmin(length_nucl, length_exit)){
|
|
184
|
+
// Transition region between VR and AM for t_P < xp - tI < t_P + 2t_c
|
|
185
|
+
#ifdef XCOLL_REFINE_ENERGY
|
|
186
|
+
calculate_critical_angle(everest, part, cg, pc);
|
|
187
|
+
#endif
|
|
188
|
+
double xp_rel = xp - everest->t_I;
|
|
189
|
+
double t_P = everest->t_P;
|
|
190
|
+
double t_c = everest->t_c;
|
|
191
|
+
double prob_MCS = (xp_rel - t_P) / (2*t_c);
|
|
192
|
+
if (RandomUniform_generate(part) > prob_MCS){
|
|
193
|
+
// We are on the VR side
|
|
194
|
+
pc = amorphous_transport(everest, part, pc, length_VR_trans, 0);
|
|
195
|
+
volume_reflection(everest, part, XC_VOLUME_REFLECTION_TRANS_MCS);
|
|
196
|
+
pc = amorphous_transport(everest, part, pc, length - length_VR_trans, 0);
|
|
197
|
+
} else {
|
|
198
|
+
// We are on the AM side
|
|
199
|
+
pc = amorphous_transport(everest, part, pc, length, XC_MULTIPLE_COULOMB_TRANS_VR);
|
|
200
|
+
}
|
|
201
|
+
#endif
|
|
202
|
+
|
|
203
|
+
} else if (length_nucl < length_exit) {
|
|
204
|
+
// MCS to nuclear interaction
|
|
205
|
+
pc = amorphous_transport(everest, part, pc, length_nucl, 0);
|
|
206
|
+
// interact
|
|
207
|
+
pc = nuclear_interaction(everest, part, pc);
|
|
208
|
+
if (LocalParticle_get_state(part) == XC_LOST_ON_EVEREST_COLL){
|
|
209
|
+
LocalParticle_set_state(part, XC_LOST_ON_EVEREST_CRYSTAL);
|
|
210
|
+
} else {
|
|
211
|
+
// We call the main Amorphous function for the leftover
|
|
212
|
+
pc = Amorphous(everest, part, cg, pc, length - length_nucl);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
} else {
|
|
216
|
+
// Exit crystal
|
|
217
|
+
// MCS to exit point
|
|
218
|
+
pc = amorphous_transport(everest, part, pc, length_exit, 0);
|
|
219
|
+
// Now drift the remaining
|
|
220
|
+
// However, if we have exited at s3, and we encounter s4 before s2, we reenter:
|
|
221
|
+
double s4 = dd*xp + sqrt( (R-d)*(R-d) / (1 + xp*xp) * dd*dd); // second solution for smaller bend
|
|
222
|
+
if (s3 < fmin(s1, s2) && s4 < s2){
|
|
223
|
+
// We drift until re-entry
|
|
224
|
+
Drift_single_particle_4d(part, s4 - exit_point);
|
|
225
|
+
// We call the main Amorphous function for the leftover
|
|
226
|
+
pc = Amorphous(everest, part, cg, pc, length - length_exit - s4 + exit_point);
|
|
227
|
+
} else {
|
|
228
|
+
// Drift leftover out of the crystal
|
|
229
|
+
if (everest->coll->record_touches){
|
|
230
|
+
InteractionRecordData_log(record, record_index, part, XC_EXIT_JAW);
|
|
231
|
+
}
|
|
232
|
+
Drift_single_particle_4d(part, length - length_exit);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return pc;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
#endif /* XCOLL_EVEREST_AMORPHOUS_H */
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
// copyright ############################### #
|
|
2
|
+
// This file is part of the Xcoll Package. #
|
|
3
|
+
// Copyright (c) CERN, 2023. #
|
|
4
|
+
// ######################################### #
|
|
5
|
+
|
|
6
|
+
#ifndef XCOLL_EVEREST_CHANNELING_H
|
|
7
|
+
#define XCOLL_EVEREST_CHANNELING_H
|
|
8
|
+
#include <math.h>
|
|
9
|
+
#include <stdio.h>
|
|
10
|
+
#include <stdlib.h>
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
// Convention:
|
|
14
|
+
// L: length of curved trajectory
|
|
15
|
+
// s: longitudinal coordinate (in collimator frame) - projection of curved trajectory
|
|
16
|
+
// t: opening angle of curve ('t' for theta)
|
|
17
|
+
// r: radius from position to bending centre (including t_I): L = t*r
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
/*gpufun*/
|
|
21
|
+
double channeling_average_density(EverestData restrict everest, CrystalGeometry restrict cg, LocalParticle* part, double pc) {
|
|
22
|
+
|
|
23
|
+
// Material properties
|
|
24
|
+
double const anuc = everest->coll->anuc;
|
|
25
|
+
double const rho = everest->coll->rho;
|
|
26
|
+
double const eum = everest->coll->eum;
|
|
27
|
+
|
|
28
|
+
double bend_r = cg->bending_radius;
|
|
29
|
+
double ratio = everest->Rc_over_R;
|
|
30
|
+
|
|
31
|
+
//Rescale the total and inelastic cross-section accordigly to the average density seen
|
|
32
|
+
double x_i = LocalParticle_get_x(part);
|
|
33
|
+
double xp = LocalParticle_get_xp(part);
|
|
34
|
+
int np = x_i/XC_PLANE_DISTANCE; //Calculate in which crystalline plane the particle enters
|
|
35
|
+
x_i -= (np + 0.5)*XC_PLANE_DISTANCE; //Rescale the incoming x to the middle of the crystalline plane
|
|
36
|
+
|
|
37
|
+
double pv = pow(pc, 2.)/sqrt(pow(pc, 2.) + pow(XC_PROTON_MASS*1.0e-3, 2.))*1.0e9; //Calculate pv=P/E TODO: this is beta?
|
|
38
|
+
double Ueff = eum*4.*pow(x_i/XC_PLANE_DISTANCE, 2.) + pv*x_i/bend_r; //Calculate effective potential
|
|
39
|
+
double Et = pv*pow(xp, 2.)/2. + Ueff; //Calculate transverse energy
|
|
40
|
+
double Ec = eum*pow(1. - ratio, 2.); //Calculate critical energy in bent crystals
|
|
41
|
+
|
|
42
|
+
//To avoid negative Et
|
|
43
|
+
double xminU = -pow(XC_PLANE_DISTANCE, 2.)*pc*1.0e9/(8.*eum*bend_r);
|
|
44
|
+
double Umin = fabs(eum*4.*pow(xminU/XC_PLANE_DISTANCE, 2.) + pv*xminU/bend_r);
|
|
45
|
+
Et = Et + Umin;
|
|
46
|
+
Ec = Ec + Umin;
|
|
47
|
+
|
|
48
|
+
//Calculate min e max of the trajectory between crystalline planes
|
|
49
|
+
double x_min = -0.5*XC_PLANE_DISTANCE*ratio - 0.5*XC_PLANE_DISTANCE*sqrt(Et/Ec);
|
|
50
|
+
double x_max = -0.5*XC_PLANE_DISTANCE*ratio + 0.5*XC_PLANE_DISTANCE*sqrt(Et/Ec);
|
|
51
|
+
|
|
52
|
+
//Change ref. frame and go back with 0 on the crystalline plane on the left
|
|
53
|
+
x_min = x_min - XC_PLANE_DISTANCE/2.;
|
|
54
|
+
x_max = x_max - XC_PLANE_DISTANCE/2.;
|
|
55
|
+
|
|
56
|
+
//Calculate the "normal density" in m^-3
|
|
57
|
+
double N_am = rho*XC_AVOGADRO*1.0e6/anuc;
|
|
58
|
+
|
|
59
|
+
//Calculate atomic density at min and max of the trajectory oscillation
|
|
60
|
+
// erf returns the error function of complex argument
|
|
61
|
+
double rho_max = erf(x_max/sqrt(2*pow(XC_THERMAL_VIBRATIONS, 2.)));
|
|
62
|
+
rho_max -= erf((XC_PLANE_DISTANCE-x_max)/sqrt(2*pow(XC_THERMAL_VIBRATIONS, 2.)));
|
|
63
|
+
rho_max *= N_am*XC_PLANE_DISTANCE/2.;
|
|
64
|
+
double rho_min = erf(x_min/sqrt(2*pow(XC_THERMAL_VIBRATIONS, 2.)));
|
|
65
|
+
rho_min -= erf((XC_PLANE_DISTANCE-x_min)/sqrt(2*pow(XC_THERMAL_VIBRATIONS, 2.)));
|
|
66
|
+
rho_min *= N_am*XC_PLANE_DISTANCE/2.;
|
|
67
|
+
|
|
68
|
+
//"zero-approximation" of average nuclear density seen along the trajectory
|
|
69
|
+
double avrrho = (rho_max - rho_min)/(x_max - x_min);
|
|
70
|
+
avrrho = 2.*avrrho/N_am;
|
|
71
|
+
|
|
72
|
+
return avrrho;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
/*gpufun*/
|
|
77
|
+
double* channel_transport(EverestData restrict everest, LocalParticle* part, double pc, double L_chan, double t_I, double t_P) {
|
|
78
|
+
// Channeling: happens over an arc length L_chan (potentially less if dechanneling)
|
|
79
|
+
// This equates to an opening angle t_P wrt. to the point P (center of miscut if at start of crystal)
|
|
80
|
+
// The chord angle xp at the start of channeling (I) is t_P/2 + t_I
|
|
81
|
+
// The angle xp at the end of channeling (F) is t_P + t_I
|
|
82
|
+
// In practice: we drift from start to end, but overwrite the angle afterwards
|
|
83
|
+
// TODO: why does channeling only have 50% energy loss?
|
|
84
|
+
|
|
85
|
+
double* result = (double*)malloc(2 * sizeof(double));
|
|
86
|
+
|
|
87
|
+
InteractionRecordData record = everest->coll->record;
|
|
88
|
+
RecordIndex record_index = everest->coll->record_index;
|
|
89
|
+
int8_t sc = everest->coll->record_scatterings;
|
|
90
|
+
|
|
91
|
+
// First log particle at start of channeling
|
|
92
|
+
int64_t i_slot;
|
|
93
|
+
if (sc) i_slot = InteractionRecordData_log(record, record_index, part, XC_CHANNELING);
|
|
94
|
+
|
|
95
|
+
// Do channeling.
|
|
96
|
+
// The distance from I to F is the chord length of the angle t_P: d = 2 r sin(t_P/2)
|
|
97
|
+
// Hence the longitudinal distance (the length to be drifted) is the projection of this using the
|
|
98
|
+
// xp at the start of channeling: s = 2 r sin(t_P/2)cos(t_P/2 + t_I)
|
|
99
|
+
double t_chord= t_I + t_P/2.;
|
|
100
|
+
double drift_length = 2.*L_chan/t_P * sin(t_P/2.) * cos(t_chord);
|
|
101
|
+
LocalParticle_set_xp(part, t_chord); // Angle at start of channeling
|
|
102
|
+
Drift_single_particle_4d(part, drift_length);
|
|
103
|
+
// In reality,the particle oscillates horizontally between the planes while channeling.
|
|
104
|
+
// This effect is mimicked by giving a random angle spread at the exit
|
|
105
|
+
double sigma_ran = 0.5*everest->t_c;
|
|
106
|
+
double ran_angle = RandomNormal_generate(part)*sigma_ran;
|
|
107
|
+
LocalParticle_set_xp(part, t_I + t_P + ran_angle); // Angle at end of channeling
|
|
108
|
+
|
|
109
|
+
// Apply energy loss along trajectory
|
|
110
|
+
double energy_loss = 0.5*calcionloss(everest, part, L_chan);
|
|
111
|
+
// TODO: LocalParticle_add_to_energy(part, - energy_loss*L_chan*1.e9, change_angle);
|
|
112
|
+
// if change_angle = 0 => LocalParticle_scale_px(part, old_rpp / new_rpp) such that xp remains the same
|
|
113
|
+
// It is done in K2, so we should do it. Though, it seems that with the current implementation in xtrack
|
|
114
|
+
// this is no longer correct with exact drifts...?
|
|
115
|
+
pc = pc - energy_loss*L_chan; //energy loss to ionization [GeV]
|
|
116
|
+
|
|
117
|
+
// Finally log particle at end of channeling
|
|
118
|
+
if (sc) InteractionRecordData_log_child(record, i_slot, part, drift_length);
|
|
119
|
+
|
|
120
|
+
result[0] = drift_length;
|
|
121
|
+
result[1] = pc;
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
double Channel(EverestData restrict everest, LocalParticle* part, CrystalGeometry restrict cg, double pc, double length) {
|
|
127
|
+
|
|
128
|
+
if (LocalParticle_get_state(part) < 1){
|
|
129
|
+
// Do nothing if already absorbed
|
|
130
|
+
return pc;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
InteractionRecordData record = everest->coll->record;
|
|
134
|
+
RecordIndex record_index = everest->coll->record_index;
|
|
135
|
+
int8_t sc = everest->coll->record_scatterings;
|
|
136
|
+
|
|
137
|
+
calculate_initial_angle(everest, part, cg);
|
|
138
|
+
#ifdef XCOLL_REFINE_ENERGY
|
|
139
|
+
calculate_critical_angle(everest, part, cg, pc);
|
|
140
|
+
#endif
|
|
141
|
+
|
|
142
|
+
// Do we channel, or are we in the transition between channeling and VR?
|
|
143
|
+
double xp = LocalParticle_get_xp(part);
|
|
144
|
+
double alpha = fabs(xp - everest->t_I) / everest->t_c;
|
|
145
|
+
double ratio = everest->Rc_over_R;
|
|
146
|
+
double xi = RandomUniform_generate(part)/(1 - ratio)/sqrt(everest->coll->eta);
|
|
147
|
+
|
|
148
|
+
if (xi > 1 || alpha > 2*sqrt(xi)*sqrt(1-xi)) {
|
|
149
|
+
#ifdef XCOLL_TRANSITION
|
|
150
|
+
// TRANSITION
|
|
151
|
+
// We feel that this transition is not needed, as it interpolates between two regions
|
|
152
|
+
// (adding a slant below the channeling region) which does not seem to be present in
|
|
153
|
+
// experimental data.
|
|
154
|
+
#ifdef XCOLL_REFINE_ENERGY
|
|
155
|
+
calculate_VI_parameters(everest, part, pc);
|
|
156
|
+
#endif
|
|
157
|
+
volume_reflection(everest, part, XC_VOLUME_REFLECTION_TRANS_CH);
|
|
158
|
+
#endif
|
|
159
|
+
pc = Amorphous(everest, part, cg, pc, length);
|
|
160
|
+
|
|
161
|
+
} else {
|
|
162
|
+
// CHANNEL
|
|
163
|
+
calculate_opening_angle(everest, part, cg);
|
|
164
|
+
double t_I = everest->t_I;
|
|
165
|
+
double t_P = everest->t_P;
|
|
166
|
+
double L_chan = everest->r*t_P;
|
|
167
|
+
|
|
168
|
+
// ------------------------------------------------
|
|
169
|
+
// Calculate curved length L_dechan of dechanneling
|
|
170
|
+
// ------------------------------------------------
|
|
171
|
+
double const_dech = calculate_dechanneling_length(everest, pc);
|
|
172
|
+
double TLdech1 = const_dech*pc*pow(1. - ratio, 2.); //Updated calculate typical dech. length(m)
|
|
173
|
+
double N_atom = 1.0e-1;
|
|
174
|
+
if(RandomUniform_generate(part) <= N_atom) {
|
|
175
|
+
TLdech1 /= 200.; // Updated dechanneling length (m)
|
|
176
|
+
}
|
|
177
|
+
double L_dechan = TLdech1*RandomExponential_generate(part); // Actual dechan. length
|
|
178
|
+
|
|
179
|
+
// -----------------------------------------------------
|
|
180
|
+
// Calculate curved length L_nucl of nuclear interaction
|
|
181
|
+
// -----------------------------------------------------
|
|
182
|
+
// Nuclear interaction length is rescaled in this case, because channeling
|
|
183
|
+
double collnt = everest->coll->collnt;
|
|
184
|
+
double avrrho = channeling_average_density(everest, cg, part, pc);
|
|
185
|
+
if (avrrho == 0) {
|
|
186
|
+
collnt = 1.e10; // very large because essentially 1/0
|
|
187
|
+
} else {
|
|
188
|
+
collnt = collnt/avrrho;
|
|
189
|
+
}
|
|
190
|
+
double L_nucl = collnt*RandomExponential_generate(part);
|
|
191
|
+
|
|
192
|
+
// printf("Channeling (%f -> %f): L_c: %f, L_d: %f, L_n: %f\n", t_I, t_P, L_chan, L_dechan, L_nucl);
|
|
193
|
+
// ------------------------------------------------------------------------
|
|
194
|
+
// Compare the 3 lengths: the first one encountered is what will be applied
|
|
195
|
+
// ------------------------------------------------------------------------
|
|
196
|
+
if (L_chan <= fmin(L_dechan, L_nucl)){
|
|
197
|
+
// Channel full length
|
|
198
|
+
double* result_chan = channel_transport(everest, part, pc, L_chan, t_I, t_P);
|
|
199
|
+
double channeled_length = result_chan[0];
|
|
200
|
+
pc = result_chan[1];
|
|
201
|
+
free(result_chan);
|
|
202
|
+
// Drift leftover outside of crystal
|
|
203
|
+
if (everest->coll->record_touches){
|
|
204
|
+
InteractionRecordData_log(record, record_index, part, XC_EXIT_JAW);
|
|
205
|
+
}
|
|
206
|
+
Drift_single_particle_4d(part, length - channeled_length);
|
|
207
|
+
|
|
208
|
+
} else if (L_dechan < L_nucl) {
|
|
209
|
+
// Channel up to L_dechan, then amorphous
|
|
210
|
+
double* result_chan = channel_transport(everest, part, pc, L_dechan, t_I, t_P*L_dechan/L_chan);
|
|
211
|
+
double channeled_length = result_chan[0];
|
|
212
|
+
pc = result_chan[1];
|
|
213
|
+
free(result_chan);
|
|
214
|
+
if (sc) InteractionRecordData_log(record, record_index, part, XC_DECHANNELING);
|
|
215
|
+
pc = Amorphous(everest, part, cg, pc, length - channeled_length);
|
|
216
|
+
|
|
217
|
+
} else {
|
|
218
|
+
// Channel up to L_nucl, then scatter, then amorphous
|
|
219
|
+
double* result_chan = channel_transport(everest, part, pc, L_nucl, t_I, t_P*L_nucl/L_chan);
|
|
220
|
+
double channeled_length = result_chan[0];
|
|
221
|
+
pc = result_chan[1];
|
|
222
|
+
free(result_chan);
|
|
223
|
+
// Rescale nuclear interaction parameters
|
|
224
|
+
everest->rescale_scattering = avrrho;
|
|
225
|
+
#ifndef XCOLL_REFINE_ENERGY
|
|
226
|
+
calculate_scattering(everest, pc);
|
|
227
|
+
#endif
|
|
228
|
+
pc = nuclear_interaction(everest, part, pc);
|
|
229
|
+
if (LocalParticle_get_state(part) == XC_LOST_ON_EVEREST_COLL){
|
|
230
|
+
LocalParticle_set_state(part, XC_LOST_ON_EVEREST_CRYSTAL);
|
|
231
|
+
} else {
|
|
232
|
+
// We call the main Amorphous function for the leftover
|
|
233
|
+
everest->rescale_scattering = 1;
|
|
234
|
+
#ifndef XCOLL_REFINE_ENERGY
|
|
235
|
+
calculate_scattering(everest, pc);
|
|
236
|
+
#endif
|
|
237
|
+
pc = Amorphous(everest, part, cg, pc, length - channeled_length);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return pc;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
#endif /* XCOLL_EVEREST_CHANNELING_H */
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// copyright ############################### #
|
|
2
|
+
// This file is part of the Xcoll Package. #
|
|
3
|
+
// Copyright (c) CERN, 2023. #
|
|
4
|
+
// ######################################### #
|
|
5
|
+
|
|
6
|
+
#ifndef XCOLL_EVEREST_CRYSTAL_PARAMETERS_H
|
|
7
|
+
#define XCOLL_EVEREST_CRYSTAL_PARAMETERS_H
|
|
8
|
+
#include <math.h>
|
|
9
|
+
#include <stdio.h>
|
|
10
|
+
#include <stdlib.h>
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/*gpufun*/
|
|
14
|
+
void calculate_initial_angle(EverestData restrict everest, LocalParticle* part, CrystalGeometry restrict cg) {
|
|
15
|
+
double R = cg->bending_radius;
|
|
16
|
+
double s = LocalParticle_get_s(part);
|
|
17
|
+
double x = LocalParticle_get_x(part);
|
|
18
|
+
double s_P = cg->s_P;
|
|
19
|
+
double x_P = cg->x_P;
|
|
20
|
+
double r = sqrt((s-s_P)*(s-s_P) + (x-x_P)*(x-x_P));
|
|
21
|
+
everest->r = r;
|
|
22
|
+
everest->t_I = R/fabs(R)*asin( (s-s_P)/r); // Tangent angle of the channeling planes (not neccecarly the same as xp)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
/*gpufun*/
|
|
27
|
+
void calculate_opening_angle(EverestData restrict everest, LocalParticle* part, CrystalGeometry restrict cg) {
|
|
28
|
+
double t = cg->bending_angle;
|
|
29
|
+
double xd = cg->width;
|
|
30
|
+
double R = cg->bending_radius;
|
|
31
|
+
double sinp = sin(cg->miscut_angle);
|
|
32
|
+
double cosp = cos(cg->miscut_angle);
|
|
33
|
+
|
|
34
|
+
// Radius from starting point to miscut centre
|
|
35
|
+
double r = everest->r;
|
|
36
|
+
// Intersection with exit face
|
|
37
|
+
double bb = R/2. * sin(2.*t) * (1 - cosp - tan(t)*sinp);
|
|
38
|
+
double s_F = bb + sqrt(bb*bb - tan(t)*sin(2.*t)*(R*R*(1-cosp) - r*r/2.));
|
|
39
|
+
double x_F = R - s_F/tan(t);
|
|
40
|
+
|
|
41
|
+
// We could intersect with the upper (positive miscut) or lower bend (negative miscut) before the exit face
|
|
42
|
+
// Distance between bending centre and miscut centre:
|
|
43
|
+
double d = R*sqrt(2*(1-cosp));
|
|
44
|
+
if (cg->miscut_angle > 0){
|
|
45
|
+
// Check if intersection with upper bend R-xd is possible
|
|
46
|
+
double Rb = R-xd;
|
|
47
|
+
if (d > fabs(r - Rb)){
|
|
48
|
+
double st_UB = (r*r - Rb*Rb)/(2*d);
|
|
49
|
+
double xt_UB = -sqrt(r*r/2. + Rb*Rb/2. - st_UB*st_UB - d*d/4.);
|
|
50
|
+
double s_UB = (st_UB/d - 0.5)*R*sinp - xt_UB/d*R*(1-cosp);
|
|
51
|
+
if (s_F > s_UB){
|
|
52
|
+
// Upper bend encountered before exit face
|
|
53
|
+
s_F = s_UB;
|
|
54
|
+
x_F = (st_UB/d + 0.5)*R*(1-cosp) + xt_UB/d*R*sinp + R*cosp;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
// Check if intersection with lower bend R is possible
|
|
59
|
+
if (d > fabs(R - r)){
|
|
60
|
+
double st_LB = (R*R - r*r)/(2*d);
|
|
61
|
+
double xt_LB = -sqrt(r*r/2. + R*R/2. - st_LB*st_LB - d*d/4.);
|
|
62
|
+
double s_LB = -(st_LB/d + 0.5)*R*sinp + xt_LB/d*R*(1-cosp);
|
|
63
|
+
if (s_F > s_LB){
|
|
64
|
+
// Lower bend encountered before exit face
|
|
65
|
+
s_F = s_LB;
|
|
66
|
+
x_F = -(st_LB/d - 0.5)*R*(1-cosp) - xt_LB/d*R*sinp + R*cosp;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Opening angle of channeling trajectory
|
|
72
|
+
double s = LocalParticle_get_s(part);
|
|
73
|
+
double x = LocalParticle_get_x(part);
|
|
74
|
+
everest->t_P = acos(1 - ( (s_F-s)*(s_F-s) - (x_F-x)*(x_F-x) ) / (2*r*r) );
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
/*gpufun*/
|
|
79
|
+
void calculate_critical_angle(EverestData restrict everest, LocalParticle* part, CrystalGeometry restrict cg, double pc) {
|
|
80
|
+
// Define typical angles/probabilities for orientation 110
|
|
81
|
+
double eum = everest->coll->eum;
|
|
82
|
+
double ai = everest->coll->ai;
|
|
83
|
+
double eta = everest->coll->eta;
|
|
84
|
+
double t_c0 = sqrt(2.e-9*eta*eum/pc); // Critical angle (rad) for straight crystals // pc is actually beta pc
|
|
85
|
+
double Rcrit = pc/(2.e-6*sqrt(eta)*eum)*ai; // Critical curvature radius [m] // pc is actually beta pc
|
|
86
|
+
|
|
87
|
+
// If Rcritical > R => no channeling is possible (Rc_over_R > 1)
|
|
88
|
+
everest->Rc_over_R = Rcrit / fabs(cg->bending_radius);
|
|
89
|
+
everest->t_c0 = t_c0;
|
|
90
|
+
everest->t_c = t_c0*(1 - everest->Rc_over_R); // Critical angle for curved crystal
|
|
91
|
+
|
|
92
|
+
if (everest->coll->orient == 2) {
|
|
93
|
+
everest->t_c *= 0.98;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
/*gpufun*/
|
|
99
|
+
void calculate_VI_parameters(EverestData restrict everest, LocalParticle* part, double pc) {
|
|
100
|
+
|
|
101
|
+
double ratio = everest->Rc_over_R;
|
|
102
|
+
double t_c0 = everest->t_c0;
|
|
103
|
+
|
|
104
|
+
// Correction by sasha drozdin/armen // TODO: pc = energy
|
|
105
|
+
// K=0.0007 is taken based on simulations using CATCH.f (V.Biryukov)
|
|
106
|
+
// TODO: From the paper (arXiv:0808.1486, eq (18), (34)) it seems the power of E should be 0.25
|
|
107
|
+
// Typo in Daniele's thesis in 3.25 (E^0.2 instead of 1/E^0.2)
|
|
108
|
+
everest->Vcapt = 7.e-4*(1./ratio - 0.7)/pow(pc, 0.2);
|
|
109
|
+
|
|
110
|
+
double Ang_rms, Ang_avr;
|
|
111
|
+
double c1 = -3./2.; // Fitting coefficient
|
|
112
|
+
double c2 = 5./3.; // Fitting coefficient
|
|
113
|
+
double c3 = 1.7; // Fitting coefficient
|
|
114
|
+
if (ratio > 1.) {
|
|
115
|
+
// no channeling possibile
|
|
116
|
+
Ang_avr = c1*t_c0*5.e-2/ratio; // Average angle reflection
|
|
117
|
+
Ang_rms = c3*0.42*t_c0*sin(1.4/ratio); // RMS scattering
|
|
118
|
+
everest->Vcapt = 0.; // Probability of VC is zero
|
|
119
|
+
} else if (ratio > 1./3.) {
|
|
120
|
+
// Strongly bent crystal
|
|
121
|
+
Ang_avr = c1*t_c0*(0.1972/ratio - 0.1472); // Average angle reflection
|
|
122
|
+
Ang_rms = c3*0.42*t_c0*sin(0.4713/ratio + 0.85); // RMS scattering
|
|
123
|
+
} else {
|
|
124
|
+
// Rcrit << R
|
|
125
|
+
Ang_avr = c1*t_c0*(1. - c2*ratio); // Average angle for VR
|
|
126
|
+
Ang_rms = c3*t_c0*ratio; // RMS scattering
|
|
127
|
+
}
|
|
128
|
+
if (everest->coll->orient == 2) {
|
|
129
|
+
Ang_avr = Ang_avr*0.93;
|
|
130
|
+
Ang_rms = Ang_rms*1.05;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
everest->Ang_rms = Ang_rms;
|
|
134
|
+
everest->Ang_avr = Ang_avr;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
#endif /* XCOLL_EVEREST_CRYSTAL_PARAMETERS_H */
|