Garfield++ 4.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.volume();
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.volume();
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.volume();
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.volume();
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.volume();
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 long random_q_low_path_length = pois(m_q_low_path_length);
238 m_q_low_path_length = long(random_q_low_path_length);
239 if (m_print_listing) {
240 mcout << "After pois:\n";
241 Iprintnf(mcout, m_q_low_path_length);
242 }
243 }
244#endif
245 if (m_q_low_path_length > 0) {
246#ifdef DIRECT_LOW_IF_LITTLE
247 const long max_q_low_path_length_for_direct = 5;
248 if (m_q_low_path_length < max_q_low_path_length_for_direct) {
249 // direct modeling
250 if (m_print_listing) {
251 mcout << "direct modeling of low scatterings\n";
253 }
254 EnergyMesh* emesh = hdecs->hmd->energy_mesh;
255 const long n1r = findInterval(emesh, ek_restr);
256 for (long nscat = 0; nscat < m_q_low_path_length; ++nscat) {
257 if (m_print_listing) Iprintn(mcout, nscat);
258 double theta_rot =
259 hdecs->low_angular_points_ran[n1r].ran(SRANLUX()) * degree;
260 if (m_print_listing) Iprintnf(mcout, theta_rot);
261 vec dir = m_currpos.dir;
262 basis temp(dir, "temp");
263 vec vturn;
264 vturn.random_round_vec();
265 vturn = vturn * sin(theta_rot);
266 vec new_dir(vturn.x, vturn.y, cos(theta_rot));
267 new_dir.down(&temp);
268 m_currpos.dir = new_dir;
269 if (m_print_listing) Iprint(mcout, new_dir);
270 }
273 } else {
274#endif
275 double sigma_ctheta = hdecs->get_sigma(ek_restr, m_q_low_path_length);
276 // actually it is mean(1-cos(theta)) or
277 // sqrt( mean( square(1-cos(theta) ) ) ) depending on USE_MEAN_COEF
278
279 if (m_print_listing) Iprintnf(mcout, sigma_ctheta);
280 // Gauss (but actually exponential distribution fits better).
281 // double ctheta = 1.0 - fabs(rnorm_improved() * sigma_ctheta);
282 // Exponential:
283 double ctheta = 0.0;
284 {
285#ifdef USE_MEAN_COEF
286#else
287 double sq2 = sqrt(2.0);
288#endif
289 do {
290 double y = 0.0;
291 do { // in order to avoid SRANLUX() = 1
292 y = SRANLUX();
293 if (m_print_listing) Iprintnf(mcout, y);
294 } while (y == 1.0);
295#ifdef USE_MEAN_COEF
296 double x = sigma_ctheta * (-log(1.0 - y));
297#else
298 double x = sigma_ctheta * 1.0 / sq2 * (-log(1.0 - y));
299#endif
300 ctheta = 1.0 - x;
301 if (m_print_listing) Iprint2nf(mcout, x, ctheta);
302 } while (ctheta <= -1.0); // avoid absurd cos(theta)
303 check_econd21(ctheta, < -1.0 ||, > 1.0, mcerr);
304 }
305 if (m_print_listing) Iprintnf(mcout, ctheta);
306 double theta_rot = acos(ctheta);
307 if (m_print_listing) Iprint2nf(mcout, theta_rot, theta_rot / degree);
308 vec dir = m_currpos.dir;
309 basis temp(dir, "temp");
310 vec vturn;
311 vturn.random_round_vec();
312 vturn = vturn * sin(theta_rot);
313 vec new_dir(vturn.x, vturn.y, cos(theta_rot));
314 new_dir.down(&temp);
315 m_currpos.dir = new_dir;
318 }
319#ifdef DIRECT_LOW_IF_LITTLE
320 }
321#endif
322 if (m_path_length) {
323 if (m_print_listing) {
324 mcout << "\nstarting to rotate by large angle" << std::endl;
325 Iprintnf(mcout, m_path_length);
326 }
327 EnergyMesh* emesh = hdecs->hmd->energy_mesh;
328 const long n1r = findInterval(emesh, ek_restr);
329 double theta_rot = hdecs->angular_points_ran[n1r].ran(SRANLUX()) * degree;
330 if (m_print_listing) Iprintnf(mcout, theta_rot);
331 vec dir = m_currpos.dir;
332 basis temp(dir, "temp");
333 vec vturn;
334 vturn.random_round_vec();
335 vturn = vturn * sin(theta_rot);
336 vec new_dir(vturn.x, vturn.y, cos(theta_rot));
337 new_dir.down(&temp);
338 m_currpos.dir = new_dir;
341 }
342 if (m_print_listing) Iprint2nf(mcout, m_currpos.dir, m_currpos.dirloc);
343}
344
345void HeedDeltaElectron::ionisation(const double eloss, const double dedx,
346 PairProd* pairprod) {
347
348 if (eloss < m_necessary_energy) {
349 m_necessary_energy -= eloss;
350 return;
351 }
352
353 if (m_print_listing) mcout << "\nstart to leave conduction electrons\n";
354 if (m_necessary_energy <= 0.0) {
355#ifdef USE_ADJUSTED_W
356 m_necessary_energy = pairprod->get_eloss(m_prev_ekin / eV) * eV;
357#else
358 m_necessary_energy = pairprod->get_eloss() * eV;
359#endif
360 }
361 if (m_print_listing) Iprintnf(mcout, m_necessary_energy / eV);
362 double eloss_left = eloss;
363 point curpt = m_prevpos.pt;
364 vec dir = m_prevpos.dir; // this approximation ignores curvature
365 double ekin = m_prev_ekin;
366 if (m_print_listing) Iprintnf(mcout, curpt);
367 while (eloss_left >= m_necessary_energy) {
368 const double step_length = m_necessary_energy / (dedx * MeV / cm);
369 if (m_print_listing) Iprintnf(mcout, step_length);
370 curpt = curpt + dir * step_length;
371 if (m_print_listing) Iprintf(mcout, curpt);
372 point ptloc = curpt;
373 m_prevpos.tid.up_absref(&ptloc);
374 if (m_print_listing) mcout << "New conduction electron\n";
375 if (m_fieldMap->inside(ptloc)) {
376 conduction_electrons.emplace_back(HeedCondElectron(ptloc, m_currpos.time));
377 conduction_ions.emplace_back(HeedCondElectron(ptloc, m_currpos.time));
378 }
379 eloss_left -= m_necessary_energy;
380 ekin -= m_necessary_energy;
381 if (ekin < 0.) break;
382 // Generate next random energy
383#ifdef USE_ADJUSTED_W
384 m_necessary_energy = eV * pairprod->get_eloss(ekin / eV);
385#else
386 m_necessary_energy = pairprod->get_eloss() * eV;
387#endif
388 if (m_print_listing) {
389 Iprintnf(mcout, eloss_left / eV);
390 Iprint2nf(mcout, ekin / eV, m_necessary_energy / eV);
391 }
392 }
393 m_necessary_energy -= eloss_left;
394 if (m_print_listing) Iprintnf(mcout, m_necessary_energy / eV);
395}
396
397void HeedDeltaElectron::print(std::ostream& file, int l) const {
398 if (l < 0) return;
399 Ifile << "HeedDeltaElectron (l=" << l
400 << "): particle_number=" << m_particle_number << "\n";
401 if (l == 1) return;
402 indn.n += 2;
403 Ifile << "s_low_mult_scattering=" << s_low_mult_scattering
404 << " s_high_mult_scattering=" << s_high_mult_scattering << '\n';
405 Ifile << "phys_mrange=" << m_phys_mrange << " stop_eloss=" << m_stop_eloss
406 << " mult_low_path_length=" << m_mult_low_path_length << '\n';
407 Ifile << "q_low_path_length=" << m_q_low_path_length
408 << " path_length=" << m_path_length
409 << " necessary_energy/eV=" << m_necessary_energy / eV << '\n';
410 Ifile << " parent_particle_number=" << parent_particle_number << '\n';
411
412 mparticle::print(file, l - 1);
413 indn.n -= 2;
414}
415}
#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 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:313
HeedFieldMap * m_fieldMap
Pointer to field map.
Definition: eparticle.h:34
stvpoint m_prevpos
Previous point.
Definition: gparticle.h:262
stvpoint m_currpos
Current point.
Definition: gparticle.h:264
bool m_alive
Status flag whether the particle is active.
Definition: gparticle.h:247
void up_absref(absref *f)
Definition: volume.cpp:27
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:368
vfloat time
Definition: gparticle.h:47
absvol * volume()
Definition: gparticle.h:137
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:179
vfloat x
Definition: vec.h:192
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:193
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
int vecerror
Definition: vec.cpp:29
DoubleAc acos(const DoubleAc &f)
Definition: DoubleAc.cpp:490
long pois(const double amu)
Definition: pois.cpp:9
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:106
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:236
#define mcout
Definition: prstream.h:126
#define Iprint3n(file, name1, name2, name3)
Definition: prstream.h:232
#define Ifile
Definition: prstream.h:195
#define Iprint(file, name)
Definition: prstream.h:197
#define mcerr
Definition: prstream.h:128
#define Iprintn(file, name)
Definition: prstream.h:204
#define Iprintf(file, name)
Definition: prstream.h:199
#define Iprint2nf(file, name1, name2)
Definition: prstream.h:222
#define Iprint2n(file, name1, name2)
Definition: prstream.h:219
#define Iprintnf(file, name)
Definition: prstream.h:206