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