Garfield++ v2r0
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}
22
23namespace Heed {
24
25using CLHEP::degree;
26using CLHEP::cm;
27using CLHEP::eV;
28using CLHEP::keV;
29using CLHEP::MeV;
30using CLHEP::c_light;
31using CLHEP::c_squared;
32
33bool HeedDeltaElectron::s_low_mult_scattering = true;
34bool HeedDeltaElectron::s_high_mult_scattering = true;
35
37 const vec& vel, vfloat time,
38 long fparent_particle_number,
39 HeedFieldMap* fieldmap,
40 bool fs_print_listing)
41 : eparticle(primvol, pt, vel, time, &electron_def, fieldmap),
42 parent_particle_number(fparent_particle_number),
43 particle_number(last_particle_number++),
44 s_print_listing(fs_print_listing),
45 phys_mrange(0.0),
46 s_stop_eloss(false),
47 s_mult_low_path_length(false),
48 q_low_path_length(0.0),
49 s_path_length(false),
50 necessary_energy(0.0),
51 total_Eloss(0.) {
52 mfunname("HeedDeltaElectron::HeedDeltaElectron(...)");
53}
54
55void HeedDeltaElectron::physics_mrange(double& fmrange) {
56 mfunname("void HeedDeltaElectron::physics_mrange(double& fmrange)");
57 if (s_print_listing) mcout << "void HeedDeltaElectron::physics_mrange\n";
58
59 s_mult_low_path_length = false;
60 q_low_path_length = 0.0;
61 s_path_length = false;
62 if (fmrange <= 0.0) return;
63 if (curr_kin_energy == 0.0) {
64 fmrange = 0.0;
65 return;
66 }
67 // Get local volume and convert it to a cross-section object.
68 const absvol* av = currpos.tid.G_lavol();
69 const HeedDeltaElectronCS* hdecs =
70 dynamic_cast<const HeedDeltaElectronCS*>(av);
71 if (!hdecs) return;
72 if (s_print_listing) Iprintnf(mcout, fmrange);
73 const double ek = curr_kin_energy / MeV;
74 EnergyMesh* emesh = hdecs->hmd->energy_mesh.get();
75 const long n1 = findInterval(emesh, ek);
76 const double e1 = emesh->get_ec(n1);
77 const double e2 = emesh->get_ec(n1 + 1);
78 const double loss1 = hdecs->eLoss[n1];
79 const double loss2 = hdecs->eLoss[n1 + 1];
80 double dedx = loss1 + (loss2 - loss1) / (e2 - e1) * (ek - e1);
81 // Min. loss 50 eV.
82 double eloss = std::max(0.1 * ek, 0.00005);
83 if (eloss > ek) {
84 eloss = ek;
85 s_stop_eloss = true;
86 } else {
87 s_stop_eloss = false;
88 }
89 fmrange = std::min(fmrange, (eloss / dedx) * cm);
90 if (s_print_listing) Iprint2nf(mcout, fmrange, ek);
91 const double ek_restr = std::max(ek, 0.0005);
92 if (s_print_listing) Iprintnf(mcout, ek_restr / keV);
93
94 const long n1r = findInterval(emesh, ek_restr);
95 double low_path_length = 0.; // in internal units
96 if (s_low_mult_scattering) {
97 const double e1r = emesh->get_ec(n1r);
98 const double e2r = emesh->get_ec(n1r + 1);
99 const double y1 = hdecs->low_lambda[n1r];
100 const double y2 = hdecs->low_lambda[n1r + 1];
101 low_path_length = (y1 + (y2 - y1) / (e2r - e1r) * (ek_restr - e1r)) * cm;
102 if (s_print_listing) Iprintnf(mcout, low_path_length / cm);
103 long qscat = hdecs->eesls->get_qscat();
104 const double sigma_ctheta = hdecs->get_sigma(ek_restr, qscat);
105 // Reduce the number of scatterings, if the angle is too large.
106 if (sigma_ctheta > 0.3) qscat = long(qscat * 0.3 / sigma_ctheta);
107 double mult_low_path_length = qscat * low_path_length;
108 if (s_print_listing) Iprintnf(mcout, mult_low_path_length);
109 if (fmrange > mult_low_path_length) {
110 fmrange = mult_low_path_length;
111 s_mult_low_path_length = true;
112 q_low_path_length = hdecs->eesls->get_qscat();
113 s_stop_eloss = false;
114 } else {
115 s_mult_low_path_length = false;
116 q_low_path_length = fmrange / low_path_length;
117 }
118 if (s_print_listing) Iprint2nf(mcout, fmrange, q_low_path_length);
119 }
120
121 if (s_high_mult_scattering) {
122 const double e1r = emesh->get_ec(n1r);
123 const double e2r = emesh->get_ec(n1r + 1);
124 if (s_print_listing) {
126 Iprintnf(mcout, n1r);
127 Iprintnf(mcout, ek_restr);
128 Iprint2nf(mcout, e1r, e2r);
129 }
130 const double y1 = hdecs->lambda[n1r];
131 const double y2 = hdecs->lambda[n1r + 1];
132 const double mean_path = y1 + (y2 - y1) / (e2r - e1r) * (ek_restr - e1r);
133 if (s_print_listing) Iprintnf(mcout, mean_path);
134 const double path_length = -mean_path * cm * log(1.0 - SRANLUX());
135 if (s_print_listing) Iprintnf(mcout, path_length);
136 if (fmrange > path_length) {
137 fmrange = path_length;
138 s_path_length = true;
139 s_mult_low_path_length = true;
140 if (s_low_mult_scattering) {
141 q_low_path_length = fmrange / low_path_length;
142 if (s_print_listing) Iprintnf(mcout, q_low_path_length);
143 }
144 s_stop_eloss = false;
145 } else {
146 s_path_length = false;
147 }
148 if (s_print_listing) Iprintnf(mcout, fmrange);
149 }
150 phys_mrange = fmrange;
151}
152
154 std::vector<gparticle*>& /*secondaries*/) {
155 mfunname("void HeedDeltaElectron::physics_after_new_speed()");
156 if (s_print_listing) {
157 mcout << "HeedDeltaElectron::physics_after_new_speed is started\n";
159 }
161 if (currpos.prange <= 0.0) {
162 if (curr_kin_energy == 0.0) {
163 // Get local volume.
164 absvol* av = currpos.tid.G_lavol();
165 if (av && av->s_sensitive && m_fieldMap->inside(currpos.ptloc)) {
166 if (s_print_listing) {
167 mcout << "HeedDeltaElectron::physics_after_new_speed: \n";
168 mcout << "This is converted to conduction\n";
169 }
170 // TODO: replace push_back by emplace_back.
171 conduction_electrons.push_back(
173 }
174 s_life = false;
175 }
176 if (s_print_listing) mcout << "exit due to currpos.prange <= 0.0\n";
177 return;
178 }
179 if (s_print_listing) mcout << "physics_after_new_speed: started" << std::endl;
180 // Get local volume and convert it to a cross-section object.
181 const absvol* av = currpos.tid.G_lavol();
182 const HeedDeltaElectronCS* hdecs =
183 dynamic_cast<const HeedDeltaElectronCS*>(av);
184 if (!hdecs) return;
185 double ek = curr_kin_energy / MeV;
186 if (s_print_listing) {
187 Iprintnf(mcout, ek);
188 Iprint3n(mcout, s_stop_eloss, phys_mrange, currpos.prange);
189 }
190 // Calculate dE/dx and energy loss. Update the kinetic energy.
191 double dedx;
192 double Eloss = 0.;
193 if (s_stop_eloss && phys_mrange == currpos.prange) {
194 Eloss = curr_kin_energy;
195 curr_kin_energy = 0.0;
196 dedx = Eloss / currpos.prange / (MeV / cm);
197 } else {
198 EnergyMesh* emesh = hdecs->hmd->energy_mesh.get();
199 const long n1 = findInterval(emesh, ek);
200 if (s_print_listing) Iprintn(mcout, n1);
201 const double e1 = emesh->get_ec(n1);
202 const double e2 = emesh->get_ec(n1 + 1);
203 const double y1 = hdecs->eLoss[n1];
204 const double y2 = hdecs->eLoss[n1 + 1];
205 dedx = y1 + (y2 - y1) / (e2 - e1) * (ek - e1);
206 Eloss = (dedx * MeV / cm) * currpos.prange;
207 total_Eloss += Eloss;
208 curr_kin_energy -= Eloss;
209 }
210 if (s_print_listing)
211 Iprint3nf(mcout, prev_kin_energy / eV, curr_kin_energy / eV, Eloss / eV);
212 if (curr_kin_energy <= 0.0) {
213 if (s_print_listing) mcout << "curr_kin_energy <= 0.0\n";
214 curr_kin_energy = 0.0;
215 curr_gamma_1 = 0.0;
216 currpos.speed = 0.0;
217 s_life = false;
218 } else {
219 const double resten = mass * c_squared;
220 curr_gamma_1 = curr_kin_energy / resten;
221 currpos.speed = c_light * lorbeta(curr_gamma_1);
222 }
223 absvol* vav = currpos.tid.G_lavol();
224 if (vav && vav->s_sensitive) {
225 if (s_print_listing) {
226 mcout << "volume is sensitive\n";
227 Iprint2nf(mcout, Eloss / eV, necessary_energy / eV);
228 }
229 if (Eloss > 0.0) ionisation(Eloss, dedx, hdecs->pairprod.get());
230 }
231 if (s_print_listing) {
232 mcout << '\n';
234 }
235 if (!s_life) {
236 // Done tracing the delta electron. Create the last conduction electron.
237 vav = currpos.tid.G_lavol();
238 if (vav && vav->s_sensitive && m_fieldMap->inside(currpos.ptloc)) {
239 if (s_print_listing) mcout << "Last conduction electron\n";
240 conduction_electrons.push_back(
242 }
243 return;
244 }
245
246 if (s_print_listing) mcout << "\nstart to rotate by low angle\n";
247 double ek_restr = std::max(ek, 0.0005);
248 if (s_print_listing) Iprint2nf(mcout, currpos.prange, phys_mrange);
249 if (currpos.prange < phys_mrange) {
250 // recalculate scatterings
251 s_path_length = false;
252 if (s_low_mult_scattering) {
253 EnergyMesh* emesh = hdecs->hmd->energy_mesh.get();
254 const long n1r = findInterval(emesh, ek_restr);
255 const double e1 = emesh->get_ec(n1r);
256 const double e2 = emesh->get_ec(n1r + 1);
257 const double y1 = hdecs->low_lambda[n1r];
258 const double y2 = hdecs->low_lambda[n1r + 1];
259 // Path length in internal units.
260 double low_path_length =
261 (y1 + (y2 - y1) / (e2 - e1) * (ek_restr - e1)) * cm;
262 if (s_print_listing) Iprintnf(mcout, low_path_length / cm);
263 s_mult_low_path_length = false;
264 q_low_path_length = currpos.prange / low_path_length;
265 if (s_print_listing) Iprintnf(mcout, q_low_path_length);
266 }
267 }
268 if (s_print_listing) Iprintnf(mcout, q_low_path_length);
269#ifdef RANDOM_POIS
270 if (q_low_path_length > 0.0) {
271 int ierror = 0;
272 long random_q_low_path_length = pois(q_low_path_length, ierror);
273 check_econd11a(ierror, == 1,
274 " q_low_path_length=" << q_low_path_length << '\n', mcerr);
275 q_low_path_length = long(random_q_low_path_length);
276 if (s_print_listing) {
277 mcout << "After pois:\n";
278 Iprintnf(mcout, q_low_path_length);
279 }
280 }
281#endif
282 if (q_low_path_length > 0) {
283#ifdef DIRECT_LOW_IF_LITTLE
284 const long max_q_low_path_length_for_direct = 5;
285 if (q_low_path_length < max_q_low_path_length_for_direct) {
286 // direct modeling
287 if (s_print_listing) {
288 mcout << "direct modeling of low scatterings\n";
290 }
291 EnergyMesh* emesh = hdecs->hmd->energy_mesh.get();
292 const long n1r = findInterval(emesh, ek_restr);
293 for (long nscat = 0; nscat < q_low_path_length; ++nscat) {
294 if (s_print_listing) Iprintn(mcout, nscat);
295 double theta_rot =
296 hdecs->low_angular_points_ran[n1r].ran(SRANLUX()) * degree;
297 if (s_print_listing) Iprintnf(mcout, theta_rot);
298 vec dir = currpos.dir;
299 basis temp(dir, "temp");
300 vec vturn;
301 vturn.random_round_vec();
302 vturn = vturn * sin(theta_rot);
303 vec new_dir(vturn.x, vturn.y, cos(theta_rot));
304 new_dir.down(&temp);
305 currpos.dir = new_dir;
306 if (s_print_listing) Iprint(mcout, new_dir);
307 }
310 } else {
311#endif
312 double sigma_ctheta = hdecs->get_sigma(ek_restr, q_low_path_length);
313 // actually it is mean(1-cos(theta)) or
314 // sqrt( mean( square(1-cos(theta) ) ) ) depending on USE_MEAN_COEF
315
316 if (s_print_listing) Iprintnf(mcout, sigma_ctheta);
317 // Gauss (but actually exponential distribution fits better).
318 // double ctheta = 1.0 - fabs(rnorm_improved() * sigma_ctheta);
319 // Exponential:
320 double ctheta = 0.0;
321 {
322#ifdef USE_MEAN_COEF
323#else
324 double sq2 = sqrt(2.0);
325#endif
326 do {
327 double y = 0.0;
328 do { // in order to avoid SRANLUX() = 1
329 y = SRANLUX();
330 if (s_print_listing) Iprintnf(mcout, y);
331 } while (y == 1.0);
332#ifdef USE_MEAN_COEF
333 double x = sigma_ctheta * (-log(1.0 - y));
334#else
335 double x = sigma_ctheta * 1.0 / sq2 * (-log(1.0 - y));
336#endif
337 ctheta = 1.0 - x;
338 if (s_print_listing) Iprint2nf(mcout, x, ctheta);
339 } while (ctheta <= -1.0); // avoid absurd cos(theta)
340 check_econd21(ctheta, < -1.0 ||, > 1.0, mcerr);
341 }
342 if (s_print_listing) Iprintnf(mcout, ctheta);
343 double theta_rot = acos(ctheta);
344 if (s_print_listing) Iprint2nf(mcout, theta_rot, theta_rot / degree);
345 vec dir = currpos.dir;
346 basis temp(dir, "temp");
347 vec vturn;
348 vturn.random_round_vec();
349 vturn = vturn * sin(theta_rot);
350 vec new_dir(vturn.x, vturn.y, cos(theta_rot));
351 new_dir.down(&temp);
352 currpos.dir = new_dir;
355 }
356#ifdef DIRECT_LOW_IF_LITTLE
357 }
358#endif
359 if (s_path_length) {
360 if (s_print_listing) {
361 mcout << "\nstarting to rotate by large angle" << std::endl;
362 Iprintnf(mcout, s_path_length);
363 }
364 EnergyMesh* emesh = hdecs->hmd->energy_mesh.get();
365 const long n1r = findInterval(emesh, ek_restr);
366 double theta_rot = hdecs->angular_points_ran[n1r].ran(SRANLUX()) * degree;
367 if (s_print_listing) Iprintnf(mcout, theta_rot);
368 vec dir = currpos.dir;
369 basis temp(dir, "temp");
370 vec vturn;
371 vturn.random_round_vec();
372 vturn = vturn * sin(theta_rot);
373 vec new_dir(vturn.x, vturn.y, cos(theta_rot));
374 new_dir.down(&temp);
375 currpos.dir = new_dir;
378 }
379 if (s_print_listing) Iprint2nf(mcout, currpos.dir, currpos.dirloc);
380}
381
382void HeedDeltaElectron::ionisation(const double eloss, const double dedx,
383 PairProd* pairprod) {
384
385 if (eloss < necessary_energy) {
386 necessary_energy -= eloss;
387 return;
388 }
389
390 if (s_print_listing) mcout << "\nstart to leave conduction electrons\n";
391 if (necessary_energy <= 0.0) {
392#ifdef USE_ADJUSTED_W
393 necessary_energy = pairprod->get_eloss(prev_kin_energy / eV) * eV;
394#else
395 necessary_energy = pairprod->get_eloss() * eV;
396#endif
397 }
398 if (s_print_listing) Iprintnf(mcout, necessary_energy / eV);
399 double eloss_left = eloss;
400 point curpt = prevpos.pt;
401 vec dir = prevpos.dir; // this approximation ignores curvature
402 double ekin = prev_kin_energy;
403 if (s_print_listing) Iprintnf(mcout, curpt);
404 while (eloss_left >= necessary_energy) {
405 const double step_length = necessary_energy / (dedx * MeV / cm);
406 if (s_print_listing) Iprintnf(mcout, step_length);
407 curpt = curpt + dir * step_length;
408 if (s_print_listing) Iprintf(mcout, curpt);
409 point ptloc = curpt;
410 prevpos.tid.up_absref(&ptloc);
411 if (s_print_listing) mcout << "New conduction electron\n";
412 // TODO: replace push_back by emplace_back.
413 if (m_fieldMap->inside(ptloc)) {
414 conduction_electrons.push_back(HeedCondElectron(ptloc, currpos.time));
415 conduction_ions.push_back(HeedCondElectron(ptloc, currpos.time));
416 }
417 eloss_left -= necessary_energy;
418 ekin -= necessary_energy;
419// Generate next random energy
420#ifdef USE_ADJUSTED_W
421 necessary_energy = eV * pairprod->get_eloss(ekin / eV);
422#else
423 necessary_energy = pairprod->get_eloss() * eV;
424#endif
425 if (s_print_listing) {
426 Iprintnf(mcout, eloss_left / eV);
427 Iprint2nf(mcout, ekin / eV, necessary_energy / eV);
428 }
429 }
430 necessary_energy -= eloss_left;
431 if (s_print_listing) Iprintnf(mcout, necessary_energy / eV);
432}
433
434void HeedDeltaElectron::print(std::ostream& file, int l) const {
435 if (l < 0) return;
436 Ifile << "HeedDeltaElectron (l=" << l
437 << "): particle_number=" << particle_number << "\n";
438 if (l == 1) return;
439 indn.n += 2;
440 Ifile << "s_low_mult_scattering=" << s_low_mult_scattering
441 << " s_high_mult_scattering=" << s_high_mult_scattering << '\n';
442 Ifile << "phys_mrange=" << phys_mrange << " s_stop_eloss=" << s_stop_eloss
443 << " s_mult_low_path_length=" << s_mult_low_path_length << '\n';
444 Ifile << "q_low_path_length=" << q_low_path_length
445 << " s_path_length=" << s_path_length
446 << " necessary_energy/eV=" << necessary_energy / eV << '\n';
447 Ifile << " parent_particle_number=" << parent_particle_number << '\n';
448
449 mparticle::print(file, l - 1);
450 indn.n -= 2;
451}
452}
#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:45
double get_ec(long n) const
Return center of a given bin.
Definition: EnergyMesh.h:53
long get_interval_number_between_centers(const double ener) const
Definition: EnergyMesh.cpp:62
double get_sigma(double energy, double nscat) const
PassivePtr< HeedMatterDef > hmd
std::vector< PointsRan > low_angular_points_ran
PassivePtr< PairProd > pairprod
std::vector< double > eLoss
std::vector< double > lambda
std::vector< PointsRan > angular_points_ran
PassivePtr< ElElasticScatLowSigma > eesls
std::vector< double > low_lambda
std::vector< HeedCondElectron > conduction_electrons
std::vector< HeedCondElectron > conduction_ions
virtual void physics_mrange(double &fmrange)
virtual void print(std::ostream &file, int l) const
HeedDeltaElectron()
Default constructor.
virtual void physics_after_new_speed(std::vector< gparticle * > &secondaries)
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:75
Basis.
Definition: vec.h:319
HeedFieldMap * m_fieldMap
Definition: eparticle.h:33
stvpoint prevpos
Definition: gparticle.h:176
stvpoint currpos
Definition: gparticle.h:177
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:178
double prev_kin_energy
Definition: mparticle.h:30
double curr_kin_energy
Definition: mparticle.h:32
double mass
Mass (not mass * speed_of_light^2)
Definition: mparticle.h:26
virtual void print(std::ostream &file, int l) const
Definition: mparticle.cpp:242
double curr_gamma_1
Definition: mparticle.h:33
Point.
Definition: vec.h:374
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:186
vfloat x
Definition: vec.h:203
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:203
Definition: BGMesh.cpp:5
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:110
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