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