Geant4  9.6.p02
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
G4CollisionOutput.cc
Go to the documentation of this file.
1 //
2 // ********************************************************************
3 // * License and Disclaimer *
4 // * *
5 // * The Geant4 software is copyright of the Copyright Holders of *
6 // * the Geant4 Collaboration. It is provided under the terms and *
7 // * conditions of the Geant4 Software License, included in the file *
8 // * LICENSE and available at http://cern.ch/geant4/license . These *
9 // * include a list of copyright holders. *
10 // * *
11 // * Neither the authors of this software system, nor their employing *
12 // * institutes,nor the agencies providing financial support for this *
13 // * work make any representation or warranty, express or implied, *
14 // * regarding this software system or assume any liability for its *
15 // * use. Please see the license in the file LICENSE and URL above *
16 // * for the full disclaimer and the limitation of liability. *
17 // * *
18 // * This code implementation is the result of the scientific and *
19 // * technical work of the GEANT4 collaboration. *
20 // * By using, copying, modifying or distributing the software (or *
21 // * any work based on the software) you agree to acknowledge its *
22 // * use in resulting scientific publications, and indicate your *
23 // * acceptance of all terms of the Geant4 Software license. *
24 // ********************************************************************
25 //
26 // $Id$
27 //
28 // 20100114 M. Kelsey -- Remove G4CascadeMomentum, use G4LorentzVector directly
29 // 20100309 M. Kelsey -- Introduced bug checking i3 for valid tuning pair
30 // 20100409 M. Kelsey -- Move non-inlinable code here out of .hh file, replace
31 // loop over push_back() with block insert().
32 // 20100418 M. Kelsey -- Add function to boost output lists to lab frame
33 // 20100520 M. Kelsey -- Add function to rotate Z axis, from G4Casc.Interface
34 // 20100620 M. Kelsey -- Add some diagnostics in setOnShell, simplify if's
35 // 20100630 M. Kelsey -- Use "getExcitationEnergyInGeV()" instead of ".001*"
36 // 20100701 M. Kelsey -- G4InuclNuclei now includes excitation energy as part
37 // of the reported mass and four-vector.
38 // 20100714 M. Kelsey -- Modify setOnShell() to avoid creating particles
39 // with negative kinetic energy.
40 // 20100715 M. Kelsey -- Add total charge and baryon number functions, and a
41 // combined "add()" function to put two of these together
42 // 20100924 M. Kelsey -- Use "OutgoingNuclei" name consistently, replacing
43 // old "TargetFragment". Add new (reusable) G4Fragment buffer
44 // and access functions for initial post-cascade processing.
45 // Move implementation of add() to .cc file.
46 // 20101019 M. Kelsey -- CoVerity report: unitialized constructor
47 // 20110214 M. Kelsey -- Follow G4InuclParticle::Model enumerator migration
48 // 20110225 M. Kelsey -- Add non-const functions to remove list elements
49 // 20110302 M. Kelsey -- Fix message in setOnShell() by moving ini_mom calc
50 // 20110307 M. Kelsey -- Need to discard existing ouput lists in trivialize()
51 // 20110311 M. Kelsey -- Include nuclear fragment in setOnShell balancing,
52 // including calculation of final-state momentum
53 // 20110519 M. Kelsey -- Drop unused "p2" variable from selectPairToTune()
54 // 20110801 M. Kelsey -- Use resize to avoid temporaries when copying from
55 // G4ReactionProductVector
56 // 20110922 M. Kelsey -- Follow G4InuclParticle::print(ostream&) migration,
57 // Add optional stream argument to printCollisionOutput
58 // 20121002 M. Kelsey -- Add strangeness calculation
59 
60 #include <algorithm>
61 
62 #include "G4CollisionOutput.hh"
63 #include "G4SystemOfUnits.hh"
64 #include "G4CascadParticle.hh"
65 #include "G4ParticleLargerEkin.hh"
66 #include "G4LorentzConvertor.hh"
67 #include "G4LorentzRotation.hh"
68 #include "G4LorentzVector.hh"
70 #include "G4ReactionProduct.hh"
71 #include "G4ThreeVector.hh"
72 
73 typedef std::vector<G4InuclElementaryParticle>::iterator particleIterator;
74 typedef std::vector<G4InuclNuclei>::iterator nucleiIterator;
75 
76 
78  : verboseLevel(0), eex_rest(0), on_shell(false) {
79  if (verboseLevel > 1)
80  G4cout << " >>> G4CollisionOutput::G4CollisionOutput" << G4endl;
81 }
82 
83 
85 {
86  if (this != &right) {
87  verboseLevel = right.verboseLevel;
88  outgoingParticles = right.outgoingParticles;
89  outgoingNuclei = right.outgoingNuclei;
90  theRecoilFragment = right.theRecoilFragment;
91  eex_rest = right.eex_rest;
92  on_shell = right.on_shell;
93  }
94  return *this;
95 }
96 
98  outgoingNuclei.clear();
99  outgoingParticles.clear();
101 }
102 
103 
104 // Merge two complete objects
105 
107  addOutgoingParticles(right.outgoingParticles);
108  addOutgoingNuclei(right.outgoingNuclei);
109  theRecoilFragment = right.theRecoilFragment;
110 }
111 
112 
113 // Append to lists
114 
115 void G4CollisionOutput::addOutgoingParticles(const std::vector<G4InuclElementaryParticle>& particles) {
116  outgoingParticles.insert(outgoingParticles.end(),
117  particles.begin(), particles.end());
118 }
119 
120 void G4CollisionOutput::addOutgoingNuclei(const std::vector<G4InuclNuclei>& nuclea) {
121  outgoingNuclei.insert(outgoingNuclei.end(), nuclea.begin(), nuclea.end());
122 }
123 
124 // These are primarily for G4IntraNucleiCascader internal checks
125 
127  addOutgoingParticle(cparticle.getParticle());
128 }
129 
130 void G4CollisionOutput::addOutgoingParticles(const std::vector<G4CascadParticle>& cparticles) {
131  for (unsigned i=0; i<cparticles.size(); i++)
132  addOutgoingParticle(cparticles[i]);
133 }
134 
135 // This comes from PreCompound de-excitation, both particles and nuclei
136 
138  if (!rproducts) return; // Sanity check, no error if null
139 
140  if (verboseLevel) {
141  G4cout << " >>> G4CollisionOutput::addOutgoingParticles(G4RPVector)"
142  << G4endl;
143  }
144 
145  G4ReactionProductVector::const_iterator j;
146  for (j=rproducts->begin(); j!=rproducts->end(); ++j) {
147  G4ParticleDefinition* pd = (*j)->GetDefinition();
149 
150  // FIXME: Momentum returned by value; extra copying here!
151  G4LorentzVector mom((*j)->GetMomentum(), (*j)->GetTotalEnergy());
152  mom /= GeV; // Convert from GEANT4 to Bertini units
153 
154  if (verboseLevel>1)
155  G4cout << " Processing " << pd->GetParticleName() << " (" << type
156  << "), momentum " << mom << " GeV" << G4endl;
157 
158  // Nucleons and nuclei are jumbled together in the list
159  // NOTE: Resize and set/fill avoid unnecessary temporary copies
160  if (type) {
161  outgoingParticles.resize(numberOfOutgoingParticles()+1);
162  outgoingParticles.back().fill(mom, pd, G4InuclParticle::PreCompound);
163 
164  if (verboseLevel>1) G4cout << outgoingParticles.back() << G4endl;
165  } else {
166  outgoingNuclei.resize(numberOfOutgoingNuclei()+1);
167  outgoingNuclei.back().fill(mom,pd->GetAtomicMass(),pd->GetAtomicNumber(),
169 
170  if (verboseLevel>1) G4cout << outgoingNuclei.back() << G4endl;
171  }
172  }
173 }
174 
175 
176 // Removing elements from lists by index
177 
179  if (index >= 0 && index < numberOfOutgoingParticles())
180  outgoingParticles.erase(outgoingParticles.begin()+(size_t)index);
181 }
182 
184  if (index >= 0 && index < numberOfOutgoingNuclei())
185  outgoingNuclei.erase(outgoingNuclei.begin()+(size_t)index);
186 }
187 
188 // Remove elements from list by reference, or by value comparison
189 
191  particleIterator pos =
192  std::find(outgoingParticles.begin(), outgoingParticles.end(), particle);
193  if (pos != outgoingParticles.end()) outgoingParticles.erase(pos);
194 }
195 
197  nucleiIterator pos =
198  std::find(outgoingNuclei.begin(), outgoingNuclei.end(), nuclei);
199  if (pos != outgoingNuclei.end()) outgoingNuclei.erase(pos);
200 }
201 
202 // Remove current recoil fragment from buffer
203 
205  static const G4Fragment emptyFragment; // Default ctor is all zeros
206  theRecoilFragment = emptyFragment;
207 }
208 
209 // Kinematics of final state, for recoil and conservation checks
210 
212  if (verboseLevel > 1)
213  G4cout << " >>> G4CollisionOutput::getTotalOutputMomentum" << G4endl;
214 
215  G4LorentzVector tot_mom;
216  G4int i(0);
217  for(i=0; i < G4int(outgoingParticles.size()); i++) {
218  tot_mom += outgoingParticles[i].getMomentum();
219  }
220  for(i=0; i < G4int(outgoingNuclei.size()); i++) {
221  tot_mom += outgoingNuclei[i].getMomentum();
222  }
223  tot_mom += theRecoilFragment.GetMomentum()/GeV; // Need Bertini units!
224 
225  return tot_mom;
226 }
227 
229  if (verboseLevel > 1)
230  G4cout << " >>> G4CollisionOutput::getTotalCharge" << G4endl;
231 
232  G4int charge = 0;
233  G4int i(0);
234  for(i=0; i < G4int(outgoingParticles.size()); i++) {
235  charge += G4int(outgoingParticles[i].getCharge());
236  }
237  for(i=0; i < G4int(outgoingNuclei.size()); i++) {
238  charge += G4int(outgoingNuclei[i].getCharge());
239  }
240  charge += theRecoilFragment.GetZ_asInt();
241 
242  return charge;
243 }
244 
246  if (verboseLevel > 1)
247  G4cout << " >>> G4CollisionOutput::getTotalBaryonNumber" << G4endl;
248 
249  G4int baryon = 0;
250  G4int i(0);
251  for(i=0; i < G4int(outgoingParticles.size()); i++) {
252  baryon += outgoingParticles[i].baryon();
253  }
254  for(i=0; i < G4int(outgoingNuclei.size()); i++) {
255  baryon += G4int(outgoingNuclei[i].getA());
256  }
257  baryon += theRecoilFragment.GetA_asInt();
258 
259  return baryon;
260 }
261 
263  if (verboseLevel > 1)
264  G4cout << " >>> G4CollisionOutput::getTotalStrangeness" << G4endl;
265 
266  G4int strange = 0;
267  G4int i(0);
268  for(i=0; i < G4int(outgoingParticles.size()); i++) {
269  strange += outgoingParticles[i].getStrangeness();
270  }
271 
272  return strange;
273 }
274 
275 
276 void G4CollisionOutput::printCollisionOutput(std::ostream& os) const {
277  os << " Output: " << G4endl
278  << " Outgoing Particles: " << outgoingParticles.size() << G4endl;
279 
280  G4int i(0);
281  for(i=0; i < G4int(outgoingParticles.size()); i++)
282  os << outgoingParticles[i] << G4endl;
283 
284  os << " Outgoing Nuclei: " << outgoingNuclei.size() << G4endl;
285  for(i=0; i < G4int(outgoingNuclei.size()); i++)
286  os << outgoingNuclei[i] << G4endl;
287 
288  if (theRecoilFragment.GetA() > 0) os << theRecoilFragment << G4endl;
289 }
290 
291 
292 // Boost particles and fragment to LAB -- "convertor" must already be configured
293 
295  if (verboseLevel > 1)
296  G4cout << " >>> G4CollisionOutput::boostToLabFrame" << G4endl;
297 
298  if (!outgoingParticles.empty()) {
299  particleIterator ipart = outgoingParticles.begin();
300  for(; ipart != outgoingParticles.end(); ipart++) {
301  ipart->setMomentum(boostToLabFrame(ipart->getMomentum(), convertor));
302  }
303 
304  std::sort(outgoingParticles.begin(), outgoingParticles.end(),
306  }
307 
308  if (!outgoingNuclei.empty()) {
309  nucleiIterator inuc = outgoingNuclei.begin();
310  for (; inuc != outgoingNuclei.end(); inuc++) {
311  inuc->setMomentum(boostToLabFrame(inuc->getMomentum(), convertor));
312  }
313  }
314 
315  if (theRecoilFragment.GetA() > 0.) {
316  theRecoilFragment.SetMomentum(boostToLabFrame(theRecoilFragment.GetMomentum(), convertor));
317  }
318 }
319 
320 // Pass by value to allow direct (internal) manipulation
323  const G4LorentzConvertor& convertor) const {
324  if (convertor.reflectionNeeded()) mom.setZ(-mom.z());
325  mom = convertor.rotate(mom);
326  mom = convertor.backToTheLab(mom);
327 
328  return mom;
329 }
330 
331 // Apply LorentzRotation to all particles in event
332 
334  if (verboseLevel > 1)
335  G4cout << " >>> G4CollisionOutput::rotateEvent" << G4endl;
336 
337  particleIterator ipart = outgoingParticles.begin();
338  for(; ipart != outgoingParticles.end(); ipart++)
339  ipart->setMomentum(ipart->getMomentum()*=rotate);
340 
341  nucleiIterator inuc = outgoingNuclei.begin();
342  for (; inuc != outgoingNuclei.end(); inuc++)
343  inuc->setMomentum(inuc->getMomentum()*=rotate);
344 
345  if (theRecoilFragment.GetA() > 0.) {
346  G4LorentzVector mom = theRecoilFragment.GetMomentum(); // Need copy
347  theRecoilFragment.SetMomentum(mom*=rotate);
348  }
349 
350 }
351 
352 
355  if (verboseLevel > 1)
356  G4cout << " >>> G4CollisionOutput::trivialize" << G4endl;
357 
358  reset(); // Discard existing output, replace with bullet/target
359 
360  if (G4InuclNuclei* nuclei_target = dynamic_cast<G4InuclNuclei*>(target)) {
361  outgoingNuclei.push_back(*nuclei_target);
362  } else {
363  G4InuclElementaryParticle* particle =
364  dynamic_cast<G4InuclElementaryParticle*>(target);
365  outgoingParticles.push_back(*particle);
366  }
367 
368  if (G4InuclNuclei* nuclei_bullet = dynamic_cast<G4InuclNuclei*>(bullet)) {
369  outgoingNuclei.push_back(*nuclei_bullet);
370  } else {
371  G4InuclElementaryParticle* particle =
372  dynamic_cast<G4InuclElementaryParticle*>(bullet);
373  outgoingParticles.push_back(*particle);
374  }
375 }
376 
377 
380  if (verboseLevel > 1)
381  G4cout << " >>> G4CollisionOutput::setOnShell" << G4endl;
382 
383  const G4double accuracy = 0.00001; // momentum concerves at the level of 10 keV
384 
385  on_shell = false;
386 
387  G4LorentzVector ini_mom = bullet->getMomentum();
388  G4LorentzVector momt = target->getMomentum();
390  if(verboseLevel > 2){
391  G4cout << " bullet momentum = " << ini_mom.e() <<", "<< ini_mom.x() <<", "<< ini_mom.y()<<", "<< ini_mom.z()<<G4endl;
392  G4cout << " target momentum = " << momt.e()<<", "<< momt.x()<<", "<< momt.y()<<", "<< momt.z()<<G4endl;
393  G4cout << " Fstate momentum = " << out_mom.e()<<", "<< out_mom.x()<<", "<< out_mom.y()<<", "<< out_mom.z()<<G4endl;
394  }
395 
396  ini_mom += momt;
397  G4LorentzVector mom_non_cons = ini_mom - out_mom;
398 
399  G4double pnc = mom_non_cons.rho();
400  G4double enc = mom_non_cons.e();
401 
403 
404  if(verboseLevel > 2) {
406  G4cout << " momentum non conservation: " << G4endl
407  << " e " << enc << " p " << pnc << G4endl
408  << " remaining exitation " << eex_rest << G4endl;
409  }
410 
411  if (std::fabs(enc) <= accuracy && pnc <= accuracy) {
412  on_shell = true;
413  return;
414  }
415 
416  // Adjust "last" particle's four-momentum to balance event
417  // ONLY adjust particles with sufficient e or p to remain physical!
418 
419  if (verboseLevel > 2) G4cout << " re-balancing four-momenta" << G4endl;
420 
421  G4int npart = outgoingParticles.size();
422  G4int nnuc = outgoingNuclei.size();
423 
424  if (npart > 0) {
425  for (G4int ip=npart-1; ip>=0; ip--) {
426  if (outgoingParticles[ip].getKineticEnergy()+enc > 0.) {
427  G4LorentzVector last_mom = outgoingParticles[ip].getMomentum();
428  last_mom += mom_non_cons;
429  outgoingParticles[ip].setMomentum(last_mom);
430  break;
431  }
432  }
433  } else if (nnuc > 0) {
434  for (G4int in=nnuc-1; in>=0; in--) {
435  if (outgoingNuclei[in].getKineticEnergy()+enc > 0.) {
436  G4LorentzVector last_mom = outgoingNuclei[in].getMomentum();
437  last_mom += mom_non_cons;
438  outgoingNuclei[in].setMomentum(last_mom);
439  break;
440  }
441  }
442  } else if (theRecoilFragment.GetA() > 0.) {
443  // NOTE: G4Fragment does not use Bertini units!
444  G4LorentzVector last_mom = theRecoilFragment.GetMomentum()/GeV;
445  if ((last_mom.e()-last_mom.m())+enc > 0.) {
446  last_mom += mom_non_cons;
447  theRecoilFragment.SetMomentum(last_mom*GeV);
448  }
449  }
450 
451  // Recompute momentum non-conservation parameters
452  out_mom = getTotalOutputMomentum();
453  mom_non_cons = ini_mom - out_mom;
454  pnc = mom_non_cons.rho();
455  enc = mom_non_cons.e();
456 
457  if(verboseLevel > 2){
459  G4cout << " momentum non conservation after (1): " << G4endl
460  << " e " << enc << " p " << pnc << G4endl;
461  }
462 
463  // Can energy be balanced just with nuclear excitation?
464  G4bool need_hard_tuning = true;
465 
466  G4double encMeV = mom_non_cons.e() / GeV; // Excitation below is in MeV
467  if (theRecoilFragment.GetA() > 0.) {
468  G4double eex = theRecoilFragment.GetExcitationEnergy();
469  if (eex > 0.0 && eex + encMeV >= 0.0) {
470  // NOTE: G4Fragment doesn't have function to change excitation energy
471  // ==> theRecoilFragment.SetExcitationEnergy(eex+encMeV);
472 
473  G4LorentzVector fragMom = theRecoilFragment.GetMomentum();
474  G4double newMass = fragMom.m() + encMeV; // .m() includes eex
475  fragMom.setVectM(fragMom.vect(), newMass);
476  need_hard_tuning = false;
477  }
478  } else if (outgoingNuclei.size() > 0) {
479  for (G4int i=0; i < G4int(outgoingNuclei.size()); i++) {
480  G4double eex = outgoingNuclei[i].getExitationEnergy();
481 
482  if(eex > 0.0 && eex + encMeV >= 0.0) {
483  outgoingNuclei[i].setExitationEnergy(eex+encMeV);
484  need_hard_tuning = false;
485  break;
486  }
487  }
488  if (need_hard_tuning && encMeV > 0.) {
489  outgoingNuclei[0].setExitationEnergy(encMeV);
490  need_hard_tuning = false;
491  }
492  }
493 
494  if (!need_hard_tuning) {
495  on_shell = true;
496  return;
497  }
498 
499  // Momentum (hard) tuning required for energy conservation
500  if (verboseLevel > 2)
501  G4cout << " trying hard (particle-pair) tuning" << G4endl;
502 
503  std::pair<std::pair<G4int, G4int>, G4int> tune_par = selectPairToTune(mom_non_cons.e());
504  std::pair<G4int, G4int> tune_particles = tune_par.first;
505  G4int mom_ind = tune_par.second;
506 
507  if(verboseLevel > 2) {
508  G4cout << " p1 " << tune_particles.first << " p2 " << tune_particles.second
509  << " ind " << mom_ind << G4endl;
510  }
511 
512  G4bool tuning_possible =
513  (tune_particles.first >= 0 && tune_particles.second >= 0 &&
514  mom_ind >= G4LorentzVector::X);
515 
516  if (!tuning_possible) {
517  if (verboseLevel > 2) G4cout << " tuning impossible " << G4endl;
518  return;
519  }
520 
521  G4LorentzVector mom1 = outgoingParticles[tune_particles.first].getMomentum();
522  G4LorentzVector mom2 = outgoingParticles[tune_particles.second].getMomentum();
523  G4double newE12 = mom1.e() + mom2.e() + mom_non_cons.e();
524  G4double R = 0.5 * (newE12 * newE12 + mom2.e() * mom2.e() - mom1.e() * mom1.e()) / newE12;
525  G4double Q = -(mom1[mom_ind] + mom2[mom_ind]) / newE12;
526  G4double UDQ = 1.0 / (Q * Q - 1.0);
527  G4double W = (R * Q + mom2[mom_ind]) * UDQ;
528  G4double V = (mom2.e() * mom2.e() - R * R) * UDQ;
529  G4double DET = W * W + V;
530 
531  if (DET < 0.0) {
532  if (verboseLevel > 2) G4cout << " DET < 0 " << G4endl;
533  return;
534  }
535 
536  // Tuning allowed only for non-negative determinant
537  G4double x1 = -(W + std::sqrt(DET));
538  G4double x2 = -(W - std::sqrt(DET));
539 
540  // choose the appropriate solution
541  G4bool xset = false;
542  G4double x = 0.0;
543 
544  if(mom_non_cons.e() > 0.0) { // x has to be > 0.0
545  if(x1 > 0.0) {
546  if(R + Q * x1 >= 0.0) {
547  x = x1;
548  xset = true;
549  };
550  };
551  if(!xset && x2 > 0.0) {
552  if(R + Q * x2 >= 0.0) {
553  x = x2;
554  xset = true;
555  };
556  };
557  } else {
558  if(x1 < 0.0) {
559  if(R + Q * x1 >= 0.) {
560  x = x1;
561  xset = true;
562  };
563  };
564  if(!xset && x2 < 0.0) {
565  if(R + Q * x2 >= 0.0) {
566  x = x2;
567  xset = true;
568  };
569  };
570  } // if(mom_non_cons.e() > 0.0)
571 
572  if(!xset) {
573  if(verboseLevel > 2)
574  G4cout << " no appropriate solution found " << G4endl;
575  return;
576  } // if(xset)
577 
578  // retune momentums
579  mom1[mom_ind] += x;
580  mom2[mom_ind] -= x;
581  outgoingParticles[tune_particles.first ].setMomentum(mom1);
582  outgoingParticles[tune_particles.second].setMomentum(mom2);
583  out_mom = getTotalOutputMomentum();
584  std::sort(outgoingParticles.begin(), outgoingParticles.end(), G4ParticleLargerEkin());
585  mom_non_cons = ini_mom - out_mom;
586  pnc = mom_non_cons.rho();
587  enc = mom_non_cons.e();
588 
589  on_shell = (std::fabs(enc) < accuracy || pnc < accuracy);
590 
591  if(verboseLevel > 2) {
592  G4cout << " momentum non conservation tuning: " << G4endl
593  << " e " << enc << " p " << pnc
594  << (on_shell?" success":" FAILURE") << G4endl;
595  }
596 }
597 
598 
599 // Returns excitation energy in GeV
600 
602  eex_rest = theRecoilFragment.GetExcitationEnergy() / GeV;
603 
604  for(G4int i=0; i < G4int(outgoingNuclei.size()); i++)
605  eex_rest += outgoingNuclei[i].getExitationEnergyInGeV();
606 }
607 
608 
609 std::pair<std::pair<G4int, G4int>, G4int>
610 G4CollisionOutput::selectPairToTune(G4double de) const {
611  if (verboseLevel > 2)
612  G4cout << " >>> G4CollisionOutput::selectPairToTune" << G4endl;
613 
614  std::pair<G4int, G4int> tup(-1, -1);
615  G4int i3 = -1;
616  std::pair<std::pair<G4int, G4int>, G4int> tuner(tup, i3); // Set invalid
617 
618  if (outgoingParticles.size() < 2) return tuner; // Nothing to do
619 
620  G4int ibest1 = -1;
621  G4int ibest2 = -1;
622  G4double pbest = 0.0;
623  G4double pcut = 0.3 * std::sqrt(1.88 * std::fabs(de));
624  G4double p1 = 0.0;
625 
626  for (G4int i = 0; i < G4int(outgoingParticles.size())-1; i++) {
627  G4LorentzVector mom1 = outgoingParticles[i].getMomentum();
628 
629  for (G4int j = i+1; j < G4int(outgoingParticles.size()); j++) {
630  G4LorentzVector mom2 = outgoingParticles[j].getMomentum();
631 
632  for (G4int l = G4LorentzVector::X; l<=G4LorentzVector::Z; l++) {
633  if (mom1[l]*mom2[l]<0.0) {
634  if (std::fabs(mom1[l])>pcut && std::fabs(mom2[l])>pcut) {
635  G4double psum = std::fabs(mom1[l]) + std::fabs(mom2[l]);
636 
637  if(psum > pbest) {
638  ibest1 = i;
639  ibest2 = j;
640  i3 = l;
641  p1 = mom1[l];
642  pbest = psum;
643  } // psum > pbest
644  } // mom1 and mom2 > pcut
645  } // mom1 ~ -mom2
646  } // for (G4int l ...
647  } // for (G4int j ...
648  } // for (G4int i ...
649 
650  if (i3 < 0) return tuner;
651 
652  tuner.second = i3; // Momentum axis for tuning
653 
654  // NOTE: Sign of de determines order for special case of p1==0.
655  if (de > 0.0) {
656  tuner.first.first = (p1>0.) ? ibest1 : ibest2;
657  tuner.first.second = (p1>0.) ? ibest2 : ibest1;
658  } else {
659  tuner.first.first = (p1<0.) ? ibest2 : ibest1;
660  tuner.first.second = (p1<0.) ? ibest1 : ibest2;
661  }
662 
663  return tuner;
664 }