OPAL (Object Oriented Parallel Accelerator Library)  2.2.0
OPAL
ParallelCyclotronTracker.cpp
Go to the documentation of this file.
1 // ------------------------------------------------------------------------
2 // $RCSfile: ParallelCyclotronTracker.cpp,v $
3 // ------------------------------------------------------------------------
4 // $Revision: 1.1 $initialLocalNum_m
5 // ------------------------------------------------------------------------
6 // Copyright: see Copyright.readme
7 // ------------------------------------------------------------------------
8 //
9 // Class: ParallelCyclotronTracker
10 // The class for tracking particles with 3D space charge in Cyclotrons and FFAs
11 //
12 // ------------------------------------------------------------------------
13 //
14 // $Date: 2007/10/17 04:00:08 $
15 // $Author: adelmann, yang, winklehner $
16 //
17 // ------------------------------------------------------------------------
18 
20 
21 #include <fstream>
22 #include <iostream>
23 #include <limits>
24 #include <vector>
25 #include <numeric>
26 
29 
32 #include "AbsBeamline/Corrector.h"
33 #include "AbsBeamline/Cyclotron.h"
35 #include "AbsBeamline/Degrader.h"
36 #include "AbsBeamline/Diagnostic.h"
37 #include "AbsBeamline/Drift.h"
39 #include "AbsBeamline/Lambertson.h"
40 #include "AbsBeamline/Offset.h"
41 #include "AbsBeamline/Marker.h"
42 #include "AbsBeamline/Monitor.h"
43 #include "AbsBeamline/Multipole.h"
44 #include "AbsBeamline/MultipoleT.h"
50 #include "AbsBeamline/Probe.h"
51 #include "AbsBeamline/RBend.h"
52 #include "AbsBeamline/Ring.h"
53 #include "AbsBeamline/RFCavity.h"
55 #include "AbsBeamline/SBend.h"
56 #include "AbsBeamline/SBend3D.h"
58 #include "AbsBeamline/Separator.h"
59 #include "AbsBeamline/Septum.h"
60 #include "AbsBeamline/Solenoid.h"
61 #include "AbsBeamline/Stripper.h"
65 
66 #include "Algorithms/Ctunes.h"
68 
69 #include "Beamlines/Beamline.h"
71 
72 #include "Elements/OpalBeamline.h"
73 
74 #include "Physics/Physics.h"
75 
77 #include "Utilities/Options.h"
78 
81 
83 #include "Structure/DataSink.h"
84 #include "Structure/LossDataSink.h"
85 
86 using Physics::pi;
87 using Physics::q_e;
88 
89 
90 constexpr double c_mmtns = Physics::c * 1.0e-6; // m/s --> mm/ns
91 
95 
96 extern Inform *gmsg;
97 
112  DataSink &ds,
113  const PartData &reference,
114  bool revBeam, bool revTrack,
115  int maxSTEPS, int timeIntegrator,
116  const int& numBunch,
117  const double& mbEta,
118  const double& mbPara,
119  const std::string& mbMode,
120  const std::string& mbBinning)
121  : Tracker(beamline, bunch, reference, revBeam, revTrack)
122  , bgf_m(nullptr)
123  , maxSteps_m(maxSTEPS)
124  , lastDumpedStep_m(0)
125  , myNode_m(Ippl::myNode())
126  , initialLocalNum_m(bunch->getLocalNum())
127  , initialTotalNum_m(bunch->getTotalNum())
128  , opalRing_m(nullptr)
129  , itsStepper_mp(nullptr)
130 {
131  itsBeamline = dynamic_cast<Beamline *>(beamline.clone());
132  itsDataSink = &ds;
133 
134  if ( numBunch > 1 ) {
135  mbHandler_m = std::unique_ptr<MultiBunchHandler>(
136  new MultiBunchHandler(bunch, numBunch, mbEta,
137  mbPara, mbMode, mbBinning)
138  );
139  }
140 
141  IntegrationTimer_m = IpplTimings::getTimer("Integration");
142  TransformTimer_m = IpplTimings::getTimer("Frametransform");
144  BinRepartTimer_m = IpplTimings::getTimer("Binaryrepart");
145  PluginElemTimer_m = IpplTimings::getTimer("PluginElements");
146  DelParticleTimer_m = IpplTimings::getTimer("DeleteParticles");
147 
148  // FIXME Change track command
149  if ( initialTotalNum_m == 1 ) {
151  } else if ( initialTotalNum_m == 2 ) {
152  mode_m = MODE::SEO;
153  } else if ( initialTotalNum_m > 2 ) {
155  } else
157 
158  if ( timeIntegrator == 0 ) {
160  } else if ( timeIntegrator == 1) {
162  } else if ( timeIntegrator == 2) {
164  } else
166 }
167 
173  if(bgf_m)
174  lossDs_m->save();
175  for(Component* component : myElements) {
176  delete(component);
177  }
178  for(auto fd : FieldDimensions) {
179  delete(fd);
180  }
181  delete itsBeamline;
182  // delete opalRing_m;
183 }
184 
185 
187  if(!bgf_m) return;
188 
189  Inform msg("bgf_main_collision_test ");
190 
195  Vector_t intecoords = 0.0;
196 
197  // This has to match the dT in the rk4 pusher
198  double dtime = itsBunch_m->getdT() * getHarmonicNumber();
199 
200  int triId = 0;
201  for(size_t i = 0; i < itsBunch_m->getLocalNum(); i++) {
202  int res = bgf_m->partInside(itsBunch_m->R[i], itsBunch_m->P[i], dtime, itsBunch_m->PType[i], itsBunch_m->Q[i], intecoords, triId);
203  //int res = bgf_m->partInside(itsBunch_m->R[i]*1.0e-3, itsBunch_m->P[i], dtime, itsBunch_m->PType[i], itsBunch_m->Q[i], intecoords, triId);
204  if(res >= 0) {
205  lossDs_m->addParticle(itsBunch_m->R[i], itsBunch_m->P[i],
206  itsBunch_m->ID[i], itsBunch_m->getT()*1e9,
208  itsBunch_m->Bin[i] = -1;
209  Inform gmsgALL("OPAL", INFORM_ALL_NODES);
210  gmsgALL << level4 << "* Particle " << itsBunch_m->ID[i] << " lost on boundary geometry" << endl;
211  }
212  }
213 }
214 
215 // only used for dumping into stat file
216 void ParallelCyclotronTracker::dumpAngle(const double& theta,
217  double& prevAzimuth,
218  double& azimuth,
219  const short& bunchNr)
220 {
221  if ( prevAzimuth < 0.0 ) { // only at first occurrence
222  double plus = 0.0;
223  if ( OpalData::getInstance()->inRestartRun() ) {
224  plus = 360.0 * (turnnumber_m - bunchNr);
225  }
226  azimuth = theta + plus;
227  } else {
228  double dtheta = theta - prevAzimuth;
229  if ( dtheta < 0 ) {
230  dtheta += 360.0;
231  }
232  if ( dtheta > 180 ) { // rotating clockwise, reduce angle
233  dtheta -= 360;
234  }
235  azimuth += dtheta;
236  }
237  prevAzimuth = theta;
238 }
239 
240 
242  // New OPAL 2.0: m --> mm
243  return 1000.0 * std::sqrt(meanR(0) * meanR(0) + meanR(1) * meanR(1));
244 }
245 
246 
248  const double& dt)
249 {
250  // the last element in dotP is the dot-product over all particles
251  std::vector<double> dotP(dl.size());
253 
254  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); ++i) {
255  dotP[itsBunch_m->bunchNum[i]] += dot(itsBunch_m->P[i], itsBunch_m->P[i]);
256  }
257 
258  allreduce(dotP.data(), dotP.size(), std::plus<double>());
259 
260  // dot-product over all particles
261  double sum = std::accumulate(dotP.begin(), dotP.end() - 1, 0);
262  dotP.back() = sum / double(itsBunch_m->getTotalNum());
263 
264  // bunch specific --> multi-bunches only
265  for (short b = 0; b < (short)dotP.size() - 1; ++b) {
266  dotP[b] /= double(itsBunch_m->getTotalNumPerBunch(b));
267  }
268 
269  } else if ( itsBunch_m->getLocalNum() == 0 ) {
270  // here we are in Options::GLOBAL mode
271  dotP[0] = 0.0;
272  } else {
273  // here we are in Options::GLOBAL mode
274  dotP[0] = dot(itsBunch_m->P[0], itsBunch_m->P[0]);
275  }
276 
277  for (size_t i = 0; i < dotP.size(); ++i) {
278  double const gamma = std::sqrt(1.0 + dotP[i]);
279  double const beta = std::sqrt(dotP[i]) / gamma;
280  dl[i] = c_mmtns * dt * 1.0e-3 * beta; // unit: m
281  }
282 }
283 
284 
285 
291 void ParallelCyclotronTracker::openFiles(size_t numFiles, std::string SfileName) {
292 
293  for (unsigned int i=0; i<numFiles; i++) {
294  std::string SfileName2 = SfileName;
295  if (i<=2) {
296  SfileName2 += std::string("-Angle" + std::to_string(int(i)) + ".dat");
297  }
298  else {
299  // for single Particle Mode, output after each turn, to define matched initial phase ellipse.
300  SfileName2 += std::string("-afterEachTurn.dat");
301  }
302 
303  outfTheta_m.emplace_back(new std::ofstream(SfileName2.c_str()));
304  outfTheta_m.back()->precision(8);
305  outfTheta_m.back()->setf(std::ios::scientific, std::ios::floatfield);
306  *outfTheta_m.back() << "# r [mm] beta_r*gamma "
307  << "theta [deg] beta_theta*gamma "
308  << "z [mm] beta_z*gamma" << std::endl;
309  }
310 }
311 
318  for (auto & file : outfTheta_m) {
319  file->close();
320  }
321 }
322 
328 
329  *gmsg << "* ----------------------------- Adding Ring ------------------------------ *" << endl;
330 
331  delete opalRing_m;
332 
333  opalRing_m = dynamic_cast<Ring*>(ring.clone());
334 
335  myElements.push_back(opalRing_m);
336 
338 
342 
343  if(referenceTheta <= -180.0 || referenceTheta > 180.0) {
344  throw OpalException("Error in ParallelCyclotronTracker::visitRing",
345  "PHIINIT is out of [-180, 180)!");
346  }
347 
348  referenceZ = 0.0;
349  referencePz = 0.0;
350 
353 
354  if(referencePtot < 0.0)
355  referencePt *= -1.0;
356 
358  cosRefTheta_m = cos(referenceTheta * Physics::deg2rad);
359 
360  double BcParameter[8] = {}; // zero initialise array
361 
363 
364  // Finally print some diagnostic
365  *gmsg << "* Initial beam radius = " << referenceR << " [mm] " << endl;
366  *gmsg << "* Initial gamma = " << itsReference.getGamma() << endl;
367  *gmsg << "* Initial beta = " << itsReference.getBeta() << endl;
368  *gmsg << "* Total reference momentum = " << referencePtot * 1000.0
369  << " [MCU]" << endl;
370  *gmsg << "* Reference azimuthal momentum = " << referencePt * 1000.0
371  << " [MCU]" << endl;
372  *gmsg << "* Reference radial momentum = " << referencePr * 1000.0
373  << " [MCU]" << endl;
374  *gmsg << "* " << opalRing_m->getSymmetry() << " fold field symmetry "
375  << endl;
376  *gmsg << "* Harmonic number h= " << opalRing_m->getHarmonicNumber() << " "
377  << endl;
378 }
379 
386 
387  *gmsg << "* -------------------------- Adding Cyclotron ---------------------------- *" << endl;
388 
389  cycl_m = dynamic_cast<Cyclotron *>(cycl.clone());
390  myElements.push_back(cycl_m);
391 
392  // Is this a Spiral Inflector Simulation? If yes, we'll give the user some
393  // useful information
395 
396  if(spiral_flag) {
397 
398  *gmsg << endl << "* This is a Spiral Inflector Simulation! This means the following:" << endl;
399  *gmsg << "* 1.) It is up to the user to provide appropriate geometry, electric and magnetic fields!" << endl;
400  *gmsg << "* (Use BANDRF type cyclotron and use RFMAPFN to load both magnetic" << endl;
401  *gmsg << "* and electric fields, setting SUPERPOSE to an array of TRUE values.)" << endl;
402  *gmsg << "* 2.) For high currents it is strongly recommended to use the SAAMG fieldsolver," << endl;
403  *gmsg << "* FFT does not give the correct results (boundary conditions are missing)." << endl;
404  *gmsg << "* 3.) The whole geometry will be meshed and used for the fieldsolver." << endl;
405  *gmsg << "* There will be no transformations of the bunch into a local frame und consequently," << endl;
406  *gmsg << "* the problem will be treated non-relativistically!" << endl;
407  *gmsg << "* (This is not an issue for spiral inflectors as they are typically < 100 keV/amu.)" << endl;
408  *gmsg << endl << "* Note: For now, multi-bunch mode (MBM) needs to be de-activated for spiral inflector" << endl;
409  *gmsg << "* and space charge needs to be solved every time-step. numBunch_m and scSolveFreq are reset." << endl;
410  if (isMultiBunch()) {
411  mbHandler_m = nullptr;
412  }
413  }
414 
415  // Fresh run (no restart):
416  if(!OpalData::getInstance()->inRestartRun()) {
417 
418  // Get reference values from cyclotron element
419  // For now, these are still stored in mm. should be the only ones. -DW
425 
426  if(referenceTheta <= -180.0 || referenceTheta > 180.0) {
427  throw OpalException("Error in ParallelCyclotronTracker::visitCyclotron",
428  "PHIINIT is out of [-180, 180)!");
429  }
430 
432 
433  // Calculate reference azimuthal (tangential) momentum from total-, z- and radial momentum:
434  float insqrt = referencePtot * referencePtot - \
436 
437  if(insqrt < 0) {
438 
439  if(insqrt > -1.0e-10) {
440 
441  referencePt = 0.0;
442 
443  } else {
444 
445  throw OpalException("Error in ParallelCyclotronTracker::visitCyclotron",
446  "Pt imaginary!");
447  }
448 
449  } else {
450 
451  referencePt = sqrt(insqrt);
452  }
453 
454  if(referencePtot < 0.0)
455  referencePt *= -1.0;
456  // End calculate referencePt
457 
458  // Restart a run:
459  } else {
460 
461  // If the user wants to save the restarted run in local frame,
462  // make sure the previous h5 file was local too
464  if (!previousH5Local) {
465  throw OpalException("Error in ParallelCyclotronTracker::visitCyclotron",
466  "You are trying a local restart from a global h5 file!");
467  }
468  // Else, if the user wants to save the restarted run in global frame,
469  // make sure the previous h5 file was global too
470  } else {
471  if (previousH5Local) {
472  throw OpalException("Error in ParallelCyclotronTracker::visitCyclotron",
473  "You are trying a global restart from a local h5 file!");
474  }
475  }
476 
477  // Adjust some of the reference variables from the h5 file
481  if(referenceTheta <= -180.0 || referenceTheta > 180.0) {
482  throw OpalException("Error in ParallelCyclotronTracker::visitCyclotron",
483  "PHIINIT is out of [-180, 180)!");
484  }
485  }
486 
488  cosRefTheta_m = cos(referenceTheta * Physics::deg2rad);
489 
490  *gmsg << endl;
491  *gmsg << "* Bunch global starting position:" << endl;
492  *gmsg << "* RINIT = " << referenceR << " [mm]" << endl;
493  *gmsg << "* PHIINIT = " << referenceTheta << " [deg]" << endl;
494  *gmsg << "* ZINIT = " << referenceZ << " [mm]" << endl;
495  *gmsg << endl;
496  *gmsg << "* Bunch global starting momenta:" << endl;
497  *gmsg << "* Initial gamma = " << itsReference.getGamma() << endl;
498  *gmsg << "* Initial beta = " << itsReference.getBeta() << endl;
499  *gmsg << "* Reference total momentum (beta * gamma) = " << referencePtot * 1000.0 << " [MCU]" << endl;
500  *gmsg << "* Reference azimuthal momentum (Pt) = " << referencePt * 1000.0 << " [MCU]" << endl;
501  *gmsg << "* Reference radial momentum (Pr) = " << referencePr * 1000.0 << " [MCU]" << endl;
502  *gmsg << "* Reference axial momentum (Pz) = " << referencePz * 1000.0 << " [MCU]" << endl;
503  *gmsg << endl;
504 
505  double sym = cycl_m->getSymmetry();
506  *gmsg << "* " << sym << "-fold field symmetry " << endl;
507 
508  // ckr: this just returned the default value as defined in Component.h
509  // double rff = cycl_m->getRfFrequ();
510  // *gmsg << "* Rf frequency= " << rff << " [MHz]" << endl;
511 
512  std::string fmfn = cycl_m->getFieldMapFN();
513  *gmsg << "* Field map file name = " << fmfn << " " << endl;
514 
515  std::string type = cycl_m->getCyclotronType();
516  *gmsg << "* Type of cyclotron = " << type << " " << endl;
517 
518  double rmin = cycl_m->getMinR();
519  double rmax = cycl_m->getMaxR();
520  *gmsg << "* Radial aperture = " << rmin << " ... " << rmax<<" [m] "<< endl;
521 
522  double zmin = cycl_m->getMinZ();
523  double zmax = cycl_m->getMaxZ();
524  *gmsg << "* Vertical aperture = " << zmin << " ... " << zmax<<" [m]"<< endl;
525 
526  double h = cycl_m->getCyclHarm();
527  *gmsg << "* Number of trimcoils = " << cycl_m->getNumberOfTrimcoils() << endl;
528  *gmsg << "* Harmonic number h = " << h << " " << endl;
529 
541  int fieldflag = cycl_m->getFieldFlag(type);
542 
543  // Read in cyclotron field maps (midplane + 3D fields if desired).
544  cycl_m->initialise(itsBunch_m, fieldflag, cycl_m->getBScale());
545 
546  double BcParameter[8] = {};
547 
548  BcParameter[0] = 0.001 * cycl_m->getRmin();
549  BcParameter[1] = 0.001 * cycl_m->getRmax();
550 
551  // Store inner radius and outer radius of cyclotron field map in the list
553 }
554 
560  *gmsg << "In BeamBeam tracker is missing " << endl;
561 }
562 
563 
565  *gmsg << "* ------------------------------ Beam Stripping ------------------------------" << endl;
566 
567  BeamStripping* elptr = dynamic_cast<BeamStripping *>(bstp.clone());
568  myElements.push_back(elptr);
569 
570  double BcParameter[8] = {};
571 
572  if(elptr->getPressureMapFN() == "") {
573  double pressure = elptr->getPressure();
574  *gmsg << "* Pressure = " << pressure << " [mbar]" << endl;
575  BcParameter[0] = pressure;
576  }
577 
578  double temperature = elptr->getTemperature();
579  *gmsg << "* Temperature = " << temperature << " [K]" << endl;
580 
581  std::string gas = elptr->getResidualGas();
582  *gmsg << "* Residual gas = " << gas << endl;
583 
584  bool stop = elptr->getStop();
585  *gmsg << std::boolalpha << "* Particles stripped will be deleted after interaction -> " << stop << endl;
586 
587  elptr->initialise(itsBunch_m, elptr->getPScale());
588 
589  BcParameter[1] = temperature;
590  BcParameter[2] = stop;
591 
592  buildupFieldList(BcParameter, ElementBase::BEAMSTRIPPING, elptr);
593 
594 }
595 
602 
603  *gmsg << "* ------------------------------ Collimator ------------------------------" << endl;
604 
605  CCollimator* elptr = dynamic_cast<CCollimator *>(coll.clone());
606  myElements.push_back(elptr);
607 
608  double xstart = elptr->getXStart();
609  *gmsg << "* Xstart = " << xstart << " [mm]" << endl;
610 
611  double xend = elptr->getXEnd();
612  *gmsg << "* Xend = " << xend << " [mm]" << endl;
613 
614  double ystart = elptr->getYStart();
615  *gmsg << "* Ystart = " << ystart << " [mm]" << endl;
616 
617  double yend = elptr->getYEnd();
618  *gmsg << "* Yend = " << yend << " [mm]" << endl;
619 
620  double zstart = elptr->getZStart();
621  *gmsg << "* Zstart = " << zstart << " [mm]" << endl;
622 
623  double zend = elptr->getZEnd();
624  *gmsg << "* Zend = " << zend << " [mm]" << endl;
625 
626  double width = elptr->getWidth();
627  *gmsg << "* Width = " << width << " [mm]" << endl;
628 
629  elptr->initialise(itsBunch_m);
630 
631  double BcParameter[8] = {};
632 
633  BcParameter[0] = 0.001 * xstart ;
634  BcParameter[1] = 0.001 * xend;
635  BcParameter[2] = 0.001 * ystart ;
636  BcParameter[3] = 0.001 * yend;
637  BcParameter[4] = 0.001 * width ;
638 
639  buildupFieldList(BcParameter, ElementBase::CCOLLIMATOR, elptr);
640 }
641 
648  *gmsg << "In Corrector; L= " << corr.getElementLength() << endl;
649  myElements.push_back(dynamic_cast<Corrector *>(corr.clone()));
650 }
651 
658  *gmsg << "In Degrader; L= " << deg.getElementLength() << endl;
659  myElements.push_back(dynamic_cast<Degrader *>(deg.clone()));
660 
661 }
662 
663 
670  *gmsg << "In Diagnostic; L= " << diag.getElementLength() << endl;
671  myElements.push_back(dynamic_cast<Diagnostic *>(diag.clone()));
672 }
673 
680  *gmsg << "In drift L= " << drift.getElementLength() << endl;
681  myElements.push_back(dynamic_cast<Drift *>(drift.clone()));
682 }
683 
690 
691 }
692 
699  *gmsg << "In Lambertson; L= " << lamb.getElementLength() << endl;
700  myElements.push_back(dynamic_cast<Lambertson *>(lamb.clone()));
701 }
702 
704  if (opalRing_m == NULL)
705  throw OpalException(
706  "ParallelCylcotronTracker::visitOffset",
707  "Attempt to place an offset when Ring not defined");
708  Offset* offNonConst = const_cast<Offset*>(&off);
709  offNonConst->updateGeometry(opalRing_m->getNextPosition(),
712 }
713 
720  // *gmsg << "In Marker; L= " << marker.getElementLength() << endl;
721  myElements.push_back(dynamic_cast<Marker *>(marker.clone()));
722  // Do nothing.
723 }
724 
731  // *gmsg << "In Monitor; L= " << corr.getElementLength() << endl;
732  myElements.push_back(dynamic_cast<Monitor *>(corr.clone()));
733  // applyDrift(flip_s * corr.getElementLength());
734 }
735 
742  *gmsg << "In Multipole; L= " << mult.getElementLength() << " however the element is missing " << endl;
743  myElements.push_back(dynamic_cast<Multipole *>(mult.clone()));
744 }
745 
752  *gmsg << "Adding MultipoleT" << endl;
753  if (opalRing_m != NULL) {
754  opalRing_m->appendElement(multT);
755  } else {
756  throw OpalException("ParallelCyclotronTracker::visitMultipoleT",
757  "Need to define a RINGDEFINITION to use MultipoleT element");
758  }
759  myElements.push_back(dynamic_cast<MultipoleT *>(multT.clone()));
760 }
761 
768  *gmsg << "Adding MultipoleTStraight" << endl;
769  if (opalRing_m != NULL) {
770  opalRing_m->appendElement(multTstraight);
771  } else {
772  throw OpalException("ParallelCyclotronTracker::visitMultipoleTStraight",
773  "Need to define a RINGDEFINITION to use MultipoleTStraight element");
774  }
775  myElements.push_back(dynamic_cast<MultipoleTStraight *>(multTstraight.clone()));
776 }
777 
784  *gmsg << "Adding MultipoleTCurvedConstRadius" << endl;
785  if (opalRing_m != NULL) {
786  opalRing_m->appendElement(multTccurv);
787  } else {
788  throw OpalException("ParallelCyclotronTracker::visitMultipoleTCurvedConstRadius",
789  "Need to define a RINGDEFINITION to use MultipoleTCurvedConstRadius element");
790  }
791  myElements.push_back(dynamic_cast<MultipoleTCurvedConstRadius *>(multTccurv.clone()));
792 }
793 
800  *gmsg << "Adding MultipoleTCurvedVarRadius" << endl;
801  if (opalRing_m != NULL) {
802  opalRing_m->appendElement(multTvcurv);
803  } else {
804  throw OpalException("ParallelCyclotronTracker::visitMultipoleTCurvedVarRadius",
805  "Need to define a RINGDEFINITION to use MultipoleTCurvedVarRadius element");
806  }
807  myElements.push_back(dynamic_cast<MultipoleTCurvedVarRadius *>(multTvcurv.clone()));
808 }
809 
816  *gmsg << "* ------------------------------ Probe ------------------------------" << endl;
817  Probe *elptr = dynamic_cast<Probe *>(prob.clone());
818  myElements.push_back(elptr);
819 
820  *gmsg << "* Name = " << elptr->getName() << endl;
821 
822  double xstart = elptr->getXStart();
823  *gmsg << "* XStart = " << xstart << " [mm]" << endl;
824 
825  double xend = elptr->getXEnd();
826  *gmsg << "* XEnd = " << xend << " [mm]" << endl;
827 
828  double ystart = elptr->getYStart();
829  *gmsg << "* YStart = " << ystart << " [mm]" << endl;
830 
831  double yend = elptr->getYEnd();
832  *gmsg << "* YEnd = " << yend << " [mm]" << endl;
833 
834  // initialise, do nothing
835  elptr->initialise(itsBunch_m);
836 
837  double BcParameter[8] = {};
838 
839  BcParameter[0] = 0.001 * xstart ;
840  BcParameter[1] = 0.001 * xend;
841  BcParameter[2] = 0.001 * ystart ;
842  BcParameter[3] = 0.001 * yend;
843  BcParameter[4] = 0.001 ; // width
844 
845  // store probe parameters in the list
846  buildupFieldList(BcParameter, ElementBase::PROBE, elptr);
847 }
848 
855  *gmsg << "In RBend; L= " << bend.getElementLength() << " however the element is missing " << endl;
856  myElements.push_back(dynamic_cast<RBend *>(bend.clone()));
857 }
858 
860  *gmsg << "Adding SBend3D" << endl;
861  if (opalRing_m != NULL)
862  opalRing_m->appendElement(bend);
863  else
864  throw OpalException("ParallelCyclotronTracker::visitSBend3D",
865  "Need to define a RINGDEFINITION to use SBend3D element");
866 }
867 
869  *gmsg << "Adding ScalingFFAMagnet" << endl;
870  if (opalRing_m != NULL) {
871  opalRing_m->appendElement(bend);
872  } else {
873  throw OpalException("ParallelCyclotronTracker::visitScalingFFAMagnet",
874  "Need to define a RINGDEFINITION to use ScalingFFAMagnet element");
875  }
876 }
877 
879  *gmsg << "Adding Variable RF Cavity" << endl;
880  if (opalRing_m != NULL)
882  else
883  throw OpalException("ParallelCyclotronTracker::visitVariableRFCavity",
884  "Need to define a RINGDEFINITION to use VariableRFCavity element");
885 }
886 
889  *gmsg << "Adding Variable RF Cavity with Fringe Field" << endl;
890  if (opalRing_m != NULL)
891  opalRing_m->appendElement(cav);
892  else
893  throw OpalException("ParallelCyclotronTracker::visitVariableRFCavityFringeField",
894  "Need to define a RINGDEFINITION to use VariableRFCavity element");
895 }
896 
898  *gmsg << "Adding Vertical FFA Magnet" << endl;
899  if (opalRing_m != NULL)
901  else
902  throw OpalException("ParallelCyclotronTracker::visitVerticalFFAMagnet",
903  "Need to define a RINGDEFINITION to use VerticalFFAMagnet element");
904 }
905 
912 
913  *gmsg << "* ------------------------------ RFCavity ------------------------------" << endl;
914 
915  RFCavity *elptr = dynamic_cast<RFCavity *>(as.clone());
916  myElements.push_back(elptr);
917 
918  if((elptr->getComponentType() != "SINGLEGAP") && (elptr->getComponentType() != "DOUBLEGAP")) {
919  *gmsg << (elptr->getComponentType()) << endl;
920  throw OpalException("ParallelCyclotronTracker::visitRFCavity",
921  "The ParallelCyclotronTracker can only play with cyclotron type RF system currently ...");
922  }
923 
924  double rmin = elptr->getRmin();
925  *gmsg << "* Minimal radius of cavity= " << rmin << " [mm]" << endl;
926 
927  double rmax = elptr->getRmax();
928  *gmsg << "* Maximal radius of cavity= " << rmax << " [mm]" << endl;
929 
930  double rff = elptr->getCycFrequency();
931  *gmsg << "* RF frequency (2*pi*f)= " << rff << " [rad/s]" << endl;
932 
933  std::string fmfn = elptr->getFieldMapFN();
934  *gmsg << "* RF Field map file name= " << fmfn << endl;
935 
936  double angle = elptr->getAzimuth();
937  *gmsg << "* Cavity azimuth position= " << angle << " [deg] " << endl;
938 
939  double gap = elptr->getGapWidth();
940  *gmsg << "* Cavity gap width= " << gap << " [mm] " << endl;
941 
942  double pdis = elptr->getPerpenDistance();
943  *gmsg << "* Cavity Shift distance= " << pdis << " [mm] " << endl;
944 
945  double phi0 = elptr->getPhi0();
946  *gmsg << "* Initial RF phase (t=0)= " << phi0 << " [deg] " << endl;
947 
948  /*
949  Setup time dependence and in case of no
950  timedependence use a polynom with a_0 = 1 and a_k = 0, k = 1,2,3.
951  */
952 
953  std::shared_ptr<AbstractTimeDependence> freq_atd = nullptr;
954  std::shared_ptr<AbstractTimeDependence> ampl_atd = nullptr;
955  std::shared_ptr<AbstractTimeDependence> phase_atd = nullptr;
956 
957  dvector_t unityVec;
958  unityVec.push_back(1.);
959  unityVec.push_back(0.);
960  unityVec.push_back(0.);
961  unityVec.push_back(0.);
962 
963  if (elptr->getFrequencyModelName() != "") {
965  *gmsg << "* Variable frequency RF Model name " << elptr->getFrequencyModelName() << endl;
966  }
967  else
968  freq_atd = std::shared_ptr<AbstractTimeDependence>(new PolynomialTimeDependence(unityVec));
969 
970  if (elptr->getAmplitudeModelName() != "") {
972  *gmsg << "* Variable amplitude RF Model name " << elptr->getAmplitudeModelName() << endl;
973  }
974  else
975  ampl_atd = std::shared_ptr<AbstractTimeDependence>(new PolynomialTimeDependence(unityVec));
976 
977  if (elptr->getPhaseModelName() != "") {
979  *gmsg << "* Variable phase RF Model name " << elptr->getPhaseModelName() << endl;
980  }
981  else
982  phase_atd = std::shared_ptr<AbstractTimeDependence>(new PolynomialTimeDependence(unityVec));
983 
984  // read cavity voltage profile data from file.
985  elptr->initialise(itsBunch_m, freq_atd, ampl_atd, phase_atd);
986 
987  double BcParameter[8] = {};
988 
989  BcParameter[0] = 0.001 * rmin;
990  BcParameter[1] = 0.001 * rmax;
991  BcParameter[2] = 0.001 * pdis;
992  BcParameter[3] = angle;
993 
994  buildupFieldList(BcParameter, ElementBase::RFCAVITY, elptr);
995 }
996 
1003  *gmsg << "In RFQuadrupole; L = " << rfq.getElementLength() << " however the element is missing " << endl;
1004  myElements.push_back(dynamic_cast<RFQuadrupole *>(rfq.clone()));
1005 }
1006 
1013  *gmsg << "In SBend; L = " << bend.getElementLength() << " however the element is missing " << endl;
1014  myElements.push_back(dynamic_cast<SBend *>(bend.clone()));
1015 }
1016 
1023  *gmsg << "In Separator L= " << sep.getElementLength() << " however the element is missing " << endl;
1024  myElements.push_back(dynamic_cast<Separator *>(sep.clone()));
1025 }
1026 
1033 
1034  *gmsg << endl << "* ------------------------------ Septum ------------------------------- *" << endl;
1035 
1036  Septum *elptr = dynamic_cast<Septum *>(sept.clone());
1037  myElements.push_back(elptr);
1038 
1039  double xstart = elptr->getXStart();
1040  *gmsg << "* XStart = " << xstart << " [mm]" << endl;
1041 
1042  double xend = elptr->getXEnd();
1043  *gmsg << "* XEnd = " << xend << " [mm]" << endl;
1044 
1045  double ystart = elptr->getYStart();
1046  *gmsg << "* YStart = " << ystart << " [mm]" << endl;
1047 
1048  double yend = elptr->getYEnd();
1049  *gmsg << "* YEnd = " << yend << " [mm]" << endl;
1050 
1051  double width = elptr->getWidth();
1052  *gmsg << "* Width = " << width << " [mm]" << endl;
1053 
1054 
1055  // initialise, do nothing
1056  elptr->initialise(itsBunch_m);
1057 
1058  double BcParameter[8] = {};
1059 
1060  BcParameter[0] = 0.001 * xstart ;
1061  BcParameter[1] = 0.001 * xend;
1062  BcParameter[2] = 0.001 * ystart ;
1063  BcParameter[3] = 0.001 * yend;
1064  BcParameter[4] = 0.001 * width ;
1065 
1066  // store septum parameters in the list
1067  buildupFieldList(BcParameter, ElementBase::SEPTUM, elptr);
1068 }
1069 
1076  myElements.push_back(dynamic_cast<Solenoid *>(solenoid.clone()));
1077  Component *elptr = *(--myElements.end());
1078  if(!elptr->hasAttribute("ELEMEDGE")) {
1079  *gmsg << "Solenoid: no position of the element given!" << endl;
1080  return;
1081  }
1082 }
1083 
1090 
1091  //*gmsg << "ParallelPlate: not in use in ParallelCyclotronTracker!" << endl;
1092 
1093  //buildupFieldList(startField, endField, elptr);
1094 
1095 }
1096 
1103  // Do nothing here.
1104 }
1105 
1113 
1114  *gmsg << "* ------------------------------ Stripper ------------------------------" << endl;
1115 
1116  Stripper *elptr = dynamic_cast<Stripper *>(stripper.clone());
1117  myElements.push_back(elptr);
1118 
1119  *gmsg << "* Name = " << elptr->getName() << endl;
1120 
1121  double xstart = elptr->getXStart();
1122  *gmsg << "* XStart = " << xstart << " [mm]" << endl;
1123 
1124  double xend = elptr->getXEnd();
1125  *gmsg << "* XEnd = " << xend << " [mm]" << endl;
1126 
1127  double ystart = elptr->getYStart();
1128  *gmsg << "* YStart = " << ystart << " [mm]" << endl;
1129 
1130  double yend = elptr->getYEnd();
1131  *gmsg << "* YEnd = " << yend << " [mm]" << endl;
1132 
1133  double opcharge = elptr->getOPCharge();
1134  *gmsg << "* Charge of outcoming particle = +e * " << opcharge << endl;
1135 
1136  double opmass = elptr->getOPMass();
1137  *gmsg << "* Mass of the outcoming particle = " << opmass << " [GeV/c^2]" << endl;
1138 
1139  bool stop = elptr->getStop();
1140  *gmsg << std::boolalpha << "* Particles stripped will be deleted after interaction -> " << stop << endl;
1141 
1142  elptr->initialise(itsBunch_m);
1143 
1144  double BcParameter[8] = {};
1145 
1146  BcParameter[0] = 0.001 * xstart ;
1147  BcParameter[1] = 0.001 * xend;
1148  BcParameter[2] = 0.001 * ystart ;
1149  BcParameter[3] = 0.001 * yend;
1150  BcParameter[4] = 0.001; // width
1151  BcParameter[5] = opcharge;
1152  BcParameter[6] = opmass;
1153 
1154  buildupFieldList(BcParameter, ElementBase::STRIPPER, elptr);
1155 }
1156 
1164 void ParallelCyclotronTracker::buildupFieldList(double BcParameter[], ElementBase::ElementType elementType, Component *elptr) {
1165  beamline_list::iterator sindex;
1166 
1167  type_pair *localpair = new type_pair();
1168  localpair->first = elementType;
1169 
1170  for(int i = 0; i < 8; i++)
1171  *(((localpair->second).first) + i) = *(BcParameter + i);
1172 
1173  (localpair->second).second = elptr;
1174 
1175  // always put cyclotron as the first element in the list.
1176  if(elementType == ElementBase::RING || elementType == ElementBase::CYCLOTRON) {
1177  sindex = FieldDimensions.begin();
1178  } else {
1179  sindex = FieldDimensions.end();
1180  }
1181  FieldDimensions.insert(sindex, localpair);
1182 
1183 }
1184 
1191  const FlaggedBeamline* fbl = static_cast<const FlaggedBeamline*>(&bl);
1192  fbl->iterate(*this, false);//*dynamic_cast<BeamlineVisitor *>(this), false);
1193 }
1194 
1196  int nlp = itsBunch_m->getLocalNum();
1197  int minnlp = 0;
1198  int maxnlp = 111111;
1199  reduce(nlp, minnlp, OpMinAssign());
1200  reduce(nlp, maxnlp, OpMaxAssign());
1201  *gmsg << s << " min local particle number " << minnlp << " max local particle number: " << maxnlp << endl;
1202 }
1203 
1209 
1210  /*
1211  Initialize common variables and structures
1212  for the integrators
1213  */
1214  if (OpalData::getInstance()->inRestartRun()) {
1216  }
1217 
1218  step_m = 0;
1219  restartStep0_m = 0;
1220  turnnumber_m = 1;
1221  azimuth_m = -1.0;
1222  prevAzimuth_m = -1.0;
1223 
1224  // Record how many bunches have already been injected. ONLY FOR MPM
1225  if (isMultiBunch())
1226  mbHandler_m->setNumBunch(itsBunch_m->getNumBunch());
1227 
1228  itsBeamline->accept(*this);
1229  if (opalRing_m != NULL)
1230  opalRing_m->lockRing();
1231 
1232  // Display the selected elements
1233  *gmsg << "* ---------------------------------------------------" << endl;
1234  *gmsg << "* The selected Beam line elements are :" << endl;
1235 
1236  for(auto fd : FieldDimensions) {
1237  ElementBase::ElementType type = fd->first;
1238  *gmsg << "* -> " << ElementBase::getTypeString(type) << endl;
1239  if(type == ElementBase::RFCAVITY) {
1240  RFCavity* cav = static_cast<RFCavity*>((fd->second).second);
1241  CavityCrossData ccd = {cav, cav->getSinAzimuth(), cav->getCosAzimuth(), cav->getPerpenDistance() * 0.001};
1242  cavCrossDatas_m.push_back(ccd);
1243  } else if( type == ElementBase::CCOLLIMATOR ||
1244  type == ElementBase::PROBE ||
1245  type == ElementBase::SEPTUM ||
1246  type == ElementBase::STRIPPER) {
1247  PluginElement* element = static_cast<PluginElement*>((fd->second).second);
1248  pluginElements_m.push_back(element);
1249  }
1250  }
1251 
1252  *gmsg << "* ---------------------------------------------------" << endl;
1253 
1254  // Get BoundaryGeometry that is already initialized
1256  if (bgf_m)
1257  lossDs_m = std::unique_ptr<LossDataSink>(new LossDataSink("GEOM",!Options::asciidump));
1258 
1259  // External field arrays for dumping
1260  for(int k = 0; k < 2; k++)
1261  FDext_m[k] = Vector_t(0.0, 0.0, 0.0);
1262 
1263  extE_m = Vector_t(0.0, 0.0, 0.0);
1264  extB_m = Vector_t(0.0, 0.0, 0.0);
1265  DumpFields::writeFields((((*FieldDimensions.begin())->second).second));
1266  DumpEMFields::writeFields((((*FieldDimensions.begin())->second).second));
1267 
1269  this,
1270  std::placeholders::_1,
1271  std::placeholders::_2,
1272  std::placeholders::_3,
1273  std::placeholders::_4);
1274 
1275  switch ( stepper_m )
1276  {
1278  *gmsg << "* 4th order Runge-Kutta integrator" << endl;
1279  itsStepper_mp.reset(new RK4<function_t>(func));
1280  break;
1282  *gmsg << "* 2nd order Leap-Frog integrator" << endl;
1283  itsStepper_mp.reset(new LF2<function_t>(func));
1284  break;
1286  *gmsg << "* Multiple time stepping (MTS) integrator" << endl;
1287  break;
1289  default:
1290  itsStepper_mp.reset(nullptr);
1291  throw OpalException("ParallelCyclotronTracker::execute",
1292  "Invalid name of TIMEINTEGRATOR in Track command");
1293  }
1294 
1296  MtsTracker();
1297  else
1298  GenericTracker();
1299 
1300 
1301  *gmsg << "* ----------------------------------------------- *" << endl;
1302  *gmsg << "* Finalizing i.e. write data and close files :" << endl;
1303  for(auto fd : FieldDimensions) {
1304  ((fd->second).second)->finalise();
1305  }
1306  *gmsg << "* ----------------------------------------------- *" << endl;
1307 }
1308 
1309 
1311  /*
1312  * variable unit meaning
1313  * ------------------------------------------------
1314  * t [ns] time
1315  * dt [ns] time step
1316  * oldReferenceTheta [rad] azimuth angle
1317  * itsBunch_m->R [m] particle position
1318  *
1319  */
1320 
1321  double t = 0, dt = 0, oldReferenceTheta = 0;
1322  std::tie(t, dt, oldReferenceTheta) = initializeTracking_m();
1323 
1324  int const numSubsteps = std::max(Options::mtsSubsteps, 1);
1325 
1326  *gmsg << "MTS: Number of substeps per step is " << numSubsteps << endl;
1327 
1328  double const dt_inner = dt / double(numSubsteps);
1329 
1330  *gmsg << "MTS: The inner time step is therefore " << dt_inner << " [ns]" << endl;
1331 
1332 // int SteptoLastInj = itsBunch_m->getSteptoLastInj();
1333 
1334  bool flagTransition = false; // flag to determine when to transit from single-bunch to multi-bunches mode
1335 
1336 
1337  *gmsg << "* ---------------------------- Start tracking ----------------------------" << endl;
1338 
1339  if ( itsBunch_m->hasFieldSolver() )
1341 
1342  for(; (step_m < maxSteps_m) && (itsBunch_m->getTotalNum()>0); step_m++) {
1343 
1344  bool finishedTurn = false;
1345 
1346  if(step_m % Options::sptDumpFreq == 0) {
1348  }
1349 
1350  Ippl::Comm->barrier();
1351 
1352  // First half kick from space charge force
1353  if(itsBunch_m->hasFieldSolver()) {
1354  kick(0.5 * dt);
1355  }
1356 
1357  // Substeps for external field integration
1358  for(int n = 0; n < numSubsteps; ++n)
1359  borisExternalFields(dt_inner);
1360 
1361 
1362  // bunch injection
1363  // TODO: Where is correct location for this piece of code? Beginning/end of step? Before field solve?
1364  injectBunch(flagTransition);
1365 
1366  if ( itsBunch_m->hasFieldSolver() ) {
1368  } else {
1369  // if field solver is not available , only update bunch, to transfer particles between nodes if needed,
1370  // reset parameters such as LocalNum, initialTotalNum_m.
1371  // INFOMSG("No space charge Effects are included!"<<endl;);
1372  if((step_m % Options::repartFreq * 100) == 0) { //TODO: why * 100?
1373  Vector_t const meanP = calcMeanP();
1374  double const phi = calculateAngle(meanP(0), meanP(1)) - 0.5 * pi;
1375  Vector_t const meanR = calcMeanR();
1376  globalToLocal(itsBunch_m->R, phi, meanR);
1378  repartition();
1379  localToGlobal(itsBunch_m->R, phi, meanR);
1380  }
1381  }
1382 
1383  // Second half kick from space charge force
1384  if(itsBunch_m->hasFieldSolver())
1385  kick(0.5 * dt);
1386 
1387  // recalculate bingamma and reset the BinID for each particles according to its current gamma
1388  if (isMultiBunch() && (step_m % Options::rebinFreq == 0)) {
1389  mbHandler_m->updateParticleBins(itsBunch_m);
1390  }
1391 
1392  // dump some data after one push in single particle tracking
1393  if ( mode_m == MODE::SINGLE ) {
1394 
1395  unsigned int i = 0;
1396 
1397  double temp_meanTheta = calculateAngle2(itsBunch_m->R[i](0),
1398  itsBunch_m->R[i](1)); // [-pi, pi]
1399 
1401  temp_meanTheta, finishedTurn);
1402 
1404  oldReferenceTheta, temp_meanTheta);
1405 
1406  oldReferenceTheta = temp_meanTheta;
1407  } else if ( mode_m == MODE::BUNCH ) {
1408  // both for single bunch and multi-bunch
1409  // avoid dump at the first step
1410  // finishedTurn has not been changed in first push
1411  if( isTurnDone() ) {
1412  ++turnnumber_m;
1413  finishedTurn = true;
1414 
1415  *gmsg << endl;
1416  *gmsg << "*** Finished turn " << turnnumber_m - 1
1417  << ", Total number of live particles: "
1418  << itsBunch_m->getTotalNum() << endl;
1419  }
1420 
1421  // Recalculate bingamma and reset the BinID for each particles according to its current gamma
1422  if (isMultiBunch() && (step_m % Options::rebinFreq == 0)) {
1423  mbHandler_m->updateParticleBins(itsBunch_m);
1424  }
1425  }
1426 
1427  // printing + updating bunch parameters + updating t
1428  update_m(t, dt, finishedTurn);
1429 
1430 
1431  }
1432 
1433  // Some post-integration stuff
1434  *gmsg << endl;
1435  *gmsg << "* ---------------------------- DONE TRACKING PARTICLES -------------------------------- * " << endl;
1436 
1437 
1438  //FIXME
1439  dvector_t Ttime = dvector_t();
1440  dvector_t Tdeltr = dvector_t();
1441  dvector_t Tdeltz = dvector_t();
1442  ivector_t TturnNumber = ivector_t();
1443 
1444  finalizeTracking_m(Ttime, Tdeltr, Tdeltz, TturnNumber);
1445 }
1446 
1447 
1453  /*
1454  * variable unit meaning
1455  * ------------------------------------------------
1456  * t [ns] time
1457  * dt [ns] time step
1458  * oldReferenceTheta [rad] azimuth angle
1459  * itsBunch_m->R [m] particle position
1460  *
1461  */
1462  // Generic Tracker that has three modes defined by timeIntegrator_m:
1463  // 0 --- RK-4 (default)
1464  // 1 --- LF-2
1465  // (2 --- MTS ... not yet implemented in generic tracker)
1466  // mbHandler_m->getNumBunch() determines the number of bunches in multibunch mode (MBM, 1 for OFF)
1467  // Total number of particles determines single particle mode (SPM, 1 particle) or
1468  // Static Equilibrium Orbit Mode (SEO, 2 particles)
1469 
1470  double t = 0, dt = 0, oldReferenceTheta = 0;
1471  std::tie(t, dt, oldReferenceTheta) = initializeTracking_m();
1472 
1473  // If initialTotalNum_m = 2, trigger SEO mode and prepare for transverse tuning calculation
1474  // Where is the IF here? -DW
1475  dvector_t Ttime, Tdeltr, Tdeltz;
1476  ivector_t TturnNumber;
1477 
1478  // Apply the plugin elements: probe, collimator, stripper, septum once before first step
1479  bool flagNeedUpdate = applyPluginElements(dt);
1480 
1481  // Destroy particles if they are marked as Bin = -1 in the plugin elements
1482  // or out of global aperture
1483  deleteParticle(flagNeedUpdate);
1484 
1485  /********************************
1486  * Main integration loop *
1487  ********************************/
1488  *gmsg << endl;
1489  *gmsg << "* --------------------------------- Start tracking ------------------------------------ *" << endl;
1490 
1491  for(; (step_m < maxSteps_m) && (itsBunch_m->getTotalNum()>0); step_m++) {
1492 
1493  bool finishedTurn = false;
1494 
1495  switch (mode_m)
1496  {
1497  case MODE::SEO:
1498  { // initialTotalNum_m == 2
1499  seoMode_m(t, dt, finishedTurn, Ttime, Tdeltr, Tdeltz, TturnNumber);
1500  break;
1501  }
1502  case MODE::SINGLE:
1503  { // initialTotalNum_m == 1
1504  singleMode_m(t, dt, finishedTurn, oldReferenceTheta);
1505  break;
1506  }
1507  case MODE::BUNCH:
1508  { // initialTotalNum_m > 2
1509  // Start Tracking for number of particles > 2 (i.e. not single and not SEO mode)
1510  bunchMode_m(t, dt, finishedTurn);
1511  break;
1512  }
1513  case MODE::UNDEFINED:
1514  default:
1515  throw OpalException("ParallelCyclotronTracker::GenericTracker()",
1516  "No such tracking mode.");
1517  }
1518  // Update bunch and some parameters and output some info
1519  update_m(t, dt, finishedTurn);
1520 
1521  } // end for: the integration is DONE after maxSteps_m steps or if all particles are lost!
1522 
1523  // Some post-integration stuff
1524  *gmsg << endl;
1525  *gmsg << "* ---------------------------- DONE TRACKING PARTICLES -------------------------------- * " << endl;
1526 
1527  finalizeTracking_m(Ttime, Tdeltr, Tdeltz, TturnNumber);
1528 }
1529 
1530 bool ParallelCyclotronTracker::getFieldsAtPoint(const double &t, const size_t &Pindex, Vector_t &Efield, Vector_t &Bfield) {
1531 
1532  bool outOfBound = this->computeExternalFields_m(Pindex, t, Efield, Bfield);
1533 
1534  // For runs without space charge effects, override this step to save time
1535  if(itsBunch_m->hasFieldSolver()) {
1536 
1537  // Don't do for reference particle
1538  if(itsBunch_m->ID[Pindex] != 0) {
1539 
1540  // add external Field and self space charge field
1541  Efield += itsBunch_m->Ef[Pindex];
1542  Bfield += itsBunch_m->Bf[Pindex];
1543  }
1544  }
1545 
1546  return outOfBound;
1547 }
1548 
1549 
1562  RFCavity * rfcavity, double &Dold)
1563 {
1564  bool flag = false;
1565  double sinx = rfcavity->getSinAzimuth();
1566  double cosx = rfcavity->getCosAzimuth();
1567  // TODO: Presumably this is still in mm, so for now, change to m -DW
1568  double PerpenDistance = 0.001 * rfcavity->getPerpenDistance();
1569  double distNew = (Rnew[0] * sinx - Rnew[1] * cosx) - PerpenDistance;
1570  double distOld = (Rold[0] * sinx - Rold[1] * cosx) - PerpenDistance;
1571  if(distOld > 0.0 && distNew <= 0.0) flag = true;
1572  // This parameter is used correct cavity phase
1573  Dold = 1.0e3 * distOld; // m --> mm
1574  return flag;
1575 }
1576 
1577 bool ParallelCyclotronTracker::RFkick(RFCavity * rfcavity, const double t, const double dt, const int Pindex){
1578  // For OPAL 2.0: As long as the RFCavity is in mm, we have to change R to mm here -DW
1579  double radius = sqrt(pow(1000.0 * itsBunch_m->R[Pindex](0), 2.0) + pow(1000.0 * itsBunch_m->R[Pindex](1), 2.0)
1580  - pow(rfcavity->getPerpenDistance() , 2.0));
1581  double rmin = rfcavity->getRmin();
1582  double rmax = rfcavity->getRmax();
1583  double nomalRadius = (radius - rmin) / (rmax - rmin);
1584  double tempP[3];
1585  if(nomalRadius <= 1.0 && nomalRadius >= 0.0) {
1586 
1587  for(int j = 0; j < 3; j++)
1588  tempP[j] = itsBunch_m->P[Pindex](j); //[px,py,pz] units: dimensionless
1589 
1590  // here evaluate voltage and conduct momenta kicking;
1591  rfcavity->getMomentaKick(nomalRadius, tempP, t, dt, itsBunch_m->ID[Pindex], itsBunch_m->getM(), itsBunch_m->getQ()); // t : ns
1592 
1593  for(int j = 0; j < 3; j++)
1594  itsBunch_m->P[Pindex](j) = tempP[j];
1595  return true;
1596  }
1597  return false;
1598 }
1599 
1600 
1601 struct adder : public std::unary_function<double, void> {
1602  adder() : sum(0) {}
1603  double sum;
1604  void operator()(double x) { sum += x; }
1605 };
1606 
1620  int lastTurn, double &nur, double &nuz) {
1621  TUNE_class *tune;
1622 
1623  int Ndat = t.size();
1624 
1625  /*
1626  remove mean
1627  */
1628  double rsum = for_each(r.begin(), r.end(), adder()).sum;
1629 
1630  for(int i = 0; i < Ndat; i++)
1631  r[i] -= rsum;
1632 
1633  double zsum = for_each(z.begin(), z.end(), adder()).sum;
1634 
1635  for(int i = 0; i < Ndat; i++)
1636  z[i] -= zsum;
1637  double ti = *(t.begin());
1638  double tf = t[t.size()-1];
1639  double T = (tf - ti);
1640 
1641  t.clear();
1642  for(int i = 0; i < Ndat; i++) {
1643  t.push_back(i);
1644  }
1645 
1646  T = t[Ndat-1];
1647 
1648  *gmsg << endl;
1649  *gmsg << "* ************************************* nuR ******************************************* *" << endl;
1650  *gmsg << endl << "* ===> " << Ndat << " data points Ti=" << ti << " Tf= " << tf << " -> T= " << T << endl;
1651 
1652  int nhis_lomb = 10;
1653  int stat = 0;
1654  // book tune class
1655  tune = new TUNE_class();
1656  stat = tune->lombAnalysis(t, r, nhis_lomb, T / lastTurn);
1657  if(stat != 0)
1658  *gmsg << "* TUNE: Lomb analysis failed" << endl;
1659  *gmsg << "* ************************************************************************************* *" << endl;
1660 
1661  delete tune;
1662  tune = NULL;
1663  // FIXME: FixMe: need to come from the inputfile
1664  nhis_lomb = 100;
1665 
1666  if(zsum != 0.0) {
1667 
1668  *gmsg << endl;
1669  *gmsg << "* ************************************* nuZ ******************************************* *" << endl;
1670  *gmsg << endl << "* ===> " << Ndat << " data points Ti=" << ti << " Tf= " << tf << " -> T= " << T << endl;
1671 
1672  // book tune class
1673  tune = new TUNE_class();
1674  stat = tune->lombAnalysis(t, z, nhis_lomb, T / lastTurn);
1675  if(stat != 0)
1676  *gmsg << "* TUNE: Lomb analysis failed" << endl;
1677  *gmsg << "* ************************************************************************************* *" << endl;
1678 
1679  delete tune;
1680  tune = NULL;
1681  }
1682  return true;
1683 }
1684 
1685 
1687  if (opalRing_m != NULL)
1688  return opalRing_m->getHarmonicNumber();
1689  Cyclotron* elcycl = dynamic_cast<Cyclotron*>(((*FieldDimensions.begin())->second).second);
1690  if (elcycl != NULL)
1691  return elcycl->getCyclHarm();
1692  throw OpalException("ParallelCyclotronTracker::getHarmonicNumber()",
1693  std::string("The first item in the FieldDimensions list does not ")
1694  +std::string("seem to be an Ring or a Cyclotron element"));
1695 }
1696 
1697 
1699  Vector_t meanR(0.0, 0.0, 0.0);
1700 
1701  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); ++i) {
1702  // take all particles if bunchNr <= -1
1703  if ( bunchNr > -1 && itsBunch_m->bunchNum[i] != bunchNr)
1704  continue;
1705 
1706  for(int d = 0; d < 3; ++d) {
1707  meanR(d) += itsBunch_m->R[i](d);
1708  }
1709  }
1710 
1711  reduce(meanR, meanR, OpAddAssign());
1712 
1713  size_t n = itsBunch_m->getTotalNum();
1714 
1715  if ( bunchNr > -1 )
1716  n = itsBunch_m->getTotalNumPerBunch(bunchNr);
1717 
1718  return meanR / Vector_t(n);
1719 }
1720 
1722  Vector_t meanP(0.0, 0.0, 0.0);
1723 
1724  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); ++i) {
1725  for(int d = 0; d < 3; ++d) {
1726  meanP(d) += itsBunch_m->P[i](d);
1727  }
1728  }
1729 
1730  reduce(meanP, meanP, OpAddAssign());
1731  return meanP / Vector_t(itsBunch_m->getTotalNum());
1732 }
1733 
1735  if((step_m % Options::repartFreq) == 0) {
1738  Ippl::Comm->barrier();
1740  }
1741 }
1742 
1744  double phi, Vector_t const translationToGlobal) {
1746  particleVectors -= translationToGlobal;
1747 
1748  Tenzor<double, 3> const rotation( cos(phi), sin(phi), 0,
1749  -sin(phi), cos(phi), 0,
1750  0, 0, 1); // clockwise rotation
1751 
1752  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); ++i) {
1753 
1754  particleVectors[i] = dot(rotation, particleVectors[i]);
1755  }
1757 }
1758 
1760  double phi, Vector_t const translationToGlobal) {
1762  Tenzor<double, 3> const rotation(cos(phi), -sin(phi), 0,
1763  sin(phi), cos(phi), 0,
1764  0, 0, 1); // counter-clockwise rotation
1765 
1766  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); ++i) {
1767 
1768  particleVectors[i] = dot(rotation, particleVectors[i]);
1769  }
1770 
1771  particleVectors += translationToGlobal;
1773 }
1774 
1775 
1777  Quaternion_t const quaternion,
1778  Vector_t const meanR) {
1780 
1781  // Translation from global to local
1782  particleVectors -= meanR;
1783 
1784  // Rotation to align P_mean with x-axis
1785  rotateWithQuaternion(particleVectors, quaternion);
1787 }
1788 
1790  Quaternion_t const quaternion,
1791  Vector_t const meanR) {
1793  // Reverse the quaternion by multiplying the axis components (x,y,z) with -1
1794  Quaternion_t reverseQuaternion = quaternion * -1.0;
1795  reverseQuaternion(0) *= -1.0;
1796 
1797  // Rotation back to original P_mean direction
1798  rotateWithQuaternion(particleVectors, reverseQuaternion);
1799 
1800  // Translation from local to global
1801  particleVectors += meanR;
1803 }
1804 
1806  double const phi,
1807  double const psi,
1808  Vector_t const meanR) {
1809 
1811  //double const tolerance = 1.0e-4; // TODO What is a good angle threshold? -DW
1812 
1813  // Translation from global to local
1814  particleVectors -= meanR;
1815 
1816  // Rotation to align P_mean with x-axis
1817  rotateAroundZ(particleVectors, phi);
1818 
1819  // If theta is large enough (i.e. there is significant momentum in z direction)
1820  // rotate around x-axis next
1821  //if (fabs(psi) > tolerance)
1822  rotateAroundX(particleVectors, psi);
1824 }
1825 
1827  double const phi,
1828  double const psi,
1829  Vector_t const meanR) {
1830 
1832  //double const tolerance = 1.0e-4; // TODO What is a good angle threshold? -DW
1833 
1834  // Translation from global to local
1835  myVector -= meanR;
1836 
1837  // Rotation to align P_mean with x-axis
1838  rotateAroundZ(myVector, phi);
1839 
1840  // If theta is large enough (i.e. there is significant momentum in z direction)
1841  // rotate around x-axis next
1842  //if (fabs(psi) > tolerance)
1843  rotateAroundX(myVector, psi);
1845 }
1846 
1848  double const phi,
1849  double const psi,
1850  Vector_t const meanR) {
1851 
1853  //double const tolerance = 1.0e-4; // TODO What is a good angle threshold? -DW
1854 
1855  // If theta is large enough (i.e. there is significant momentum in z direction)
1856  // rotate back around x-axis next
1857  //if (fabs(psi) > tolerance)
1858  rotateAroundX(particleVectors, -psi);
1859 
1860  // Rotation to align P_mean with x-axis
1861  rotateAroundZ(particleVectors, -phi);
1862 
1863  // Translation from local to global
1864  particleVectors += meanR;
1866 }
1867 
1869  double const phi,
1870  double const psi,
1871  Vector_t const meanR) {
1872 
1874  //double const tolerance = 1.0e-4; // TODO What is a good angle threshold? -DW
1875 
1876  // If theta is large enough (i.e. there is significant momentum in z direction)
1877  // rotate back around x-axis next
1878  //if (fabs(psi) > tolerance)
1879  rotateAroundX(myVector, -psi);
1880 
1881  // Rotation to align P_mean with x-axis
1882  rotateAroundZ(myVector, -phi);
1883 
1884  // Translation from local to global
1885  myVector += meanR;
1887 }
1888 
1890 
1891  Vector_t const quaternionVectorComponent = Vector_t(quaternion(1), quaternion(2), quaternion(3));
1892  double const quaternionScalarComponent = quaternion(0);
1893 
1894  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); ++i) {
1895 
1896  particleVectors[i] = 2.0f * dot(quaternionVectorComponent, particleVectors[i]) * quaternionVectorComponent +
1897  (quaternionScalarComponent * quaternionScalarComponent -
1898  dot(quaternionVectorComponent, quaternionVectorComponent)) * particleVectors[i] + 2.0f *
1899  quaternionScalarComponent * cross(quaternionVectorComponent, particleVectors[i]);
1900  }
1901 }
1902 
1904 
1905  double tolerance = 1.0e-10;
1906  double length2 = dot(quaternion, quaternion);
1907 
1908  if (fabs(length2) > tolerance && fabs(length2 - 1.0f) > tolerance) {
1909 
1910  double length = sqrt(length2);
1911  quaternion /= length;
1912  }
1913 }
1914 
1916 
1917  double tolerance = 1.0e-10;
1918  double length2 = dot(vector, vector);
1919 
1920  if (fabs(length2) > tolerance && fabs(length2 - 1.0f) > tolerance) {
1921 
1922  double length = sqrt(length2);
1923  vector /= length;
1924  }
1925 }
1926 
1927 inline void ParallelCyclotronTracker::rotateAroundZ(ParticleAttrib<Vector_t> & particleVectors, double const phi) {
1928  // Clockwise rotation of particles 'particleVectors' by 'phi' around Z axis
1929 
1930  Tenzor<double, 3> const rotation( cos(phi), sin(phi), 0,
1931  -sin(phi), cos(phi), 0,
1932  0, 0, 1);
1933 
1934  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); ++i) {
1935 
1936  particleVectors[i] = dot(rotation, particleVectors[i]);
1937  }
1938 }
1939 
1940 inline void ParallelCyclotronTracker::rotateAroundZ(Vector_t & myVector, double const phi) {
1941  // Clockwise rotation of single vector 'myVector' by 'phi' around Z axis
1942 
1943  Tenzor<double, 3> const rotation( cos(phi), sin(phi), 0,
1944  -sin(phi), cos(phi), 0,
1945  0, 0, 1);
1946 
1947  myVector = dot(rotation, myVector);
1948 }
1949 
1950 inline void ParallelCyclotronTracker::rotateAroundX(ParticleAttrib<Vector_t> & particleVectors, double const psi) {
1951  // Clockwise rotation of particles 'particleVectors' by 'psi' around X axis
1952 
1953  Tenzor<double, 3> const rotation(1, 0, 0,
1954  0, cos(psi), sin(psi),
1955  0, -sin(psi), cos(psi));
1956 
1957  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); ++i) {
1958 
1959  particleVectors[i] = dot(rotation, particleVectors[i]);
1960  }
1961 }
1962 
1963 inline void ParallelCyclotronTracker::rotateAroundX(Vector_t & myVector, double const psi) {
1964  // Clockwise rotation of single vector 'myVector' by 'psi' around X axis
1965 
1966  Tenzor<double, 3> const rotation(1, 0, 0,
1967  0, cos(psi), sin(psi),
1968  0, -sin(psi), cos(psi));
1969 
1970  myVector = dot(rotation, myVector);
1971 }
1972 
1974  // four vector (w,x,y,z) of the quaternion of P_mean with the positive x-axis
1975 
1976  normalizeVector(u);
1977  normalizeVector(v);
1978 
1979  double k_cos_theta = dot(u, v);
1980  double k = sqrt(dot(u, u) * dot(v, v));
1981  double tolerance1 = 1.0e-5;
1982  double tolerance2 = 1.0e-8;
1983  Vector_t resultVectorComponent;
1984 
1985  if (fabs(k_cos_theta / k + 1.0) < tolerance1) {
1986  // u and v are almost exactly antiparallel so we need to do
1987  // 180 degree rotation around any vector orthogonal to u
1988 
1989  resultVectorComponent = cross(u, xaxis);
1990 
1991  // If by chance u is parallel to xaxis, use zaxis instead
1992  if (dot(resultVectorComponent, resultVectorComponent) < tolerance2) {
1993 
1994  resultVectorComponent = cross(u, zaxis);
1995  }
1996 
1997  double halfAngle = 0.5 * pi;
1998  double sinHalfAngle = sin(halfAngle);
1999 
2000  resultVectorComponent *= sinHalfAngle;
2001 
2002  k = 0.0;
2003  k_cos_theta = cos(halfAngle);
2004 
2005  } else {
2006 
2007  resultVectorComponent = cross(u, v);
2008 
2009  }
2010 
2011  quaternion(0) = k_cos_theta + k;
2012  quaternion(1) = resultVectorComponent(0);
2013  quaternion(2) = resultVectorComponent(1);
2014  quaternion(3) = resultVectorComponent(2);
2015 
2016  normalizeQuaternion(quaternion);
2017 }
2018 
2019 
2021  /* h [ns]
2022  * dt1 [ns]
2023  * dt2 [ns]
2024  */
2026 
2027  // h [ns] --> h [s]
2028  h *= 1.0e-9;
2029 
2030  bool flagNeedUpdate = false;
2031  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); ++i) {
2032  Vector_t const oldR = itsBunch_m->R[i];
2033  double const gamma = sqrt(1.0 + dot(itsBunch_m->P[i], itsBunch_m->P[i]));
2034  double const c_gamma = Physics::c / gamma;
2035  Vector_t const v = itsBunch_m->P[i] * c_gamma;
2036  itsBunch_m->R[i] += h * v;
2037  for(const auto & ccd : cavCrossDatas_m) {
2038  double const distNew = (itsBunch_m->R[i][0] * ccd.sinAzimuth - itsBunch_m->R[i][1] * ccd.cosAzimuth) - ccd.perpenDistance;
2039  bool tagCrossing = false;
2040  double distOld;
2041  if(distNew <= 0.0) {
2042  distOld = (oldR[0] * ccd.sinAzimuth - oldR[1] * ccd.cosAzimuth) - ccd.perpenDistance;
2043  if(distOld > 0.0) tagCrossing = true;
2044  }
2045  if(tagCrossing) {
2046  double const dt1 = distOld / sqrt(dot(v, v));
2047  double const dt2 = h - dt1;
2048 
2049  // Retrack particle from the old postion to cavity gap point
2050  itsBunch_m->R[i] = oldR + dt1 * v;
2051 
2052  // Momentum kick
2053  //itsBunch_m->R[i] *= 1000.0; // RFkick uses [itsBunch_m->R] == mm
2054  RFkick(ccd.cavity, itsBunch_m->getT() * 1.0e9, dt1, i);
2055  //itsBunch_m->R[i] *= 0.001;
2056 
2057  itsBunch_m->R[i] += dt2 * itsBunch_m->P[i] * c_gamma;
2058  }
2059  }
2060  flagNeedUpdate |= (itsBunch_m->Bin[i] < 0);
2061  }
2062 
2064  return flagNeedUpdate;
2065 }
2066 
2067 
2070 
2071  bool flagNeedUpdate = false;
2072  BorisPusher pusher;
2073  double const q = itsBunch_m->Q[0] / q_e; // For now all particles have the same charge
2074  double const M = itsBunch_m->M[0] * 1.0e9; // For now all particles have the same rest energy
2075 
2076  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); ++i) {
2077 
2078  pusher.kick(itsBunch_m->R[i], itsBunch_m->P[i],
2079  itsBunch_m->Ef[i], itsBunch_m->Bf[i],
2080  h * 1.0e-9, M, q);
2081  flagNeedUpdate |= (itsBunch_m->Bin[i] < 0);
2082  }
2084  return flagNeedUpdate;
2085 }
2086 
2087 
2089  // h in [ns]
2090 
2091  // push particles for first half step
2092  bool flagNeedUpdate = push(0.5 * h);
2093 
2094  // Evaluate external fields
2096  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); ++i) {
2097 
2098  itsBunch_m->Ef[i] = Vector_t(0.0, 0.0, 0.0);
2099  itsBunch_m->Bf[i] = Vector_t(0.0, 0.0, 0.0);
2100 
2101  computeExternalFields_m(i, itsBunch_m->getT() * 1e9 /*ns*/,
2102  itsBunch_m->Ef[i], itsBunch_m->Bf[i]);
2103  }
2105 
2106  // Kick particles for full step
2107  flagNeedUpdate |= kick(h);
2108 
2109  // push particles for second half step
2110  flagNeedUpdate |= push(0.5 * h);
2111 
2112  // apply the plugin elements: probe, collimator, stripper, septum
2113  flagNeedUpdate |= applyPluginElements(h);
2114  // destroy particles if they are marked as Bin=-1 in the plugin elements or out of global apeture
2115  deleteParticle(flagNeedUpdate);
2116 }
2117 
2118 
2121  // Plugin Elements are all defined in mm, change beam to mm before applying
2122  itsBunch_m->R *= Vector_t(1000.0);
2123 
2124  for(beamline_list::iterator sindex = ++(FieldDimensions.begin()); sindex != FieldDimensions.end(); ++sindex) {
2125  if(((*sindex)->first) == ElementBase::BEAMSTRIPPING) {
2126  BeamStripping *bstp = static_cast<BeamStripping *>(((*sindex)->second).second);
2128  itsBunch_m->getT()*1e9 /*[ns]*/, dt);
2129  }
2130  }
2131 
2132  bool flag = false;
2133  for (PluginElement* element : pluginElements_m) {
2134  bool tmp = element->check(itsBunch_m,
2135  turnnumber_m,
2136  itsBunch_m->getT() * 1e9 /*[ns]*/,
2137  dt);
2138  flag |= tmp;
2139 
2140  if ( tmp ) {
2142  *gmsg << "* Total number of particles after PluginElement= "
2143  << itsBunch_m->getTotalNum() << endl;
2144  }
2145  }
2146 
2147  itsBunch_m->R *= Vector_t(0.001);
2149  return flag;
2150 }
2151 
2154  // Update immediately if any particles are lost during this step
2155 
2156  if ((step_m + 1) % Options::delPartFreq != 0) {
2158  return false;
2159  }
2160 
2161  allreduce(flagNeedUpdate, 1, std::logical_or<bool>());
2162 
2163  if(flagNeedUpdate) {
2164  short bunchCount = itsBunch_m->getNumBunch();
2165  std::vector<size_t> locLostParticleNum(bunchCount, 0);
2166 
2167  const int leb = itsBunch_m->getLastemittedBin();
2168  std::unique_ptr<size_t[]> localBinCount;
2169 
2170  if ( isMultiBunch() ) {
2171  localBinCount = std::unique_ptr<size_t[]>(new size_t[leb]);
2172  for (int i = 0; i < leb; ++i)
2173  localBinCount[i] = 0;
2174  }
2175 
2176  for(unsigned int i = 0; i < itsBunch_m->getLocalNum(); i++) {
2177  if(itsBunch_m->Bin[i] < 0) {
2178  ++locLostParticleNum[itsBunch_m->bunchNum[i]];
2179  itsBunch_m->destroy(1, i);
2180  } else if ( isMultiBunch() ) {
2181  /* we need to count the local number of particles
2182  * per energy bin
2183  */
2184  ++localBinCount[itsBunch_m->Bin[i]];
2185  }
2186  }
2187 
2188  if ( isMultiBunch() ) {
2189  // set the local bin count
2190  for (int i = 0; i < leb; ++i) {
2191  itsBunch_m->setLocalBinCount(localBinCount[i], i);
2192  }
2193  }
2194 
2195  std::vector<size_t> localnum(bunchCount + 1);
2196  for (size_t i = 0; i < localnum.size() - 1; ++i) {
2197  localnum[i] = itsBunch_m->getLocalNumPerBunch(i) - locLostParticleNum[i];
2198  itsBunch_m->setLocalNumPerBunch(localnum[i], i);
2199  }
2200 
2201  /* We need to destroy the particles now
2202  * before we compute the means. We also
2203  * have to update the total number of particles
2204  * otherwise the statistics are wrong.
2205  */
2206  itsBunch_m->performDestroy(true);
2207 
2208  /* total number of particles of individual bunches
2209  * last index of vector contains total number over all
2210  * bunches, used as a check
2211  */
2212  std::vector<size_t> totalnum(bunchCount + 1);
2213  localnum[bunchCount] = itsBunch_m->getLocalNum();
2214 
2215  allreduce(localnum.data(), totalnum.data(), localnum.size(), std::plus<size_t>());
2216  itsBunch_m->setTotalNum(totalnum[bunchCount]);
2217 
2218  for (short i = 0; i < bunchCount; ++i) {
2219  itsBunch_m->setTotalNumPerBunch(totalnum[i], i);
2220  }
2221 
2222  size_t sum = std::accumulate(totalnum.begin(),
2223  totalnum.end() - 1, 0);
2224 
2225  if ( sum != totalnum[bunchCount] ) {
2226  throw OpalException("ParallelCyclotronTracker::deleteParticle()",
2227  "Total number of particles " + std::to_string(totalnum[bunchCount]) +
2228  " != " + std::to_string(sum) + " (sum over all bunches)");
2229  }
2230 
2231  size_t globLostParticleNum = 0;
2232  size_t locNumLost = std::accumulate(locLostParticleNum.begin(),
2233  locLostParticleNum.end(), 0);
2234 
2235  reduce(locNumLost, globLostParticleNum, 1, std::plus<size_t>());
2236 
2237  if ( globLostParticleNum > 0 ) {
2238  *gmsg << "At step " << step_m
2239  << ", lost " << globLostParticleNum << " particles "
2240  << "on stripper, collimator, septum, geometry, "
2241  << "out of cyclotron aperture or beam stripping"
2242  << endl;
2243  }
2244 
2245  if (totalnum[bunchCount] == 0) {
2247  return flagNeedUpdate;
2248  }
2249 
2250  Vector_t const meanR = calcMeanR();
2251  Vector_t const meanP = calcMeanP();
2252 
2253  // Bunch (local) azimuth at meanR w.r.t. y-axis
2254  double const phi = calculateAngle(meanP(0), meanP(1)) - 0.5 * pi;
2255 
2256  // Bunch (local) elevation at meanR w.r.t. xy plane
2257  double const psi = 0.5 * pi - acos(meanP(2) / sqrt(dot(meanP, meanP)));
2258 
2259  // For statistics data, the bunch is transformed into a local coordinate system
2260  // with meanP in direction of y-axis -DW
2261  globalToLocal(itsBunch_m->R, phi, psi, meanR);
2262  globalToLocal(itsBunch_m->P, phi, psi, Vector_t(0.0)); // P should be rotated, but not shifted
2263 
2264  //itsBunch_m->R *= Vector_t(0.001); // mm --> m
2265 
2266  // Now destroy particles and update pertinent parameters in local frame
2267  // Note that update() will be called within boundp() -DW
2268  itsBunch_m->boundp();
2269  //itsBunch_m->update();
2270 
2272 
2273  //itsBunch_m->R *= Vector_t(1000.0); // m --> mm
2274 
2275  localToGlobal(itsBunch_m->R, phi, psi, meanR);
2276  localToGlobal(itsBunch_m->P, phi, psi, Vector_t(0.0));
2277 
2278  // If particles were deleted, recalculate bingamma and reset BinID for remaining particles
2279  if ( isMultiBunch() )
2280  mbHandler_m->updateParticleBins(itsBunch_m);
2281  }
2282 
2284  return flagNeedUpdate;
2285 }
2286 
2288 
2289  std::string f = OpalData::getInstance()->getInputBasename() + std::string("-trackOrbit.dat");
2290 
2291  outfTrackOrbit_m.setf(std::ios::scientific, std::ios::floatfield);
2292  outfTrackOrbit_m.precision(8);
2293 
2294  if(myNode_m == 0) {
2295 
2296  if(OpalData::getInstance()->inRestartRun()) {
2297 
2298  outfTrackOrbit_m.open(f.c_str(), std::ios::app);
2299  outfTrackOrbit_m << "# Restart at integration step " << itsBunch_m->getLocalTrackStep() << std::endl;
2300  } else {
2301 
2302  outfTrackOrbit_m.open(f.c_str());
2303  outfTrackOrbit_m << "# The six-dimensional phase space data in the global Cartesian coordinates" << std::endl;
2304  outfTrackOrbit_m << "# Part. ID x [m] beta_x*gamma y [m] beta_y*gamma z [m] beta_z*gamma" << std::endl;
2305  }
2306  }
2307 }
2308 
2310  if(!OpalData::getInstance()->inRestartRun()) {
2311  // Start a new run (no restart)
2312 
2313  double const initialReferenceTheta = referenceTheta * Physics::deg2rad;
2314 
2315  // TODO: Replace with TracerParticle
2316  // Force the initial phase space values of the particle with ID = 0 to zero,
2317  // to set it as a reference particle.
2318  if(initialTotalNum_m > 2) {
2319  for(size_t i = 0; i < initialLocalNum_m; ++i) {
2320  if(itsBunch_m->ID[i] == 0) {
2321  itsBunch_m->R[i] = Vector_t(0.0);
2322  itsBunch_m->P[i] = Vector_t(0.0);
2323  }
2324  }
2325  }
2326 
2327  // Initialize global R
2328  //itsBunch_m->R *= Vector_t(1000.0); // m --> mm
2329 
2330  // NEW OPAL 2.0: Immediately change to m -DW
2331  Vector_t const initMeanR = Vector_t(0.001 * referenceR * cosRefTheta_m, // mm --> m
2332  0.001 * referenceR * sinRefTheta_m, // mm --> m
2333  0.001 * referenceZ); // mm --> m
2334 
2335  localToGlobal(itsBunch_m->R, initialReferenceTheta, initMeanR);
2336 
2337  // Initialize global P (Cartesian, but input P_ref is in Pr, Ptheta, Pz,
2338  // so translation has to be done before the rotation this once)
2339  // Cave: In the local frame, the positive y-axis is the direction of movement -DW
2340  for(size_t i = 0; i < initialLocalNum_m; ++i) {
2341  itsBunch_m->P[i](0) += referencePr;
2342  itsBunch_m->P[i](1) += referencePt;
2343  itsBunch_m->P[i](2) += referencePz;
2344  }
2345 
2346  // Out of the three coordinates of meanR (R, Theta, Z) only the angle
2347  // changes the momentum vector...
2348  localToGlobal(itsBunch_m->P, initialReferenceTheta);
2349 
2350  // Initialize the bin number of the first bunch to 0
2351  for(size_t i = 0; i < initialLocalNum_m; ++i) {
2352  itsBunch_m->Bin[i] = 0;
2353  }
2354 
2355  // Backup initial distribution if multi bunch mode
2356  if ((initialTotalNum_m > 2) && isMultiBunch() && mbHandler_m->isForceMode()) {
2357  mbHandler_m->saveBunch(itsBunch_m);
2358  }
2359 
2360  // Else: Restart from the distribution in the h5 file
2361  } else {
2362 
2363  // Do a local frame restart (we have already checked that the old h5 file was saved in local
2364  // frame as well).
2366 
2367  *gmsg << "* Restart in the local frame" << endl;
2368 
2369  //itsBunch_m->R *= Vector_t(1000.0); // m --> mm
2370 
2371  // referenceR and referenceZ are already in mm
2372  // New OPAL 2.0: Init in m -DW
2373  Vector_t const initMeanR = Vector_t(0.001 * referenceR * cosRefTheta_m,
2374  0.001 * referenceR * sinRefTheta_m,
2375  0.001 * referenceZ);
2376 
2377  // Do the tranformations
2380 
2381  // Initialize the bin number of the first bunch to 0
2382  for(size_t i = 0; i < initialLocalNum_m; ++i) {
2383  itsBunch_m->Bin[i] = 0;
2384  }
2385 
2386  // Or do a global frame restart (no transformations necessary)
2387  } else {
2388 
2389  *gmsg << "* Restart in the global frame" << endl;
2390 
2392  //itsBunch_m->R *= Vector_t(1000.0); // m --> mm
2393  }
2394  }
2395 
2396  // set the number of particles per bunch
2398 
2399  // ------------ Get some Values ---------------------------------------------------------- //
2400  Vector_t const meanR = calcMeanR();
2401  Vector_t const meanP = calcMeanP();
2402 
2403  // Bunch (local) azimuth at meanR w.r.t. y-axis
2404  double const phi = calculateAngle(meanP(0), meanP(1)) - 0.5 * pi;
2405 
2406  // Bunch (local) elevation at meanR w.r.t. xy plane
2407  double const psi = 0.5 * pi - acos(meanP(2) / sqrt(dot(meanP, meanP)));
2408 
2409  double radius = std::sqrt(meanR[0] * meanR[0] + meanR[1] * meanR[1]); // [m]
2410 
2411  if ( isMultiBunch() )
2412  mbHandler_m->setRadiusTurns(radius);
2413 
2414  // Do boundp and repartition in the local frame at beginning of this run
2415  globalToLocal(itsBunch_m->R, phi, psi, meanR);
2416  globalToLocal(itsBunch_m->P, phi, psi); // P should be rotated, but not shifted
2417 
2418  //itsBunch_m->R *= Vector_t(0.001); // mm --> m
2419 
2420  itsBunch_m->boundp();
2421 
2422  checkNumPart(std::string("* Before repartition: "));
2423  repartition();
2424  checkNumPart(std::string("* After repartition: "));
2425 
2427 
2428  *gmsg << endl << "* *********************** Bunch information in local frame: ************************";
2429  *gmsg << *itsBunch_m << endl;
2430 
2431  //itsBunch_m->R *= Vector_t(1000.0); // m --> mm
2432 
2433  localToGlobal(itsBunch_m->R, phi, psi, meanR);
2434  localToGlobal(itsBunch_m->P, phi, psi);
2435 
2436  // Save initial distribution if not a restart
2437  if(!OpalData::getInstance()->inRestartRun()) {
2438 
2439  step_m -= 1;
2440 
2443 
2444  step_m += 1;
2445  }
2446 
2447  // Print out the Bunch information at beginning of the run. Because the bunch information
2448  // displays in units of m we have to change back and forth one more time.
2449  //itsBunch_m->R *= Vector_t(0.001); // mm --> m
2450 
2452 
2453  // multi-bunch simulation only
2454  saveInjectValues();
2455 
2456  *gmsg << endl << "* *********************** Bunch information in global frame: ***********************";
2457  *gmsg << *itsBunch_m << endl;
2458 
2459  //itsBunch_m->R *= Vector_t(1000.0); // m --> mm
2460 }
2461 
2462 //TODO: This can be completely rewritten with TracerParticle -DW
2465 
2466  if(Ippl::getNodes() > 1 ) {
2467 
2468  double x;
2469  int id;
2470  dvector_t tmpr;
2471  ivector_t tmpi;
2472 
2474 
2475  // for all nodes, find the location of particle with ID = 0 & 1 in bunch containers
2476  int found[2] = {-1, -1};
2477  int counter = 0;
2478 
2479  for(size_t i = 0; i < itsBunch_m->getLocalNum(); ++i) {
2480  if(itsBunch_m->ID[i] == 0) {
2481  found[counter] = i;
2482  counter++;
2483  }
2484  if(itsBunch_m->ID[i] == 1) {
2485  found[counter] = i;
2486  counter++;
2487  }
2488  }
2489 
2490  if(myNode_m == 0) {
2491  int notReceived = Ippl::getNodes() - 1;
2492  int numberOfPart = 0;
2493  // receive other nodes
2494  while(notReceived > 0) {
2495 
2496  int node = COMM_ANY_NODE;
2497  Message *rmsg = Ippl::Comm->receive_block(node, tag);
2498 
2499  if(rmsg == nullptr)
2500  ERRORMSG("Could not receive from client nodes in main." << endl);
2501 
2502  --notReceived;
2503 
2504  rmsg->get(&numberOfPart);
2505 
2506  for(int i = 0; i < numberOfPart; ++i) {
2507  rmsg->get(&id);
2508  tmpi.push_back(id);
2509  for(int ii = 0; ii < 6; ii++) {
2510  rmsg->get(&x);
2511  tmpr.push_back(x);
2512  }
2513  }
2514  delete rmsg;
2515  }
2516  // own node
2517  for(int i = 0; i < counter; ++i) {
2518 
2519  tmpi.push_back(itsBunch_m->ID[found[i]]);
2520 
2521  for(int j = 0; j < 3; ++j) {
2522 
2523  tmpr.push_back(itsBunch_m->R[found[i]](j));
2524  tmpr.push_back(itsBunch_m->P[found[i]](j));
2525  }
2526  }
2527  // store
2528  dvector_t::iterator itParameter = tmpr.begin();
2529 
2530  for(auto tmpid : tmpi) {
2531 
2532  outfTrackOrbit_m << "ID" << tmpid;
2533 
2534  if (tmpid == 0) { // for stat file
2535  itsBunch_m->RefPartR_m[0] = *itParameter;
2536  itsBunch_m->RefPartR_m[1] = *(itParameter + 2);
2537  itsBunch_m->RefPartR_m[2] = *(itParameter + 4);
2538  itsBunch_m->RefPartP_m[0] = *(itParameter + 1);
2539  itsBunch_m->RefPartP_m[1] = *(itParameter + 3);
2540  itsBunch_m->RefPartP_m[2] = *(itParameter + 5);
2541  }
2542  for(int ii = 0; ii < 6; ii++) {
2543 
2544  outfTrackOrbit_m << " " << *itParameter;
2545  ++itParameter;
2546  }
2547 
2549  }
2550  } else {
2551  // for other nodes
2552  Message *smsg = new Message();
2553  smsg->put(counter);
2554 
2555  for(int i = 0; i < counter; i++) {
2556 
2557  smsg->put(itsBunch_m->ID[found[i]]);
2558 
2559  for(int j = 0; j < 3; j++) {
2560 
2561  smsg->put(itsBunch_m->R[found[i]](j));
2562  smsg->put(itsBunch_m->P[found[i]](j));
2563  }
2564  }
2565 
2566  if(!Ippl::Comm->send(smsg, 0, tag))
2567  ERRORMSG("Ippl::Comm->send(smsg, 0, tag) failed " << endl);
2568  }
2569 
2570  Ippl::Comm->barrier();
2571 
2572  } else {
2573 
2574  for(size_t i = 0; i < itsBunch_m->getLocalNum(); i++) {
2575  if(itsBunch_m->ID[i] == 0 || itsBunch_m->ID[i] == 1) {
2576 
2577  outfTrackOrbit_m << "ID" << itsBunch_m->ID[i] << " ";
2578  outfTrackOrbit_m << itsBunch_m->R[i](0) << " " << itsBunch_m->P[i](0) << " ";
2579  outfTrackOrbit_m << itsBunch_m->R[i](1) << " " << itsBunch_m->P[i](1) << " ";
2580  outfTrackOrbit_m << itsBunch_m->R[i](2) << " " << itsBunch_m->P[i](2) << std::endl;
2581 
2582  if (itsBunch_m->ID[i] == 0) { // for stat file
2585  }
2586  }
2587  }
2588  }
2589 
2591 }
2592 
2594 
2596 
2597  // dump stat file per bunch in case of multi-bunch mode
2598  if (isMultiBunch()) {
2599  double phi = 0.0, psi = 0.0;
2600  Vector_t meanR = calcMeanR();
2601 
2602  // Bunch (global) angle w.r.t. x-axis (cylinder coordinates)
2603  double theta = calculateAngle(meanR(0), meanR(1)) * Physics::rad2deg;
2604 
2605  // fix azimuth_m --> make monotonically increasing
2607 
2609 
2611  Vector_t meanP = calcMeanP();
2612 
2613  // Bunch (local) azimuth at meanR w.r.t. y-axis
2614  phi = calculateAngle(meanP(0), meanP(1)) - 0.5 * pi;
2615 
2616  // Bunch (local) elevation at meanR w.r.t. xy plane
2617  psi = 0.5 * pi - acos(meanP(2) / sqrt(dot(meanP, meanP)));
2618 
2619  // Rotate so Pmean is in positive y direction. No shift, so that normalized emittance and
2620  // unnormalized emittance as well as centroids are calculated correctly in
2621  // PartBunch::calcBeamParameters()
2622  globalToLocal(itsBunch_m->R, phi, psi, meanR);
2623  globalToLocal(itsBunch_m->P, phi, psi);
2624  }
2625 
2627 
2629  localToGlobal(itsBunch_m->R, phi, psi, meanR);
2630  localToGlobal(itsBunch_m->P, phi, psi);
2631  }
2632 
2634  return;
2635  }
2636 
2637  /*
2638  in case before a bunchDumpPhasespaceData has
2639  happen, the calcBeamParameters() is not needed.
2640  maybe we have to introduce a dirty-stat-data flag.
2641  this flag would be set if the bunch is dirty and
2642  reset by calcBeamParameters
2643  */
2644  itsBunch_m->R *= Vector_t(0.001); // mm --> m
2646  itsBunch_m->R *= Vector_t(1000.0); // m --> mm
2647 
2648  // --------------------------------- Get some Values ---------------------------------------- //
2649  double const temp_t = itsBunch_m->getT() * 1e9; // s -> ns
2650  Vector_t meanR;
2651  Vector_t meanP;
2653  meanR = calcMeanR();
2654  meanP = calcMeanP();
2655  } else if (itsBunch_m->getLocalNum() > 0) {
2656  meanR = itsBunch_m->R[0];
2657  meanP = itsBunch_m->P[0];
2658  }
2659  double phi = 0;
2660  double psi = 0;
2661 
2662  // Bunch (global) angle w.r.t. x-axis (cylinder coordinates)
2663  double azimuth = calculateAngle(meanR(0), meanR(1)) * Physics::rad2deg;
2664 
2665  // fix azimuth_m --> make monotonically increasing
2666  dumpAngle(azimuth, prevAzimuth_m, azimuth_m);
2667 
2668  // -------------- Calculate the external fields at the center of the bunch ----------------- //
2669  beamline_list::iterator DumpSindex = FieldDimensions.begin();
2670 
2671  extE_m = Vector_t(0.0, 0.0, 0.0);
2672  extB_m = Vector_t(0.0, 0.0, 0.0);
2673 
2674  (((*DumpSindex)->second).second)->apply(meanR, meanP, temp_t, extE_m, extB_m);
2675 
2676  // If we are saving in local frame, bunch and fields at the bunch center have to be rotated
2677  // TODO: Make decision if we maybe want to always save statistics data in local frame? -DW
2679  // -------------------- ----------- Do Transformations ---------------------------------- //
2680  // Bunch (local) azimuth at meanR w.r.t. y-axis
2681  phi = calculateAngle(meanP(0), meanP(1)) - 0.5 * pi;
2682 
2683  // Bunch (local) elevation at meanR w.r.t. xy plane
2684  psi = 0.5 * pi - acos(meanP(2) / sqrt(dot(meanP, meanP)));
2685 
2686  // Rotate so Pmean is in positive y direction. No shift, so that normalized emittance and
2687  // unnormalized emittance as well as centroids are calculated correctly in
2688  // PartBunch::calcBeamParameters()
2689  globalToLocal(extB_m, phi, psi);
2690  globalToLocal(extE_m, phi, psi);
2691  globalToLocal(itsBunch_m->R, phi, psi);
2692  globalToLocal(itsBunch_m->P, phi, psi);
2693  }
2694 
2695  //itsBunch_m->R *= Vector_t(0.001); // mm -> m
2696 
2697  FDext_m[0] = extB_m / 10.0; // kgauss --> T
2698  FDext_m[1] = extE_m; // kV/mm? -DW
2699 
2700  // Save the stat file
2702 
2703  //itsBunch_m->R *= Vector_t(1000.0); // m -> mm
2704 
2705  // If we are in local mode, transform back after saving
2707  localToGlobal(itsBunch_m->R, phi, psi);
2708  localToGlobal(itsBunch_m->P, phi, psi);
2709  }
2710 
2712 }
2713 
2714 
2716  // --------------------------------- Particle dumping --------------------------------------- //
2717  // Note: Don't dump when
2718  // 1. after one turn
2719  // in order to sychronize the dump step for multi-bunch and single bunch for comparison
2720  // with each other during post-process phase.
2721  // ------------------------------------------------------------------------------------------ //
2723 
2724  // --------------------------------- Get some Values ---------------------------------------- //
2725  double const temp_t = itsBunch_m->getT() * 1.0e9; // s -> ns
2726 
2727  Vector_t meanR;
2728  Vector_t meanP;
2729 
2730  // in case of multi-bunch mode take always bunch mean (although it takes all bunches)
2732  meanR = calcMeanR();
2733  meanP = calcMeanP();
2734  } else if (itsBunch_m->getLocalNum() > 0) {
2735  meanR = itsBunch_m->R[0];
2736  meanP = itsBunch_m->P[0];
2737  }
2738 
2739  double const betagamma_temp = sqrt(dot(meanP, meanP));
2740 
2741  double const E = itsBunch_m->get_meanKineticEnergy();
2742 
2743  // Bunch (global) angle w.r.t. x-axis (cylinder coordinates)
2744  double const theta = atan2(meanR(1), meanR(0));
2745 
2746  // Bunch (local) azimuth at meanR w.r.t. y-axis
2747  double const phi = calculateAngle(meanP(0), meanP(1)) - 0.5 * pi;
2748 
2749  // Bunch (local) elevation at meanR w.r.t. xy plane
2750  double const psi = 0.5 * pi - acos(meanP(2) / sqrt(dot(meanP, meanP)));
2751 
2752  // ---------------- Re-calculate reference values in format of input values ----------------- //
2753  // Position:
2754  // New OPAL 2.0: Init in m (back to mm just for output) -DW
2755  referenceR = computeRadius(meanR); // includes m --> mm conversion
2756  referenceTheta = theta / Physics::deg2rad;
2757  referenceZ = 1000.0 * meanR(2);
2758 
2759  // Momentum in Theta-hat, R-hat, Z-hat coordinates at position meanR:
2760  referencePtot = betagamma_temp;
2761  referencePz = meanP(2);
2762  referencePr = meanP(0) * cos(theta) + meanP(1) * sin(theta);
2765 
2766  // ----- Calculate the external fields at the center of the bunch (Cave: Global Frame) ----- //
2767  beamline_list::iterator DumpSindex = FieldDimensions.begin();
2768 
2769  extE_m = Vector_t(0.0, 0.0, 0.0);
2770  extB_m = Vector_t(0.0, 0.0, 0.0);
2771 
2772  (((*DumpSindex)->second).second)->apply(meanR, meanP, temp_t, extE_m, extB_m);
2773  FDext_m[0] = extB_m * 0.1; // kgauss --> T
2774  FDext_m[1] = extE_m;
2775 
2776  // -------------- If flag psDumpFrame is global, dump bunch in global frame ------------- //
2778  bool dumpLocalFrame = true;
2779  std::string dumpString = "local";
2781  dumpLocalFrame = false;
2782  dumpString = "global";
2783  }
2784 
2785  if (dumpLocalFrame == true) {
2786  // ---------------- If flag psDumpFrame is local, dump bunch in local frame ---------------- //
2787 
2788  // The bunch is transformed into a local coordinate system with meanP in direction of y-axis
2789  globalToLocal(itsBunch_m->R, phi, psi, meanR);
2790  //globalToLocal(itsBunch_m->R, phi, psi, meanR * Vector_t(0.001));
2791  globalToLocal(itsBunch_m->P, phi, psi); // P should only be rotated
2792 
2793  globalToLocal(extB_m, phi, psi);
2794  globalToLocal(extE_m, phi, psi);
2795  }
2796 
2797  FDext_m[0] = extB_m * 0.1; // kgauss --> T
2798  FDext_m[1] = extE_m;
2799 
2800  lastDumpedStep_m = itsDataSink->dumpH5(itsBunch_m, // Local and in m
2801  FDext_m, E,
2802  referencePr,
2803  referencePt,
2804  referencePz,
2805  referenceR,
2807  referenceZ,
2808  phi / Physics::deg2rad, // P_mean azimuth
2809  // at ref. R/Th/Z
2810  psi / Physics::deg2rad, // P_mean elevation
2811  // at ref. R/Th/Z
2812  dumpLocalFrame); // Flag localFrame
2813 
2814  if (dumpLocalFrame == true) {
2815  // Return to global frame
2816  localToGlobal(itsBunch_m->R, phi, psi, meanR);
2817  //localToGlobal(itsBunch_m->R, phi, psi, meanR * Vector_t(0.001));
2818  localToGlobal(itsBunch_m->P, phi, psi);
2819  }
2820 
2821 
2822  // Tell user in which mode we are dumping
2823  // New: no longer dumping for num part < 3, omit phase space dump number info
2824  if (lastDumpedStep_m == -1){
2825  *gmsg << endl << "* Integration step " << step_m + 1
2826  << " (no phase space dump for <= 2 particles)" << endl;
2827  } else {
2828  *gmsg << endl << "* Phase space dump " << lastDumpedStep_m
2829  << " (" << dumpString << " frame) at integration step " << step_m + 1 << endl;
2830  }
2831  }
2832 
2833  // Print dump information on screen
2834  *gmsg << "* T = " << temp_t << " ns"
2835  << ", Live Particles: " << itsBunch_m->getTotalNum() << endl;
2836  *gmsg << "* E = " << E << " MeV"
2837  << ", beta * gamma = " << betagamma_temp << endl;
2838  *gmsg << "* Bunch position: R = " << referenceR << " mm"
2839  << ", Theta = " << referenceTheta << " Deg"
2840  << ", Z = " << referenceZ << " mm" << endl;
2841  *gmsg << "* Local Azimuth = " << phi / Physics::deg2rad << " Deg"
2842  << ", Local Elevation = " << psi / Physics::deg2rad << " Deg" << endl;
2843 
2845 }
2846 
2848 {
2849  return (step_m > 10) && (((step_m + 1) %setup_m.stepsPerTurn) == 0);
2850 }
2851 
2852 void ParallelCyclotronTracker::update_m(double& t, const double& dt,
2853  const bool& finishedTurn)
2854 {
2855  // Reference parameters
2856  t += dt;
2857 
2858  updateTime(dt);
2859 
2861  if (!(step_m + 1 % 1000))
2862  *gmsg << "Step " << step_m + 1 << endl;
2863 
2864  updatePathLength(dt);
2865 
2866  // Here is global frame, don't do itsBunch_m->boundp();
2867 
2868  if (itsBunch_m->getTotalNum()>0) {
2869  // Only dump last step if we have particles left.
2870  // Check separately for phase space (ps) and statistics (stat) data dump frequency
2871  if ( mode_m != MODE::SEO && ( ((step_m + 1) % Options::psDumpFreq == 0) ||
2872  (Options::psDumpEachTurn && finishedTurn)))
2873  {
2874  // Write phase space data to h5 file
2876  }
2877 
2878  if ( mode_m != MODE::SEO && ( ((step_m + 1) % Options::statDumpFreq == 0) ||
2879  (Options::psDumpEachTurn && finishedTurn)))
2880  {
2881  // Write statistics data to stat file
2883  }
2884  }
2885 
2886  if (Options::psDumpEachTurn && finishedTurn)
2887  for (PluginElement* element : pluginElements_m)
2888  element->save();
2889 }
2890 
2891 
2892 std::tuple<double, double, double> ParallelCyclotronTracker::initializeTracking_m() {
2893  // Read in some control parameters
2896 
2897  // Define 3 special azimuthal angles where dump particle's six parameters
2898  // at each turn into 3 ASCII files. only important in single particle tracking
2899  azimuth_angle_m.resize(3);
2900  azimuth_angle_m[0] = 0.0;
2901  azimuth_angle_m[1] = 22.5 * Physics::deg2rad;
2902  azimuth_angle_m[2] = 45.0 * Physics::deg2rad;
2903 
2904  double harm = getHarmonicNumber();
2905  double dt = itsBunch_m->getdT() * 1.0e9 * harm; // time step size (s --> ns)
2906  double t = itsBunch_m->getT() * 1.0e9; // current time (s --> ns)
2907 
2908  double oldReferenceTheta = referenceTheta * Physics::deg2rad; // init here, reset each step
2909  setup_m.deltaTheta = pi / (setup_m.stepsPerTurn); // half of the average angle per step
2910 
2911  //int stepToLastInj = itsBunch_m->getSteptoLastInj(); // TODO: Do we need this? -DW
2912 
2913  // Record how many bunches have already been injected. ONLY FOR MBM
2914  if (isMultiBunch())
2915  mbHandler_m->setNumBunch(itsBunch_m->getNumBunch());
2916 
2918 
2919  // Get data from h5 file for restart run and reset current step
2920  // to last step of previous simulation
2921  if(OpalData::getInstance()->inRestartRun()) {
2922 
2925 
2927 
2928  *gmsg << "* Restart at integration step " << restartStep0_m
2929  << " at turn " << turnnumber_m - 1 << endl;
2930 
2931  initPathLength();
2932  }
2933 
2934  setup_m.stepsNextCheck = step_m + setup_m.stepsPerTurn; // Steps to next check for transition
2935 
2937 
2938  if (isMultiBunch())
2939  mbHandler_m->updateParticleBins(itsBunch_m);
2940 
2941  // --- Output to user --- //
2942  *gmsg << "* Beginning of this run is at t = " << t << " [ns]" << endl;
2943  *gmsg << "* The time step is set to dt = " << dt << " [ns]" << endl;
2944 
2945  if ( isMultiBunch() ) {
2946 
2947  *gmsg << "* MBM: Time interval between neighbour bunches is set to "
2948  << setup_m.stepsPerTurn * dt << "[ns]" << endl;
2949  *gmsg << "* MBM: The particles energy bin reset frequency is set to "
2950  << Options::rebinFreq << endl;
2951  }
2952 
2953  *gmsg << "* Single particle trajectory dump frequency is set to " << Options::sptDumpFreq << endl;
2954  *gmsg << "* The frequency to solve space charge fields is set to " << setup_m.scSolveFreq << endl;
2955  *gmsg << "* The repartition frequency is set to " << Options::repartFreq << endl;
2956 
2957 
2958  switch ( mode_m )
2959  {
2960  case MODE::SEO:
2961  *gmsg << endl;
2962  *gmsg << "* ------------------------- STATIC EQUILIBRIUM ORBIT MODE ----------------------------- *" << endl
2963  << "* Instruction: When the total particle number is equal to 2, SEO mode is triggered *" << endl
2964  << "* automatically. This mode does NOT include any RF cavities. The initial distribution *" << endl
2965  << "* file must be specified. In the file the first line is for reference particle and the *" << endl
2966  << "* second line is for off-center particle. The tune is calculated by FFT routines based *" << endl
2967  << "* on these two particles. *" << endl
2968  << "* ---------------- NOTE: SEO MODE ONLY WORKS SERIALLY ON SINGLE NODE ------------------ *" << endl;
2969 
2970  if(Ippl::getNodes() != 1)
2971  throw OpalException("Error in ParallelCyclotronTracker::initializeTracking_m",
2972  "SEO MODE ONLY WORKS SERIALLY ON SINGLE NODE!");
2973  break;
2974  case MODE::SINGLE:
2975  *gmsg << endl;
2976  *gmsg << "* ------------------------------ SINGLE PARTICLE MODE --------------------------------- *" << endl
2977  << "* Instruction: When the total particle number is equal to 1, single particle mode is *" << endl
2978  << "* triggered automatically. The initial distribution file must be specified which should *" << endl
2979  << "* contain only one line for the single particle *" << endl
2980  << "* ---------NOTE: SINGLE PARTICLE MODE ONLY WORKS SERIALLY ON A SINGLE NODE ------------ *" << endl;
2981 
2982  if(Ippl::getNodes() != 1)
2983  throw OpalException("Error in ParallelCyclotronTracker::initializeTracking_m",
2984  "SINGLE PARTICLE MODE ONLY WORKS SERIALLY ON A SINGLE NODE!");
2985 
2986  // For single particle mode open output files
2988  break;
2989  case MODE::BUNCH:
2990  break;
2991  case MODE::UNDEFINED:
2992  default:
2993  throw OpalException("ParallelCyclotronTracker::initializeTracking_m()",
2994  "No such tracking mode.");
2995  }
2996 
2997  return std::make_tuple(t, dt, oldReferenceTheta);
2998 }
2999 
3000 
3002  dvector_t& Tdeltr,
3003  dvector_t& Tdeltz, ivector_t& TturnNumber)
3004 {
3005  for(size_t ii = 0; ii < (itsBunch_m->getLocalNum()); ii++) {
3006  if(itsBunch_m->ID[ii] == 0) {
3007  double FinalMomentum2 = pow(itsBunch_m->P[ii](0), 2.0) + pow(itsBunch_m->P[ii](1), 2.0) + pow(itsBunch_m->P[ii](2), 2.0);
3008  double FinalEnergy = (sqrt(1.0 + FinalMomentum2) - 1.0) * itsBunch_m->getM() * 1.0e-6;
3009  *gmsg << "* Final energy of reference particle = " << FinalEnergy << " [MeV]" << endl;
3010  *gmsg << "* Total phase space dump number(includes the initial distribution) = " << lastDumpedStep_m + 1 << endl;
3011  *gmsg << "* One can restart simulation from the last dump step (--restart " << lastDumpedStep_m << ")" << endl;
3012  }
3013  }
3014 
3015  Ippl::Comm->barrier();
3016 
3017  switch ( mode_m )
3018  {
3019  case MODE::SEO:
3020  {
3021  // Calculate tunes after tracking.
3022  *gmsg << endl;
3023  *gmsg << "* **************** The result for tune calulation (NO space charge) ******************* *" << endl
3024  << "* Number of tracked turns: " << TturnNumber.back() << endl;
3025  double nur, nuz;
3026  getTunes(Ttime, Tdeltr, Tdeltz, TturnNumber.back(), nur, nuz);
3027  break;
3028  }
3029  case MODE::SINGLE:
3030  closeFiles();
3031  // fall through
3032  case MODE::BUNCH: // we do nothing
3033  case MODE::UNDEFINED:
3034  default:
3035  {
3036  // not for multibunch
3037  if (!isMultiBunch()) {
3038  *gmsg << "*" << endl;
3039  *gmsg << "* Finished during turn " << turnnumber_m << " (" << turnnumber_m - 1 << " turns completed)" << endl;
3040  *gmsg << "* Cave: Turn number is not correct for restart mode"<< endl;
3041  }
3042  }
3043  }
3044 
3045  Ippl::Comm->barrier();
3046 
3047  if (myNode_m == 0)
3048  outfTrackOrbit_m.close();
3049 
3050  *gmsg << endl << "* *********************** Bunch information in global frame: ***********************";
3051 
3052  if (itsBunch_m->getTotalNum() > 0){
3053  // Print out the Bunch information at end of the run.
3055  *gmsg << *itsBunch_m << endl;
3056 
3057  } else {
3058 
3059  *gmsg << endl << "* No Particles left in bunch!" << endl;
3060  *gmsg << "* **********************************************************************************" << endl;
3061 
3062  }
3063 }
3064 
3065 
3066 void ParallelCyclotronTracker::seoMode_m(double& t, const double dt, bool& finishedTurn,
3067  dvector_t& Ttime, dvector_t& Tdeltr,
3068  dvector_t& Tdeltz, ivector_t& TturnNumber)
3069 {
3070 
3071  // 2 particles: Trigger SEO mode
3072  // (Switch off cavity and calculate betatron oscillation tuning)
3073  double r_tuning[2], z_tuning[2] ;
3074 
3076  for(size_t i = 0; i < (itsBunch_m->getLocalNum()); i++) {
3077 
3078  if((step_m % Options::sptDumpFreq == 0)) {
3079 
3080  outfTrackOrbit_m << "ID" << (itsBunch_m->ID[i]);
3081  outfTrackOrbit_m << " " << itsBunch_m->R[i](0)
3082  << " " << itsBunch_m->P[i](0)
3083  << " " << itsBunch_m->R[i](1)
3084  << " " << itsBunch_m->P[i](1)
3085  << " " << itsBunch_m->R[i](2)
3086  << " " << itsBunch_m->P[i](2)
3087  << std::endl;
3088  }
3089 
3090  double OldTheta = calculateAngle(itsBunch_m->R[i](0), itsBunch_m->R[i](1));
3091  r_tuning[i] = itsBunch_m->R[i](0) * cos(OldTheta) +
3092  itsBunch_m->R[i](1) * sin(OldTheta);
3093 
3094  z_tuning[i] = itsBunch_m->R[i](2);
3095 
3096 
3097  // Integrate for one step in the lab Cartesian frame (absolute value).
3098  itsStepper_mp->advance(itsBunch_m, i, t, dt);
3099 
3100  if( (i == 0) && isTurnDone() )
3101  turnnumber_m++;
3102 
3103  } // end for: finish one step tracking for all particles for initialTotalNum_m == 2 mode
3105 
3106  // store dx and dz for future tune calculation if higher precision needed, reduce freqSample.
3107  if(step_m % Options::sptDumpFreq == 0) {
3108 
3109  Ttime.push_back(t * 1.0e-9);
3110  Tdeltz.push_back(z_tuning[1]);
3111  Tdeltr.push_back(r_tuning[1] - r_tuning[0]);
3112  TturnNumber.push_back(turnnumber_m);
3113  }
3114 }
3115 
3116 
3117 void ParallelCyclotronTracker::singleMode_m(double& t, const double dt,
3118  bool& finishedTurn, double& oldReferenceTheta) {
3119  // 1 particle: Trigger single particle mode
3120 
3121  // ********************************************************************************** //
3122  // * This was moved here b/c collision should be tested before the actual * //
3123  // * timestep (bgf_main_collision_test() predicts the next step automatically) * //
3124 
3125  // apply the plugin elements: probe, collimator, stripper, septum
3126  bool flagNeedUpdate = applyPluginElements(dt);
3127 
3128  // check if we lose particles at the boundary
3130  // ********************************************************************************** //
3131 
3132  if (itsBunch_m->getLocalNum() == 0) return; // might happen if particle is in collimator
3133 
3135 
3136  unsigned int i = 0; // we only have a single particle
3137 
3138  if ( step_m % Options::sptDumpFreq == 0 ) {
3139 
3140  outfTrackOrbit_m << "ID" <<itsBunch_m->ID[i]
3141  << " " << itsBunch_m->R[i](0)
3142  << " " << itsBunch_m->P[i](0)
3143  << " " << itsBunch_m->R[i](1)
3144  << " " << itsBunch_m->P[i](1)
3145  << " " << itsBunch_m->R[i](2)
3146  << " " << itsBunch_m->P[i](2)
3147  << std::endl;
3148  }
3149 
3150  double temp_meanTheta = calculateAngle2(itsBunch_m->R[i](0),
3151  itsBunch_m->R[i](1)); // [-pi, pi]
3152 
3153 
3155  temp_meanTheta, finishedTurn);
3156 
3158  oldReferenceTheta, temp_meanTheta);
3159 
3160  oldReferenceTheta = temp_meanTheta;
3161 
3162  // used for gap crossing checking
3163  Vector_t Rold = itsBunch_m->R[i]; // [x,y,z] (mm)
3164  Vector_t Pold = itsBunch_m->P[i]; // [px,py,pz] (beta*gamma)
3165 
3166  // integrate for one step in the lab Cartesian frame (absolute value).
3167  itsStepper_mp->advance(itsBunch_m, i, t, dt);
3168 
3169  // If gap crossing happens, do momenta kicking (not if gap crossing just happened)
3170  if (itsBunch_m->cavityGapCrossed[i] == true)
3171  itsBunch_m->cavityGapCrossed[i] = false;
3172  else
3173  gapCrossKick_m(i, t, dt, Rold, Pold);
3175 
3176  // Destroy particles if they are marked as Bin = -1 in the plugin elements
3177  // or out of the global aperture
3178  flagNeedUpdate |= (itsBunch_m->Bin[i] < 0);
3179  deleteParticle(flagNeedUpdate);
3180 }
3181 
3182 
3183 void ParallelCyclotronTracker::bunchMode_m(double& t, const double dt, bool& finishedTurn) {
3184 
3185  // Flag for transition from single-bunch to multi-bunches mode
3186  static bool flagTransition = false;
3187 
3188  // single particle dumping
3189  if(step_m % Options::sptDumpFreq == 0)
3191 
3192  // Find out if we need to do bunch injection
3193  injectBunch(flagTransition);
3194 
3195 // oldReferenceTheta = calculateAngle2(meanR(0), meanR(1));
3196 
3197  // Calculate SC field before each time step and keep constant during integration.
3198  // Space Charge effects are included only when total macropaticles number is NOT LESS THAN 1000.
3199  if (itsBunch_m->hasFieldSolver()) {
3200 
3201  if (step_m % setup_m.scSolveFreq == 0) {
3203  } else {
3204  // If we are not solving for the space charge fields at this time step
3205  // we will apply the fields from the previous step and have to rotate them
3206  // accordingly. For this we find the quaternion between the previous mean momentum (PreviousMeanP)
3207  // and the current mean momentum (meanP) and rotate the fields with this quaternion.
3208 
3209  Vector_t const meanP = calcMeanP();
3210 
3211  Quaternion_t quaternionToNewMeanP;
3212 
3213  getQuaternionTwoVectors(PreviousMeanP, meanP, quaternionToNewMeanP);
3214 
3215  // Reset PreviousMeanP. Cave: This HAS to be after the quaternion is calculated!
3217 
3218  // Rotate the fields
3219  globalToLocal(itsBunch_m->Ef, quaternionToNewMeanP);
3220  globalToLocal(itsBunch_m->Bf, quaternionToNewMeanP);
3221  }
3222  }
3223 
3224  // *** This was moved here b/c collision should be tested before the **********************
3225  // *** actual timestep (bgf_main_collision_test() predicts the next step automatically) -DW
3226  // Apply the plugin elements: probe, collimator, stripper, septum
3227  bool flagNeedUpdate = applyPluginElements(dt);
3228 
3229  // check if we lose particles at the boundary
3231 
3233 
3234  for(size_t i = 0; i < itsBunch_m->getLocalNum(); i++) {
3235 
3236  // used for gap crossing checking
3237  Vector_t Rold = itsBunch_m->R[i]; // [x,y,z] (mm)
3238  Vector_t Pold = itsBunch_m->P[i]; // [px,py,pz] (beta*gamma)
3239 
3240  // Integrate for one step in the lab Cartesian frame (absolute value).
3241  itsStepper_mp->advance(itsBunch_m, i, t, dt);
3242 
3243  // If gap crossing happens, do momenta kicking (not if gap crossing just happened)
3244  if (itsBunch_m->cavityGapCrossed[i] == true)
3245  itsBunch_m->cavityGapCrossed[i] = false;
3246  else
3247  gapCrossKick_m(i, t, dt, Rold, Pold);
3248 
3249  flagNeedUpdate |= (itsBunch_m->Bin[i] < 0);
3250  }
3251 
3253 
3254  // Destroy particles if they are marked as Bin = -1 in the plugin elements
3255  // or out of global aperture
3256  deleteParticle(flagNeedUpdate);
3257 
3258  // Recalculate bingamma and reset the BinID for each particles according to its current gamma
3259  if (isMultiBunch() && step_m % Options::rebinFreq == 0) {
3260  mbHandler_m->updateParticleBins(itsBunch_m);
3261  }
3262 
3263  // Some status output for user after each turn
3264  if ( isTurnDone() ) {
3265  turnnumber_m++;
3266  finishedTurn = true;
3267 
3268  *gmsg << endl;
3269  *gmsg << "*** Finished turn " << turnnumber_m - 1
3270  << ", Total number of live particles: "
3271  << itsBunch_m->getTotalNum() << endl;
3272  }
3273 
3274  Ippl::Comm->barrier();
3275 }
3276 
3277 
3279  double dt,
3280  const Vector_t& Rold,
3281  const Vector_t& Pold)
3282 {
3283  for (beamline_list::iterator sindex = ++(FieldDimensions.begin());
3284  sindex != FieldDimensions.end(); ++sindex)
3285  {
3286  bool tag_crossing = false;
3287  double DistOld = 0.0; //mm
3288  RFCavity * rfcav;
3289 
3290  if (((*sindex)->first) == ElementBase::RFCAVITY) {
3291  // here check gap cross in the list, if do , set tag_crossing to TRUE
3292  rfcav = static_cast<RFCavity *>(((*sindex)->second).second);
3293  tag_crossing = checkGapCross(Rold, itsBunch_m->R[i], rfcav, DistOld);
3294  }
3295 
3296  if ( tag_crossing ) {
3297  itsBunch_m->cavityGapCrossed[i] = true;
3298 
3299  double oldMomentum2 = dot(Pold, Pold);
3300  double oldBetgam = sqrt(oldMomentum2);
3301  double oldGamma = sqrt(1.0 + oldMomentum2);
3302  double oldBeta = oldBetgam / oldGamma;
3303  double dt1 = DistOld / (Physics::c * oldBeta * 1.0e-6); // ns
3304  double dt2 = dt - dt1;
3305 
3306  // retrack particle from the old postion to cavity gap point
3307  // restore the old coordinates and momenta
3308  itsBunch_m->R[i] = Rold;
3309  itsBunch_m->P[i] = Pold;
3310 
3311  if (dt / dt1 < 1.0e9)
3312  itsStepper_mp->advance(itsBunch_m, i, t, dt1);
3313 
3314  // Momentum kick
3315  RFkick(rfcav, t, dt1, i);
3316 
3317  /* Retrack particle from cavity gap point for
3318  * the left time to finish the entire timestep
3319  */
3320  if (dt / dt2 < 1.0e9)
3321  itsStepper_mp->advance(itsBunch_m, i, t, dt2);
3322  }
3323  }
3324 }
3325 
3326 
3328  const Vector_t& R,
3329  const Vector_t& P,
3330  const double& oldReferenceTheta,
3331  const double& temp_meanTheta)
3332 {
3333  for (unsigned int i=0; i<=2; i++) {
3334  if ((oldReferenceTheta < azimuth_angle_m[i] - setup_m.deltaTheta) &&
3335  ( temp_meanTheta >= azimuth_angle_m[i] - setup_m.deltaTheta)) {
3336  *(outfTheta_m[i]) << "#Turn number = " << turnnumber_m
3337  << ", Time = " << t << " [ns]" << std::endl
3338  << " " << std::hypot(R(0), R(1))
3339  << " " << P(0) * std::cos(temp_meanTheta) + P(1) * std::sin(temp_meanTheta)
3340  << " " << temp_meanTheta * Physics::rad2deg
3341  << " " << - P(0) * std::sin(temp_meanTheta) + P(1) * std::cos(temp_meanTheta)
3342  << " " << R(2)
3343  << " " << P(2) << std::endl;
3344  }
3345  }
3346 }
3347 
3349  const Vector_t& R,
3350  const Vector_t& P,
3351  const double& temp_meanTheta,
3352  bool& finishedTurn)
3353 {
3354  if ( isTurnDone() ) {
3355 
3356  ++turnnumber_m;
3357 
3358  finishedTurn = true;
3359 
3360  *gmsg << "* SPT: Finished turn " << turnnumber_m - 1 << endl;
3361 
3362  *(outfTheta_m[3]) << "#Turn number = " << turnnumber_m
3363  << ", Time = " << t << " [ns]" << std::endl
3364  << " " << std::sqrt(R(0) * R(0) + R(1) * R(1))
3365  << " " << P(0) * std::cos(temp_meanTheta) +
3366  P(1) * std::sin(temp_meanTheta)
3367  << " " << temp_meanTheta / Physics::deg2rad
3368  << " " << - P(0) * std::sin(temp_meanTheta) +
3369  P(1) * std::cos(temp_meanTheta)
3370  << " " << R(2)
3371  << " " << P(2) << std::endl;
3372  }
3373 }
3374 
3375 
3377  // Firstly reset E and B to zero before fill new space charge field data for each track step
3378  itsBunch_m->Bf = Vector_t(0.0);
3379  itsBunch_m->Ef = Vector_t(0.0);
3380 
3381  if (spiral_flag && itsBunch_m->getFieldSolverType() == "SAAMG") {
3382  // --- Single bunch mode with spiral inflector --- //
3383 
3384  // If we are doing a spiral inflector simulation and are using the SAAMG solver
3385  // we don't rotate or translate the bunch and gamma is 1.0 (non-relativistic).
3386 
3387  //itsBunch_m->R *= Vector_t(0.001); // mm --> m
3388 
3389  itsBunch_m->setGlobalMeanR(Vector_t(0.0, 0.0, 0.0));
3390  itsBunch_m->setGlobalToLocalQuaternion(Quaternion_t(1.0, 0.0, 0.0, 0.0));
3391 
3393 
3394  //itsBunch_m->R *= Vector_t(1000.0); // m --> mm
3395 
3396  } else {
3397 
3398  Vector_t const meanR = calcMeanR(); // (m)
3399 
3401 
3402  // Since calcMeanP takes into account all particles of all bins (TODO: Check this! -DW)
3403  // Using the quaternion method with PreviousMeanP and yaxis should give the correct result
3404 
3405  Quaternion_t quaternionToYAxis;
3406 
3407  getQuaternionTwoVectors(PreviousMeanP, yaxis, quaternionToYAxis);
3408 
3409  globalToLocal(itsBunch_m->R, quaternionToYAxis, meanR);
3410 
3411  //itsBunch_m->R *= Vector_t(0.001); // mm --> m
3412 
3413  if ((step_m + 1) % Options::boundpDestroyFreq == 0)
3415  else
3416  itsBunch_m->boundp();
3417 
3418  if (hasMultiBunch()) {
3419  // --- Multibunch mode --- //
3420 
3421  // Calculate gamma for each energy bin
3423 
3424  repartition();
3425 
3426  // Calculate space charge field for each energy bin
3427  for(int b = 0; b < itsBunch_m->getLastemittedBin(); b++) {
3428 
3430  //itsBunch_m->setGlobalMeanR(0.001 * meanR);
3431  itsBunch_m->setGlobalMeanR(meanR);
3432  itsBunch_m->setGlobalToLocalQuaternion(quaternionToYAxis);
3434  }
3435 
3437 
3438  } else {
3439  // --- Single bunch mode --- //
3440 
3441  double temp_meangamma = std::sqrt(1.0 + dot(PreviousMeanP, PreviousMeanP));
3442 
3443  repartition();
3444 
3445  //itsBunch_m->setGlobalMeanR(0.001 * meanR);
3446  itsBunch_m->setGlobalMeanR(meanR);
3447  itsBunch_m->setGlobalToLocalQuaternion(quaternionToYAxis);
3448 
3449  itsBunch_m->computeSelfFields_cycl(temp_meangamma);
3450 
3451  }
3452 
3453  //scale coordinates back
3454  //itsBunch_m->R *= Vector_t(1000.0); // m --> mm
3455 
3456  // Transform coordinates back to global
3457  localToGlobal(itsBunch_m->R, quaternionToYAxis, meanR);
3458 
3459  // Transform self field back to global frame (rotate only)
3460  localToGlobal(itsBunch_m->Ef, quaternionToYAxis);
3461  localToGlobal(itsBunch_m->Bf, quaternionToYAxis);
3462  }
3463 }
3464 
3465 
3466 bool ParallelCyclotronTracker::computeExternalFields_m(const size_t& i, const double& t,
3467  Vector_t& Efield, Vector_t& Bfield)
3468 {
3469  beamline_list::iterator sindex = FieldDimensions.begin();
3470 
3471  // Flag whether a particle is out of field
3472  bool outOfBound = (((*sindex)->second).second)->apply(i, t, Efield, Bfield);
3473 
3474  Bfield *= 0.10; // kGauss --> T
3475  Efield *= 1.0e6; // kV/mm --> V/m
3476 
3477  return outOfBound;
3478 }
3479 
3480 
3481 void ParallelCyclotronTracker::injectBunch(bool& flagTransition) {
3482  if (!isMultiBunch() || step_m != setup_m.stepsNextCheck) {
3483  return;
3484  }
3485 
3486  const short result = mbHandler_m->injectBunch(itsBunch_m, itsReference,
3487  flagTransition);
3488 
3489  switch ( result ) {
3490  case 0: {
3491  // nothing happened
3492  break;
3493  }
3494  case 1: {
3495  // bunch got saved
3496  saveInjectValues();
3498  if ( flagTransition ) {
3499  *gmsg << "* MBM: Saving beam distribution at turn " << turnnumber_m << endl;
3500  *gmsg << "* MBM: After one revolution, Multi-Bunch Mode will be invoked" << endl;
3501  }
3502  break;
3503  }
3504  case 2: {
3505  // bunch got injected
3507  break;
3508  }
3509  default:
3510  throw OpalException("ParallelCyclotronTracker::injectBunch()",
3511  "Unknown return value " + std::to_string(result));
3512  }
3513 }
3514 
3515 
3517  if ( !isMultiBunch() ) {
3518  return;
3519  }
3520 
3521  Vector_t meanR = calcMeanR();
3522 
3523  // Bunch (global) angle w.r.t. x-axis (cylinder coordinates)
3524  double theta = calculateAngle(meanR(0), meanR(1)) * Physics::rad2deg;
3525 
3526  // fix azimuth_m --> make monotonically increasing
3528 
3529  const double radius = computeRadius(meanR);
3530 
3531  MultiBunchHandler::injection_t& inj = mbHandler_m->getInjectionValues();
3532 
3533  inj.time = itsBunch_m->getT() * 1.0e9; /*ns*/
3534  inj.pathlength = itsBunch_m->get_sPos();
3535  inj.azimuth = azimuth_m;
3536  inj.radius = radius;
3537 }
3538 
3539 
3541  /* the last element includes all particles,
3542  * all other elements are bunch number specific
3543  */
3544  std::vector<double> lpaths(1);
3545 
3546  if ( isMultiBunch() ) {
3547  lpaths.resize(mbHandler_m->getNumBunch() + 1);
3548  }
3549 
3550  computePathLengthUpdate(lpaths, dt);
3551 
3552  pathLength_m += lpaths.back();
3554 
3555  if ( isMultiBunch() ) {
3556  mbHandler_m->updatePathLength(lpaths);
3557  }
3558 }
3559 
3560 
3562  // t is in seconds
3563  double t = itsBunch_m->getT();
3564 
3565  // dt: ns --> s
3566  itsBunch_m->setT(t + dt * 1.0e-9);
3567 
3568  if ( isMultiBunch() ) {
3569  mbHandler_m->updateTime(dt);
3570  }
3571 }
3572 
3573 
3575  if (!isMultiBunch()) {
3576  return;
3577  }
3578 
3579  for (short b = 0; b < mbHandler_m->getNumBunch(); ++b) {
3580  Vector_t meanR = calcMeanR(b);
3581  MultiBunchHandler::beaminfo_t& binfo = mbHandler_m->getBunchInfo(b);
3582 
3583  binfo.radius = computeRadius(meanR);
3584  double azimuth = calculateAngle(meanR(0), meanR(1)) * Physics::rad2deg;
3585  dumpAngle(azimuth, binfo.prevAzimuth, binfo.azimuth, b);
3586  }
3587 }
3588 
3589 
3591  if ( isMultiBunch() ) {
3592  // we need to reset the path length of each bunch
3594  }
3595 }
virtual std::string getFieldMapFN() const
Definition: RFCavity.h:368
virtual double getMaxR() const
Definition: Cyclotron.cpp:310
static int getNodes()
Definition: IpplInfo.cpp:773
virtual double getRmin() const
Definition: Cyclotron.cpp:285
void updatePathLength(const double &dt)
ParticleAttrib< Vector_t > P
double getXStart() const
Member variable access.
double getT() const
bool computeExternalFields_m(const size_t &i, const double &t, Vector_t &Efield, Vector_t &Bfield)
double calculateAngle(double x, double y)
virtual void visitOffset(const Offset &)
Apply the algorithm to a Offset.
int lombAnalysis(double *x, double *y, int Ndat, int nhis)
Definition: Ctunes.cpp:146
virtual ElementBase * clone() const override
Definition: Ring.h:155
PETE_TUTree< FnArcCos, typename T::PETE_Expr_t > acos(const PETE_Expr< T > &l)
Definition: PETE.h:808
virtual std::string getPressureMapFN() const
constexpr double e
The value of .
Definition: Physics.h:40
void destroy(size_t M, size_t I, bool doNow=false)
void finalizeTracking_m(dvector_t &Ttime, dvector_t &Tdeltr, dvector_t &Tdeltz, ivector_t &TturnNumber)
ParticleAttrib< Vector_t > Ef
virtual void initialise(PartBunchBase< double, 3 > *bunch, double &startField, double &endField) override
Definition: RFCavity.cpp:252
Vector_t RefPartR_m
void setOpenMode(OPENMODE openMode)
Definition: OpalData.cpp:377
double getBeamRInit() const
Definition: Ring.h:235
void dumpAngle(const double &theta, double &prevAzimuth, double &azimuth, const short &bunchNr=0)
std::tuple< double, double, double > initializeTracking_m()
void operator()(double x)
virtual bool getSpiralFlag() const
Definition: Cyclotron.cpp:160
Definition: Offset.h:66
Definition: TSVMeta.h:24
std::string getFieldSolverType() const
Return the fieldsolver type if we have a fieldsolver.
bool getStop() const
Definition: Stripper.cpp:67
std::vector< double > azimuth_angle_m
the different azimuthal angles for the outfTheta_m output files
Interface for septum magnet.
Definition: Septum.h:11
long long getLocalTrackStep() const
virtual double getZinit() const
Definition: Cyclotron.cpp:136
#define INFORM_ALL_NODES
Definition: Inform.h:38
double getZStart()
Member variable access.
Definition: CCollimator.h:78
void writeMultiBunchStatistics(PartBunchBase< double, 3 > *beam, MultiBunchHandler *mbhandler)
Definition: DataSink.cpp:279
Interface for electrostatic separator.
Definition: Separator.h:33
Quaternion Quaternion_t
Definition: Quaternion.h:43
virtual void visitMarker(const Marker &)
Apply the algorithm to a Marker.
double getZEnd()
Definition: CCollimator.h:83
#define IPPL_APP_TAG4
Definition: Tags.h:107
Interface for a Cyclotron.
Definition: Cyclotron.h:91
std::list< Component * > myElements
void setLocalNumPerBunch(size_t numpart, short n)
PETE_TUTree< FnFabs, typename T::PETE_Expr_t > fabs(const PETE_Expr< T > &l)
Definition: PETE.h:815
void setGlobalMeanR(Vector_t globalMeanR)
virtual void visitCCollimator(const CCollimator &)
Apply the algorithm to a collimator.
Interface for beam position monitors.
Definition: Monitor.h:41
T::PETE_Expr_t::PETE_Return_t max(const PETE_Expr< T > &expr, NDIndex< D > &loc)
Definition: ReductionLoc.h:123
virtual double getPHIinit() const
Definition: Cyclotron.cpp:128
Interface for RF Quadrupole.
Definition: RFQuadrupole.h:30
double getPressure() const
The base class for all OPAL exceptions.
Definition: OpalException.h:28
Tps< T > sin(const Tps< T > &x)
Sine.
Definition: TpsMath.h:111
virtual void computeSelfFields_cycl(double gamma)=0
double getBeamPRInit() const
Definition: Ring.h:247
virtual bool checkBeamStripping(PartBunchBase< double, 3 > *bunch, Cyclotron *cycl, const int turnnumber, const double t, const double tstep)
void setGlobalToLocalQuaternion(Quaternion_t globalToLocalQuaternion)
#define ERRORMSG(msg)
Definition: IpplInfo.h:399
ParticleAttrib< double > Q
void lockRing()
Definition: Ring.cpp:287
std::vector< PluginElement * > pluginElements_m
virtual void visitDegrader(const Degrader &)
Apply the algorithm to a Degrader.
virtual void visitVerticalFFAMagnet(const VerticalFFAMagnet &bend)
Apply the algorithm to a VerticalFFAMagnet.
Interface for RF cavity.
Definition: ParallelPlate.h:36
virtual void visitRFCavity(const RFCavity &)
Apply the algorithm to a RFCavity.
IpplTimings::TimerRef DumpTimer_m
Particle reference data.
Definition: PartData.h:38
double getdT() const
Inform * gmsg
Definition: Main.cpp:21
virtual void do_binaryRepart()
double getM() const
Interface for general corrector.
Definition: Corrector.h:35
virtual double getSymmetry() const
Definition: Cyclotron.cpp:244
Abstract collimator.
Definition: RBend.h:73
Interface for beam diagnostics.
Definition: Diagnostic.h:32
void barrier(void)
4-th order Runnge-Kutta stepper
Definition: RK4.h:9
double computeRadius(const Vector_t &meanR) const
double getYEnd() const
double get_meanKineticEnergy() const
Interface for a marker.
Definition: Marker.h:32
void setLocalTrackStep(long long n)
step in a TRACK command
struct ParallelCyclotronTracker::settings setup_m
virtual void visitRFQuadrupole(const RFQuadrupole &)
Apply the algorithm to a RFQuadrupole.
const std::string & getCyclotronType() const
Definition: Cyclotron.cpp:253
IpplTimings::TimerRef IntegrationTimer_m
const int COMM_ANY_NODE
Definition: Communicate.h:40
virtual void visitVariableRFCavity(const VariableRFCavity &cav)
Apply the algorithm to a VariabelRFCavity.
virtual const std::string & getName() const
Get element name.
Definition: ElementBase.cpp:95
ParticleAttrib< short > bunchNum
#define IPPL_APP_CYCLE
Definition: Tags.h:113
int maxSteps_m
The maximal number of steps the system is integrated.
void localToGlobal(ParticleAttrib< Vector_t > &vectorArray, double phi, Vector_t const translationToGlobal=0)
virtual void visitSeptum(const Septum &)
Apply the algorithm to a Septum.
void globalToLocal(ParticleAttrib< Vector_t > &vectorArray, double phi, Vector_t const translationToGlobal=0)
Interface for drift space.
Definition: Drift.h:33
virtual void visitDrift(const Drift &)
Apply the algorithm to a Drift.
double getBeta() const
The relativistic beta per particle.
Definition: PartData.h:127
virtual ElementBase * clone() const override
virtual double getRmin() const
Definition: RFCavity.cpp:383
constexpr double rad2deg
The conversion factor from radians to degrees.
Definition: Physics.h:46
Inform & level4(Inform &inf)
Definition: Inform.cpp:48
void rotateAroundZ(ParticleAttrib< Vector_t > &particleVectors, double const phi)
void update_m(double &t, const double &dt, const bool &finishedTurn)
Update time and path length, write to output files.
void setTotalNumPerBunch(size_t numpart, short n)
T::PETE_Expr_t::PETE_Return_t sum(const PETE_Expr< T > &expr)
Definition: PETE.h:1213
size_t getTotalNum() const
void getQuaternionTwoVectors(Vector_t u, Vector_t v, Quaternion_t &quaternion)
virtual void visitSolenoid(const Solenoid &)
Apply the algorithm to a Solenoid.
double dot(const Vector3D &lhs, const Vector3D &rhs)
Vector dot product.
Definition: Vector3D.cpp:118
Interface for general multipole.
Definition: Multipole.h:46
void gapCrossKick_m(size_t i, double t, double dt, const Vector_t &Rold, const Vector_t &Pold)
int next_tag(int t, int s=1000)
Definition: TagMaker.h:43
Vector_t calcMeanR(short bunchNr=-1) const
virtual double getMinZ() const
Definition: Cyclotron.cpp:319
virtual void initialise(PartBunchBase< double, 3 > *bunch, double &startField, double &endField) override
Definition: Ring.cpp:159
virtual void visitMultipoleTCurvedVarRadius(const MultipoleTCurvedVarRadius &)
Apply the algorithm to a MultipoleTCurvedVarRadius.
int statDumpFreq
The frequency to dump statistical values, e.e. dump data when stepstatDumpFreq==0.
Definition: Options.cpp:54
double bega
The reference variables.
T deg(T x)
Convert radians to degrees.
Definition: matheval.hpp:82
void bunchMode_m(double &t, const double dt, bool &finishedTurn)
void calcGammas_cycl()
virtual ElementBase * clone() const override
double getWidth() const
Definition: Septum.cpp:44
std::unique_ptr< Stepper< function_t > > itsStepper_mp
IpplTimings::TimerRef DelParticleTimer_m
void set_sPos(double s)
virtual double getAzimuth() const
Definition: RFCavity.cpp:391
unsigned int delPartFreq
The frequency to delete particles (currently: OPAL-cycl only)
Definition: Options.cpp:112
bool reduce(Communicate &, InputIterator, InputIterator, OutputIterator, const ReduceOp &, bool *IncludeVal=0)
Definition: GlobalComm.hpp:55
virtual double getCyclHarm() const
Definition: Cyclotron.cpp:281
static OpalData * getInstance()
Definition: OpalData.cpp:209
double getYStart() const
Interface for probe.
Definition: Probe.h:16
virtual void visitProbe(const Probe &)
Apply the algorithm to a Probe.
virtual double getMinR() const
Definition: Cyclotron.cpp:306
static void writeFields(Component *field)
Definition: DumpFields.cpp:126
void calcBeamParameters()
Vector_t getNextPosition() const
Definition: Ring.cpp:209
virtual void execute()
Apply the algorithm to the top-level beamline.
void rotateAroundX(ParticleAttrib< Vector_t > &particleVectors, double const psi)
Template class for beam lines.
Definition: TBeamline.h:40
double getSymmetry() const
Definition: Ring.h:286
int sptDumpFreq
The frequency to dump single particle trajectory of particles with ID = 0 &amp; 1.
Definition: Options.cpp:57
ParticleAttrib< short > PType
ParticleIndex_t & ID
virtual double getPhi0() const
Definition: RFCavity.cpp:411
virtual double getGapWidth() const
Definition: RFCavity.cpp:407
virtual void visitFlexibleCollimator(const FlexibleCollimator &)
Apply the algorithm to a flexible collimator.
constexpr double pi
The value of .
Definition: Physics.h:31
virtual double getBScale() const
Definition: Cyclotron.cpp:269
virtual void visitRBend(const RBend &)
Apply the algorithm to a RBend.
bool getFieldsAtPoint(const double &t, const size_t &Pindex, Vector_t &Efield, Vector_t &Bfield)
virtual double getPerpenDistance() const
Definition: RFCavity.cpp:403
void countTotalNumPerBunch()
virtual void accept(BeamlineVisitor &visitor) const =0
Apply visitor.
virtual double getElementLength() const
Get design length.
Definition: ElementBase.h:511
bool getTunes(dvector_t &t, dvector_t &r, dvector_t &z, int lastTurn, double &nur, double &nuz)
virtual std::string getComponentType() const override
Definition: RFCavity.cpp:438
void injectBunch(bool &flagTransition)
virtual void visitMultipoleTCurvedConstRadius(const MultipoleTCurvedConstRadius &)
Apply the algorithm to a MultipoleTCurvedConstRadius.
int repartFreq
The frequency to do particles repartition for better load balance between nodes.
Definition: Options.cpp:60
virtual void visitMultipole(const Multipole &)
Apply the algorithm to a Multipole.
int partInside(const Vector_t &r, const Vector_t &v, const double dt, int Parttype, const double Qloss, Vector_t &intecoords, int &triId)
virtual void initialise(PartBunchBase< double, 3 > *bunch, double &startField, double &endField)
Definition: Cyclotron.cpp:1023
const double pi
Definition: fftpack.cpp:894
void allreduce(const T *input, T *output, int count, Op op)
Definition: GlobalComm.hpp:510
virtual void initialise(PartBunchBase< double, 3 > *bunch, double &startField, double &endField) override
Pure virtual implementation of Component.
Definition: Septum.cpp:33
constexpr double c
The velocity of light in m/s.
Definition: Physics.h:52
bool isTurnDone()
Check if turn done.
Vector3D cross(const Vector3D &lhs, const Vector3D &rhs)
Vector cross product.
Definition: Vector3D.cpp:111
Interface for cyclotron collimator.
Definition: CCollimator.h:13
size_t getTotalNumPerBunch(short n) const
virtual void visitCyclotronValley(const CyclotronValley &)
Apply the algorithm to a CyclotronValley.it is empty for cyclotrontracker .
static void startTimer(TimerRef t)
Definition: IpplTimings.h:187
static void writeFields(Component *field)
virtual void visitRing(const Ring &ring)
Apply the algorithm to an Ring.
virtual void visitScalingFFAMagnet(const ScalingFFAMagnet &bend)
Apply the algorithm to a ScalingFFAMagnet.
double getChargePerParticle() const
get the macro particle charge
void kick(const Vector_t &R, Vector_t &P, const Vector_t &Ef, const Vector_t &Bf, const double &dt) const
Definition: BorisPusher.h:48
void computePathLengthUpdate(std::vector< double > &dl, const double &dt)
Abstract beam-beam interaction.
Definition: BeamBeam.h:37
virtual bool getStop() const
double getOPMass() const
Definition: Stripper.cpp:59
std::string getFrequencyModelName()
Definition: RFCavity.h:503
Definition: SBend.h:68
virtual void iterate(BeamlineVisitor &, bool r2l) const
Apply visitor to all elements of the line.
Definition: TBeamline.h:215
Vector_t RefPartP_m
virtual double getPZinit() const
Definition: Cyclotron.cpp:144
IpplTimings::TimerRef TransformTimer_m
virtual std::string getFieldMapFN() const
Definition: Cyclotron.cpp:168
std::string getTypeString() const
Definition: ElementBase.h:705
void dumpAzimuthAngles_m(const double &t, const Vector_t &R, const Vector_t &P, const double &oldReferenceTheta, const double &temp_meanTheta)
double getTemperature() const
bool RFkick(RFCavity *rfcavity, const double t, const double dt, const int Pindex)
virtual ElementBase * clone() const override
Interface for cyclotron valley.
PETE_TBTree< FnArcTan2, PETE_Scalar< Vektor< T1, Dim > >, typename T2::PETE_Expr_t > atan2(const Vektor< T1, Dim > &l, const PETE_Expr< T2 > &r)
void dumpH5(PartBunchBase< double, 3 > *beam, Vector_t FDext[]) const
Definition: DataSink.cpp:69
unsigned int getNumberOfTrimcoils() const
Definition: Cyclotron.cpp:277
IpplTimings::TimerRef BinRepartTimer_m
Tps< T > pow(const Tps< T > &x, int y)
Integer power.
Definition: TpsMath.h:76
virtual double getPRinit() const
Definition: Cyclotron.cpp:120
Vektor< double, 3 > Vector_t
Definition: Vektor.h:6
std::unique_ptr< MultiBunchHandler > mbHandler_m
std::vector< std::unique_ptr< std::ofstream > > outfTheta_m
output coordinates at different azimuthal angles and one after every turn
int rebinFreq
The frequency to reset energy bin ID for all particles.
Definition: Options.cpp:69
virtual void visitCorrector(const Corrector &)
Apply the algorithm to a Corrector.
Interface for solenoids.
Definition: Solenoid.h:36
Leap-Frog 2nd order.
Definition: LF2.h:9
An abstract sequence of beam line components.
Definition: Beamline.h:37
void setTotalNum(size_t n)
const PartData itsReference
The reference information.
std::pair< ElementBase::ElementType, element_pair > type_pair
int getFieldFlag(const std::string &type) const
Definition: Cyclotron.cpp:184
virtual void visitVariableRFCavityFringeField(const VariableRFCavityFringeField &cav)
Apply the algorithm to a VariabelRFCavity.
void openFiles(size_t numFiles, std::string fn)
@ open / close output coordinate files
size_t getLocalNum() const
virtual ElementBase * clone() const =0
Return clone.
size_t getLocalNumPerBunch(short n) const
Message & get(const T &cval)
Definition: Message.h:484
virtual double getPScale() const
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
std::function< bool(const double &, const size_t &, Vector_t &, Vector_t &)> function_t
virtual void visitSBend3D(const SBend3D &)
Apply the algorithm to a SBend3D.
virtual void visitCyclotron(const Cyclotron &cycl)
Apply the algorithm to a Cyclotorn.
int psDumpFreq
The frequency to dump the phase space, i.e.dump data when steppsDumpFreq==0.
Definition: Options.cpp:48
double getBeamPhiInit() const
Definition: Ring.h:241
void setLocalBinCount(size_t num, int bin)
Message & put(const T &val)
Definition: Message.h:414
bool psDumpEachTurn
phase space dump flag for OPAL-cycl
Definition: Options.cpp:30
virtual void visitMultipoleT(const MultipoleT &)
Apply the algorithm to a MultipoleT.
double getGamma() const
The relativistic gamma per particle.
Definition: PartData.h:132
short getNumBunch() const
DumpFrame psDumpFrame
flag to decide in which coordinate frame the phase space will be dumped for OPAL-cycl ...
Definition: Options.cpp:31
double getXEnd() const
virtual double getRmax() const
Definition: Cyclotron.cpp:290
void performDestroy(bool updateLocalNum=false)
virtual double getCycFrequency() const
Definition: RFCavity.cpp:445
bool asciidump
Definition: Options.cpp:18
void singleMode_m(double &t, const double dt, bool &finishedTurn, double &oldReferenceTheta)
void getMomentaKick(const double normalRadius, double momentum[], const double t, const double dtCorrt, const int PID, const double restMass, const int chargenumber)
used in OPAL-cycl
Definition: RFCavity.cpp:459
ParticleAttrib< short > cavityGapCrossed
particle just crossed cavity gap (for ParallelCyclotronTracker)
virtual double getRmax() const
Definition: RFCavity.cpp:387
IpplTimings::TimerRef PluginElemTimer_m
static Vector_t const xaxis
The positive axes unit vectors.
Interface for RF cavity.
Definition: RFCavity.h:37
virtual void visitBeamStripping(const BeamStripping &)
Apply the algorithm to a Beam Stripping.
virtual double getRinit() const
Definition: Cyclotron.cpp:112
virtual double getSinAzimuth() const
Definition: RFCavity.cpp:395
Tps< T > cos(const Tps< T > &x)
Cosine.
Definition: TpsMath.h:129
Abstract collimator.
Definition: Degrader.h:37
virtual void visitBeamline(const Beamline &)
Apply the algorithm to a beam line.
void setT(double t)
constexpr double q_e
The elementary charge in As.
Definition: Physics.h:76
double getQ() const
Access to reference data.
std::vector< CavityCrossData > cavCrossDatas_m
void normalizeVector(Vector_t &vector)
void appendElement(const Component &element)
Definition: Ring.cpp:227
constexpr double deg2rad
The conversion factor from degrees to radians.
Definition: Physics.h:49
virtual void visitBeamBeam(const BeamBeam &)
Apply the algorithm to a BeamBeam.
virtual std::string getResidualGas() const
void setMultiBunchInitialPathLengh(MultiBunchHandler *mbhandler_p)
Definition: DataSink.cpp:300
Vector_t getNextNormal() const
Definition: Ring.cpp:218
bool applyPluginElements(const double dt)
int scSolveFreq
The frequency to solve space charge fields.
Definition: Options.cpp:72
ParticleAttrib< int > Bin
void dumpSDDS(PartBunchBase< double, 3 > *beam, Vector_t FDext[], const double &azimuth=-1) const
Definition: DataSink.cpp:99
virtual void visitMonitor(const Monitor &)
Apply the algorithm to a Monitor.
virtual void boundp()
Ring describes a ring type geometry for tracking.
Definition: Ring.h:64
virtual void visitSBend(const SBend &)
Apply the algorithm to a SBend.
virtual void setBinCharge(int bin, double q)
Set the charge of one bin to the value of q and all other to zero.
ParticleAttrib< Vector_t > Bf
std::string getAmplitudeModelName()
Definition: RFCavity.h:473
double getHarmonicNumber()
Definition: Ring.h:222
ParticleAttrib< double > M
void updateGeometry(Vector_t startPosition, Vector_t startDirection)
Definition: Offset.cpp:185
int boundpDestroyFreq
Definition: Options.cpp:91
ParticlePos_t & R
int mtsSubsteps
Definition: Options.cpp:75
virtual void visitSeparator(const Separator &)
Apply the algorithm to a Separator.
virtual bool hasAttribute(const std::string &aKey) const
Test for existence of an attribute.
std::string::iterator iterator
Definition: MSLang.h:16
void rotateWithQuaternion(ParticleAttrib< Vector_t > &vectorArray, Quaternion_t const quaternion)
virtual void visitLambertson(const Lambertson &)
Apply the algorithm to a Lambertson.
virtual double getCosAzimuth() const
Definition: RFCavity.cpp:399
virtual double getMaxZ() const
Definition: Cyclotron.cpp:327
ElementBase * clone() const override
Definition: MultipoleT.cpp:84
static TimerRef getTimer(const char *nm)
Definition: IpplTimings.h:182
bool for_each(const BareFieldIterator< T, D > &p, SameFieldID s, C)
Definition: AssignDefs.h:30
Interface for a single beam element.
Definition: Component.h:51
virtual void initialise(PartBunchBase< double, 3 > *bunch, double &startField, double &endField) override
Pure virtual implementation of Component.
double getWidth()
Definition: CCollimator.h:88
void seoMode_m(double &t, const double dt, bool &finishedTurn, dvector_t &Ttime, dvector_t &Tdeltr, dvector_t &Tdeltz, ivector_t &TturnNumber)
static void stopTimer(TimerRef t)
Definition: IpplTimings.h:192
virtual void initialise(PartBunchBase< double, 3 > *bunch, double &startField, double &endField)
void dumpThetaEachTurn_m(const double &t, const Vector_t &R, const Vector_t &P, const double &temp_meanTheta, bool &finishedTurn)
std::string getInputBasename()
get input file name without extension
Definition: OpalData.cpp:717
bool checkGapCross(Vector_t Rold, Vector_t Rnew, RFCavity *rfcavity, double &DistOld)
Message * receive_block(int &node, int &tag)
void normalizeQuaternion(Quaternion_t &quaternion)
int getStepsPerTurn() const
static std::shared_ptr< AbstractTimeDependence > getTimeDependence(std::string name)
Definition: Inform.h:41
virtual void visitDiagnostic(const Diagnostic &)
Apply the algorithm to a Diagnostic.
static Communicate * Comm
Definition: IpplInfo.h:93
virtual void visitMultipoleTStraight(const MultipoleTStraight &)
Apply the algorithm to a MultipoleTStraight.
void buildupFieldList(double BcParameter[], ElementBase::ElementType elementType, Component *elptr)
double calculateAngle2(double x, double y)
PartBunchBase< double, 3 > * itsBunch_m
The bunch of particles to be tracked.
Definition: Tracker.h:174
double getOPCharge() const
Definition: Stripper.cpp:55
constexpr double c_mmtns
std::vector< double > dvector_t
Inform & endl(Inform &inf)
Definition: Inform.cpp:42
BoundaryGeometry * getGlobalGeometry()
Definition: OpalData.cpp:506
virtual void visitStripper(const Stripper &)
Apply the algorithm to a charge stripper.
std::string getPhaseModelName()
Definition: RFCavity.h:488
Interface for a Lambertson septum.
Definition: Lambertson.h:33
virtual void visitParallelPlate(const ParallelPlate &)
Apply the algorithm to a ParallelPlate, it is empty for cyclotrontracker .
std::unique_ptr< LossDataSink > lossDs_m
Track particles or bunches.
Definition: Tracker.h:84