Garfield++ 3.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
HeedDeltaElectron.cpp
Go to the documentation of this file.
7
8// 2003, I. Smirnov
9
10#define USE_ADJUSTED_W
11#define RANDOM_POIS
12#define DIRECT_LOW_IF_LITTLE
13
14namespace {
15
16long findInterval(Heed::EnergyMesh* emesh, const double energy) {
17
18 const long n = emesh->get_interval_number_between_centers(energy);
19 return std::min(std::max(n, 0L), emesh->get_q() - 2);
20}
21
22double interpolate(Heed::EnergyMesh* emesh, const double x,
23 const std::vector<double>& y) {
24
25 const long n = findInterval(emesh, x);
26 const double x1 = emesh->get_ec(n);
27 const double x2 = emesh->get_ec(n + 1);
28 const double y1 = y[n];
29 const double y2 = y[n + 1];
30 return y1 + (x - x1) * (y2 - y1) / (x2 - x1);
31}
32
33}
34
35namespace Heed {
36
37using CLHEP::degree;
38using CLHEP::cm;
39using CLHEP::eV;
40using CLHEP::keV;
41using CLHEP::MeV;
42using CLHEP::c_light;
43using CLHEP::c_squared;
44
45bool HeedDeltaElectron::s_low_mult_scattering = true;
46bool HeedDeltaElectron::s_high_mult_scattering = true;
47
49 const vec& vel, vfloat ftime,
50 long fparent_particle_number,
51 HeedFieldMap* fieldmap,
52 bool fprint_listing)
53 : eparticle(primvol, pt, vel, ftime, &electron_def, fieldmap),
54 parent_particle_number(fparent_particle_number),
55 m_particle_number(last_particle_number++),
56 m_print_listing(fprint_listing) {
57 mfunname("HeedDeltaElectron::HeedDeltaElectron(...)");
58}
59
60void HeedDeltaElectron::physics_mrange(double& fmrange) {
61 mfunname("void HeedDeltaElectron::physics_mrange(double& fmrange)");
62 if (m_print_listing) mcout << "HeedDeltaElectron::physics_mrange\n";
63
64 m_mult_low_path_length = false;
65 m_q_low_path_length = 0.0;
66 m_path_length = false;
67 if (fmrange <= 0.0) return;
68 if (m_curr_ekin <= 0.0) {
69 fmrange = 0.0;
70 return;
71 }
72 // Get local volume and convert it to a cross-section object.
73 const absvol* av = m_currpos.tid.G_lavol();
74 auto hdecs = dynamic_cast<const HeedDeltaElectronCS*>(av);
75 if (!hdecs) return;
76 if (m_print_listing) Iprintnf(mcout, fmrange);
77 const double ek = m_curr_ekin / MeV;
78 // Get the dE/dx at this kinetic energy.
79 EnergyMesh* emesh = hdecs->hmd->energy_mesh;
80 const double dedx = interpolate(emesh, ek, hdecs->eLoss);
81 // Min. loss 50 eV.
82 double eloss = std::max(0.1 * ek, 0.00005);
83 if (eloss > ek) {
84 eloss = ek;
85 m_stop_eloss = true;
86 } else {
87 m_stop_eloss = false;
88 }
89 fmrange = std::min(fmrange, (eloss / dedx) * cm);
90 if (m_print_listing) Iprint2nf(mcout, fmrange, ek);
91 const double ek_restr = std::max(ek, 0.0005);
92 if (m_print_listing) Iprintnf(mcout, ek_restr / keV);
93
94 double low_path_length = 0.; // in internal units
95 if (s_low_mult_scattering) {
96 low_path_length = interpolate(emesh, ek_restr, hdecs->low_lambda) * cm;
97 if (m_print_listing) Iprintnf(mcout, low_path_length / cm);
98 long qscat = hdecs->eesls->get_qscat();
99 const double sigma_ctheta = hdecs->get_sigma(ek_restr, qscat);
100 // Reduce the number of scatterings, if the angle is too large.
101 if (sigma_ctheta > 0.3) qscat = long(qscat * 0.3 / sigma_ctheta);
102 const double mult_low_path_length = qscat * low_path_length;
103 if (m_print_listing) Iprintnf(mcout, mult_low_path_length);
104 if (fmrange > mult_low_path_length) {
105 fmrange = mult_low_path_length;
106 m_mult_low_path_length = true;
107 m_q_low_path_length = hdecs->eesls->get_qscat();
108 m_stop_eloss = false;
109 } else {
110 m_mult_low_path_length = false;
111 m_q_low_path_length = fmrange / low_path_length;
112 }
113 if (m_print_listing) Iprint2nf(mcout, fmrange, m_q_low_path_length);
114 }
115
116 if (s_high_mult_scattering) {
117 const double mean_path = interpolate(emesh, ek_restr, hdecs->lambda);
118 if (m_print_listing) Iprintnf(mcout, mean_path);
119 const double path_length = -mean_path * cm * log(1.0 - SRANLUX());
120 if (m_print_listing) Iprintnf(mcout, path_length);
121 if (fmrange > path_length) {
122 fmrange = path_length;
123 m_path_length = true;
124 m_mult_low_path_length = true;
125 if (s_low_mult_scattering) {
126 m_q_low_path_length = fmrange / low_path_length;
127 if (m_print_listing) Iprintnf(mcout, m_q_low_path_length);
128 }
129 m_stop_eloss = false;
130 } else {
131 m_path_length = false;
132 }
133 if (m_print_listing) Iprintnf(mcout, fmrange);
134 }
135 m_phys_mrange = fmrange;
136}
137
139 std::vector<gparticle*>& /*secondaries*/) {
140 mfunname("void HeedDeltaElectron::physics_after_new_speed()");
141 if (m_print_listing) {
142 mcout << "HeedDeltaElectron::physics_after_new_speed\n";
144 }
146 if (m_currpos.prange <= 0.0) {
147 if (m_curr_ekin <= 0.0) {
148 // Get local volume.
149 absvol* av = m_currpos.tid.G_lavol();
150 if (av && av->s_sensitive && m_fieldMap->inside(m_currpos.ptloc)) {
151 if (m_print_listing) mcout << "Convert to conduction electron.\n";
152 conduction_electrons.emplace_back(
154 }
155 m_alive = false;
156 }
157 if (m_print_listing) mcout << "exit due to currpos.prange <= 0.0\n";
158 return;
159 }
160 // Get local volume and convert it to a cross-section object.
161 const absvol* av = m_currpos.tid.G_lavol();
162 auto hdecs = dynamic_cast<const HeedDeltaElectronCS*>(av);
163 if (!hdecs) return;
164 double ek = m_curr_ekin / MeV;
165 if (m_print_listing) {
166 Iprintnf(mcout, ek);
167 Iprint3n(mcout, m_stop_eloss, m_phys_mrange, m_currpos.prange);
168 }
169 // Calculate dE/dx and energy loss. Update the kinetic energy.
170 double dedx;
171 double Eloss = 0.;
172 if (m_stop_eloss && m_phys_mrange == m_currpos.prange) {
173 Eloss = m_curr_ekin;
174 m_curr_ekin = 0.0;
175 dedx = Eloss / m_currpos.prange / (MeV / cm);
176 } else {
177 EnergyMesh* emesh = hdecs->hmd->energy_mesh;
178 dedx = interpolate(emesh, ek, hdecs->eLoss);
179 Eloss = std::min(m_currpos.prange * dedx * MeV / cm, m_curr_ekin);
180 m_total_eloss += Eloss;
181 m_curr_ekin -= Eloss;
182 }
183 if (m_print_listing)
184 Iprint3nf(mcout, m_prev_ekin / eV, m_curr_ekin / eV, Eloss / eV);
185 if (m_curr_ekin <= 0.0) {
186 if (m_print_listing) mcout << "m_curr_ekin <= 0.0\n";
187 m_curr_ekin = 0.0;
188 m_curr_gamma_1 = 0.0;
189 m_currpos.speed = 0.0;
190 m_alive = false;
191 } else {
192 const double resten = m_mass * c_squared;
193 m_curr_gamma_1 = m_curr_ekin / resten;
195 }
196 absvol* vav = m_currpos.tid.G_lavol();
197 if (vav && vav->s_sensitive) {
198 if (m_print_listing) {
199 mcout << "volume is sensitive\n";
200 Iprint2nf(mcout, Eloss / eV, m_necessary_energy / eV);
201 }
202 if (Eloss > 0.0) ionisation(Eloss, dedx, hdecs->pairprod);
203 }
204 if (m_print_listing) {
205 mcout << '\n';
207 }
208 if (!m_alive) {
209 // Done tracing the delta electron. Create the last conduction electron.
210 vav = m_currpos.tid.G_lavol();
211 if (vav && vav->s_sensitive && m_fieldMap->inside(m_currpos.ptloc)) {
212 if (m_print_listing) mcout << "Last conduction electron\n";
213 conduction_electrons.emplace_back(
215 }
216 return;
217 }
218
219 if (m_print_listing) mcout << "\nstart to rotate by low angle\n";
220 double ek_restr = std::max(ek, 0.0005);
221 if (m_print_listing) Iprint2nf(mcout, m_currpos.prange, m_phys_mrange);
222 if (m_currpos.prange < m_phys_mrange) {
223 // recalculate scatterings
224 m_path_length = false;
225 if (s_low_mult_scattering) {
226 EnergyMesh* emesh = hdecs->hmd->energy_mesh;
227 const double low_path_length = interpolate(emesh, ek_restr, hdecs->low_lambda) * cm;
228 if (m_print_listing) Iprintnf(mcout, low_path_length / cm);
229 m_mult_low_path_length = false;
230 m_q_low_path_length = m_currpos.prange / low_path_length;
231 if (m_print_listing) Iprintnf(mcout, m_q_low_path_length);
232 }
233 }
234 if (m_print_listing) Iprintnf(mcout, m_q_low_path_length);
235#ifdef RANDOM_POIS
236 if (m_q_low_path_length > 0.0) {
237 int ierror = 0;
238 long random_q_low_path_length = pois(m_q_low_path_length, ierror);
239 check_econd11a(ierror, == 1,
240 " q_low_path_length=" << m_q_low_path_length << '\n', mcerr);
241 m_q_low_path_length = long(random_q_low_path_length);
242 if (m_print_listing) {
243 mcout << "After pois:\n";
244 Iprintnf(mcout, m_q_low_path_length);
245 }
246 }
247#endif
248 if (m_q_low_path_length > 0) {
249#ifdef DIRECT_LOW_IF_LITTLE
250 const long max_q_low_path_length_for_direct = 5;
251 if (m_q_low_path_length < max_q_low_path_length_for_direct) {
252 // direct modeling
253 if (m_print_listing) {
254 mcout << "direct modeling of low scatterings\n";
256 }
257 EnergyMesh* emesh = hdecs->hmd->energy_mesh;
258 const long n1r = findInterval(emesh, ek_restr);
259 for (long nscat = 0; nscat < m_q_low_path_length; ++nscat) {
260 if (m_print_listing) Iprintn(mcout, nscat);
261 double theta_rot =
262 hdecs->low_angular_points_ran[n1r].ran(SRANLUX()) * degree;
263 if (m_print_listing) Iprintnf(mcout, theta_rot);
264 vec dir = m_currpos.dir;
265 basis temp(dir, "temp");
266 vec vturn;
267 vturn.random_round_vec();
268 vturn = vturn * sin(theta_rot);
269 vec new_dir(vturn.x, vturn.y, cos(theta_rot));
270 new_dir.down(&temp);
271 m_currpos.dir = new_dir;
272 if (m_print_listing) Iprint(mcout, new_dir);
273 }
276 } else {
277#endif
278 double sigma_ctheta = hdecs->get_sigma(ek_restr, m_q_low_path_length);
279 // actually it is mean(1-cos(theta)) or
280 // sqrt( mean( square(1-cos(theta) ) ) ) depending on USE_MEAN_COEF
281
282 if (m_print_listing) Iprintnf(mcout, sigma_ctheta);
283 // Gauss (but actually exponential distribution fits better).
284 // double ctheta = 1.0 - fabs(rnorm_improved() * sigma_ctheta);
285 // Exponential:
286 double ctheta = 0.0;
287 {
288#ifdef USE_MEAN_COEF
289#else
290 double sq2 = sqrt(2.0);
291#endif
292 do {
293 double y = 0.0;
294 do { // in order to avoid SRANLUX() = 1
295 y = SRANLUX();
296 if (m_print_listing) Iprintnf(mcout, y);
297 } while (y == 1.0);
298#ifdef USE_MEAN_COEF
299 double x = sigma_ctheta * (-log(1.0 - y));
300#else
301 double x = sigma_ctheta * 1.0 / sq2 * (-log(1.0 - y));
302#endif
303 ctheta = 1.0 - x;
304 if (m_print_listing) Iprint2nf(mcout, x, ctheta);
305 } while (ctheta <= -1.0); // avoid absurd cos(theta)
306 check_econd21(ctheta, < -1.0 ||, > 1.0, mcerr);
307 }
308 if (m_print_listing) Iprintnf(mcout, ctheta);
309 double theta_rot = acos(ctheta);
310 if (m_print_listing) Iprint2nf(mcout, theta_rot, theta_rot / degree);
311 vec dir = m_currpos.dir;
312 basis temp(dir, "temp");
313 vec vturn;
314 vturn.random_round_vec();
315 vturn = vturn * sin(theta_rot);
316 vec new_dir(vturn.x, vturn.y, cos(theta_rot));
317 new_dir.down(&temp);
318 m_currpos.dir = new_dir;
321 }
322#ifdef DIRECT_LOW_IF_LITTLE
323 }
324#endif
325 if (m_path_length) {
326 if (m_print_listing) {
327 mcout << "\nstarting to rotate by large angle" << std::endl;
328 Iprintnf(mcout, m_path_length);
329 }
330 EnergyMesh* emesh = hdecs->hmd->energy_mesh;
331 const long n1r = findInterval(emesh, ek_restr);
332 double theta_rot = hdecs->angular_points_ran[n1r].ran(SRANLUX()) * degree;
333 if (m_print_listing) Iprintnf(mcout, theta_rot);
334 vec dir = m_currpos.dir;
335 basis temp(dir, "temp");
336 vec vturn;
337 vturn.random_round_vec();
338 vturn = vturn * sin(theta_rot);
339 vec new_dir(vturn.x, vturn.y, cos(theta_rot));
340 new_dir.down(&temp);
341 m_currpos.dir = new_dir;
344 }
345 if (m_print_listing) Iprint2nf(mcout, m_currpos.dir, m_currpos.dirloc);
346}
347
348void HeedDeltaElectron::ionisation(const double eloss, const double dedx,
349 PairProd* pairprod) {
350
351 if (eloss < m_necessary_energy) {
352 m_necessary_energy -= eloss;
353 return;
354 }
355
356 if (m_print_listing) mcout << "\nstart to leave conduction electrons\n";
357 if (m_necessary_energy <= 0.0) {
358#ifdef USE_ADJUSTED_W
359 m_necessary_energy = pairprod->get_eloss(m_prev_ekin / eV) * eV;
360#else
361 m_necessary_energy = pairprod->get_eloss() * eV;
362#endif
363 }
364 if (m_print_listing) Iprintnf(mcout, m_necessary_energy / eV);
365 double eloss_left = eloss;
366 point curpt = m_prevpos.pt;
367 vec dir = m_prevpos.dir; // this approximation ignores curvature
368 double ekin = m_prev_ekin;
369 if (m_print_listing) Iprintnf(mcout, curpt);
370 while (eloss_left >= m_necessary_energy) {
371 const double step_length = m_necessary_energy / (dedx * MeV / cm);
372 if (m_print_listing) Iprintnf(mcout, step_length);
373 curpt = curpt + dir * step_length;
374 if (m_print_listing) Iprintf(mcout, curpt);
375 point ptloc = curpt;
376 m_prevpos.tid.up_absref(&ptloc);
377 if (m_print_listing) mcout << "New conduction electron\n";
378 if (m_fieldMap->inside(ptloc)) {
379 conduction_electrons.emplace_back(HeedCondElectron(ptloc, m_currpos.time));
380 conduction_ions.emplace_back(HeedCondElectron(ptloc, m_currpos.time));
381 }
382 eloss_left -= m_necessary_energy;
383 ekin -= m_necessary_energy;
384 if (ekin < 0.) break;
385 // Generate next random energy
386#ifdef USE_ADJUSTED_W
387 m_necessary_energy = eV * pairprod->get_eloss(ekin / eV);
388#else
389 m_necessary_energy = pairprod->get_eloss() * eV;
390#endif
391 if (m_print_listing) {
392 Iprintnf(mcout, eloss_left / eV);
393 Iprint2nf(mcout, ekin / eV, m_necessary_energy / eV);
394 }
395 }
396 m_necessary_energy -= eloss_left;
397 if (m_print_listing) Iprintnf(mcout, m_necessary_energy / eV);
398}
399
400void HeedDeltaElectron::print(std::ostream& file, int l) const {
401 if (l < 0) return;
402 Ifile << "HeedDeltaElectron (l=" << l
403 << "): particle_number=" << m_particle_number << "\n";
404 if (l == 1) return;
405 indn.n += 2;
406 Ifile << "s_low_mult_scattering=" << s_low_mult_scattering
407 << " s_high_mult_scattering=" << s_high_mult_scattering << '\n';
408 Ifile << "phys_mrange=" << m_phys_mrange << " stop_eloss=" << m_stop_eloss
409 << " mult_low_path_length=" << m_mult_low_path_length << '\n';
410 Ifile << "q_low_path_length=" << m_q_low_path_length
411 << " path_length=" << m_path_length
412 << " necessary_energy/eV=" << m_necessary_energy / eV << '\n';
413 Ifile << " parent_particle_number=" << parent_particle_number << '\n';
414
415 mparticle::print(file, l - 1);
416 indn.n -= 2;
417}
418}
#define check_econd21(a, sign1_b1_sign0, sign2_b2, stream)
Definition: FunNameStack.h:191
#define check_econd11(a, signb, stream)
Definition: FunNameStack.h:155
#define check_econd11a(a, signb, add, stream)
Definition: FunNameStack.h:172
#define mfunname(string)
Definition: FunNameStack.h:45
long get_q() const
Return number of bins.
Definition: EnergyMesh.h:42
double get_ec(long n) const
Return center of a given bin.
Definition: EnergyMesh.h:50
long get_interval_number_between_centers(const double ener) const
Definition: EnergyMesh.cpp:62
std::vector< HeedCondElectron > conduction_electrons
std::vector< HeedCondElectron > conduction_ions
void physics_mrange(double &fmrange) override
void print(std::ostream &file, int l) const override
Print-out.
HeedDeltaElectron()=default
Default constructor.
void physics_after_new_speed(std::vector< gparticle * > &secondaries) override
Apply any other processes (turn the trajectory, kill the particle, ...).
Retrieve electric and magnetic field from Sensor.
Definition: HeedFieldMap.h:15
bool inside(const point &pt)
double get_eloss() const
Calculate energy loss (in eV).
Definition: PairProd.cpp:43
bool s_sensitive
Definition: volume.h:73
Basis.
Definition: vec.h:311
HeedFieldMap * m_fieldMap
Pointer to field map.
Definition: eparticle.h:34
stvpoint m_prevpos
Previous point.
Definition: gparticle.h:248
stvpoint m_currpos
Current point.
Definition: gparticle.h:250
bool m_alive
Status flag whether the particle is active.
Definition: gparticle.h:233
void up_absref(absref *f)
Definition: volume.cpp:26
absvol * G_lavol() const
Get last address of volume.
Definition: volume.cpp:17
Abstract base classs for volume "manipulators".
Definition: volume.h:128
double m_prev_ekin
Previous kinetic energy.
Definition: mparticle.h:77
double m_curr_ekin
Current kinetic energy.
Definition: mparticle.h:73
void print(std::ostream &file, int l) const override
Print-out.
Definition: mparticle.cpp:243
double m_curr_gamma_1
Current .
Definition: mparticle.h:80
double m_mass
Mass (not mass * speed_of_light^2)
Definition: mparticle.h:70
Point.
Definition: vec.h:366
vfloat time
Definition: gparticle.h:47
vec dirloc
Unit vector, in the local system (last system in the tree).
Definition: gparticle.h:29
vec dir
Unit vector, in the first system in the tree.
Definition: gparticle.h:25
vfloat prange
Range from previous point.
Definition: gparticle.h:46
vfloat speed
Longitudinal velocity.
Definition: gparticle.h:31
point pt
Coordinates in the first system in the tree.
Definition: gparticle.h:23
manip_absvol_treeid tid
Definition: gparticle.h:32
point ptloc
Coordinates in the local system (last system in the tree).
Definition: gparticle.h:27
Definition: vec.h:177
vfloat x
Definition: vec.h:190
void down(const basis *fabas)
Definition: vec.cpp:168
void random_round_vec()
Generate random unit vector in plane perpendicular to z-axis.
Definition: vec.cpp:229
vfloat y
Definition: vec.h:191
Definition: BGMesh.cpp:6
DoubleAc cos(const DoubleAc &f)
Definition: DoubleAc.cpp:432
long last_particle_number
Definition: HeedParticle.h:9
double lorbeta(const double gamma_1)
as function of .
Definition: lorgamma.cpp:23
long pois(const double amu, int &ierror)
Definition: pois.cpp:9
int vecerror
Definition: vec.cpp:29
DoubleAc acos(const DoubleAc &f)
Definition: DoubleAc.cpp:490
particle_def electron_def("electron", "e-", electron_mass_c2/c_squared, electron_charge, 1, 0, 0.5, spin_def(0.0, 0.0))
Definition: particle_def.h:99
DoubleAc sin(const DoubleAc &f)
Definition: DoubleAc.cpp:384
indentation indn
Definition: prstream.cpp:15
DoubleAc sqrt(const DoubleAc &f)
Definition: DoubleAc.cpp:314
double vfloat
Definition: vfloat.h:16
#define Iprint3nf(file, name1, name2, name3)
Definition: prstream.h:237
#define mcout
Definition: prstream.h:126
#define Iprint3n(file, name1, name2, name3)
Definition: prstream.h:233
#define Ifile
Definition: prstream.h:196
#define Iprint(file, name)
Definition: prstream.h:198
#define mcerr
Definition: prstream.h:128
#define Iprintn(file, name)
Definition: prstream.h:205
#define Iprintf(file, name)
Definition: prstream.h:200
#define Iprint2nf(file, name1, name2)
Definition: prstream.h:223
#define Iprint2n(file, name1, name2)
Definition: prstream.h:220
#define Iprintnf(file, name)
Definition: prstream.h:207