25 if (particle !=
"electron" && particle !=
"e" && particle !=
"e-") {
26 std::cerr <<
m_className <<
"::SetParticle: Only electrons are allowed.\n";
31 const double t0,
const double dx0,
32 const double dy0,
const double dz0) {
37 std::cerr <<
m_className <<
"::NewTrack: Sensor is not defined.\n";
45 std::cerr <<
" No medium at initial position.\n";
50 std::cerr <<
" Medium at initial position is not ionisable.\n";
55 if (!medium->
IsGas()) {
57 std::cerr <<
" Medium at initial position is not a gas.\n";
61 if (!SetupGas(medium)) {
63 std::cerr <<
" Properties of medium " << medium->
GetName()
64 <<
" are not available.\n";
68 if (!UpdateCrossSection()) {
70 std::cerr <<
" Cross-sections could not be calculated.\n";
74 m_mediumName = medium->
GetName();
80 const double dd = sqrt(dx0 * dx0 + dy0 * dy0 + dz0 * dz0);
84 std::cout <<
" Direction vector has zero norm.\n";
85 std::cout <<
" Initial direction is randomized.\n";
100 double& tcls,
int& ncls,
double& edep,
109 <<
" Track not initialized. Call NewTrack first.\n";
118 m_t += d / (sqrt(
m_beta2) * SpeedOfLight);
131 if (medium->
GetName() != m_mediumName ||
143 const int nComponents = m_components.size();
144 for (
int i = 0; i < nComponents; ++i) {
153 const double e0 = ElectronMass * (sqrt(1. / (1. -
m_beta2)) - 1.);
155 m_components[iComponent].wSplit *
156 tan(
RndmUniform() * atan((e0 - m_components[iComponent].ethr) /
157 (2. * m_components[iComponent].wSplit)));
158 esec = m_components[iComponent].wSplit *
159 pow(esec / m_components[iComponent].wSplit, 0.9524);
160 m_electrons.resize(1);
161 m_electrons[0].energy = esec;
162 m_electrons[0].x = xcls;
163 m_electrons[0].y = ycls;
164 m_electrons[0].z = zcls;
174 std::cerr <<
m_className <<
"::GetClusterDensity:\n";
175 std::cerr <<
" Track has not been initialized.\n";
180 std::cerr <<
m_className <<
"::GetClusterDensity:\n";
181 std::cerr <<
" Mean free path is not available.\n";
190 std::cerr <<
m_className <<
"::GetStoppingPower:\n";
191 std::cerr <<
" Track has not been initialised.\n";
195 constexpr double prefactor =
196 4 * Pi * HbarC * HbarC / (ElectronMass * ElectronMass);
201 const double e0 = ElectronMass * (sqrt(1. / (1. -
m_beta2)) - 1.);
202 const int nComponents = m_components.size();
203 for (
int i = nComponents; i--;) {
206 m_mediumDensity * m_components[i].fraction * (prefactor /
m_beta2) *
207 (m_components[i].m2Ion * (lnBg2 -
m_beta2) + m_components[i].cIon);
209 (e0 - m_components[i].ethr) / (2 * m_components[i].wSplit);
212 (m_components[i].wSplit / (2 * atan(ew))) * log(1. + ew * ew);
213 dedx += cmean * emean;
219bool TrackElectron::SetupGas(
Medium* gas) {
220 m_components.clear();
224 std::cerr <<
" Medium is not defined.\n";
230 if (nComponents <= 0) {
232 std::cerr <<
" Medium composition is not defined.\n";
235 m_components.resize(nComponents);
241 for (
int i = nComponents; i--;) {
242 std::string gasname =
"";
245 m_components[i].fraction =
frac;
246 m_components[i].p = 0.;
247 if (gasname ==
"CF4") {
248 m_components[i].m2Ion = 7.2;
249 m_components[i].cIon = 93.;
250 m_components[i].x0Dens = 1.;
251 m_components[i].x1Dens = 0.;
252 m_components[i].cDens = 0.;
253 m_components[i].aDens = 0.;
254 m_components[i].mDens = 0.;
255 m_components[i].ethr = 15.9;
256 m_components[i].wSplit = 19.5;
257 }
else if (gasname ==
"Ar") {
258 m_components[i].m2Ion = 3.593;
259 m_components[i].cIon = 39.7;
260 m_components[i].x0Dens = 1.7635;
261 m_components[i].x1Dens = 4.4855;
262 m_components[i].cDens = 11.9480;
263 m_components[i].aDens = 0.19714;
264 m_components[i].mDens = 2.9618;
265 m_components[i].ethr = 15.75961;
266 m_components[i].wSplit = 15.;
267 }
else if (gasname ==
"He") {
268 m_components[i].m2Ion = 0.489;
269 m_components[i].cIon = 5.5;
270 m_components[i].x0Dens = 2.2017;
271 m_components[i].x1Dens = 3.6122;
272 m_components[i].cDens = 11.1393;
273 m_components[i].aDens = 0.13443;
274 m_components[i].mDens = 5.8347;
275 m_components[i].ethr = 24.58739;
276 m_components[i].wSplit = 10.5;
277 }
else if (gasname ==
"He-3") {
278 m_components[i].m2Ion = 0.489;
279 m_components[i].cIon = 5.5;
280 m_components[i].x0Dens = 2.2017;
281 m_components[i].x1Dens = 3.6122;
282 m_components[i].cDens = 11.1393;
283 m_components[i].aDens = 0.13443;
284 m_components[i].mDens = 5.8347;
285 m_components[i].ethr = 24.58739;
286 m_components[i].wSplit = 10.5;
287 }
else if (gasname ==
"Ne") {
288 m_components[i].m2Ion = 1.69;
289 m_components[i].cIon = 17.8;
290 m_components[i].x0Dens = 2.0735;
291 m_components[i].x1Dens = 4.6421;
292 m_components[i].cDens = 11.9041;
293 m_components[i].aDens = 0.08064;
294 m_components[i].mDens = 3.5771;
295 m_components[i].ethr = 21.56454;
296 m_components[i].wSplit = 19.5;
297 }
else if (gasname ==
"Kr") {
298 m_components[i].m2Ion = 5.5;
299 m_components[i].cIon = 56.9;
300 m_components[i].x0Dens = 1.7158;
301 m_components[i].x1Dens = 5.0748;
302 m_components[i].cDens = 12.5115;
303 m_components[i].aDens = 0.07446;
304 m_components[i].mDens = 3.4051;
305 m_components[i].ethr = 13.996;
306 m_components[i].wSplit = 21.;
307 }
else if (gasname ==
"Xe") {
308 m_components[i].m2Ion = 8.04;
309 m_components[i].cIon = 75.25;
310 m_components[i].x0Dens = 1.5630;
311 m_components[i].x1Dens = 4.7371;
312 m_components[i].cDens = 12.7281;
313 m_components[i].aDens = 0.23314;
314 m_components[i].mDens = 2.7414;
315 m_components[i].ethr = 12.129843;
316 m_components[i].wSplit = 23.7;
317 }
else if (gasname ==
"CH4") {
318 m_components[i].m2Ion = 3.75;
319 m_components[i].cIon = 42.5;
320 m_components[i].x0Dens = 1.6263;
321 m_components[i].x1Dens = 3.9716;
322 m_components[i].cDens = 9.5243;
323 m_components[i].aDens = 0.09253;
324 m_components[i].mDens = 3.6257;
325 m_components[i].ethr = 12.65;
326 m_components[i].wSplit = 8.;
327 }
else if (gasname ==
"iC4H10") {
328 m_components[i].m2Ion = 15.5;
329 m_components[i].cIon = 160.;
330 m_components[i].x0Dens = 1.3788;
331 m_components[i].x1Dens = 3.7524;
332 m_components[i].cDens = 8.5633;
333 m_components[i].aDens = 0.10852;
334 m_components[i].mDens = 3.4884;
335 m_components[i].ethr = 10.67;
336 m_components[i].wSplit = 7.;
337 }
else if (gasname ==
"CO2") {
338 m_components[i].m2Ion = 5.6;
339 m_components[i].cIon = 57.91;
340 m_components[i].x0Dens = 1.6294;
341 m_components[i].x1Dens = 4.1825;
342 m_components[i].aDens = 0.11768;
343 m_components[i].mDens = 3.3227;
344 m_components[i].ethr = 13.777;
345 m_components[i].wSplit = 13.;
346 }
else if (gasname ==
"N2") {
347 m_components[i].m2Ion = 3.35;
348 m_components[i].cIon = 38.1;
349 m_components[i].x0Dens = 1.7378;
350 m_components[i].x1Dens = 4.1323;
351 m_components[i].cDens = 10.5400;
352 m_components[i].aDens = 0.15349;
353 m_components[i].mDens = 3.2125;
354 m_components[i].ethr = 15.581;
355 m_components[i].wSplit = 13.8;
358 std::cerr <<
" Cross-section for " << gasname
359 <<
" is not available.\n";
365 m_components.clear();
371bool TrackElectron::UpdateCrossSection() {
372 constexpr double prefactor =
373 4 * Pi * HbarC * HbarC / (ElectronMass * ElectronMass);
376 const double eta = m_mediumDensity / LoschmidtNumber;
377 const double x = 0.5 * (lnBg2 + log(eta)) / log(10.);
379 const int nComponents = m_components.size();
380 for (
int i = nComponents; i--;) {
382 if (m_components[i].x0Dens < m_components[i].x1Dens &&
383 x >= m_components[i].x0Dens) {
384 delta = 2 * log(10.) *
x - m_components[i].cDens;
385 if (x < m_components[i].x1Dens) {
386 delta += m_components[i].aDens *
387 pow(m_components[i].x1Dens - x, m_components[i].mDens);
390 const double cs = (m_components[i].fraction * prefactor /
m_beta2) *
391 (m_components[i].m2Ion * (lnBg2 -
m_beta2 - delta) +
392 m_components[i].cIon);
393 m_components[i].p = cs;
398 std::cerr <<
m_className <<
"::UpdateCrossSection:\n";
399 std::cerr <<
" Total cross-section <= 0.\n";
403 m_mfp = 1. / (csSum * m_mediumDensity);
405 for (
int i = 0; i < nComponents; ++i) {
406 m_components[i].p /= csSum;
407 if (i > 0) m_components[i].p += m_components[i - 1].p;
Abstract base class for media.
virtual void GetComponent(const unsigned int i, std::string &label, double &f)
Get the name and fraction of a given component.
virtual double GetNumberDensity() const
Get the number density [cm-3].
unsigned int GetNumberOfComponents() const
Get number of components of the medium.
const std::string & GetName() const
Get the medium name/identifier.
virtual bool IsGas() const
Is this medium a gas?
bool IsIonisable() const
Is charge deposition by charged particles/photon enabled in this medium?
bool IsInArea(const double x, const double y, const double z)
Check if a point is inside the user area.
bool GetMedium(const double x, const double y, const double z, Medium *&medium)
Get the medium at (x, y, z).
virtual void SetParticle(const std::string &particle)
virtual double GetClusterDensity()
virtual bool NewTrack(const double x0, const double y0, const double z0, const double t0, const double dx0, const double dy0, const double dz0)
virtual bool GetCluster(double &xcls, double &ycls, double &zcls, double &tcls, int &ncls, double &ecls, double &extra)
virtual double GetStoppingPower()
Get the stopping power (mean energy loss [eV] per cm).
Abstract base class for track generation.
void SetBetaGamma(const double bg)
Set the relative momentum of the particle.
std::string m_particleName
double RndmUniform()
Draw a random number uniformly distributed in the range [0, 1).
void RndmDirection(double &dx, double &dy, double &dz, const double length=1.)
Draw a random (isotropic) direction vector.
double RndmUniformPos()
Draw a random number uniformly distributed in the range (0, 1).
DoubleAc pow(const DoubleAc &f, double p)