OPAL (Object Oriented Parallel Accelerator Library) 2022.1
OPAL
Distribution.cpp
Go to the documentation of this file.
1//
2// Class Distribution
3// This class defines the initial beam that is injected or emitted into the simulation.
4//
5// Copyright (c) 2008 - 2022, Paul Scherrer Institut, Villigen PSI, Switzerland
6// All rights reserved
7//
8// This file is part of OPAL.
9//
10// OPAL is free software: you can redistribute it and/or modify
11// it under the terms of the GNU General Public License as published by
12// the Free Software Foundation, either version 3 of the License, or
13// (at your option) any later version.
14//
15// You should have received a copy of the GNU General Public License
16// along with OPAL. If not, see <https://www.gnu.org/licenses/>.
17//
19
24#include "Algorithms/PartBins.h"
27#include "BasicActions/Option.h"
32#include "Physics/Physics.h"
33#include "Physics/Units.h"
37#include "Utilities/Options.h"
38#include "Utilities/Util.h"
39#include "Utility/IpplTimings.h"
40
41#include <gsl/gsl_histogram.h>
42#include <gsl/gsl_linalg.h>
43#include <gsl/gsl_randist.h>
44#include <gsl/gsl_rng.h>
45#include <gsl/gsl_sf_erf.h>
46
47#include <boost/filesystem.hpp>
48#include <boost/regex.hpp>
49#include <boost/numeric/odeint/stepper/runge_kutta4.hpp>
50
51#include <sys/time.h>
52
53#include <cmath>
54#include <cfloat>
55#include <iomanip>
56#include <iostream>
57#include <map>
58#include <numeric>
59
60extern Inform *gmsg;
61
62constexpr double SMALLESTCUTOFF = 1e-12;
63
64/* Increase tEmission_m by twice this percentage
65 * to ensure that no particles fall on the leading edge of
66 * the first emission time step or the trailing edge of the last
67 * emission time step. */
68const double Distribution::percentTEmission_m = 0.0005;
69
70namespace {
71 SymTenzor<double, 6> getUnit6x6() {
73 for (unsigned int i = 0; i < 6u; ++ i) {
74 unit6x6(i,i) = 1.0;
75 }
76 return unit6x6;
77 }
78}
79
80
82 Definition( Attrib::Legacy::Distribution::SIZE, "DISTRIBUTION",
83 "The DISTRIBUTION statement defines data for the 6D particle distribution."),
84 distrTypeT_m(DistributionType::NODIST),
85 numberOfDistributions_m(1),
86 emitting_m(false),
87 emissionModel_m(EmissionModel::NONE),
88 tEmission_m(0.0),
89 tBin_m(0.0),
90 currentEmissionTime_m(0.0),
91 currentEnergyBin_m(1),
92 currentSampleBin_m(0),
93 numberOfEnergyBins_m(0),
94 numberOfSampleBins_m(0),
95 energyBins_m(nullptr),
96 energyBinHist_m(nullptr),
97 randGen_m(nullptr),
98 pTotThermal_m(0.0),
99 pmean_m(0.0),
100 cathodeWorkFunc_m(0.0),
101 laserEnergy_m(0.0),
102 cathodeFermiEnergy_m(0.0),
103 cathodeTemp_m(0.0),
104 emitEnergyUpperLimit_m(0.0),
105 totalNumberParticles_m(0),
106 totalNumberEmittedParticles_m(0),
107 avrgpz_m(0.0),
108 inputMoUnits_m(InputMomentumUnits::NONE),
109 sigmaTRise_m(0.0),
110 sigmaTFall_m(0.0),
111 tPulseLengthFWHM_m(0.0),
112 correlationMatrix_m(getUnit6x6()),
113 sepPeaks_m(0.0),
114 nPeaks_m(1),
115 laserProfileFileName_m(""),
116 laserImageName_m(""),
117 laserIntensityCut_m(0.0),
118 laserProfile_m(nullptr),
119 I_m(0.0),
120 E_m(0.0)
121{
123
124 Distribution *defaultDistribution = clone("UNNAMED_Distribution");
125 defaultDistribution->builtin = true;
126
127 try {
128 OpalData::getInstance()->define(defaultDistribution);
129 } catch(...) {
130 delete defaultDistribution;
131 }
132
133 gsl_rng_env_setup();
134 randGen_m = gsl_rng_alloc(gsl_rng_default);
135}
142Distribution::Distribution(const std::string &name, Distribution *parent):
143 Definition(name, parent),
144 distT_m(parent->distT_m),
145 distrTypeT_m(DistributionType::NODIST),
146 numberOfDistributions_m(parent->numberOfDistributions_m),
147 emitting_m(parent->emitting_m),
148 particleRefData_m(parent->particleRefData_m),
149 addedDistributions_m(parent->addedDistributions_m),
150 particlesPerDist_m(parent->particlesPerDist_m),
151 emissionModel_m(parent->emissionModel_m),
152 tEmission_m(parent->tEmission_m),
153 tBin_m(parent->tBin_m),
154 currentEmissionTime_m(parent->currentEmissionTime_m),
155 currentEnergyBin_m(parent->currentEnergyBin_m),
156 currentSampleBin_m(parent->currentSampleBin_m),
157 numberOfEnergyBins_m(parent->numberOfEnergyBins_m),
158 numberOfSampleBins_m(parent->numberOfSampleBins_m),
159 energyBins_m(nullptr),
160 energyBinHist_m(nullptr),
161 randGen_m(nullptr),
162 pTotThermal_m(parent->pTotThermal_m),
163 pmean_m(parent->pmean_m),
164 cathodeWorkFunc_m(parent->cathodeWorkFunc_m),
165 laserEnergy_m(parent->laserEnergy_m),
166 cathodeFermiEnergy_m(parent->cathodeFermiEnergy_m),
167 cathodeTemp_m(parent->cathodeTemp_m),
168 emitEnergyUpperLimit_m(parent->emitEnergyUpperLimit_m),
169 totalNumberParticles_m(parent->totalNumberParticles_m),
170 totalNumberEmittedParticles_m(parent->totalNumberEmittedParticles_m),
171 xDist_m(parent->xDist_m),
172 pxDist_m(parent->pxDist_m),
173 yDist_m(parent->yDist_m),
174 pyDist_m(parent->pyDist_m),
175 tOrZDist_m(parent->tOrZDist_m),
176 pzDist_m(parent->pzDist_m),
177 xWrite_m(parent->xWrite_m),
178 pxWrite_m(parent->pxWrite_m),
179 yWrite_m(parent->yWrite_m),
180 pyWrite_m(parent->pyWrite_m),
181 tOrZWrite_m(parent->tOrZWrite_m),
182 pzWrite_m(parent->pzWrite_m),
183 avrgpz_m(parent->avrgpz_m),
184 inputMoUnits_m(parent->inputMoUnits_m),
185 sigmaTRise_m(parent->sigmaTRise_m),
186 sigmaTFall_m(parent->sigmaTFall_m),
187 tPulseLengthFWHM_m(parent->tPulseLengthFWHM_m),
188 sigmaR_m(parent->sigmaR_m),
189 sigmaP_m(parent->sigmaP_m),
190 cutoffR_m(parent->cutoffR_m),
191 cutoffP_m(parent->cutoffP_m),
192 correlationMatrix_m(parent->correlationMatrix_m),
193 sepPeaks_m(parent->sepPeaks_m),
194 nPeaks_m(parent->nPeaks_m),
195 laserProfileFileName_m(parent->laserProfileFileName_m),
196 laserImageName_m(parent->laserImageName_m),
197 laserIntensityCut_m(parent->laserIntensityCut_m),
198 laserProfile_m(nullptr),
199 I_m(parent->I_m),
200 E_m(parent->E_m),
201 tRise_m(parent->tRise_m),
202 tFall_m(parent->tFall_m),
203 sigmaRise_m(parent->sigmaRise_m),
204 sigmaFall_m(parent->sigmaFall_m),
205 cutoff_m(parent->cutoff_m)
206{
207 gsl_rng_env_setup();
208 randGen_m = gsl_rng_alloc(gsl_rng_default);
209}
210
212
213 delete energyBins_m;
214 gsl_histogram_free(energyBinHist_m);
215 gsl_rng_free(randGen_m);
216 delete laserProfile_m;
217}
218
219
229
230 size_t locNumber = n / Ippl::getNodes();
231
232 // make sure the total number is exact
233 size_t remainder = n % Ippl::getNodes();
234 size_t myNode = Ippl::myNode();
235 if (myNode < remainder)
236 ++ locNumber;
237
238 return locNumber;
239}
240
243 return dynamic_cast<Distribution *>(object) != 0;
244}
245
247 return new Distribution(name, this);
248}
249
251}
252
254}
255
256void Distribution::create(size_t &numberOfParticles, double massIneV, double charge) {
257
258 /*
259 * If Options::cZero is true than we reflect generated distribution
260 * to ensure that the transverse averages are 0.0.
261 *
262 * For now we just cut the number of generated particles in half.
263 */
264 size_t numberOfLocalParticles = numberOfParticles;
266 numberOfLocalParticles = (numberOfParticles + 1) / 2;
267 }
268
269 size_t mySeed = Options::seed;
270
271 if (Options::seed == -1) {
272 struct timeval tv;
273 gettimeofday(&tv,0);
274 mySeed = tv.tv_sec + tv.tv_usec;
275 }
276
278 numberOfLocalParticles = getNumOfLocalParticlesToCreate(numberOfLocalParticles);
279 *gmsg << level2 << "* Generation of distribution with seed = " << mySeed << " + core_id\n"
280 << "* is scalable with number of particles and cores." << endl;
281 mySeed += Ippl::myNode();
282 } else {
283 *gmsg << level2 << "* Generation of distribution with seed = " << mySeed << "\n"
284 << "* isn't scalable with number of particles and cores." << endl;
285 }
286
287 gsl_rng_set(randGen_m, mySeed);
288
289 switch (distrTypeT_m) {
290
292 createMatchedGaussDistribution(numberOfLocalParticles, massIneV, charge);
293 break;
295 createDistributionFromFile(numberOfParticles, massIneV);
296 break;
298 createDistributionGauss(numberOfLocalParticles, massIneV);
299 break;
301 createDistributionBinomial(numberOfLocalParticles, massIneV);
302 break;
306 createDistributionFlattop(numberOfLocalParticles, massIneV);
307 break;
309 createDistributionMultiGauss(numberOfLocalParticles, massIneV);
310 break;
311 default:
312 throw OpalException("Distribution::create",
313 "Unknown \"TYPE\" of \"DISTRIBUTION\"");
314 }
315
316 if (emitting_m) {
317
318 unsigned int numAdditionalRNsPerParticle = 0;
322
323 numAdditionalRNsPerParticle = 2;
325 if (Options::cZero) {
326 numAdditionalRNsPerParticle = 40;
327 } else {
328 numAdditionalRNsPerParticle = 20;
329 }
330 }
331
332 int saveProcessor = -1;
333 const int myNode = Ippl::myNode();
334 const int numNodes = Ippl::getNodes();
336
337 for (size_t partIndex = 0; partIndex < numberOfLocalParticles; ++ partIndex) {
338
339 // Save to each processor in turn.
340 ++ saveProcessor;
341 if (saveProcessor >= numNodes)
342 saveProcessor = 0;
343
344 if (scalable || myNode == saveProcessor) {
345 std::vector<double> rns;
346 for (unsigned int i = 0; i < numAdditionalRNsPerParticle; ++ i) {
347 double x = gsl_rng_uniform(randGen_m);
348 rns.push_back(x);
349 }
350 additionalRNs_m.push_back(rns);
351 } else {
352 for (unsigned int i = 0; i < numAdditionalRNsPerParticle; ++ i) {
353 gsl_rng_uniform(randGen_m);
354 }
355 }
356 }
357 }
358
359 // Scale coordinates according to distribution input.
361
362 // Now reflect particles if Options::cZero is true
363 reflectDistribution(numberOfLocalParticles);
364
365 adjustPhaseSpace(massIneV);
366
367 if (Options::seed != -1)
368 Options::seed = gsl_rng_uniform_int(randGen_m, gsl_rng_max(randGen_m));
369
370 if (particlesPerDist_m.empty()) {
371 particlesPerDist_m.push_back(tOrZDist_m.size());
372 } else {
373 particlesPerDist_m[0] = tOrZDist_m.size();
374 }
375}
376
377void Distribution::doRestartOpalT(PartBunchBase<double, 3> *beam, size_t /*Np*/, int restartStep, H5PartWrapper *dataSource) {
378
380
381 long numParticles = dataSource->getNumParticles();
382 size_t numParticlesPerNode = numParticles / Ippl::getNodes();
383
384 size_t firstParticle = numParticlesPerNode * Ippl::myNode();
385 size_t lastParticle = firstParticle + numParticlesPerNode - 1;
386 if (Ippl::myNode() == Ippl::getNodes() - 1)
387 lastParticle = numParticles - 1;
389
390 numParticles = lastParticle - firstParticle + 1;
391 PAssert_GE(numParticles, 0);
392
393 beam->create(numParticles);
394
395 dataSource->readHeader();
396 dataSource->readStep(beam, firstParticle, lastParticle);
397
398 beam->boundp();
399
400 double actualT = beam->getT();
401 long long ltstep = beam->getLocalTrackStep();
402 long long gtstep = beam->getGlobalTrackStep();
403
405
406 *gmsg << "Total number of particles in the h5 file= " << beam->getTotalNum() << "\n"
407 << "Global step= " << gtstep << "; Local step= " << ltstep << "; "
408 << "restart step= " << restartStep << "\n"
409 << "time of restart= " << actualT << "; phishift= " << OpalData::getInstance()->getGlobalPhaseShift() << endl;
410}
411
413 size_t /*Np*/,
414 int /*restartStep*/,
415 const int specifiedNumBunch,
416 H5PartWrapper *dataSource) {
417
418 // h5_int64_t rc;
420 INFOMSG("---------------- Start reading hdf5 file----------------" << endl);
421
422 long numParticles = dataSource->getNumParticles();
423 size_t numParticlesPerNode = numParticles / Ippl::getNodes();
424
425 size_t firstParticle = numParticlesPerNode * Ippl::myNode();
426 size_t lastParticle = firstParticle + numParticlesPerNode - 1;
427 if (Ippl::myNode() == Ippl::getNodes() - 1)
428 lastParticle = numParticles - 1;
430
431 numParticles = lastParticle - firstParticle + 1;
432 PAssert_GE(numParticles, 0);
433
434 beam->create(numParticles);
435
436 dataSource->readHeader();
437 dataSource->readStep(beam, firstParticle, lastParticle);
438
439 beam->Q = beam->getChargePerParticle();
440
441 beam->boundp();
442
443 double meanE = static_cast<H5PartWrapperForPC*>(dataSource)->getMeanKineticEnergy();
444
445 const int globalN = beam->getTotalNum();
446 INFOMSG("Restart from hdf5 format file " << OpalData::getInstance()->getRestartFileName() << endl);
447 INFOMSG("total number of particles = " << globalN << endl);
448 INFOMSG("* Restart Energy = " << meanE << " (MeV), Path lenght = " << beam->get_sPos() << " (m)" << endl);
449 INFOMSG("Tracking Step since last bunch injection is " << beam->getSteptoLastInj() << endl);
450 INFOMSG(beam->getNumBunch() << " Bunches(bins) exist in this file" << endl);
451
452 double gamma = 1 + meanE / (beam->getM() * Units::eV2MeV);
453 double beta = std::sqrt(1.0 - (1.0 / std::pow(gamma, 2)));
454
455 INFOMSG("* Gamma = " << gamma << ", Beta = " << beta << endl);
456
457 if (dataSource->predecessorIsSameFlavour()) {
458 INFOMSG("Restart from hdf5 file generated by OPAL-cycl" << endl);
459 if (specifiedNumBunch > 1) {
460 // the allowed maximal bin number is set to 1000
461 energyBins_m = new PartBinsCyc(1000, beam->getNumBunch());
462 beam->setPBins(energyBins_m);
463 }
464
465 } else {
466 INFOMSG("Restart from hdf5 file generated by OPAL-t" << endl);
467
468 Vector_t meanR(0.0, 0.0, 0.0);
469 Vector_t meanP(0.0, 0.0, 0.0);
470 unsigned long int newLocalN = beam->getLocalNum();
471 for (unsigned int i = 0; i < newLocalN; ++i) {
472 for (int d = 0; d < 3; ++d) {
473 meanR(d) += beam->R[i](d);
474 meanP(d) += beam->P[i](d);
475 }
476 }
477 reduce(meanR, meanR, OpAddAssign());
478 meanR /= Vector_t(globalN);
479 reduce(meanP, meanP, OpAddAssign());
480 meanP /= Vector_t(globalN);
481 INFOMSG("Rmean = " << meanR << "[m], Pmean=" << meanP << endl);
482
483 for (unsigned int i = 0; i < newLocalN; ++i) {
484 beam->R[i] -= meanR;
485 beam->P[i] -= meanP;
486 }
487 }
488
489 INFOMSG("---------------Finished reading hdf5 file---------------" << endl);
491}
492
494 Distribution *dist = dynamic_cast<Distribution *>(OpalData::getInstance()->find(name));
495
496 if (dist == 0) {
497 throw OpalException("Distribution::find()", "Distribution \"" + name + "\" not found.");
498 }
499
500 return dist;
501}
502
504 if (tEmission_m > 0.0) {
505 return tEmission_m;
506 }
507
508 setDistType();
509
514 double tratio = std::sqrt(2.0 * std::log(10.0)) - std::sqrt(2.0 * std::log(10.0 / 9.0));
515 sigmaRise_m = tRise_m / tratio;
516 sigmaFall_m = tFall_m / tratio;
517
518 switch(distrTypeT_m) {
520 double a = tPulseLengthFWHM_m / 2;
521 double sig = tRise_m / 2;
522 double inv_erf08 = 0.906193802436823; // erfinv(0.8)
523 double sqr2 = std::sqrt(2.);
524 double t = a - sqr2 * sig * inv_erf08;
525 double tmps = sig;
526 double tmpt = t;
527 for (int i = 0; i < 10; ++ i) {
528 sig = (t + tRise_m - a) / (sqr2 * inv_erf08);
529 t = a - 0.5 * sqr2 * (sig + tmps) * inv_erf08;
530 sig = (0.5 * (t + tmpt) + tRise_m - a) / (sqr2 * inv_erf08);
531 tmps = sig;
532 tmpt = t;
533 }
534 tEmission_m = tPulseLengthFWHM_m + 10 * sig;
535 break;
536 }
539 break;
540 }
541 default:
542 tEmission_m = 0.0;
543 }
544 return tEmission_m;
545}
546
548
549 os << "\n"
550 << "* ************* D I S T R I B U T I O N ********************************************" << endl;
551 os << "* " << endl;
552 if (OpalData::getInstance()->inRestartRun()) {
553 os << "* In restart. Distribution read in from .h5 file." << endl;
554 } else {
555 if (!addedDistributions_m.empty()) {
556 os << "* Main Distribution" << endl
557 << "-----------------" << endl;
558 }
559 if (particlesPerDist_m.empty())
560 printDist(os, 0);
561 else
562 printDist(os, particlesPerDist_m.at(0));
563
564 size_t distCount = 1;
565 for (unsigned distIndex = 0; distIndex < addedDistributions_m.size(); distIndex++) {
566 os << "* " << endl;
567 os << "* Added Distribution #" << distCount << endl;
568 os << "* ----------------------" << endl;
569 addedDistributions_m.at(distIndex)->printDist(os, particlesPerDist_m.at(distCount));
570 distCount++;
571 }
572
573 os << "* " << endl;
574 if (numberOfEnergyBins_m > 0) {
575 os << "* Number of energy bins = " << numberOfEnergyBins_m << endl;
576
577 // if (numberOfEnergyBins_m > 1)
578 // printEnergyBins(os);
579 }
580
581 if (emitting_m) {
582 os << "* Distribution is emitted. " << endl;
583 os << "* Emission time = " << tEmission_m << " [sec]" << endl;
584 os << "* Time per bin = " << tEmission_m / numberOfEnergyBins_m << " [sec]" << endl;
585 os << "* Delta t during emission = " << tBin_m / numberOfSampleBins_m << " [sec]" << endl;
586 os << "* " << endl;
588 } else {
589 os << "* Distribution is injected." << endl;
590 }
591
593 os << "*\n* Write initial distribution to file '" << outFilename_m << "'" << endl;
594 }
595 }
596 os << "* " << endl;
597 os << "* **********************************************************************************" << endl;
598
599 return os;
600}
601
603 /*
604 * Move particle coordinates from added distributions to main distribution.
605 */
606
607 size_t idx = 1;
609 for (addedDistIt = addedDistributions_m.begin();
610 addedDistIt != addedDistributions_m.end(); ++ addedDistIt, ++ idx) {
611
612 particlesPerDist_m[idx] = (*addedDistIt)->tOrZDist_m.size();
613
614 for (double dist : (*addedDistIt)->getXDist()) {
615 xDist_m.push_back(dist);
616 }
617 (*addedDistIt)->eraseXDist();
618
619 for (double dist : (*addedDistIt)->getBGxDist()) {
620 pxDist_m.push_back(dist);
621 }
622 (*addedDistIt)->eraseBGxDist();
623
624 for (double dist : (*addedDistIt)->getYDist()) {
625 yDist_m.push_back(dist);
626 }
627 (*addedDistIt)->eraseYDist();
628
629 for (double dist : (*addedDistIt)->getBGyDist()) {
630 pyDist_m.push_back(dist);
631 }
632 (*addedDistIt)->eraseBGyDist();
633
634 for (double dist : (*addedDistIt)->getTOrZDist()) {
635 tOrZDist_m.push_back(dist);
636 }
637 (*addedDistIt)->eraseTOrZDist();
638
639 for (double dist : (*addedDistIt)->getBGzDist()) {
640 pzDist_m.push_back(dist);
641 }
642 (*addedDistIt)->eraseBGzDist();
643 }
644}
645
646void Distribution::applyEmissionModel(double lowEnergyLimit, double &px, double &py, double &pz, std::vector<double> &additionalRNs) {
647
648 switch (emissionModel_m) {
649
652 break;
654 applyEmissModelAstra(px, py, pz, additionalRNs);
655 break;
657 applyEmissModelNonEquil(lowEnergyLimit, px, py, pz, additionalRNs);
658 break;
659 default:
660 break;
661 }
662}
663
664void Distribution::applyEmissModelAstra(double &px, double &py, double &pz, std::vector<double> &additionalRNs) {
665
666 double phi = 2.0 * std::acos(std::sqrt(additionalRNs[0]));
667 double theta = Physics::two_pi * additionalRNs[1];
668
669 px = pTotThermal_m * std::sin(phi) * std::cos(theta);
670 py = pTotThermal_m * std::sin(phi) * std::sin(theta);
671 pz = pTotThermal_m * std::abs(std::cos(phi));
672
673}
674
676 pz += pTotThermal_m;
677}
678
679void Distribution::applyEmissModelNonEquil(double lowEnergyLimit,
680 double &bgx,
681 double &bgy,
682 double &bgz,
683 std::vector<double> &additionalRNs) {
684
685 // Generate emission energy.
686 double energy = 0.0;
687 bool allow = false;
688
689 const double expRelativeLaserEnergy = exp(laserEnergy_m / cathodeTemp_m);
690 // double energyRange = emitEnergyUpperLimit_m - lowEnergyLimit;
691 unsigned int counter = 0;
692 while (!allow) {
693 energy = lowEnergyLimit + additionalRNs[counter++] * emitEnergyUpperLimit_m;
694 double randFuncValue = additionalRNs[counter++];
695 double expRelativeEnergy = exp((energy - cathodeFermiEnergy_m) / cathodeTemp_m);
696 double funcValue = ((1.0
697 - 1.0 / (1.0 + expRelativeEnergy * expRelativeLaserEnergy)) /
698 (1.0 + expRelativeEnergy));
699
700 if (randFuncValue <= funcValue)
701 allow = true;
702
703 if (counter == additionalRNs.size()) {
704 for (unsigned int i = 0; i < counter; ++ i) {
705 additionalRNs[i] = gsl_rng_uniform(randGen_m);
706 }
707
708 counter = 0;
709 }
710 }
711
712 while (additionalRNs.size() - counter < 2) {
713 -- counter;
714 additionalRNs[counter] = gsl_rng_uniform(randGen_m);
715 }
716
717 // Compute emission angles.
718 double energyInternal = energy + laserEnergy_m;
719 double energyExternal = energy - lowEnergyLimit; // uniformly distributed (?) value between 0 and emitEnergyUpperLimit_m
720
721 double thetaInMax = std::acos(std::sqrt((lowEnergyLimit + laserEnergy_m) / (energyInternal)));
722 double thetaIn = additionalRNs[counter++] * thetaInMax;
723 double sinThetaOut = std::sin(thetaIn) * std::sqrt(energyInternal / energyExternal);
724 double phi = Physics::two_pi * additionalRNs[counter];
725
726 // Compute emission momenta.
727 double betaGammaExternal
728 = std::sqrt(std::pow(energyExternal / (Physics::m_e * Units::GeV2eV) + 1.0, 2) - 1.0);
729
730 bgx = betaGammaExternal * sinThetaOut * std::cos(phi);
731 bgy = betaGammaExternal * sinThetaOut * std::sin(phi);
732 bgz = betaGammaExternal * std::sqrt(1.0 - std::pow(sinThetaOut, 2));
733
734}
735
736void Distribution::calcPartPerDist(size_t numberOfParticles) {
737
738 if (numberOfDistributions_m == 1) {
739 particlesPerDist_m.push_back(numberOfParticles);
740 return;
741 }
742
743 std::map<unsigned int, size_t> nPartFromFiles;
744 double totalWeight = 0.0;
745 for (unsigned int i = 0; i <= addedDistributions_m.size(); ++ i) {
746 Distribution *currDist = this;
747 if (i > 0)
748 currDist = addedDistributions_m[i - 1];
749
750 if (currDist->distrTypeT_m == DistributionType::FROMFILE) {
751 std::ifstream inputFile;
752 if (Ippl::myNode() == 0) {
753 std::string fileName = Attributes::getString(currDist->itsAttr[Attrib::Distribution::FNAME]);
754 inputFile.open(fileName.c_str());
755 }
756 size_t nPart = getNumberOfParticlesInFile(inputFile);
757 nPartFromFiles.insert(std::make_pair(i, nPart));
758 if (nPart > numberOfParticles) {
759 throw OpalException("Distribution::calcPartPerDist",
760 "Number of particles is too small");
761 }
762
763 numberOfParticles -= nPart;
764 } else {
765 totalWeight += currDist->getWeight();
766 }
767 }
768
769 size_t numberOfCommittedPart = 0;
770 for (unsigned int i = 0; i <= addedDistributions_m.size(); ++ i) {
771 Distribution *currDist = this;
772 if (i > 0)
773 currDist = addedDistributions_m[i - 1];
774
775 if (currDist->distrTypeT_m == DistributionType::FROMFILE) {
776 particlesPerDist_m.push_back(nPartFromFiles[i]);
777 } else {
778 size_t particlesCurrentDist = numberOfParticles * currDist->getWeight() / totalWeight;
779 particlesPerDist_m.push_back(particlesCurrentDist);
780 numberOfCommittedPart += particlesCurrentDist;
781 }
782 }
783
784 // Remaining particles go into first distribution that isn't FROMFILE
785 if (numberOfParticles != numberOfCommittedPart) {
786 size_t diffNumber = numberOfParticles - numberOfCommittedPart;
787 for (unsigned int i = 0; i <= addedDistributions_m.size(); ++ i) {
788 Distribution *currDist = this;
789 if (i > 0)
790 currDist = addedDistributions_m[i - 1];
791
792 if (currDist->distrTypeT_m != DistributionType::FROMFILE) {
793 particlesPerDist_m.at(i) += diffNumber;
794 diffNumber = 0;
795 break;
796 }
797 }
798 if (diffNumber != 0) {
799 throw OpalException("Distribution::calcPartPerDist",
800 "Particles can't be distributed to distributions in array");
801 }
802 }
803}
804
806
807 // There must be at least on energy bin for an emitted beam->
810 if (numberOfEnergyBins_m == 0)
812
813 int emissionSteps = std::abs(static_cast<int> (Attributes::getReal(itsAttr[Attrib::Distribution::EMISSIONSTEPS])));
814
815 // There must be at least one emission step.
816 if (emissionSteps == 0)
817 emissionSteps = 1;
818
819 // Set number of sample bins per energy bin from the number of emission steps.
820 numberOfSampleBins_m = static_cast<int> (std::ceil(emissionSteps / numberOfEnergyBins_m));
821 if (numberOfSampleBins_m <= 0)
823
824 // Initialize emission counters.
827
828}
829
831
833
834 switch (distrTypeT_m) {
835
838 emitting_m = true;
839 break;
840 default:
841 break;
842 }
843}
844
845void Distribution::checkParticleNumber(size_t &numberOfParticles) {
846
847 size_t numberOfDistParticles = tOrZDist_m.size();
848 reduce(numberOfDistParticles, numberOfDistParticles, OpAddAssign());
849
850 if (numberOfDistParticles == 0) {
851 throw OpalException("Distribution::checkParticleNumber",
852 "Zero particles in the distribution! "
853 "The number of particles needs to be specified.");
854 }
855
856 if (numberOfDistParticles != numberOfParticles) {
857 throw OpalException("Distribution::checkParticleNumber",
858 "The number of particles in the initial\n"
859 "distribution " +
860 std::to_string(numberOfDistParticles) + "\n"
861 "is different from the number of particles\n"
862 "defined by the BEAM command\n" +
863 std::to_string(numberOfParticles) + ".\n"
864 "This often happens when using a FROMFILE type\n"
865 "distribution and not matching the number of\n"
866 "particles in the input distribution file(s) with\n"
867 "the number given in the BEAM command.");
868 }
869}
870
872 // If the distribution was read from a file, the file momentum pmean_m[2]
873 // should coincide with the momentum given in the beam command avrgpz_m.
874
875 if (std::abs(pmean_m[2] - avrgpz_m) / pmean_m[2] > 1e-2) {
876 throw OpalException("Distribution::checkFileMomentum",
877 "The z-momentum of the particle distribution\n" +
878 std::to_string(pmean_m[2]) + "\n"
879 "is different from the momentum given in the \"BEAM\" command\n" +
880 std::to_string(avrgpz_m) + ".\n"
881 "When using a \"FROMFILE\" type distribution\n"
882 "the momentum in the \"BEAM\" command should be\n"
883 "the same as the momentum of the particles in the file.");
884 }
885}
886
888 /*
889 * Toggle what units to use for inputing momentum.
890 */
891 static const std::map<std::string, InputMomentumUnits> stringInputMomentumUnits_s = {
892 {"NONE", InputMomentumUnits::NONE},
893 {"EVOVERC", InputMomentumUnits::EVOVERC}
894 };
895
897 if (inputUnits.empty()) {
898 inputMoUnits_m = inputMoUnits;
899 } else {
900 inputMoUnits_m = stringInputMomentumUnits_s.at(inputUnits);
901 }
902}
903
904void Distribution::createDistributionBinomial(size_t numberOfParticles, double massIneV) {
905
907 generateBinomial(numberOfParticles);
908}
909
910void Distribution::createDistributionFlattop(size_t numberOfParticles, double massIneV) {
911
912 setDistParametersFlattop(massIneV);
913
914 if (emitting_m) {
915 if (laserProfile_m == nullptr)
916 generateFlattopT(numberOfParticles);
917 else
918 generateFlattopLaserProfile(numberOfParticles);
919 } else
920 generateFlattopZ(numberOfParticles);
921}
922
923void Distribution::createDistributionMultiGauss(size_t numberOfParticles, double massIneV) {
924
925 gsl_qrng *quasiRandGen2D = selectRandomGenerator(Options::rngtype,2);
926
928
929 // Generate the distribution
930 int saveProcessor = -1;
931 const int myNode = Ippl::myNode();
932 const int numNodes = Ippl::getNodes();
934
935 double L = (nPeaks_m - 1) * sepPeaks_m;
936
937 for (size_t partIndex = 0; partIndex < numberOfParticles; partIndex++) {
938 double x = 0.0, y = 0.0, tOrZ = 0.0;
939 double px = 0.0, py = 0.0, pz = 0.0;
940 double r;
941
942 // Transverse coordinates
943 sampleUniformDisk(quasiRandGen2D, x, y);
944 x *= sigmaR_m[0];
945 y *= sigmaR_m[1];
946
947 // Longitudinal coordinates
948 bool allow = false;
949 double randNums[2] = {0.0, 0.0};
950 while (!allow) {
951 if (quasiRandGen2D != nullptr) {
952 gsl_qrng_get(quasiRandGen2D, randNums);
953 } else {
954 randNums[0] = gsl_rng_uniform(randGen_m);
955 randNums[1] = gsl_rng_uniform(randGen_m);
956 }
957 r = randNums[1] * nPeaks_m;
958 tOrZ = (2 * randNums[0] - 1) * (L/2 + sigmaR_m[2] * cutoffR_m[2]);
959
960 double proba = 0.0;
961 for (unsigned i = 0; i < nPeaks_m; i++)
962 proba += exp( - .5 * std::pow( (tOrZ + L/2 - i * sepPeaks_m) / sigmaR_m[2], 2) );
963 allow = (r <= proba);
964 }
965
966 if (!emitting_m) {
967 // Momentum has non-zero spread only if bunch is being emitted
968 allow = false;
969 while (!allow) {
970 px = gsl_ran_gaussian(randGen_m, 1.0);
971 py = gsl_ran_gaussian(randGen_m, 1.0);
972 pz = gsl_ran_gaussian(randGen_m, 1.0);
973 allow = ( (std::pow( x / cutoffP_m[0], 2) + std::pow( y / cutoffP_m[1], 2) <= 1.0)
974 && (std::abs(pz) <= cutoffP_m[2]) );
975 }
976 px *= sigmaP_m[0];
977 py *= sigmaP_m[1];
978 pz *= sigmaP_m[2];
979 pz += avrgpz_m;
980 }
981
982 // Save to each processor in turn.
983 saveProcessor++;
984 if (saveProcessor >= numNodes)
985 saveProcessor = 0;
986
987 if (scalable || myNode == saveProcessor) {
988 xDist_m.push_back(x);
989 pxDist_m.push_back(px);
990 yDist_m.push_back(y);
991 pyDist_m.push_back(py);
992 tOrZDist_m.push_back(tOrZ);
993 pzDist_m.push_back(pz);
994 }
995 }
996
997 gsl_qrng_free(quasiRandGen2D);
998}
999
1000size_t Distribution::getNumberOfParticlesInFile(std::ifstream &inputFile) {
1001
1002 size_t numberOfParticlesRead = 0;
1003 if (Ippl::myNode() == 0) {
1004 const boost::regex commentExpr("[[:space:]]*#.*");
1005 const std::string repl("");
1006 std::string line;
1007 std::stringstream linestream;
1008 long tempInt = 0;
1009
1010 do {
1011 std::getline(inputFile, line);
1012 line = boost::regex_replace(line, commentExpr, repl);
1013 } while (line.length() == 0 && !inputFile.fail());
1014
1015 linestream.str(line);
1016 linestream >> tempInt;
1017 if (tempInt <= 0) {
1018 throw OpalException("Distribution::getNumberOfParticlesInFile",
1019 "The file '" +
1021 "' does not seem to be an ASCII file containing a distribution.");
1022 }
1023 numberOfParticlesRead = static_cast<size_t>(tempInt);
1024 }
1025 reduce(numberOfParticlesRead, numberOfParticlesRead, OpAddAssign());
1026
1027 return numberOfParticlesRead;
1028}
1029
1030void Distribution::createDistributionFromFile(size_t /*numberOfParticles*/, double massIneV) {
1031 // Data input file is only read by node 0.
1032 std::ifstream inputFile;
1034 if (!boost::filesystem::exists(fileName)) {
1035 throw OpalException(
1036 "Distribution::createDistributionFromFile",
1037 "Open file operation failed, please check if '" + fileName + "' really exists.");
1038 }
1039 if (Ippl::myNode() == 0) {
1040 inputFile.open(fileName.c_str());
1041 }
1042
1043 size_t numberOfParticlesRead = getNumberOfParticlesInFile(inputFile);
1044 /*
1045 * We read in the particle information using node zero, but save the particle
1046 * data to each processor in turn.
1047 */
1048 unsigned int saveProcessor = 0;
1049
1050 pmean_m = 0.0;
1051
1052 unsigned int distributeFrequency = 1000;
1053 size_t singleDataSize = 6;
1054 unsigned int dataSize = distributeFrequency * singleDataSize;
1055 std::vector<double> data(dataSize);
1056
1057 if (Ippl::myNode() == 0) {
1058 constexpr unsigned int bufferSize = 1024;
1059 char lineBuffer[bufferSize];
1060 unsigned int numParts = 0;
1061 std::vector<double>::iterator currentPosition = data.begin();
1062 while (!inputFile.eof()) {
1063 inputFile.getline(lineBuffer, bufferSize);
1064
1065 Vector_t R(0.0), P(0.0);
1066
1067 std::istringstream line(lineBuffer);
1068 line >> R(0);
1069 if (line.rdstate())
1070 break;
1071 line >> P(0);
1072 line >> R(1);
1073 line >> P(1);
1074 line >> R(2);
1075 line >> P(2);
1076
1077 if (saveProcessor >= (unsigned)Ippl::getNodes())
1078 saveProcessor = 0;
1079
1081 P(0) = Util::convertMomentumEVoverCToBetaGamma(P(0), massIneV);
1082 P(1) = Util::convertMomentumEVoverCToBetaGamma(P(1), massIneV);
1083 P(2) = Util::convertMomentumEVoverCToBetaGamma(P(2), massIneV);
1084 }
1085 pmean_m += P;
1086
1087 if (saveProcessor > 0u) {
1088 currentPosition = std::copy(&(R[0]), &(R[0]) + 3, currentPosition);
1089 currentPosition = std::copy(&(P[0]), &(P[0]) + 3, currentPosition);
1090
1091 if (currentPosition == data.end()) {
1092 MPI_Bcast(&dataSize, 1, MPI_UNSIGNED, 0, Ippl::getComm());
1093 MPI_Bcast(&(data[0]), dataSize, MPI_DOUBLE, 0, Ippl::getComm());
1094
1095 currentPosition = data.begin();
1096 }
1097 } else {
1098 xDist_m.push_back(R(0));
1099 yDist_m.push_back(R(1));
1100 tOrZDist_m.push_back(R(2));
1101 pxDist_m.push_back(P(0));
1102 pyDist_m.push_back(P(1));
1103 pzDist_m.push_back(P(2));
1104 }
1105
1106 ++numParts;
1107 ++saveProcessor;
1108 }
1109
1110 dataSize =
1111 (numberOfParticlesRead == numParts ? currentPosition - data.begin()
1113
1114 MPI_Bcast(&dataSize, 1, MPI_UNSIGNED, 0, Ippl::getComm());
1115 if (numberOfParticlesRead != numParts) {
1116 throw OpalException(
1117 "Distribution::createDistributionFromFile",
1118 "Found " + std::to_string(numParts) + " particles in file '" + fileName
1119 + "' instead of " + std::to_string(numberOfParticlesRead));
1120 }
1121 MPI_Bcast(&(data[0]), dataSize, MPI_DOUBLE, 0, Ippl::getComm());
1122
1123 } else {
1124 do {
1125 MPI_Bcast(&dataSize, 1, MPI_UNSIGNED, 0, Ippl::getComm());
1126 if (dataSize == std::numeric_limits<unsigned int>::max()) {
1127 throw OpalException(
1128 "Distribution::createDistributionFromFile",
1129 "Couldn't find " + std::to_string(numberOfParticlesRead)
1130 + " particles in file '" + fileName + "'");
1131 }
1132 MPI_Bcast(&(data[0]), dataSize, MPI_DOUBLE, 0, Ippl::getComm());
1133
1134 size_t i = 0;
1135 while (i < dataSize) {
1136 if (saveProcessor + 1 == (unsigned)Ippl::myNode()) {
1137 const double* tmp = &(data[i]);
1138 xDist_m.push_back(tmp[0]);
1139 yDist_m.push_back(tmp[1]);
1140 tOrZDist_m.push_back(tmp[2]);
1141 pxDist_m.push_back(tmp[3]);
1142 pyDist_m.push_back(tmp[4]);
1143 pzDist_m.push_back(tmp[5]);
1144 }
1145 i += singleDataSize;
1146
1147 ++saveProcessor;
1148 if (saveProcessor + 1 >= (unsigned)Ippl::getNodes()) {
1149 saveProcessor = 0;
1150 }
1151 }
1152 } while (dataSize == distributeFrequency * singleDataSize);
1153 }
1154
1155 pmean_m /= numberOfParticlesRead;
1157
1158 if (Ippl::myNode() == 0)
1159 inputFile.close();
1160}
1161
1163 double massIneV,
1164 double charge)
1165{
1166
1167 /*
1168 ToDo:
1169 - eliminate physics and error
1170 */
1171
1173 if (lineName.empty()) return;
1174
1175 const BeamSequence* lineSequence = BeamSequence::find(lineName);
1176 if (lineSequence == nullptr)
1177 throw OpalException("Distribution::CreateMatchedGaussDistribution",
1178 "didn't find any Cyclotron element in line");
1179
1180 SpecificElementVisitor<Cyclotron> CyclotronVisitor(*lineSequence->fetchLine());
1181 CyclotronVisitor.execute();
1182 size_t NumberOfCyclotrons = CyclotronVisitor.size();
1183
1184 if (NumberOfCyclotrons > 1) {
1185 throw OpalException("Distribution::createMatchedGaussDistribution",
1186 "I am confused, found more than one Cyclotron element in line");
1187 }
1188 if (NumberOfCyclotrons == 0) {
1189 throw OpalException("Distribution::createMatchedGaussDistribution",
1190 "didn't find any Cyclotron element in line");
1191 }
1192
1193 /* FIXME we need to remove const-ness otherwise we can't update the injection radius
1194 * and injection radial momentum. However, there should be a better solution ..
1195 */
1196 Cyclotron* CyclotronElement = const_cast<Cyclotron*>(CyclotronVisitor.front());
1197
1201
1202 if ( Nint < 0 )
1203 throw OpalException("Distribution::createMatchedGaussDistribution()",
1204 "Negative number of integration steps");
1205
1206 if ( Nsectors < 0 )
1207 throw OpalException("Distribution::createMatchedGaussDistribution()",
1208 "Negative number of sectors");
1209
1210 if ( Nsectors > 1 && full == false )
1211 throw OpalException("Distribution::createMatchedGaussDistribution()",
1212 "Averaging over sectors can only be done with SECTOR=FALSE");
1213
1214 *gmsg << "* ----------------------------------------------------" << endl;
1215 *gmsg << "* About to find closed orbit and matched distribution " << endl;
1216 *gmsg << "* I= " << I_m*Units::A2mA << " (mA) E= " << E_m*Units::eV2MeV << " (MeV)" << endl;
1220 if ( full ) {
1221 *gmsg << "* SECTOR: " << "match using all sectors, with" << endl
1222 << "* NSECTORS = " << Nsectors << " to average the field over" << endl;
1223 }
1224 else
1225 *gmsg << "* SECTOR: " << "match using single sector" << endl;
1226
1227 *gmsg << "* NSTEPS = " << Nint << endl
1228 << "* HN = " << CyclotronElement->getCyclHarm()
1229 << " PHIINIT = " << CyclotronElement->getPHIinit() << endl
1230 << "* FIELD MAP = " << CyclotronElement->getFieldMapFN() << endl
1231 << "* ----------------------------------------------------" << endl;
1232
1233 if ( CyclotronElement->getFMLowE() < 0 ||
1234 CyclotronElement->getFMHighE() < 0 )
1235 {
1236 throw OpalException("Distribution::createMatchedGaussDistribution()",
1237 "Missing attributes 'FMLOWE' and/or 'FMHIGHE' in "
1238 "'CYCLOTRON' definition.");
1239 }
1240
1241 std::size_t maxitCOF =
1243
1244 double rguess =
1246
1247 double denergy = Units::GeV2MeV *
1249
1250 if ( denergy < 0.0 )
1251 throw OpalException("Distribution:createMatchedGaussDistribution()",
1252 "DENERGY < 0");
1253
1254 double accuracy =
1256
1257 if ( Options::cloTuneOnly ) {
1258 *gmsg << "* Stopping after closed orbit and tune calculation" << endl;
1259 typedef std::vector<double> container_t;
1260 typedef boost::numeric::odeint::runge_kutta4<container_t> rk4_t;
1262
1263 cof_t cof(massIneV*Units::eV2MeV, charge, Nint, CyclotronElement, full, Nsectors);
1264 cof.findOrbit(accuracy, maxitCOF, E_m*Units::eV2MeV, denergy, rguess, true);
1265
1266 throw EarlyLeaveException("Distribution::createMatchedGaussDistribution()",
1267 "Do only tune calculation.");
1268 }
1269
1270 bool writeMap = true;
1271
1272 std::unique_ptr<SigmaGenerator> siggen = std::unique_ptr<SigmaGenerator>(
1273 new SigmaGenerator(I_m,
1278 massIneV*Units::eV2MeV,
1279 charge,
1280 CyclotronElement,
1281 Nint,
1282 Nsectors,
1284 writeMap));
1285
1286 if (siggen->match(accuracy,
1288 maxitCOF,
1289 CyclotronElement,
1290 denergy,
1291 rguess,
1292 full)) {
1293
1294 std::array<double,3> Emit = siggen->getEmittances();
1295
1296 if (rguess > 0)
1297 *gmsg << "* RGUESS " << rguess << " (m) " << endl;
1298
1299 *gmsg << "* Converged (Ex, Ey, Ez) = (" << Emit[0] << ", " << Emit[1] << ", "
1300 << Emit[2] << ") pi mm mrad for E= " << E_m * Units::eV2MeV << " (MeV)" << endl;
1301 *gmsg << "* Sigma-Matrix " << endl;
1302
1303 for (unsigned int i = 0; i < siggen->getSigma().size1(); ++ i) {
1304 *gmsg << std::setprecision(4) << std::setw(11) << siggen->getSigma()(i,0);
1305 for (unsigned int j = 1; j < siggen->getSigma().size2(); ++ j) {
1306 if (std::abs(siggen->getSigma()(i,j)) < 1.0e-15) {
1307 *gmsg << " & " << std::setprecision(4) << std::setw(11) << 0.0;
1308 }
1309 else{
1310 *gmsg << " & " << std::setprecision(4) << std::setw(11) << siggen->getSigma()(i,j);
1311 }
1312
1313 }
1314 *gmsg << " \\\\" << endl;
1315 }
1316
1317 generateMatchedGauss(siggen->getSigma(), numberOfParticles, massIneV);
1318
1319 // update injection radius and radial momentum
1320 CyclotronElement->setRinit(siggen->getInjectionRadius() * Units::m2mm);
1321 CyclotronElement->setPRinit(siggen->getInjectionMomentum());
1322 }
1323 else {
1324 *gmsg << "* Not converged for " << E_m*Units::eV2MeV << " MeV" << endl;
1325
1326 throw OpalException("Distribution::CreateMatchedGaussDistribution",
1327 "didn't find any matched distribution.");
1328 }
1329}
1330
1331void Distribution::createDistributionGauss(size_t numberOfParticles, double massIneV) {
1332
1333 setDistParametersGauss(massIneV);
1334
1335 if (emitting_m) {
1336 generateTransverseGauss(numberOfParticles);
1337 generateLongFlattopT(numberOfParticles);
1338 } else {
1339 generateGaussZ(numberOfParticles);
1340 }
1341}
1342
1344 size_t numberOfParticles,
1345 double current, const Beamline &/*bl*/) {
1346
1347 /*
1348 * setup data for matched distribution generation
1349 */
1350 E_m = (beam->getInitialGamma() - 1.0) * beam->getM();
1351 I_m = current;
1352
1353 size_t numberOfPartToCreate = numberOfParticles;
1354 totalNumberParticles_m = numberOfParticles;
1355 if (beam->getTotalNum() != 0) {
1356 numberOfPartToCreate = beam->getLocalNum();
1357 }
1358
1359 // Setup particle bin structure.
1360 setupParticleBins(beam->getM(),beam);
1361
1362 /*
1363 * Set what units to use for input momentum units. Default in OPAL-cycl
1364 * is eV/c.
1365 */
1367
1368 /*
1369 * Determine the number of particles for each distribution. For OPAL-cycl
1370 * there are currently no arrays of distributions supported
1371 */
1372 calcPartPerDist(numberOfParticles);
1373
1374 // Set distribution type.
1375 setDistType();
1376
1377 // Emitting particles is not supported in OPAL-cycl.
1378 emitting_m = false;
1379
1380 // Create distribution.
1381 create(numberOfPartToCreate, beam->getM(), beam->getQ());
1382
1383 // this variable is needed to be compatible with OPAL-T
1384 particlesPerDist_m.push_back(tOrZDist_m.size());
1385
1386 shiftDistCoordinates(beam->getM());
1387
1388 // Setup energy bins.
1389 if (numberOfEnergyBins_m > 0) {
1391 beam->setPBins(energyBins_m);
1392 }
1393
1394 // Check number of particles in distribution.
1395 checkParticleNumber(numberOfParticles);
1396
1397 initializeBeam(beam);
1399
1400 injectBeam(beam);
1401
1402 OpalData::getInstance()->addProblemCharacteristicValue("NP", numberOfParticles);
1403}
1404
1406 std::vector<Distribution *> addedDistributions,
1407 size_t &numberOfParticles) {
1408
1409 addedDistributions_m = addedDistributions;
1410 createOpalT(beam, numberOfParticles);
1411}
1412
1414 size_t &numberOfParticles) {
1415
1417
1418 // This is PC from BEAM
1421 deltaP = Util::convertMomentumEVoverCToBetaGamma(deltaP, beam->getM());
1422 }
1423
1424 avrgpz_m = beam->getP()/beam->getM() + deltaP;
1425
1426 totalNumberParticles_m = numberOfParticles;
1427
1428 /*
1429 * Set what units to use for input momentum units. Default in OPAL-T is
1430 * unitless (i.e. BetaXGamma, BetaYGamma, BetaZGamma).
1431 */
1433
1434 // Set distribution type(s).
1435 setDistType();
1436 for (Distribution* addedDist : addedDistributions_m)
1437 addedDist->setDistType();
1438
1439 /*
1440 * Determine the number of particles for each distribution. Note
1441 * that if a distribution is generated from an input file, then
1442 * the number of particles in that file will override what is
1443 * determined here.
1444 */
1445 calcPartPerDist(numberOfParticles);
1446
1447 // Check if this is to be an emitted beam->
1449
1450 /*
1451 * Force added distributions to the same emission condition as the main
1452 * distribution.
1453 */
1454 for (Distribution* addedDist : addedDistributions_m)
1455 addedDist->setDistToEmitted(emitting_m);
1456
1457 if (emitting_m)
1458 setupEmissionModel(beam);
1459
1460 // Create distributions.
1461 create(particlesPerDist_m.at(0), beam->getM(), beam->getQ());
1462
1463 size_t distCount = 1;
1464 for (Distribution* addedDist : addedDistributions_m) {
1465 addedDist->create(particlesPerDist_m.at(distCount), beam->getM(), beam->getQ());
1466 distCount++;
1467 }
1468
1469 // Move added distribution particles to main distribution.
1471
1474
1475 // Check number of particles in distribution.
1476 checkParticleNumber(numberOfParticles);
1477
1478 if (emitting_m) {
1480 } else {
1483 } else {
1484 pmean_m = Vector_t(0, 0, avrgpz_m);
1485 }
1486 }
1487
1488 /*
1489 * Find max. and min. particle positions in the bunch. For the
1490 * case of an emitted beam these will be in time (seconds). For
1491 * an injected beam in z (meters).
1492 */
1493 double maxTOrZ = getMaxTOrZ();
1494 double minTOrZ = getMinTOrZ();
1495
1496 /*
1497 * Set emission time and/or shift distributions if necessary.
1498 * For an emitted beam we place all particles at negative time.
1499 * For an injected beam we just ensure that there are no
1500 * particles at z < 0.
1501 */
1502
1503 if (emitting_m) {
1504 setEmissionTime(maxTOrZ, minTOrZ);
1505 }
1506 shiftBeam(maxTOrZ, minTOrZ);
1507
1508 shiftDistCoordinates(beam->getM());
1509
1510 if (numberOfEnergyBins_m > 0) {
1511 setupEnergyBins(maxTOrZ, minTOrZ);
1513 }
1514
1515 initializeBeam(beam);
1517
1518 if (emitting_m && Options::cZero) {
1519 std::vector<std::vector<double> > mirrored;
1520 const auto end = additionalRNs_m.end();
1521
1525
1526 for (auto it = additionalRNs_m.begin(); it != end; ++ it) {
1527 std::vector<double> tmp;
1528 tmp.push_back((*it).front());
1529 tmp.push_back((*it).back() + 0.5);
1530 mirrored.push_back(tmp);
1531 }
1532 } else {
1533 size_t numAdditionals = additionalRNs_m.front().size() / 2;
1534 for (auto it = additionalRNs_m.begin(); it != end; ++ it) {
1535 std::vector<double> tmp((*it).begin() + numAdditionals, (*it).end());
1536 mirrored.push_back(tmp);
1537 (*it).erase((*it).begin() + numAdditionals, (*it).end());
1538 }
1539 }
1540
1541 additionalRNs_m.insert(additionalRNs_m.end(), mirrored.begin(), mirrored.end());
1542 }
1543
1544 /*
1545 * If this is an injected beam, we create particles right away.
1546 * Emitted beams get created during the course of the simulation.
1547 */
1548 if (!emitting_m)
1549 injectBeam(beam);
1550
1551 OpalData::getInstance()->addProblemCharacteristicValue("NP", numberOfParticles);
1553}
1554
1580
1581 // Number of particles that have already been emitted and are on this processor.
1582 size_t numberOfEmittedParticles = beam->getLocalNum();
1583 size_t oldNumberOfEmittedParticles = numberOfEmittedParticles;
1584
1585 if (!tOrZDist_m.empty() && emitting_m) {
1586
1587 /*
1588 * Loop through emission beam coordinate containers and emit particles with
1589 * the appropriate time coordinate. Once emitted, remove particle from the
1590 * "to be emitted" list.
1591 */
1592 std::vector<size_t> particlesToBeErased;
1593 double phiEffective = (cathodeWorkFunc_m
1594 - std::sqrt(std::max(0.0, (Physics::q_e * beam->getQ() * eZ) /
1595 (4.0 * Physics::pi * Physics::epsilon_0))));
1596 double lowEnergyLimit = cathodeFermiEnergy_m + phiEffective - laserEnergy_m;
1597
1598 for (size_t particleIndex = 0; particleIndex < tOrZDist_m.size(); particleIndex++) {
1599
1600 // Advance particle time.
1601 tOrZDist_m.at(particleIndex) += beam->getdT();
1602
1603 // If particle time is now greater than zero, we emit it.
1604 if (tOrZDist_m.at(particleIndex) >= 0.0) {
1605
1606 particlesToBeErased.push_back(particleIndex);
1607
1608 beam->create(1);
1609 double deltaT = tOrZDist_m.at(particleIndex);
1610 beam->dt[numberOfEmittedParticles] = deltaT;
1611
1612 double oneOverCDt = 1.0 / (Physics::c * deltaT);
1613
1614 double px = pxDist_m.at(particleIndex);
1615 double py = pyDist_m.at(particleIndex);
1616 double pz = pzDist_m.at(particleIndex);
1617 std::vector<double> additionalRNs;
1618 if (additionalRNs_m.size() > particleIndex) {
1619 additionalRNs = additionalRNs_m[particleIndex];
1620 } else {
1621 throw OpalException("Distribution::emitParticles",
1622 "not enough additional particles");
1623 }
1624 applyEmissionModel(lowEnergyLimit, px, py, pz, additionalRNs);
1625
1626 double particleGamma
1627 = std::sqrt(1.0
1628 + std::pow(px, 2)
1629 + std::pow(py, 2)
1630 + std::pow(pz, 2));
1631
1632 beam->R[numberOfEmittedParticles]
1633 = Vector_t(oneOverCDt * (xDist_m.at(particleIndex)
1634 + px * deltaT * Physics::c / (2.0 * particleGamma)),
1635 oneOverCDt * (yDist_m.at(particleIndex)
1636 + py * deltaT * Physics::c / (2.0 * particleGamma)),
1637 pz / (2.0 * particleGamma));
1638 beam->P[numberOfEmittedParticles]
1639 = Vector_t(px, py, pz);
1640 beam->Bin[numberOfEmittedParticles] = currentEnergyBin_m - 1;
1641 beam->Q[numberOfEmittedParticles] = beam->getChargePerParticle();
1642 beam->M[numberOfEmittedParticles] = beam->getMassPerParticle();
1643 beam->Ef[numberOfEmittedParticles] = Vector_t(0.0);
1644 beam->Bf[numberOfEmittedParticles] = Vector_t(0.0);
1645 beam->PType[numberOfEmittedParticles] = beam->getPType();
1646 beam->POrigin[numberOfEmittedParticles] = ParticleOrigin::REGULAR;
1647 beam->TriID[numberOfEmittedParticles] = 0;
1648 numberOfEmittedParticles++;
1649
1651
1652 // Save particles to vectors for writing initial distribution.
1653 xWrite_m.push_back(xDist_m.at(particleIndex));
1654 pxWrite_m.push_back(px);
1655 yWrite_m.push_back(yDist_m.at(particleIndex));
1656 pyWrite_m.push_back(py);
1657 tOrZWrite_m.push_back(-(beam->getdT() - deltaT + currentEmissionTime_m));
1658 pzWrite_m.push_back(pz);
1659 binWrite_m.push_back(currentEnergyBin_m);
1660 }
1661 }
1662
1663 // Now erase particles that were emitted.
1664 std::vector<size_t>::reverse_iterator ptbErasedIt;
1665 for (ptbErasedIt = particlesToBeErased.rbegin();
1666 ptbErasedIt < particlesToBeErased.rend();
1667 ++ptbErasedIt) {
1668
1669 /*
1670 * We don't use the vector class function erase because it
1671 * is much slower than doing a swap and popping off the end
1672 * of the vector.
1673 */
1674 std::swap( xDist_m.at(*ptbErasedIt), xDist_m.back());
1675 std::swap(pxDist_m.at(*ptbErasedIt), pxDist_m.back());
1676 std::swap( yDist_m.at(*ptbErasedIt), yDist_m.back());
1677 std::swap(pyDist_m.at(*ptbErasedIt), pyDist_m.back());
1678 std::swap(tOrZDist_m.at(*ptbErasedIt), tOrZDist_m.back());
1679 std::swap(pzDist_m.at(*ptbErasedIt), pzDist_m.back());
1680 if (additionalRNs_m.size() == xDist_m.size()) {
1681 std::swap(additionalRNs_m.at(*ptbErasedIt), additionalRNs_m.back());
1682
1683 additionalRNs_m.pop_back();
1684 }
1685
1686 xDist_m.pop_back();
1687 pxDist_m.pop_back();
1688 yDist_m.pop_back();
1689 pyDist_m.pop_back();
1690 tOrZDist_m.pop_back();
1691 pzDist_m.pop_back();
1692
1693 }
1694
1695 /*
1696 * Set energy bin to emitted if all particles in the bin have been emitted.
1697 * However, be careful with the last energy bin. We cannot emit it until all
1698 * of the particles have been accounted for. So when on the last bin, keep it
1699 * open for the rest of the beam->
1700 */
1703
1704 INFOMSG(level3 << "* Bin number: "
1706 << " has emitted all particles (new emit)." << endl);
1709
1710 }
1711
1712 /*
1713 * Set beam to emitted. Make sure temporary storage is cleared.
1714 */
1716 emitting_m = false;
1717
1718 xDist_m.clear();
1719 pxDist_m.clear();
1720 yDist_m.clear();
1721 pyDist_m.clear();
1722 tOrZDist_m.clear();
1723 pzDist_m.clear();
1724
1726 }
1727
1728 }
1729 currentEmissionTime_m += beam->getdT();
1730
1731 // Write emitted particles to file.
1733
1734 size_t currentEmittedParticles = numberOfEmittedParticles - oldNumberOfEmittedParticles;
1735 reduce(currentEmittedParticles, currentEmittedParticles, OpAddAssign());
1736 totalNumberEmittedParticles_m += currentEmittedParticles;
1737
1738 return currentEmittedParticles;
1739
1740}
1741
1743 xDist_m.erase(xDist_m.begin(), xDist_m.end());
1744}
1745
1747 pxDist_m.erase(pxDist_m.begin(), pxDist_m.end());
1748}
1749
1751 yDist_m.erase(yDist_m.begin(), yDist_m.end());
1752}
1753
1755 pyDist_m.erase(pyDist_m.begin(), pyDist_m.end());
1756}
1757
1759 tOrZDist_m.erase(tOrZDist_m.begin(), tOrZDist_m.end());
1760}
1761
1763 pzDist_m.erase(pzDist_m.begin(), pzDist_m.end());
1764}
1765
1766void Distribution::sampleUniformDisk(gsl_qrng* quasiRandGen2D, double& x1, double& x2)
1767{
1768 bool allow = false;
1769 double randNums[2] = {0.0, 0.0};
1770 while (!allow) {
1771 if (quasiRandGen2D != nullptr)
1772 gsl_qrng_get(quasiRandGen2D, randNums);
1773 else {
1774 randNums[0] = gsl_rng_uniform(randGen_m);
1775 randNums[1] = gsl_rng_uniform(randGen_m);
1776 }
1777
1778 x1 = 2 * randNums[0] - 1;
1779 x2 = 2 * randNums[1] - 1;
1780 allow = (std::pow(x1, 2) + std::pow(x2, 2) <= 1);
1781 }
1782}
1783
1785 double upper = 0.0;
1786 double lower = 0.0;
1787 gsl_histogram_get_range(energyBinHist_m,
1788 gsl_histogram_bins(energyBinHist_m) - 1,
1789 &lower, &upper);
1790 const size_t numberOfParticles = tOrZDist_m.size();
1791 for (size_t partIndex = 0; partIndex < numberOfParticles; partIndex++) {
1792
1793 double &tOrZ = tOrZDist_m.at(partIndex);
1794
1795 if (gsl_histogram_increment(energyBinHist_m, tOrZ) == GSL_EDOM) {
1796 gsl_histogram_increment(energyBinHist_m, 0.5 * (lower + upper));
1797 }
1798 }
1799
1801}
1802
1804
1805 for (size_t particleIndex = 0; particleIndex < tOrZDist_m.size(); particleIndex++) {
1806
1807 std::vector<double> partCoord;
1808
1809 partCoord.push_back(xDist_m.at(particleIndex));
1810 partCoord.push_back(yDist_m.at(particleIndex));
1811 partCoord.push_back(tOrZDist_m.at(particleIndex));
1812 partCoord.push_back(pxDist_m.at(particleIndex));
1813 partCoord.push_back(pyDist_m.at(particleIndex));
1814 partCoord.push_back(pzDist_m.at(particleIndex));
1815 partCoord.push_back(0.0);
1816
1817 energyBins_m->fill(partCoord);
1818
1819 }
1820
1822}
1823
1824size_t Distribution::findEBin(double tOrZ) {
1825
1826 if (tOrZ <= gsl_histogram_min(energyBinHist_m)) {
1827 return 0;
1828 } else if (tOrZ >= gsl_histogram_max(energyBinHist_m)) {
1829 return numberOfEnergyBins_m - 1;
1830 } else {
1831 size_t binNumber;
1832 gsl_histogram_find(energyBinHist_m, tOrZ, &binNumber);
1833 return binNumber / numberOfSampleBins_m;
1834 }
1835}
1836
1837void Distribution::generateAstraFlattopT(size_t numberOfParticles) {
1838
1839 /*
1840 * Legacy function to support the ASTRAFLATTOPOTH
1841 * distribution type.
1842 */
1844
1845 gsl_qrng *quasiRandGen = gsl_qrng_alloc(gsl_qrng_halton, 2);
1846
1847 int numberOfSampleBins
1849 int numberOfEnergyBins
1851
1852 int binTotal = numberOfSampleBins * numberOfEnergyBins;
1853
1854 double *distributionTable = new double[binTotal + 1];
1855
1856 double a = tPulseLengthFWHM_m / 2.;
1857 double sig = tRise_m / 2.;
1858 double inv_erf08 = 0.906193802436823; // erfinv(0.8)
1859 double sqr2 = std::sqrt(2.0);
1860 double t = a - sqr2 * sig * inv_erf08;
1861 double tmps = sig;
1862 double tmpt = t;
1863
1864 for (int i = 0; i < 10; ++ i) {
1865 sig = (t + tRise_m - a) / (sqr2 * inv_erf08);
1866 t = a - 0.5 * sqr2 * (sig + tmps) * inv_erf08;
1867 sig = (0.5 * (t + tmpt) + tRise_m - a) / (sqr2 * inv_erf08);
1868 tmps = sig;
1869 tmpt = t;
1870 }
1871
1872 /*
1873 * Emission time is set here during distribution particle creation only for
1874 * the ASTRAFLATTOPTH distribution type.
1875 */
1876 tEmission_m = tPulseLengthFWHM_m + 10. * sig;
1877 tBin_m = tEmission_m / numberOfEnergyBins;
1878
1879 double lo = -tBin_m / 2.0 * numberOfEnergyBins;
1880 double hi = tBin_m / 2.0 * numberOfEnergyBins;
1881 double dx = tBin_m / numberOfSampleBins;
1882 double x = lo;
1883 double tot = 0;
1884 double weight = 2.0;
1885
1886 // sample the function that describes the histogram of the requested distribution
1887 for (int i = 0; i < binTotal + 1; ++ i, x += dx, weight = 6. - weight) {
1888 distributionTable[i] = gsl_sf_erf((x + a) / (sqr2 * sig)) - gsl_sf_erf((x - a) / (sqr2 * sig));
1889 tot += distributionTable[i] * weight;
1890 }
1891 tot -= distributionTable[binTotal] * (5. - weight);
1892 tot -= distributionTable[0];
1893
1894 int saveProcessor = -1;
1895 const int myNode = Ippl::myNode();
1896 const int numNodes = Ippl::getNodes();
1898 double tCoord = 0.0;
1899
1900 int effectiveNumParticles = 0;
1901 int largestBin = 0;
1902 std::vector<int> numParticlesInBin(numberOfEnergyBins,0);
1903 for (int k = 0; k < numberOfEnergyBins; ++ k) {
1904 double loc_fraction = -distributionTable[numberOfSampleBins * k] / tot;
1905
1906 weight = 2.0;
1907 for (int i = numberOfSampleBins * k; i < numberOfSampleBins * (k + 1) + 1;
1908 ++ i, weight = 6. - weight) {
1909 loc_fraction += distributionTable[i] * weight / tot;
1910 }
1911 loc_fraction -= distributionTable[numberOfSampleBins * (k + 1)]
1912 * (5. - weight) / tot;
1913 numParticlesInBin[k] = static_cast<int>(std::round(loc_fraction * numberOfParticles));
1914 effectiveNumParticles += numParticlesInBin[k];
1915 if (numParticlesInBin[k] > numParticlesInBin[largestBin]) largestBin = k;
1916 }
1917
1918 numParticlesInBin[largestBin] += (numberOfParticles - effectiveNumParticles);
1919
1920 for (int k = 0; k < numberOfEnergyBins; ++ k) {
1921 gsl_ran_discrete_t *table
1922 = gsl_ran_discrete_preproc(numberOfSampleBins,
1923 &(distributionTable[numberOfSampleBins * k]));
1924
1925 for (int i = 0; i < numParticlesInBin[k]; i++) {
1926 double xx[2];
1927 gsl_qrng_get(quasiRandGen, xx);
1928 tCoord = hi * (xx[1] + static_cast<int>(gsl_ran_discrete(randGen_m, table))
1929 - binTotal / 2 + k * numberOfSampleBins) / (binTotal / 2);
1930
1931 saveProcessor++;
1932 if (saveProcessor >= numNodes)
1933 saveProcessor = 0;
1934
1935 if (scalable || myNode == saveProcessor) {
1936 tOrZDist_m.push_back(tCoord);
1937 pzDist_m.push_back(0.0);
1938 }
1939 }
1940 gsl_ran_discrete_free(table);
1941 }
1942
1943 gsl_qrng_free(quasiRandGen);
1944 delete [] distributionTable;
1945
1946}
1947
1948void Distribution::generateBinomial(size_t numberOfParticles) {
1949
1969 // Calculate emittance and Twiss parameters.
1970 Vector_t emittance;
1971 Vector_t alpha, beta, gamma;
1972 for (unsigned int index = 0; index < 3; index++) {
1973 emittance(index) = sigmaR_m[index] * sigmaP_m[index]
1974 * std::cos(std::asin(correlationMatrix_m(2 * index + 1, 2 * index)));
1975
1976 if (std::abs(emittance(index)) > std::numeric_limits<double>::epsilon()) {
1977 beta(index) = std::pow(sigmaR_m[index], 2) / emittance(index);
1978 gamma(index) = std::pow(sigmaP_m[index], 2) / emittance(index);
1979 } else {
1982 }
1983 alpha(index) = -correlationMatrix_m(2 * index + 1, 2 * index)
1984 * std::sqrt(beta(index) * gamma(index));
1985 }
1986
1987 Vector_t M, PM, L, PL, X, PX;
1988 Vector_t CHI, COSCHI, SINCHI(0.0);
1989 Vector_t AMI;
1991 for (unsigned int index = 0; index < 3; index++) {
1992 // gamma(index) *= cutoffR_m[index];
1993 // beta(index) *= cutoffP_m[index];
1994 COSCHI[index] = std::sqrt(1.0 / (1.0 + std::pow(alpha(index), 2)));
1995 SINCHI[index] = -alpha(index) * COSCHI[index];
1996 CHI[index] = std::atan2(SINCHI[index], COSCHI[index]);
1997 AMI[index] = 1.0 / mBinomial_m[index];
1998 M[index] = std::sqrt(emittance(index) * beta(index));
1999 PM[index] = std::sqrt(emittance(index) * gamma(index));
2000 L[index] = std::sqrt((mBinomial_m[index] + 1.0) / 2.0) * M[index];
2001 PL[index] = std::sqrt((mBinomial_m[index] + 1.0) / 2.0) * PM[index];
2002
2003 if (mBinomial_m[index] < 10000) {
2004 X[index] = L[index];
2005 PX[index] = PL[index];
2006 splitter[index] = new MDependentBehavior(mBinomial_m[index]);
2007 } else {
2008 X[index] = M[index];
2009 PX[index] = PM[index];
2010 splitter[index] = new GaussianLikeBehavior();
2011 }
2012 }
2013
2014 int saveProcessor = -1;
2015 const int myNode = Ippl::myNode();
2016 const int numNodes = Ippl::getNodes();
2018
2019 double temp = 1.0 - std::pow(correlationMatrix_m(1, 0), 2);
2020 const double tempa = std::copysign(std::sqrt(std::abs(temp)), temp);
2021 const double l32 = (correlationMatrix_m(4, 1) -
2022 correlationMatrix_m(1, 0) * correlationMatrix_m(4, 0)) / tempa;
2023 temp = 1 - std::pow(correlationMatrix_m(4, 0), 2) - l32 * l32;
2024 const double l33 = std::copysign(std::sqrt(std::abs(temp)), temp);
2025
2026 const double l42 = (correlationMatrix_m(5, 1) -
2027 correlationMatrix_m(1, 0) * correlationMatrix_m(5, 0)) / tempa;
2028 const double l43 = (correlationMatrix_m(5, 4) -
2029 correlationMatrix_m(4, 0) * correlationMatrix_m(5, 0) - l42 * l32) / l33;
2030 temp = 1 - std::pow(correlationMatrix_m(5, 0), 2) - l42 * l42 - l43 * l43;
2031 const double l44 = std::copysign(std::sqrt(std::abs(temp)), temp);
2032
2033 Vector_t x = Vector_t(0.0);
2034 Vector_t p = Vector_t(0.0);
2035 for (size_t partIndex = 0; partIndex < numberOfParticles; partIndex++) {
2036
2037 double A = 0.0;
2038 double AL = 0.0;
2039 double Ux = 0.0, U = 0.0;
2040 double Vx = 0.0, V = 0.0;
2041
2042 A = splitter[0]->get(gsl_rng_uniform(randGen_m));
2043 AL = Physics::two_pi * gsl_rng_uniform(randGen_m);
2044 Ux = A * std::cos(AL);
2045 Vx = A * std::sin(AL);
2046 x[0] = X[0] * Ux;
2047 p[0] = PX[0] * (Ux * SINCHI[0] + Vx * COSCHI[0]);
2048
2049 A = splitter[1]->get(gsl_rng_uniform(randGen_m));
2050 AL = Physics::two_pi * gsl_rng_uniform(randGen_m);
2051 U = A * std::cos(AL);
2052 V = A * std::sin(AL);
2053 x[1] = X[1] * U;
2054 p[1] = PX[1] * (U * SINCHI[1] + V * COSCHI[1]);
2055
2056 A = splitter[2]->get(gsl_rng_uniform(randGen_m));
2057 AL = Physics::two_pi * gsl_rng_uniform(randGen_m);
2058 U = A * std::cos(AL);
2059 V = A * std::sin(AL);
2060 x[2] = X[2] * (Ux * correlationMatrix_m(4, 0) + Vx * l32 + U * l33);
2061 p[2] = PX[2] * (Ux * correlationMatrix_m(5, 0) + Vx * l42 + U * l43 + V * l44);
2062
2063 // Save to each processor in turn.
2064 saveProcessor++;
2065 if (saveProcessor >= numNodes)
2066 saveProcessor = 0;
2067
2068 if (scalable || myNode == saveProcessor) {
2069 xDist_m.push_back(x[0]);
2070 pxDist_m.push_back(p[0]);
2071 yDist_m.push_back(x[1]);
2072 pyDist_m.push_back(p[1]);
2073 tOrZDist_m.push_back(x[2]);
2074 pzDist_m.push_back(avrgpz_m + p[2]);
2075 }
2076 }
2077
2078 for (unsigned int index = 0; index < 3; index++) {
2079 delete splitter[index];
2080 }
2081}
2082
2083void Distribution::generateFlattopLaserProfile(size_t numberOfParticles) {
2084
2085 int saveProcessor = -1;
2086 const int myNode = Ippl::myNode();
2087 const int numNodes = Ippl::getNodes();
2089
2090 for (size_t partIndex = 0; partIndex < numberOfParticles; partIndex++) {
2091
2092 double x = 0.0;
2093 double px = 0.0;
2094 double y = 0.0;
2095 double py = 0.0;
2096
2097 laserProfile_m->getXY(x, y);
2098
2099 // Save to each processor in turn.
2100 saveProcessor++;
2101 if (saveProcessor >= numNodes)
2102 saveProcessor = 0;
2103
2104 if (scalable || myNode == saveProcessor) {
2105 xDist_m.push_back(x * sigmaR_m[0]);
2106 pxDist_m.push_back(px);
2107 yDist_m.push_back(y * sigmaR_m[1]);
2108 pyDist_m.push_back(py);
2109 }
2110 }
2111
2113 generateAstraFlattopT(numberOfParticles);
2114 else
2115 generateLongFlattopT(numberOfParticles);
2116
2117}
2118
2119void Distribution::generateFlattopT(size_t numberOfParticles) {
2120
2121 gsl_qrng *quasiRandGen2D = selectRandomGenerator(Options::rngtype,2);
2122
2123 int saveProcessor = -1;
2124 const int myNode = Ippl::myNode();
2125 const int numNodes = Ippl::getNodes();
2127 for (size_t partIndex = 0; partIndex < numberOfParticles; partIndex++) {
2128
2129 double x = 0.0;
2130 double px = 0.0;
2131 double y = 0.0;
2132 double py = 0.0;
2133
2134 sampleUniformDisk(quasiRandGen2D, x, y);
2135 x *= sigmaR_m[0];
2136 y *= sigmaR_m[1];
2137
2138 // Save to each processor in turn.
2139 saveProcessor++;
2140 if (saveProcessor >= numNodes)
2141 saveProcessor = 0;
2142
2143 if (scalable || myNode == saveProcessor) {
2144 xDist_m.push_back(x);
2145 pxDist_m.push_back(px);
2146 yDist_m.push_back(y);
2147 pyDist_m.push_back(py);
2148 }
2149
2150 }
2151
2152 gsl_qrng_free(quasiRandGen2D);
2153
2155 generateAstraFlattopT(numberOfParticles);
2156 else
2157 generateLongFlattopT(numberOfParticles);
2158
2159}
2160
2161void Distribution::generateFlattopZ(size_t numberOfParticles) {
2162
2163 gsl_qrng *quasiRandGen1D = selectRandomGenerator(Options::rngtype,1);
2164 gsl_qrng *quasiRandGen2D = selectRandomGenerator(Options::rngtype,2);
2165
2166 int saveProcessor = -1;
2167 const int myNode = Ippl::myNode();
2168 const int numNodes = Ippl::getNodes();
2170
2171 for (size_t partIndex = 0; partIndex < numberOfParticles; partIndex++) {
2172
2173 double x = 0.0;
2174 double px = 0.0;
2175 double y = 0.0;
2176 double py = 0.0;
2177 double z = 0.0;
2178 double pz = 0.0;
2179
2180 sampleUniformDisk(quasiRandGen2D, x, y);
2181 x *= sigmaR_m[0];
2182 y *= sigmaR_m[1];
2183
2184 if (quasiRandGen1D != nullptr)
2185 gsl_qrng_get(quasiRandGen1D, &z);
2186 else
2187 z = gsl_rng_uniform(randGen_m);
2188
2189 z = (z - 0.5) * sigmaR_m[2];
2190
2191 // Save to each processor in turn.
2192 saveProcessor++;
2193 if (saveProcessor >= numNodes)
2194 saveProcessor = 0;
2195
2196 if (scalable || myNode == saveProcessor) {
2197 xDist_m.push_back(x);
2198 pxDist_m.push_back(px);
2199 yDist_m.push_back(y);
2200 pyDist_m.push_back(py);
2201 tOrZDist_m.push_back(z);
2202 pzDist_m.push_back(avrgpz_m + pz);
2203 }
2204 }
2205
2206 gsl_qrng_free(quasiRandGen1D);
2207 gsl_qrng_free(quasiRandGen2D);
2208}
2209
2210void Distribution::generateGaussZ(size_t numberOfParticles) {
2211
2212 gsl_matrix * corMat = gsl_matrix_alloc (6, 6);
2213 gsl_vector * rx = gsl_vector_alloc(6);
2214 gsl_vector * ry = gsl_vector_alloc(6);
2215
2216 for (unsigned int i = 0; i < 6; ++ i) {
2217 gsl_matrix_set(corMat, i, i, correlationMatrix_m(i, i));
2218 for (unsigned int j = 0; j < i; ++ j) {
2219 gsl_matrix_set(corMat, i, j, correlationMatrix_m(i, j));
2220 gsl_matrix_set(corMat, j, i, correlationMatrix_m(i, j));
2221 }
2222 }
2223
2224#define DISTDBG1
2225#ifdef DISTDBG1
2226 *gmsg << "* m before gsl_linalg_cholesky_decomp" << endl;
2227 for (int i = 0; i < 6; i++) {
2228 for (int j = 0; j < 6; j++) {
2229 if (j==0)
2230 *gmsg << "* r(" << std::setprecision(1) << i << ", : ) = "
2231 << std::setprecision(4) << std::setw(10) << gsl_matrix_get (corMat, i, j);
2232 else
2233 *gmsg << " & " << std::setprecision(4) << std::setw(10) << gsl_matrix_get (corMat, i, j);
2234 }
2235 *gmsg << " \\\\" << endl;
2236 }
2237#endif
2238/*
2239 //Sets the GSL error handler off, exception will be handled internally with a renormalization method
2240 gsl_set_error_handler_off();
2241*/
2242 int errcode = gsl_linalg_cholesky_decomp(corMat);
2243/*
2244 double rn = 1e-12;
2245
2246 while (errcode == GSL_EDOM) {
2247
2248 // Resets the correlation matrix
2249 for (unsigned int i = 0; i < 6; ++ i) {
2250 gsl_matrix_set(corMat, i, i, correlationMatrix_m(i, i));
2251 for (unsigned int j = 0; j < i; ++ j) {
2252 gsl_matrix_set(corMat, i, j, correlationMatrix_m(i, j));
2253 gsl_matrix_set(corMat, j, i, correlationMatrix_m(i, j));
2254 }
2255 }
2256 // Applying a renormalization method corMat = corMat + rn*Unitymatrix
2257 // This is the renormalization
2258 for(int i = 0; i < 6; i++){
2259 double corMati = gsl_matrix_get(corMat, i , i);
2260 gsl_matrix_set(corMat, i, i, corMati + rn);
2261 }
2262 //Just to be sure that the renormalization worked
2263 errcode = gsl_linalg_cholesky_decomp(corMat);
2264 if(errcode != GSL_EDOM) *gmsg << "* WARNING: Correlation matrix had to be renormalized"<<endl;
2265 else rn *= 10;
2266 }
2267 //Sets again the standard GSL error handler on
2268 gsl_set_error_handler(nullptr);
2269*/
2270 //Just to be sure
2271 if (errcode == GSL_EDOM) {
2272 throw OpalException("Distribution::GenerateGaussZ",
2273 "gsl_linalg_cholesky_decomp failed");
2274 }
2275 // so make the upper part zero.
2276 for (int i = 0; i < 5; ++ i) {
2277 for (int j = i+1; j < 6 ; ++ j) {
2278 gsl_matrix_set (corMat, i, j, 0.0);
2279 }
2280 }
2281#define DISTDBG2
2282#ifdef DISTDBG2
2283 *gmsg << "* m after gsl_linalg_cholesky_decomp" << endl;
2284 for (int i = 0; i < 6; i++) {
2285 for (int j = 0; j < 6; j++) {
2286 if (j==0)
2287 *gmsg << "* r(" << std::setprecision(1) << i << ", : ) = "
2288 << std::setprecision(4) << std::setw(10) << gsl_matrix_get (corMat, i, j);
2289 else
2290 *gmsg << " & " << std::setprecision(4) << std::setw(10) << gsl_matrix_get (corMat, i, j);
2291 }
2292 *gmsg << " \\\\" << endl;
2293 }
2294#endif
2295
2296 int saveProcessor = -1;
2297 const int myNode = Ippl::myNode();
2298 const int numNodes = Ippl::getNodes();
2300
2301 for (size_t partIndex = 0; partIndex < numberOfParticles; partIndex++) {
2302 bool allow = false;
2303
2304 double x = 0.0;
2305 double px = 0.0;
2306 double y = 0.0;
2307 double py = 0.0;
2308 double z = 0.0;
2309 double pz = 0.0;
2310
2311 while (!allow) {
2312 gsl_vector_set(rx, 0, gsl_ran_gaussian(randGen_m, 1.0));
2313 gsl_vector_set(rx, 1, gsl_ran_gaussian(randGen_m, 1.0));
2314 gsl_vector_set(rx, 2, gsl_ran_gaussian(randGen_m, 1.0));
2315 gsl_vector_set(rx, 3, gsl_ran_gaussian(randGen_m, 1.0));
2316 gsl_vector_set(rx, 4, gsl_ran_gaussian(randGen_m, 1.0));
2317 gsl_vector_set(rx, 5, gsl_ran_gaussian(randGen_m, 1.0));
2318
2319 gsl_blas_dgemv(CblasNoTrans, 1.0, corMat, rx, 0.0, ry);
2320
2321 x = gsl_vector_get(ry, 0);
2322 px = gsl_vector_get(ry, 1);
2323 y = gsl_vector_get(ry, 2);
2324 py = gsl_vector_get(ry, 3);
2325 z = gsl_vector_get(ry, 4);
2326 pz = gsl_vector_get(ry, 5);
2327
2328 bool xAndYOk = (std::pow( x / cutoffR_m[0], 2) + std::pow( y / cutoffR_m[1], 2) <= 1.0);
2329 bool pxAndPyOk = (std::pow(px / cutoffP_m[0], 2) + std::pow(py / cutoffP_m[1], 2) <= 1.0);
2330
2331 bool zOk = (std::abs(z) <= cutoffR_m[2]);
2332 bool pzOk = (std::abs(pz) <= cutoffP_m[2]);
2333
2334 allow = (xAndYOk && pxAndPyOk && zOk && pzOk);
2335 }
2336
2337 x *= sigmaR_m[0];
2338 y *= sigmaR_m[1];
2339 z *= sigmaR_m[2];
2340 px *= sigmaP_m[0];
2341 py *= sigmaP_m[1];
2342 pz *= sigmaP_m[2];
2343
2344 // Save to each processor in turn.
2345 saveProcessor++;
2346 if (saveProcessor >= numNodes)
2347 saveProcessor = 0;
2348
2349 if (scalable || myNode == saveProcessor) {
2350 xDist_m.push_back(x);
2351 pxDist_m.push_back(px);
2352 yDist_m.push_back(y);
2353 pyDist_m.push_back(py);
2354 tOrZDist_m.push_back(z);
2355 pzDist_m.push_back(avrgpz_m + pz);
2356
2357 //*gmsg << "x,y,z,px,py,pz " << std::setw(11) << x << std::setw(11) << y << std::setw(11) << z << std::setw(11) << px << std::setw(11) << py << std::setw(11) << avrgpz_m + pz << endl;
2358 }
2359 }
2360
2361 gsl_vector_free(rx);
2362 gsl_vector_free(ry);
2363 gsl_matrix_free(corMat);
2364}
2365
2367 size_t numberOfParticles, double massIneV)
2368{
2369 /* This particle distribution generation is based on a
2370 * symplectic method described in
2371 * https://arxiv.org/abs/1205.3601
2372 */
2373
2374 /* Units of the Sigma Matrix:
2375 * spatial: m
2376 * moment: rad
2377 *
2378 * Attention: The vertical and longitudinal direction must be interchanged!
2379 */
2380 for (unsigned int i = 0; i < 3; ++ i) {
2381 if ( sigma(2 * i, 2 * i) < 0 || sigma(2 * i + 1, 2 * i + 1) < 0 )
2382 throw OpalException("Distribution::generateMatchedGauss()",
2383 "Negative value on the diagonal of the sigma matrix.");
2384 }
2385
2386 double bgam = Util::getBetaGamma(E_m, massIneV);
2387
2388 /*
2389 * only used for printing
2390 */
2391
2392 // horizontal
2393 sigmaR_m[0] = std::sqrt(sigma(0, 0));
2394 sigmaP_m[0] = std::sqrt(sigma(1, 1)) * bgam;
2395
2396 // longitudinal
2397 sigmaR_m[1] = std::sqrt(sigma(4, 4));
2398 sigmaP_m[1] = std::sqrt(sigma(5, 5)) * bgam;
2399
2400 // vertical
2401 sigmaR_m[2] = std::sqrt(sigma(2, 2));
2402 sigmaP_m[2] = std::sqrt(sigma(3, 3)) * bgam;
2403
2404 correlationMatrix_m(1, 0) = sigma(0, 1) / (std::sqrt(sigma(0, 0) * sigma(1, 1)));
2405 correlationMatrix_m(3, 2) = sigma(2, 3) / (std::sqrt(sigma(2, 2) * sigma(3, 3)));
2406 correlationMatrix_m(5, 4) = sigma(4, 5) / (std::sqrt(sigma(4, 4) * sigma(5, 5)));
2407 correlationMatrix_m(4, 0) = sigma(0, 4) / (std::sqrt(sigma(0, 0) * sigma(4, 4)));
2408 correlationMatrix_m(4, 1) = sigma(1, 4) / (std::sqrt(sigma(1, 1) * sigma(4, 4)));
2409 correlationMatrix_m(5, 0) = sigma(0, 5) / (std::sqrt(sigma(0, 0) * sigma(5, 5)));
2410 correlationMatrix_m(5, 1) = sigma(1, 5) / (std::sqrt(sigma(1, 1) * sigma(5, 5)));
2411
2413
2414 /*
2415 * decouple horizontal and longitudinal direction
2416 */
2417
2418 // extract horizontal and longitudinal directions
2420 A(0, 0) = sigma(0, 0);
2421 A(1, 1) = sigma(1, 1);
2422 A(2, 2) = sigma(4, 4);
2423 A(3, 3) = sigma(5, 5);
2424
2425 A(0, 1) = sigma(0, 1);
2426 A(0, 2) = sigma(0, 4);
2427 A(0, 3) = sigma(0, 5);
2428 A(1, 0) = sigma(1, 0);
2429 A(2, 0) = sigma(4, 0);
2430 A(3, 0) = sigma(5, 0);
2431
2432 A(1, 2) = sigma(1, 4);
2433 A(2, 1) = sigma(4, 1);
2434 A(1, 3) = sigma(1, 5);
2435 A(3, 1) = sigma(5, 1);
2436 A(2, 3) = sigma(4, 5);
2437 A(3, 2) = sigma(5, 4);
2438
2439
2440 RealDiracMatrix rdm;
2442
2443 RealDiracMatrix::vector_t variances(8);
2444 for (int i = 0; i < 4; ++i) {
2445 variances(i) = std::sqrt(A(i, i));
2446 }
2447
2448 /*
2449 * decouple vertical direction
2450 */
2451 A *= 0.0;
2452 A(0, 0) = 1;
2453 A(1, 1) = 1;
2454 A(2, 2) = sigma(2, 2);
2455 A(3, 3) = sigma(3, 3);
2456 A(2, 3) = sigma(2, 3);
2457 A(3, 2) = sigma(3, 2);
2458
2460
2461 for (int i = 0; i < 4; ++i) {
2462 variances(4 + i) = std::sqrt(A(i, i));
2463 }
2464
2465 int saveProcessor = -1;
2466 const int myNode = Ippl::myNode();
2467 const int numNodes = Ippl::getNodes();
2469
2470 RealDiracMatrix::vector_t p1(4), p2(4);
2471 for (size_t i = 0; i < numberOfParticles; i++) {
2472 for (int j = 0; j < 4; j++) {
2473 p1(j) = gsl_ran_gaussian(randGen_m, 1.0) * variances(j);
2474 p2(j) = gsl_ran_gaussian(randGen_m, 1.0) * variances(4 + j);
2475 }
2476
2477 p1 = boost::numeric::ublas::prod(R1, p1);
2478 p2 = boost::numeric::ublas::prod(R2, p2);
2479
2480 // Save to each processor in turn.
2481 saveProcessor++;
2482 if (saveProcessor >= numNodes)
2483 saveProcessor = 0;
2484
2485 if (scalable || myNode == saveProcessor) {
2486 xDist_m.push_back(p1(0));
2487 pxDist_m.push_back(p1(1) * bgam);
2488 yDist_m.push_back(p1(2));
2489 pyDist_m.push_back(p1(3) * bgam);
2490 tOrZDist_m.push_back(p2(2));
2491 pzDist_m.push_back(p2(3) * bgam);
2492 }
2493 }
2494}
2495
2496void Distribution::generateLongFlattopT(size_t numberOfParticles) {
2497
2498 double flattopTime = tPulseLengthFWHM_m
2499 - std::sqrt(2.0 * std::log(2.0)) * (sigmaTRise_m + sigmaTFall_m);
2500
2501 if (flattopTime < 0.0)
2502 flattopTime = 0.0;
2503
2504 double normalizedFlankArea = 0.5 * std::sqrt(Physics::two_pi) * gsl_sf_erf(cutoffR_m[2] / std::sqrt(2.0));
2505 double distArea = flattopTime
2506 + (sigmaTRise_m + sigmaTFall_m) * normalizedFlankArea;
2507
2508 // Find number of particles in rise, fall and flat top.
2509 size_t numRise = numberOfParticles * sigmaTRise_m * normalizedFlankArea / distArea;
2510 size_t numFall = numberOfParticles * sigmaTFall_m * normalizedFlankArea / distArea;
2511 size_t numFlat = numberOfParticles - numRise - numFall;
2512
2513 // Generate particles in tail.
2514 int saveProcessor = -1;
2515 const int myNode = Ippl::myNode();
2516 const int numNodes = Ippl::getNodes();
2518
2519 for (size_t partIndex = 0; partIndex < numFall; partIndex++) {
2520
2521 double t = 0.0;
2522 double pz = 0.0;
2523
2524 bool allow = false;
2525 while (!allow) {
2526 t = gsl_ran_gaussian_tail(randGen_m, 0, sigmaTFall_m);
2527 if (t <= sigmaTFall_m * cutoffR_m[2]) {
2528 t = -t + sigmaTFall_m * cutoffR_m[2];
2529 allow = true;
2530 }
2531 }
2532
2533 // Save to each processor in turn.
2534 saveProcessor++;
2535 if (saveProcessor >= numNodes)
2536 saveProcessor = 0;
2537
2538 if (scalable || myNode == saveProcessor) {
2539 tOrZDist_m.push_back(t);
2540 pzDist_m.push_back(pz);
2541 }
2542 }
2543
2544 /*
2545 * Generate particles in flat top. The flat top can also have sinusoidal
2546 * modulations.
2547 */
2548 double modulationAmp = Attributes::getReal(itsAttr[Attrib::Distribution::FTOSCAMPLITUDE]) / 100.0;
2549 if (modulationAmp > 1.0)
2550 modulationAmp = 1.0;
2551 double numModulationPeriods
2553 double modulationPeriod = 0.0;
2554 if (numModulationPeriods != 0.0)
2555 modulationPeriod = flattopTime / numModulationPeriods;
2556
2557 gsl_qrng *quasiRandGen1D = selectRandomGenerator(Options::rngtype,1);
2558 gsl_qrng *quasiRandGen2D = selectRandomGenerator(Options::rngtype,2);
2559
2560 for (size_t partIndex = 0; partIndex < numFlat; partIndex++) {
2561
2562 double t = 0.0;
2563 double pz = 0.0;
2564
2565 if (modulationAmp == 0.0 || numModulationPeriods == 0.0) {
2566
2567 if (quasiRandGen1D != nullptr)
2568 gsl_qrng_get(quasiRandGen1D, &t);
2569 else
2570 t = gsl_rng_uniform(randGen_m);
2571
2572 t = flattopTime * t;
2573
2574 } else {
2575
2576 bool allow = false;
2577 double randNums[2] = {0.0, 0.0};
2578 while (!allow) {
2579 if (quasiRandGen2D != nullptr) {
2580 gsl_qrng_get(quasiRandGen2D, randNums);
2581 } else {
2582 randNums[0]= gsl_rng_uniform(randGen_m);
2583 randNums[1]= gsl_rng_uniform(randGen_m);
2584 }
2585 t = randNums[0] * flattopTime;
2586
2587 double funcValue = (1.0 + modulationAmp
2588 * std::sin(Physics::two_pi * t / modulationPeriod))
2589 / (1.0 + std::abs(modulationAmp));
2590
2591 allow = (randNums[1] <= funcValue);
2592 }
2593 }
2594 // Shift the uniform part of distribution behind the fall
2595 t += sigmaTFall_m * cutoffR_m[2];
2596
2597 // Save to each processor in turn.
2598 saveProcessor++;
2599 if (saveProcessor >= numNodes)
2600 saveProcessor = 0;
2601
2602 if (scalable || myNode == saveProcessor) {
2603 tOrZDist_m.push_back(t);
2604 pzDist_m.push_back(pz);
2605 }
2606 }
2607
2608 // Generate particles in rise.
2609 for (size_t partIndex = 0; partIndex < numRise; partIndex++) {
2610
2611 double t = 0.0;
2612 double pz = 0.0;
2613
2614 bool allow = false;
2615 while (!allow) {
2616 t = gsl_ran_gaussian_tail(randGen_m, 0, sigmaTRise_m);
2617 if (t <= sigmaTRise_m * cutoffR_m[2]) {
2618 t += sigmaTFall_m * cutoffR_m[2] + flattopTime;
2619 allow = true;
2620 }
2621 }
2622
2623 // Save to each processor in turn.
2624 saveProcessor++;
2625 if (saveProcessor >= numNodes)
2626 saveProcessor = 0;
2627
2628 if (scalable || myNode == saveProcessor) {
2629 tOrZDist_m.push_back(t);
2630 pzDist_m.push_back(pz);
2631 }
2632 }
2633
2634 gsl_qrng_free(quasiRandGen1D);
2635 gsl_qrng_free(quasiRandGen2D);
2636}
2637
2638void Distribution::generateTransverseGauss(size_t numberOfParticles) {
2639
2640 // Generate coordinates.
2641 gsl_matrix * corMat = gsl_matrix_alloc (4, 4);
2642 gsl_vector * rx = gsl_vector_alloc(4);
2643 gsl_vector * ry = gsl_vector_alloc(4);
2644
2645 for (unsigned int i = 0; i < 4; ++ i) {
2646 gsl_matrix_set(corMat, i, i, correlationMatrix_m(i, i));
2647 for (unsigned int j = 0; j < i; ++ j) {
2648 gsl_matrix_set(corMat, i, j, correlationMatrix_m(i, j));
2649 gsl_matrix_set(corMat, j, i, correlationMatrix_m(i, j));
2650 }
2651 }
2652
2653 int errcode = gsl_linalg_cholesky_decomp(corMat);
2654
2655 if (errcode == GSL_EDOM) {
2656 throw OpalException("Distribution::GenerateTransverseGauss",
2657 "gsl_linalg_cholesky_decomp failed");
2658 }
2659
2660 for (int i = 0; i < 3; ++ i) {
2661 for (int j = i+1; j < 4 ; ++ j) {
2662 gsl_matrix_set (corMat, i, j, 0.0);
2663 }
2664 }
2665
2666 int saveProcessor = -1;
2667 const int myNode = Ippl::myNode();
2668 const int numNodes = Ippl::getNodes();
2670
2671 for (size_t partIndex = 0; partIndex < numberOfParticles; partIndex++) {
2672
2673 double x = 0.0;
2674 double px = 0.0;
2675 double y = 0.0;
2676 double py = 0.0;
2677
2678 bool allow = false;
2679 while (!allow) {
2680
2681 gsl_vector_set(rx, 0, gsl_ran_gaussian (randGen_m,1.0));
2682 gsl_vector_set(rx, 1, gsl_ran_gaussian (randGen_m,1.0));
2683 gsl_vector_set(rx, 2, gsl_ran_gaussian (randGen_m,1.0));
2684 gsl_vector_set(rx, 3, gsl_ran_gaussian (randGen_m,1.0));
2685
2686 gsl_blas_dgemv(CblasNoTrans, 1.0, corMat, rx, 0.0, ry);
2687 x = gsl_vector_get(ry, 0);
2688 px = gsl_vector_get(ry, 1);
2689 y = gsl_vector_get(ry, 2);
2690 py = gsl_vector_get(ry, 3);
2691
2692 bool xAndYOk = (std::pow( x / cutoffR_m[0], 2) + std::pow( y / cutoffR_m[1], 2) <= 1.0);
2693 bool pxAndPyOk = (std::pow(px / cutoffP_m[0], 2) + std::pow(py / cutoffP_m[1], 2) <= 1.0);
2694
2695 allow = (xAndYOk && pxAndPyOk);
2696
2697 }
2698 x *= sigmaR_m[0];
2699 y *= sigmaR_m[1];
2700 px *= sigmaP_m[0];
2701 py *= sigmaP_m[1];
2702
2703 // Save to each processor in turn.
2704 saveProcessor++;
2705 if (saveProcessor >= numNodes)
2706 saveProcessor = 0;
2707
2708 if (scalable || myNode == saveProcessor) {
2709 xDist_m.push_back(x);
2710 pxDist_m.push_back(px);
2711 yDist_m.push_back(y);
2712 pyDist_m.push_back(py);
2713 }
2714 }
2715
2716 gsl_matrix_free(corMat);
2717 gsl_vector_free(rx);
2718 gsl_vector_free(ry);
2719}
2720
2722
2723 /*
2724 * Set emission time, the current beam bunch number and
2725 * set the beam energy bin structure, if it has one.
2726 */
2728 beam->setNumBunch(1);
2729 if (numberOfEnergyBins_m > 0)
2731}
2732
2734
2736
2739
2740 bool hasID1 = !id1.empty();
2741 bool hasID2 = !id2.empty();
2742
2743 if (hasID1 || hasID2)
2744 *gmsg << "* Use special ID1 or ID2 particle in distribution" << endl;
2745
2746
2747 size_t numberOfParticles = tOrZDist_m.size();
2748 beam->create(numberOfParticles);
2749 for (size_t partIndex = 0; partIndex < numberOfParticles; partIndex++) {
2750
2751 beam->R[partIndex] = Vector_t(xDist_m.at(partIndex),
2752 yDist_m.at(partIndex),
2753 tOrZDist_m.at(partIndex));
2754
2755 beam->P[partIndex] = Vector_t(pxDist_m.at(partIndex),
2756 pyDist_m.at(partIndex),
2757 pzDist_m.at(partIndex));
2758
2759 beam->Q[partIndex] = beam->getChargePerParticle();
2760 beam->M[partIndex] = beam->getMassPerParticle();
2761 beam->Ef[partIndex] = Vector_t(0.0);
2762 beam->Bf[partIndex] = Vector_t(0.0);
2763 beam->PType[partIndex] = beam->getPType();
2764 beam->POrigin[partIndex] = ParticleOrigin::REGULAR;
2765 beam->TriID[partIndex] = 0;
2766 if (numberOfEnergyBins_m > 0) {
2767 size_t binNumber = findEBin(tOrZDist_m.at(partIndex));
2768 beam->Bin[partIndex] = binNumber;
2769 beam->iterateEmittedBin(binNumber);
2770 } else
2771 beam->Bin[partIndex] = 0;
2772
2773 if (hasID1) {
2774 if (beam->ID[partIndex] == 1) {
2775 beam->R[partIndex] = Vector_t(id1[0],id1[1],id1[2]);
2776 beam->P[partIndex] = Vector_t(id1[3],id1[4],id1[5]);
2777 }
2778 }
2779
2780 if (hasID2) {
2781 if (beam->ID[partIndex] == 2) {
2782 beam->R[partIndex] = Vector_t(id2[0],id2[1],id2[2]);
2783 beam->P[partIndex] = Vector_t(id2[3],id2[4],id2[5]);
2784 }
2785 }
2786 }
2787
2788 xDist_m.clear();
2789 pxDist_m.clear();
2790 yDist_m.clear();
2791 pyDist_m.clear();
2792 tOrZDist_m.clear();
2793 pzDist_m.clear();
2794
2795 beam->boundp();
2796 beam->calcEMean();
2797}
2798
2800 return emitting_m;
2801}
2802
2804 return currentEnergyBin_m;
2805}
2806
2808
2809 double maxTOrZ = std::numeric_limits<int>::min();
2810 for (auto tOrZ : tOrZDist_m) {
2811 if (maxTOrZ < tOrZ)
2812 maxTOrZ = tOrZ;
2813 }
2814
2815 reduce(maxTOrZ, maxTOrZ, OpMaxAssign());
2816
2817 return maxTOrZ;
2818}
2819
2821
2822 double minTOrZ = std::numeric_limits<int>::max();
2823 for (auto tOrZ : tOrZDist_m) {
2824 if (minTOrZ > tOrZ)
2825 minTOrZ = tOrZ;
2826 }
2827
2828 reduce(minTOrZ, minTOrZ, OpMinAssign());
2829
2830 return minTOrZ;
2831}
2832
2834 return static_cast<size_t> (numberOfEnergyBins_m * numberOfSampleBins_m);
2835}
2836
2838 return numberOfEnergyBins_m;
2839}
2840
2843}
2844
2846 return tBin_m;
2847}
2848
2851}
2852
2853std::vector<double>& Distribution::getXDist() {
2854 return xDist_m;
2855}
2856
2857std::vector<double>& Distribution::getBGxDist() {
2858 return pxDist_m;
2859}
2860
2861std::vector<double>& Distribution::getYDist() {
2862 return yDist_m;
2863}
2864
2865std::vector<double>& Distribution::getBGyDist() {
2866 return pyDist_m;
2867}
2868
2869std::vector<double>& Distribution::getTOrZDist() {
2870 return tOrZDist_m;
2871}
2872
2873std::vector<double>& Distribution::getBGzDist() {
2874 return pzDist_m;
2875}
2876
2877void Distribution::printDist(Inform &os, size_t numberOfParticles) const {
2878
2879 if (numberOfParticles > 0) {
2880 size_t np = numberOfParticles * (Options::cZero && !(distrTypeT_m == DistributionType::FROMFILE)? 2: 1);
2881 reduce(np, np, OpAddAssign());
2882 os << "* Number of particles: "
2883 << np
2884 << endl
2885 << "* " << endl;
2886 }
2887
2888 os << "* Distribution input momentum units: ";
2889 switch (inputMoUnits_m) {
2891 os << "[Beta Gamma]" << "\n* " << endl;
2892 break;
2893 }
2895 os << "[eV/c]" << "\n* " << endl;
2896 break;
2897 }
2898 default:
2899 throw OpalException("Distribution::printDist",
2900 "Unknown \"INPUTMOUNITS\" for \"DISTRIBUTION\" command");
2901 }
2902
2903 switch (distrTypeT_m) {
2906 break;
2908 printDistGauss(os);
2909 break;
2912 break;
2916 printDistFlattop(os);
2917 break;
2920 break;
2923 break;
2924 default:
2925 throw OpalException("Distribution::printDist",
2926 "Unknown \"TYPE\" of \"DISTRIBUTION\"");
2927 }
2928
2929}
2930
2932
2933 os << "* Distribution type: BINOMIAL" << endl;
2934 os << "* " << endl;
2935 os << "* SIGMAX = " << sigmaR_m[0] << " [m]" << endl;
2936 os << "* SIGMAY = " << sigmaR_m[1] << " [m]" << endl;
2937 if (emitting_m)
2938 os << "* SIGMAT = " << sigmaR_m[2] << " [sec]" << endl;
2939 else
2940 os << "* SIGMAZ = " << sigmaR_m[2] << " [m]" << endl;
2941 os << "* SIGMAPX = " << sigmaP_m[0] << " [Beta Gamma]" << endl;
2942 os << "* SIGMAPY = " << sigmaP_m[1] << " [Beta Gamma]" << endl;
2943 os << "* SIGMAPZ = " << sigmaP_m[2] << " [Beta Gamma]" << endl;
2944 os << "* MX = " << mBinomial_m[0] << endl;
2945 os << "* MY = " << mBinomial_m[1] << endl;
2946 if (emitting_m)
2947 os << "* MT = " << mBinomial_m[2] << endl;
2948 else
2949 os << "* MZ = " << mBinomial_m[2] << endl;
2950 os << "* CORRX = " << correlationMatrix_m(1, 0) << endl;
2951 os << "* CORRY = " << correlationMatrix_m(3, 2) << endl;
2952 os << "* CORRZ = " << correlationMatrix_m(5, 4) << endl;
2953 os << "* R61 = " << correlationMatrix_m(5, 0) << endl;
2954 os << "* R62 = " << correlationMatrix_m(5, 1) << endl;
2955 os << "* R51 = " << correlationMatrix_m(4, 0) << endl;
2956 os << "* R52 = " << correlationMatrix_m(4, 1) << endl;
2957}
2958
2960
2961 switch (distrTypeT_m) {
2962
2964 os << "* Distribution type: ASTRAFLATTOPTH" << endl;
2965 break;
2966
2968 os << "* Distribution type: GUNGAUSSFLATTOPTH" << endl;
2969 break;
2970
2971 default:
2972 os << "* Distribution type: FLATTOP" << endl;
2973 break;
2974
2975 }
2976 os << "* " << endl;
2977
2978 if (laserProfile_m != nullptr) {
2979
2980 os << "* Transverse profile determined by laser image: " << endl
2981 << endl
2982 << "* Laser profile: " << laserProfileFileName_m << endl
2983 << "* Laser image: " << laserImageName_m << endl
2984 << "* Intensity cut: " << laserIntensityCut_m << endl;
2985
2986 } else {
2987
2988 os << "* SIGMAX = " << sigmaR_m[0] << " [m]" << endl;
2989 os << "* SIGMAY = " << sigmaR_m[1] << " [m]" << endl;
2990
2991 }
2992
2993 if (emitting_m) {
2994
2996
2997 os << "* Time Rise = " << tRise_m
2998 << " [sec]" << endl;
2999 os << "* TPULSEFWHM = " << tPulseLengthFWHM_m
3000 << " [sec]" << endl;
3001
3002 } else {
3003 os << "* Sigma Time Rise = " << sigmaTRise_m
3004 << " [sec]" << endl;
3005 os << "* TPULSEFWHM = " << tPulseLengthFWHM_m
3006 << " [sec]" << endl;
3007 os << "* Sigma Time Fall = " << sigmaTFall_m
3008 << " [sec]" << endl;
3009 os << "* Longitudinal cutoff = " << cutoffR_m[2]
3010 << " [units of Sigma Time]" << endl;
3011 os << "* Flat top modulation amplitude = "
3013 << " [Percent of distribution amplitude]" << endl;
3014 os << "* Flat top modulation periods = "
3016 << endl;
3017 }
3018
3019 } else
3020 os << "* SIGMAZ = " << sigmaR_m[2]
3021 << " [m]" << endl;
3022}
3023
3025
3026 os << "* Distribution type: MULTIGAUSS" << endl;
3027 os << "* " << endl;
3028
3029
3030 os << "* SIGMAX = " << sigmaR_m[0] << " [m]" << endl;
3031 os << "* SIGMAY = " << sigmaR_m[1] << " [m]" << endl;
3032
3033 std::string longUnits;
3034 if (emitting_m)
3035 longUnits = " [sec]";
3036 else
3037 longUnits = " [m]";
3038
3039 os << "* SIGMAZ = " << sigmaR_m[2] << longUnits << endl;
3040 os << "* CUTOFFLONG = " << cutoffR_m[2] << " [units of SIGMAZ]" << endl;
3041 os << "* SEPPEAKS = " << sepPeaks_m << longUnits << endl;
3042 os << "* NPEAKS = " << nPeaks_m << endl;
3043
3044 if (!emitting_m) {
3045 os << "* SIGMAPX = " << sigmaP_m[0] << " [Beta Gamma]" << endl;
3046 os << "* SIGMAPY = " << sigmaP_m[1] << " [Beta Gamma]" << endl;
3047 os << "* SIGMAPZ = " << sigmaP_m[2] << " [Beta Gamma]" << endl;
3048 os << "* CUTOFFPX = " << cutoffP_m[0] << " [units of SIGMAPX]" << endl;
3049 os << "* CUTOFFPY = " << cutoffP_m[1] << " [units of SIGMAPY]" << endl;
3050 os << "* CUTOFFPZ = " << cutoffP_m[2] << " [units of SIGMAPZ]" << endl;
3051 }
3052}
3053
3055 os << "* Distribution type: FROMFILE" << endl;
3056 os << "* " << endl;
3057 os << "* Input file: '"
3059}
3060
3061
3063 os << "* Distribution type: MATCHEDGAUSS" << endl;
3064 os << "* SIGMAX = " << sigmaR_m[0] << " [m]" << endl;
3065 os << "* SIGMAY = " << sigmaR_m[1] << " [m]" << endl;
3066 os << "* SIGMAZ = " << sigmaR_m[2] << " [m]" << endl;
3067 os << "* SIGMAPX = " << sigmaP_m[0] << " [Beta Gamma]" << endl;
3068 os << "* SIGMAPY = " << sigmaP_m[1] << " [Beta Gamma]" << endl;
3069 os << "* SIGMAPZ = " << sigmaP_m[2] << " [Beta Gamma]" << endl;
3070// os << "* AVRGPZ = " << avrgpz_m << " [Beta Gamma]" << endl;
3071
3072 os << "* CORRX = " << correlationMatrix_m(1, 0) << endl;
3073 os << "* CORRY = " << correlationMatrix_m(3, 2) << endl;
3074 os << "* CORRZ = " << correlationMatrix_m(5, 4) << endl;
3075 os << "* R61 = " << correlationMatrix_m(5, 0) << endl;
3076 os << "* R62 = " << correlationMatrix_m(5, 1) << endl;
3077 os << "* R51 = " << correlationMatrix_m(4, 0) << endl;
3078 os << "* R52 = " << correlationMatrix_m(4, 1) << endl;
3079// os << "* CUTOFFX = " << cutoffR_m[0] << " [units of SIGMAX]" << endl;
3080// os << "* CUTOFFY = " << cutoffR_m[1] << " [units of SIGMAY]" << endl;
3081// os << "* CUTOFFLONG = " << cutoffR_m[2] << " [units of SIGMAZ]" << endl;
3082// os << "* CUTOFFPX = " << cutoffP_m[0] << " [units of SIGMAPX]" << endl;
3083// os << "* CUTOFFPY = " << cutoffP_m[1] << " [units of SIGMAPY]" << endl;
3084// os << "* CUTOFFPZ = " << cutoffP_m[2] << " [units of SIGMAPY]" << endl;
3085}
3086
3088 os << "* Distribution type: GAUSS" << endl;
3089 os << "* " << endl;
3090 if (emitting_m) {
3091 os << "* Sigma Time Rise = " << sigmaTRise_m
3092 << " [sec]" << endl;
3093 os << "* TPULSEFWHM = " << tPulseLengthFWHM_m
3094 << " [sec]" << endl;
3095 os << "* Sigma Time Fall = " << sigmaTFall_m
3096 << " [sec]" << endl;
3097 os << "* Longitudinal cutoff = " << cutoffR_m[2]
3098 << " [units of Sigma Time]" << endl;
3099 os << "* Flat top modulation amplitude = "
3101 << " [Percent of distribution amplitude]" << endl;
3102 os << "* Flat top modulation periods = "
3104 << endl;
3105 os << "* SIGMAX = " << sigmaR_m[0] << " [m]" << endl;
3106 os << "* SIGMAY = " << sigmaR_m[1] << " [m]" << endl;
3107 os << "* SIGMAPX = " << sigmaP_m[0]
3108 << " [Beta Gamma]" << endl;
3109 os << "* SIGMAPY = " << sigmaP_m[1]
3110 << " [Beta Gamma]" << endl;
3111 os << "* CORRX = " << correlationMatrix_m(1, 0) << endl;
3112 os << "* CORRY = " << correlationMatrix_m(3, 2) << endl;
3113 os << "* CUTOFFX = " << cutoffR_m[0]
3114 << " [units of SIGMAX]" << endl;
3115 os << "* CUTOFFY = " << cutoffR_m[1]
3116 << " [units of SIGMAY]" << endl;
3117 os << "* CUTOFFPX = " << cutoffP_m[0]
3118 << " [units of SIGMAPX]" << endl;
3119 os << "* CUTOFFPY = " << cutoffP_m[1]
3120 << " [units of SIGMAPY]" << endl;
3121 } else {
3122 os << "* SIGMAX = " << sigmaR_m[0] << " [m]" << endl;
3123 os << "* SIGMAY = " << sigmaR_m[1] << " [m]" << endl;
3124 os << "* SIGMAZ = " << sigmaR_m[2] << " [m]" << endl;
3125 os << "* SIGMAPX = " << sigmaP_m[0] << " [Beta Gamma]" << endl;
3126 os << "* SIGMAPY = " << sigmaP_m[1] << " [Beta Gamma]" << endl;
3127 os << "* SIGMAPZ = " << sigmaP_m[2] << " [Beta Gamma]" << endl;
3128 os << "* AVRGPZ = " << avrgpz_m << " [Beta Gamma]" << endl;
3129
3130 os << "* CORRX = " << correlationMatrix_m(1, 0) << endl;
3131 os << "* CORRY = " << correlationMatrix_m(3, 2) << endl;
3132 os << "* CORRZ = " << correlationMatrix_m(5, 4) << endl;
3133 os << "* R61 = " << correlationMatrix_m(5, 0) << endl;
3134 os << "* R62 = " << correlationMatrix_m(5, 1) << endl;
3135 os << "* R51 = " << correlationMatrix_m(4, 0) << endl;
3136 os << "* R52 = " << correlationMatrix_m(4, 1) << endl;
3137 os << "* CUTOFFX = " << cutoffR_m[0] << " [units of SIGMAX]" << endl;
3138 os << "* CUTOFFY = " << cutoffR_m[1] << " [units of SIGMAY]" << endl;
3139 os << "* CUTOFFLONG = " << cutoffR_m[2] << " [units of SIGMAZ]" << endl;
3140 os << "* CUTOFFPX = " << cutoffP_m[0] << " [units of SIGMAPX]" << endl;
3141 os << "* CUTOFFPY = " << cutoffP_m[1] << " [units of SIGMAPY]" << endl;
3142 os << "* CUTOFFPZ = " << cutoffP_m[2] << " [units of SIGMAPY]" << endl;
3143 }
3144}
3145
3147
3148 os << "* ------------- THERMAL EMITTANCE MODEL --------------------------------------------" << endl;
3149
3150 switch (emissionModel_m) {
3151
3154 break;
3157 break;
3160 break;
3161 default:
3162 break;
3163 }
3164
3165 os << "* ----------------------------------------------------------------------------------" << endl;
3166
3167}
3168
3170 os << "* THERMAL EMITTANCE in ASTRA MODE" << endl;
3171 os << "* Kinetic energy (thermal emittance) = "
3173 << " [eV] " << endl;
3174}
3175
3177 os << "* THERMAL EMITTANCE in NONE MODE" << endl;
3178 os << "* Kinetic energy added to longitudinal momentum = "
3180 << " [eV] " << endl;
3181}
3182
3184 os << "* THERMAL EMITTANCE in NONEQUIL MODE" << endl;
3185 os << "* Cathode work function = " << cathodeWorkFunc_m << " [eV] " << endl;
3186 os << "* Cathode Fermi energy = " << cathodeFermiEnergy_m << " [eV] " << endl;
3187 os << "* Cathode temperature = " << cathodeTemp_m / Physics::kB << " [K] " << endl;
3188 os << "* Photocathode laser energy = " << laserEnergy_m << " [eV] " << endl;
3189}
3190
3192
3193 os << "* " << endl;
3194 for (int binIndex = numberOfEnergyBins_m - 1; binIndex >=0; binIndex--) {
3195 size_t sum = 0;
3196 for (int sampleIndex = 0; sampleIndex < numberOfSampleBins_m; sampleIndex++)
3197 sum += gsl_histogram_get(energyBinHist_m,
3198 binIndex * numberOfSampleBins_m + sampleIndex);
3199
3200 os << "* Energy Bin # " << numberOfEnergyBins_m - binIndex
3201 << " contains " << sum << " particles" << endl;
3202 }
3203 os << "* " << endl;
3204
3205}
3206
3208 /*
3209 * Allow a rebin (numberOfEnergyBins_m = 0) if all particles are emitted or
3210 * injected.
3211 */
3212 if (!emitting_m) {
3214 return true;
3215 } else {
3216 return false;
3217 }
3218}
3219
3220void Distribution::reflectDistribution(size_t &numberOfParticles) {
3221
3223 return;
3224
3225 size_t currentNumPart = tOrZDist_m.size();
3226 for (size_t partIndex = 0; partIndex < currentNumPart; partIndex++) {
3227 xDist_m.push_back(-xDist_m.at(partIndex));
3228 pxDist_m.push_back(-pxDist_m.at(partIndex));
3229 yDist_m.push_back(-yDist_m.at(partIndex));
3230 pyDist_m.push_back(-pyDist_m.at(partIndex));
3231 tOrZDist_m.push_back(tOrZDist_m.at(partIndex));
3232 pzDist_m.push_back(pzDist_m.at(partIndex));
3233 }
3234 numberOfParticles = tOrZDist_m.size();
3235 reduce(numberOfParticles, numberOfParticles, OpAddAssign());
3236
3237 // if numberOfParticles is odd then delete last particle
3238 if (Ippl::myNode() == 0 &&
3239 (numberOfParticles + 1) / 2 != numberOfParticles / 2) {
3240 xDist_m.pop_back();
3241 pxDist_m.pop_back();
3242 yDist_m.pop_back();
3243 pyDist_m.pop_back();
3244 tOrZDist_m.pop_back();
3245 pzDist_m.pop_back();
3246 }
3247}
3248
3250 // at this point the distributions of an array of distributions are still separated
3251
3256 const double longmult = (emitting_m ?
3260
3261 for (size_t particleIndex = 0; particleIndex < tOrZDist_m.size(); ++ particleIndex) {
3262 xDist_m.at(particleIndex) *= xmult;
3263 pxDist_m.at(particleIndex) *= pxmult;
3264 yDist_m.at(particleIndex) *= ymult;
3265 pyDist_m.at(particleIndex) *= pymult;
3266 tOrZDist_m.at(particleIndex) *= longmult;
3267 pzDist_m.at(particleIndex) *= pzmult;
3268 }
3269}
3270
3271gsl_qrng* Distribution::selectRandomGenerator(std::string,unsigned int dimension)
3272{
3273 gsl_qrng *quasiRandGen = nullptr;
3274 if (Options::rngtype != std::string("RANDOM")) {
3275 INFOMSG("RNGTYPE= " << Options::rngtype << endl);
3276 if (Options::rngtype == std::string("HALTON")) {
3277 quasiRandGen = gsl_qrng_alloc(gsl_qrng_halton, dimension);
3278 } else if (Options::rngtype == std::string("SOBOL")) {
3279 quasiRandGen = gsl_qrng_alloc(gsl_qrng_sobol, dimension);
3280 } else if (Options::rngtype == std::string("NIEDERREITER")) {
3281 quasiRandGen = gsl_qrng_alloc(gsl_qrng_niederreiter_2, dimension);
3282 } else {
3283 INFOMSG("RNGTYPE= " << Options::rngtype << " not known, using HALTON" << endl);
3284 quasiRandGen = gsl_qrng_alloc(gsl_qrng_halton, dimension);
3285 }
3286 }
3287 return quasiRandGen;
3288}
3289
3292 = Attributes::makePredefinedString("TYPE","Distribution type.",
3293 {"FROMFILE",
3294 "GAUSS",
3295 "BINOMIAL",
3296 "FLATTOP",
3297 "MULTIGAUSS",
3298 "GUNGAUSSFLATTOPTH",
3299 "ASTRAFLATTOPTH",
3300 "GAUSSMATCHED"});
3302 = Attributes::makeString("DISTRIBUTION","This attribute isn't supported any more. Use TYPE instead");
3304 = Attributes::makeString("LINE", "Beamline that contains a cyclotron or ring ", "");
3306 = Attributes::makeReal("EX", "Projected normalized emittance EX (m-rad), used to create matched distribution ", 1E-6);
3308 = Attributes::makeReal("EY", "Projected normalized emittance EY (m-rad) used to create matched distribution ", 1E-6);
3310 = Attributes::makeReal("ET", "Projected normalized emittance ET (m-rad) used to create matched distribution ", 1E-6);
3311 // itsAttr[Attrib::Distribution::E2]
3312 // = Attributes::makeReal("E2", "If E2<Eb, we compute the tunes from the beams energy Eb to E2 with dE=0.25 MeV ", 0.0);
3314 = Attributes::makeReal("RESIDUUM", "Residuum for the closed orbit finder and sigma matrix generator ", 1e-8);
3316 = Attributes::makeReal("MAXSTEPSCO", "Maximum steps used to find closed orbit ", 100);
3318 = Attributes::makeReal("MAXSTEPSSI", "Maximum steps used to find matched distribution ",500);
3320 = Attributes::makeReal("ORDERMAPS", "Order used in the field expansion ", 7);
3322 = Attributes::makeBool("SECTOR", "Match using single sector (true)"
3323 " (false: using all sectors) (default: true)",
3324 true);
3326 = Attributes::makeReal("NSTEPS", "Number of integration steps of closed orbit finder (matched gauss)"
3327 " (default: 720)", 720);
3328
3330 = Attributes::makeReal("RGUESS", "Guess value of radius (m) for closed orbit finder ", -1);
3331
3333 = Attributes::makeReal("DENERGY", "Energy step size for closed orbit finder [GeV]", 0.001);
3334
3336 = Attributes::makeReal("NSECTORS", "Number of sectors to average field, for closed orbit finder ", 1);
3337
3339 = Attributes::makeString("FNAME", "File for reading in 6D particle "
3340 "coordinates.", "");
3341
3342
3344 = Attributes::makeBool("WRITETOFILE", "Write initial distribution to file.",
3345 false);
3346
3348 = Attributes::makeReal("WEIGHT", "Weight of distribution when used in a "
3349 "distribution list.", 1.0);
3350
3352 = Attributes::makePredefinedString("INPUTMOUNITS", "Tell OPAL what the input units are of the momentum.", {"NONE", "EVOVERC"});
3353
3354 // Attributes for beam emission.
3356 = Attributes::makeBool("EMITTED", "Emitted beam, from cathode, as opposed to "
3357 "an injected beam.", false);
3359 = Attributes::makeReal("EMISSIONSTEPS", "Number of time steps to use during emission.",
3360 1);
3362 = Attributes::makePredefinedString("EMISSIONMODEL", "Model used to emit electrons from a "
3363 "photocathode.",
3364 {"NONE", "ASTRA", "NONEQUIL"}, "NONE");
3366 = Attributes::makeReal("EKIN", "Kinetic energy used in ASTRA thermal emittance "
3367 "model (eV). (Thermal energy added in with random "
3368 "number generator.)", 1.0);
3370 = Attributes::makeReal("ELASER", "Laser energy (eV) for photocathode "
3371 "emission. (Default 255 nm light.)", 4.86);
3373 = Attributes::makeReal("W", "Workfunction of photocathode material (eV)."
3374 " (Default atomically clean copper.)", 4.31);
3376 = Attributes::makeReal("FE", "Fermi energy of photocathode material (eV)."
3377 " (Default atomically clean copper.)", 7.0);
3379 = Attributes::makeReal("CATHTEMP", "Temperature of photocathode (K)."
3380 " (Default 300 K.)", 300.0);
3382 = Attributes::makeReal("NBIN", "Number of energy bins to use when doing a space "
3383 "charge solve.", 0.0);
3384
3385 // Parameters for shifting or scaling initial distribution.
3386 itsAttr[Attrib::Distribution::XMULT] = Attributes::makeReal("XMULT", "Multiplier for x dimension.", 1.0);
3387 itsAttr[Attrib::Distribution::YMULT] = Attributes::makeReal("YMULT", "Multiplier for y dimension.", 1.0);
3388 itsAttr[Attrib::Distribution::ZMULT] = Attributes::makeReal("TMULT", "Multiplier for z dimension.", 1.0);
3389 itsAttr[Attrib::Distribution::TMULT] = Attributes::makeReal("TMULT", "Multiplier for t dimension.", 1.0);
3390
3391 itsAttr[Attrib::Distribution::PXMULT] = Attributes::makeReal("PXMULT", "Multiplier for px dimension.", 1.0);
3392 itsAttr[Attrib::Distribution::PYMULT] = Attributes::makeReal("PYMULT", "Multiplier for py dimension.", 1.0);
3393 itsAttr[Attrib::Distribution::PZMULT] = Attributes::makeReal("PZMULT", "Multiplier for pz dimension.", 1.0);
3394
3396 = Attributes::makeReal("OFFSETX", "Average x offset of distribution.", 0.0);
3398 = Attributes::makeReal("OFFSETY", "Average y offset of distribution.", 0.0);
3400 = Attributes::makeReal("OFFSETZ", "Average z offset of distribution.", 0.0);
3402 = Attributes::makeReal("OFFSETT", "Average t offset of distribution.", 0.0);
3403
3405 = Attributes::makeReal("OFFSETPX", "Average px offset of distribution.", 0.0);
3407 = Attributes::makeReal("OFFSETPY", "Average py offset of distribution.", 0.0);
3409 = Attributes::makeReal("OFFSETPZ", "Average pz offset of distribution.", 0.0);
3411 = Attributes::makeReal("OFFSETP", "Momentum shift relative to referenc particle.", 0.0);
3412
3413 // Parameters for defining an initial distribution.
3414 itsAttr[Attrib::Distribution::SIGMAX] = Attributes::makeReal("SIGMAX", "SIGMAx (m)", 0.0);
3415 itsAttr[Attrib::Distribution::SIGMAY] = Attributes::makeReal("SIGMAY", "SIGMAy (m)", 0.0);
3416 itsAttr[Attrib::Distribution::SIGMAR] = Attributes::makeReal("SIGMAR", "SIGMAr (m)", 0.0);
3417 itsAttr[Attrib::Distribution::SIGMAZ] = Attributes::makeReal("SIGMAZ", "SIGMAz (m)", 0.0);
3418 itsAttr[Attrib::Distribution::SIGMAT] = Attributes::makeReal("SIGMAT", "SIGMAt (m)", 0.0);
3420 = Attributes::makeReal("TPULSEFWHM", "Pulse FWHM for emitted distribution.", 0.0);
3422 = Attributes::makeReal("TRISE", "Rise time for emitted distribution.", 0.0);
3424 = Attributes::makeReal("TFALL", "Fall time for emitted distribution.", 0.0);
3425 itsAttr[Attrib::Distribution::SIGMAPX] = Attributes::makeReal("SIGMAPX", "SIGMApx", 0.0);
3426 itsAttr[Attrib::Distribution::SIGMAPY] = Attributes::makeReal("SIGMAPY", "SIGMApy", 0.0);
3427 itsAttr[Attrib::Distribution::SIGMAPZ] = Attributes::makeReal("SIGMAPZ", "SIGMApz", 0.0);
3428 itsAttr[Attrib::Distribution::SEPPEAKS] = Attributes::makeReal("SEPPEAKS", "Separation between "
3429 "Gaussian peaks in MultiGauss "
3430 "distribution.", 0.0);
3431 itsAttr[Attrib::Distribution::NPEAKS] = Attributes::makeReal("NPEAKS", "Number of Gaussian "
3432 " pulses in MultiGauss "
3433 "distribution.", 0.0);
3435 = Attributes::makeReal("MX", "Defines the binomial distribution in x, "
3436 "0.0 ... infinity.", 10001.0);
3438 = Attributes::makeReal("MY", "Defines the binomial distribution in y, "
3439 "0.0 ... infinity.", 10001.0);
3441 = Attributes::makeReal("MZ", "Defines the binomial distribution in z, "
3442 "0.0 ... infinity.", 10001.0);
3444 = Attributes::makeReal("MT", "Defines the binomial distribution in t, "
3445 "0.0 ... infinity.", 10001.0);
3446
3447 itsAttr[Attrib::Distribution::CUTOFFX] = Attributes::makeReal("CUTOFFX", "Distribution cutoff x "
3448 "direction in units of sigma.", 3.0);
3449 itsAttr[Attrib::Distribution::CUTOFFY] = Attributes::makeReal("CUTOFFY", "Distribution cutoff r "
3450 "direction in units of sigma.", 3.0);
3451 itsAttr[Attrib::Distribution::CUTOFFR] = Attributes::makeReal("CUTOFFR", "Distribution cutoff radial "
3452 "direction in units of sigma.", 3.0);
3454 = Attributes::makeReal("CUTOFFLONG", "Distribution cutoff z or t direction in "
3455 "units of sigma.", 3.0);
3456 itsAttr[Attrib::Distribution::CUTOFFPX] = Attributes::makeReal("CUTOFFPX", "Distribution cutoff px "
3457 "dimension in units of sigma.", 3.0);
3458 itsAttr[Attrib::Distribution::CUTOFFPY] = Attributes::makeReal("CUTOFFPY", "Distribution cutoff py "
3459 "dimension in units of sigma.", 3.0);
3460 itsAttr[Attrib::Distribution::CUTOFFPZ] = Attributes::makeReal("CUTOFFPZ", "Distribution cutoff pz "
3461 "dimension in units of sigma.", 3.0);
3462
3464 = Attributes::makeReal("FTOSCAMPLITUDE", "Amplitude of oscillations superimposed "
3465 "on flat top portion of emitted GAUSS "
3466 "distribtuion (in percent of flat top "
3467 "amplitude)",0.0);
3469 = Attributes::makeReal("FTOSCPERIODS", "Number of oscillations superimposed on "
3470 "flat top portion of emitted GAUSS "
3471 "distribution", 0.0);
3472
3473 /*
3474 * TODO: Find out what these correlations really mean and write
3475 * good descriptions for them.
3476 */
3478 = Attributes::makeReal("CORRX", "x/px correlation, (R12 in transport "
3479 "notation).", 0.0);
3481 = Attributes::makeReal("CORRY", "y/py correlation, (R34 in transport "
3482 "notation).", 0.0);
3484 = Attributes::makeReal("CORRZ", "z/pz correlation, (R56 in transport "
3485 "notation).", 0.0);
3487 = Attributes::makeReal("CORRT", "t/pt correlation, (R56 in transport "
3488 "notation).", 0.0);
3489
3491 = Attributes::makeReal("R51", "x/z correlation, (R51 in transport "
3492 "notation).", 0.0);
3494 = Attributes::makeReal("R52", "xp/z correlation, (R52 in transport "
3495 "notation).", 0.0);
3496
3498 = Attributes::makeReal("R61", "x/pz correlation, (R61 in transport "
3499 "notation).", 0.0);
3501 = Attributes::makeReal("R62", "xp/pz correlation, (R62 in transport "
3502 "notation).", 0.0);
3503
3505 = Attributes::makeRealArray("R", "r correlation");
3506
3507 // Parameters for using laser profile to generate a distribution.
3509 = Attributes::makeString("LASERPROFFN", "File containing a measured laser image "
3510 "profile (x,y).", "");
3512 = Attributes::makeString("IMAGENAME", "Name of the laser image.", "");
3514 = Attributes::makeReal("INTENSITYCUT", "For background subtraction of laser "
3515 "image profile, in % of max intensity.",
3516 0.0);
3518 = Attributes::makeBool("FLIPX", "Flip laser profile horizontally", false);
3520 = Attributes::makeBool("FLIPY", "Flip laser profile vertically", false);
3522 = Attributes::makeBool("ROTATE90", "Rotate laser profile 90 degrees counter clockwise", false);
3524 = Attributes::makeBool("ROTATE180", "Rotate laser profile 180 degrees counter clockwise", false);
3526 = Attributes::makeBool("ROTATE270", "Rotate laser profile 270 degrees counter clockwise", false);
3527
3528 /*
3529 * Feature request Issue #14
3530 */
3531
3533 = Attributes::makeRealArray("ID1", "User defined particle with ID=1");
3535 = Attributes::makeRealArray("ID2", "User defined particle with ID=2");
3536
3537
3539 = Attributes::makeBool("SCALABLE", "If true then distribution is scalable with "
3540 "respect of number of particles and number of cores", false);
3541
3542 /*
3543 * Legacy attributes (or ones that need to be implemented.)
3544 */
3545
3546 // Parameters for emitting a distribution.
3547 // itsAttr[Attrib::Legacy::Distribution::DEBIN]
3548 // = Attributes::makeReal("DEBIN", "Energy band for a bin in keV that defines "
3549 // "when to combine bins. That is, when the energy "
3550 // "spread of all bins is below this number "
3551 // "combine bins into a single bin.", 1000000.0);
3553 = Attributes::makeReal("SBIN", "Number of sample bins to use per energy bin "
3554 "when emitting a distribution.", 100.0);
3555 /*
3556 * Specific to type GAUSS and GUNGAUSSFLATTOPTH and ASTRAFLATTOPTH. These
3557 * last two distribution will eventually just become a subset of GAUSS.
3558 */
3560
3562 = Attributes::makeReal("CUTOFF", "Longitudinal cutoff for Gaussian in units "
3563 "of sigma.", 3.0);
3564
3565
3566 // Mixed use attributes (used by more than one distribution type).
3568 = Attributes::makeReal("T", "Not supported anymore");
3569
3571 = Attributes::makeReal("PT", "Not supported anymore.");
3572
3573
3574 // Attributes that are not yet implemented.
3575 // itsAttr[Attrib::Legacy::Distribution::ALPHAX]
3576 // = Attributes::makeReal("ALPHAX", "Courant Snyder parameter.", 0.0);
3577 // itsAttr[Attrib::Legacy::Distribution::ALPHAY]
3578 // = Attributes::makeReal("ALPHAY", "Courant Snyder parameter.", 0.0);
3579 // itsAttr[Attrib::Legacy::Distribution::BETAX]
3580 // = Attributes::makeReal("BETAX", "Courant Snyder parameter.", 1.0);
3581 // itsAttr[Attrib::Legacy::Distribution::BETAY]
3582 // = Attributes::makeReal("BETAY", "Courant Snyder parameter.", 1.0);
3583
3584 // itsAttr[Attrib::Legacy::Distribution::DX]
3585 // = Attributes::makeReal("DX", "Dispersion in x (R16 in Transport notation).", 0.0);
3586 // itsAttr[Attrib::Legacy::Distribution::DDX]
3587 // = Attributes::makeReal("DDX", "First derivative of DX.", 0.0);
3588
3589 // itsAttr[Attrib::Legacy::Distribution::DY]
3590 // = Attributes::makeReal("DY", "Dispersion in y (R36 in Transport notation).", 0.0);
3591 // itsAttr[Attrib::Legacy::Distribution::DDY]
3592 // = Attributes::makeReal("DDY", "First derivative of DY.", 0.0);
3593
3595}
3596
3598 emitting_m = emitted;
3599}
3600
3603 throw OpalException("Distribution::setDistType()",
3604 "The attribute \"DISTRIBUTION\" isn't supported any more, use \"TYPE\" instead");
3605 }
3606
3607 static const std::map<std::string, DistributionType> typeStringToDistType_s = {
3608 {"NODIST", DistributionType::NODIST},
3609 {"FROMFILE", DistributionType::FROMFILE},
3610 {"GAUSS", DistributionType::GAUSS},
3611 {"BINOMIAL", DistributionType::BINOMIAL},
3612 {"FLATTOP", DistributionType::FLATTOP},
3613 {"MULTIGAUSS", DistributionType::MULTIGAUSS},
3614 {"GUNGAUSSFLATTOPTH", DistributionType::GUNGAUSSFLATTOPTH},
3615 {"ASTRAFLATTOPTH", DistributionType::ASTRAFLATTOPTH},
3616 {"GAUSSMATCHED", DistributionType::MATCHEDGAUSS}
3617 };
3618
3620 if (distT_m.empty()) {
3621 throw OpalException("Distribution::setDistType",
3622 "The attribute \"TYPE\" isn't set for the \"DISTRIBUTION\"!");
3623 } else {
3624 distrTypeT_m = typeStringToDistType_s.at(distT_m);
3625 }
3626}
3627
3632 // SIGMAZ overrides SIGMAT
3635 // SIGMAR overrides SIGMAX/Y
3639 }
3640}
3641
3642void Distribution::setSigmaP_m(double massIneV) {
3646
3647 // SIGMAPZ overrides SIGMAPT. SIGMAPT is left for legacy compatibility.
3650 WARNMSG("The attribute SIGMAPT may be removed in a future version\n"
3651 << "use SIGMAPZ instead" << endl);
3652 }
3653
3654 // Check what input units we are using for momentum.
3659 }
3660}
3661
3662void Distribution::setEmissionTime(double &maxT, double &minT) {
3663
3664 if (addedDistributions_m.empty()) {
3665
3666 switch (distrTypeT_m) {
3667
3673 break;
3675 /*
3676 * Don't do anything. Emission time is set during the distribution
3677 * creation. Only this distribution type does it this way. This is
3678 * a legacy feature.
3679 */
3680 break;
3681 default:
3682 /*
3683 * Increase maxT and decrease minT by percentTEmission_m of total
3684 * time to ensure that no particles fall on the leading edge of
3685 * the first emission time step or the trailing edge of the last
3686 * emission time step.
3687 */
3688 double deltaT = maxT - minT;
3689 maxT += deltaT * percentTEmission_m;
3690 minT -= deltaT * percentTEmission_m;
3691 tEmission_m = (maxT - minT);
3692 break;
3693 }
3694
3695 } else {
3696 /*
3697 * Increase maxT and decrease minT by percentTEmission_m of total
3698 * time to ensure that no particles fall on the leading edge of
3699 * the first emission time step or the trailing edge of the last
3700 * emission time step.
3701 */
3702 double deltaT = maxT - minT;
3703 maxT += deltaT * percentTEmission_m;
3704 minT -= deltaT * percentTEmission_m;
3705 tEmission_m = (maxT - minT);
3706 }
3708}
3709
3711
3712 /*
3713 * Set Distribution parameters. Do all the necessary checks depending
3714 * on the input attributes.
3715 */
3716 std::vector<double> cr = Attributes::getRealArray(itsAttr[Attrib::Distribution::R]);
3717
3718 if (!cr.empty()) {
3719 throw OpalException("Distribution::setDistParametersBinomial",
3720 "Attribute R is not supported for binomial distribution\n"
3721 "use CORR[X|Y|Z] and R51, R52, R61, R62 instead");
3722 }
3723
3731
3732 //CORRZ overrides CORRT. We initially use CORRT for legacy compatibility.
3733 if (!itsAttr[Attrib::Distribution::CORRZ].defaultUsed())
3735
3736 setSigmaR_m();
3737 setSigmaP_m(massIneV);
3738
3742
3746
3750
3751 if (mBinomial_m[2] == 0.0
3754
3755 if (emitting_m) {
3758 }
3759}
3760
3762
3763 /*
3764 * Set distribution parameters. Do all the necessary checks depending
3765 * on the input attributes.
3766 */
3767 setSigmaP_m(massIneV);
3768
3772
3776
3777 // CORRZ overrides CORRT.
3780
3781 setSigmaR_m();
3782 if (emitting_m) {
3783 sigmaR_m[2] = 0.0;
3784
3787
3789
3790 /*
3791 * If TRISE and TFALL are defined > 0.0 then these attributes
3792 * override SIGMAT.
3793 */
3796
3797 double timeRatio = std::sqrt(2.0 * std::log(10.0)) - std::sqrt(2.0 * std::log(10.0 / 9.0));
3798
3801 / timeRatio;
3802
3805 / timeRatio;
3806
3807 }
3808
3809 // For an emitted beam, the longitudinal cutoff >= 0.
3810 cutoffR_m[2] = std::abs(cutoffR_m[2]);
3811 }
3812
3813 // Set laser profile/
3815 if (!(laserProfileFileName_m == std::string(""))) {
3818 short flags = 0;
3824
3828 flags);
3829 }
3830
3831 // Legacy for ASTRAFLATTOPTH.
3834
3835}
3836
3838
3839 setSigmaR_m();
3841
3842 if (!emitting_m)
3843 setSigmaP_m(massIneV);
3844
3848
3851}
3852
3854
3855 /*
3856 * Set distribution parameters. Do all the necessary checks depending
3857 * on the input attributes.
3858 * In case of DistributionType::MATCHEDGAUSS we only need to set the cutoff parameters
3859 */
3860
3861
3865
3866
3870
3874 }
3875
3877 setSigmaP_m(massIneV);
3878
3879 std::vector<double> cr = Attributes::getRealArray(itsAttr[Attrib::Distribution::R]);
3880
3881 if (!cr.empty()) {
3882 if (cr.size() == 15) {
3883 *gmsg << "* Use r to specify correlations" << endl;
3884 unsigned int k = 0;
3885 for (unsigned int i = 0; i < 5; ++ i) {
3886 for (unsigned int j = i + 1; j < 6; ++ j, ++ k) {
3887 correlationMatrix_m(j, i) = cr.at(k);
3888 }
3889 }
3890
3891 }
3892 else {
3893 throw OpalException("Distribution::SetDistParametersGauss",
3894 "Inconsistent set of correlations specified, check manual");
3895 }
3896 }
3897 else {
3905
3906 // CORRZ overrides CORRT.
3909 }
3910 }
3911
3913 setSigmaR_m();
3914
3915 if (emitting_m) {
3916 sigmaR_m[2] = 0.0;
3917
3920
3922
3923 /*
3924 * If TRISE and TFALL are defined then these attributes
3925 * override SIGMAT.
3926 */
3929
3930 double timeRatio = std::sqrt(2.0 * std::log(10.0)) - std::sqrt(2.0 * std::log(10.0 / 9.0));
3931
3934 / timeRatio;
3935
3938 / timeRatio;
3939
3940 }
3941
3942 // For and emitted beam, the longitudinal cutoff >= 0.
3943 cutoffR_m[2] = std::abs(cutoffR_m[2]);
3944
3945 }
3946
3947 // if cutoff 0 then infinite cutoff (except for CUTOFFLONG)
3948 for (int i=0; i<3; i++) {
3949 if (cutoffR_m[i] < SMALLESTCUTOFF && i!=2)
3951 if (cutoffP_m[i] < SMALLESTCUTOFF)
3953 }
3954}
3955
3957
3958 static const std::map<std::string, EmissionModel> stringEmissionModel_s = {
3959 {"NONE", EmissionModel::NONE},
3960 {"ASTRA", EmissionModel::ASTRA},
3961 {"NONEQUIL", EmissionModel::NONEQUIL}
3962 };
3963
3965 if (model.empty()) {
3967 } else {
3968 emissionModel_m = stringEmissionModel_s.at(model);
3969 }
3970
3971 /*
3972 * The ASTRAFLATTOPTH of GUNGAUSSFLATTOPTH distributions always uses the
3973 * ASTRA emission model.
3974 */
3978 }
3979
3980 switch (emissionModel_m) {
3981 case EmissionModel::ASTRA: {
3983 break;
3984 }
3987 break;
3988 }
3989 default: {
3990 break;
3991 }
3992 }
3993}
3994
3996
3998 pTotThermal_m = Util::getBetaGamma(wThermal, beam->getM());
3999 pmean_m = Vector_t(0.0, 0.0, 0.5 * pTotThermal_m);
4000}
4001
4003
4005 pTotThermal_m = Util::getBetaGamma(wThermal, beam->getM());
4006 double avgPz = std::accumulate(pzDist_m.begin(), pzDist_m.end(), 0.0);
4007 size_t numParticles = pzDist_m.size();
4008 reduce(avgPz, avgPz, OpAddAssign());
4009 reduce(numParticles, numParticles, OpAddAssign());
4010 avgPz /= numParticles;
4011
4012 pmean_m = Vector_t(0.0, 0.0, pTotThermal_m + avgPz);
4013}
4014
4016
4021
4022 /*
4023 * Upper limit on energy distribution theoretically goes to infinity.
4024 * Practically we limit to a probability of 1 part in a billion.
4025 */
4027 + cathodeTemp_m * std::log(1.0e9 - 1.0);
4028
4029 // TODO: get better estimate of pmean
4031}
4032
4033void Distribution::setupEnergyBins(double maxTOrZ, double minTOrZ) {
4034
4036
4037 if (emitting_m)
4038 gsl_histogram_set_ranges_uniform(energyBinHist_m, -tEmission_m, 0.0);
4039 else
4040 gsl_histogram_set_ranges_uniform(energyBinHist_m, minTOrZ, maxTOrZ);
4041
4042}
4043
4045
4048
4049 if (numberOfEnergyBins_m > 0) {
4050 delete energyBins_m;
4051
4052 int sampleBins = static_cast<int>(std::abs(Attributes::getReal(itsAttr[Attrib::Legacy::Distribution::SBIN])));
4053 energyBins_m = new PartBins(numberOfEnergyBins_m, sampleBins);
4054
4055 if (!itsAttr[Attrib::Legacy::Distribution::PT].defaultUsed())
4056 throw OpalException("Distribution::setupParticleBins",
4057 "PT is obsolete. The moments of the beam is defined with OFFSETPZ");
4058
4059 // we get gamma from PC of the beam
4060 const double pz = beam->getP()/beam->getM();
4061 double gamma = std::hypot(pz, 1.0);
4062 energyBins_m->setGamma(gamma);
4063
4064 } else {
4065 energyBins_m = nullptr;
4066 }
4067}
4068
4069void Distribution::shiftBeam(double &maxTOrZ, double &minTOrZ) {
4070
4071 if (emitting_m) {
4072
4073 if (addedDistributions_m.empty()) {
4074
4076 for (double& tOrZ : tOrZDist_m)
4077 tOrZ -= tEmission_m / 2.0;
4078
4079 minTOrZ -= tEmission_m / 2.0;
4080 maxTOrZ -= tEmission_m / 2.0;
4084 for (double& tOrZ : tOrZDist_m)
4085 tOrZ -= tEmission_m;
4086
4087 minTOrZ -= tEmission_m;
4088 maxTOrZ -= tEmission_m;
4089 } else {
4090 for (double& tOrZ : tOrZDist_m)
4091 tOrZ -= maxTOrZ;
4092
4093 minTOrZ -= maxTOrZ;
4094 maxTOrZ -= maxTOrZ;
4095 }
4096
4097 } else {
4098 for (double& tOrZ : tOrZDist_m)
4099 tOrZ -= maxTOrZ;
4100
4101 minTOrZ -= maxTOrZ;
4102 maxTOrZ -= maxTOrZ;
4103 }
4104
4106 double avgZ[] = {0.0, 1.0 * tOrZDist_m.size()};
4107 for (double tOrZ : tOrZDist_m)
4108 avgZ[0] += tOrZ;
4109
4110 reduce(avgZ, avgZ + 2, avgZ, OpAddAssign());
4111 avgZ[0] /= avgZ[1];
4112
4113 for (double& tOrZ : tOrZDist_m)
4114 tOrZ -= avgZ[0];
4115 }
4116}
4117
4119 if (emitting_m)
4121
4122 return 0.0;
4123}
4124
4126
4127 size_t startIdx = 0;
4128 for (unsigned int i = 0; i <= addedDistributions_m.size(); ++ i) {
4129 Distribution *currDist = this;
4130 if (i > 0)
4131 currDist = addedDistributions_m[i - 1];
4132 double deltaX = Attributes::getReal(currDist->itsAttr[Attrib::Distribution::OFFSETX]);
4133 double deltaY = Attributes::getReal(currDist->itsAttr[Attrib::Distribution::OFFSETY]);
4134
4135 /*
4136 * OFFSETZ overrides T if it is nonzero. We initially use T
4137 * for legacy compatiblity. OFFSETT always overrides T, even
4138 * when zero, for an emitted beam.
4139 */
4141 throw OpalException("Distribution::shiftDistCoordinates",
4142 "Attribute T isn't supported anymore; use OFFSETZ instead");
4143 }
4144
4145 double deltaTOrZ = 0.0;
4146 if (!emitting_m)
4149
4150 double deltaPx = Attributes::getReal(currDist->itsAttr[Attrib::Distribution::OFFSETPX]);
4151 double deltaPy = Attributes::getReal(currDist->itsAttr[Attrib::Distribution::OFFSETPY]);
4152 double deltaPz = Attributes::getReal(currDist->itsAttr[Attrib::Distribution::OFFSETPZ]);
4153
4155 WARNMSG("PT & PZ are obsolete and will be ignored. The moments of the beam is defined with PC" << endl);
4156
4157 // Check input momentum units.
4159 deltaPx = Util::convertMomentumEVoverCToBetaGamma(deltaPx, massIneV);
4160 deltaPy = Util::convertMomentumEVoverCToBetaGamma(deltaPy, massIneV);
4161 deltaPz = Util::convertMomentumEVoverCToBetaGamma(deltaPz, massIneV);
4162 }
4163
4164 size_t endIdx = startIdx + particlesPerDist_m[i];
4165 for (size_t particleIndex = startIdx; particleIndex < endIdx; ++ particleIndex) {
4166 xDist_m.at(particleIndex) += deltaX;
4167 pxDist_m.at(particleIndex) += deltaPx;
4168 yDist_m.at(particleIndex) += deltaY;
4169 pyDist_m.at(particleIndex) += deltaPy;
4170 tOrZDist_m.at(particleIndex) += deltaTOrZ;
4171 pzDist_m.at(particleIndex) += deltaPz;
4172 }
4173
4174 startIdx = endIdx;
4175 }
4176}
4177
4179
4181 return;
4182 }
4183
4184 unsigned int totalNum = tOrZDist_m.size();
4185 reduce(totalNum, totalNum, OpAddAssign());
4186 if (Ippl::myNode() != 0)
4187 return;
4188
4191 OpalData::getInstance()->getInputBasename() + "_" + getOpalName() + ".dat"
4192 });
4193
4194 std::ofstream outputFile(outFilename_m);
4195 if (outputFile.bad()) {
4196 *gmsg << "Unable to open output file '" << outFilename_m << "'" << endl;
4197 } else {
4198 outputFile.setf(std::ios::left);
4199 outputFile << "# ";
4200 outputFile.width(17);
4201 outputFile << "x [m]";
4202 outputFile.width(17);
4203 outputFile << "px [betax gamma]";
4204 outputFile.width(17);
4205 outputFile << "y [m]";
4206 outputFile.width(17);
4207 outputFile << "py [betay gamma]";
4208 if (emitting_m) {
4209 outputFile.width(17);
4210 outputFile << "t [s]";
4211 } else {
4212 outputFile.width(17);
4213 outputFile << "z [m]";
4214 }
4215 outputFile.width(17);
4216 outputFile << "pz [betaz gamma]" ;
4217 if (emitting_m) {
4218 outputFile.width(17);
4219 outputFile << "Bin Number" << std::endl;
4220 } else {
4221 if (numberOfEnergyBins_m > 0) {
4222 outputFile.width(17);
4223 outputFile << "Bin Number";
4224 }
4225 outputFile << std::endl;
4226
4227 outputFile << "# " << totalNum << std::endl;
4228 }
4229 }
4230 outputFile.close();
4231}
4232
4234
4236 xWrite_m.clear();
4237 pxWrite_m.clear();
4238 yWrite_m.clear();
4239 pyWrite_m.clear();
4240 tOrZWrite_m.clear();
4241 pzWrite_m.clear();
4242 binWrite_m.clear();
4243
4244 return;
4245 }
4246
4247 // Gather particles to be written onto node 0.
4248 std::vector<char> msgbuf;
4249 const size_t bitsPerParticle = (6 * sizeof(double) + sizeof(size_t));
4250 size_t totalSendBits = xWrite_m.size() * bitsPerParticle;
4251
4252 std::vector<unsigned long> numberOfBits(Ippl::getNodes(), 0);
4253 numberOfBits[Ippl::myNode()] = totalSendBits;
4254
4255 if (Ippl::myNode() == 0) {
4256 MPI_Reduce(MPI_IN_PLACE, &(numberOfBits[0]), Ippl::getNodes(), MPI_UNSIGNED_LONG, MPI_SUM, 0, Ippl::getComm());
4257 } else {
4258 MPI_Reduce(&(numberOfBits[0]), nullptr, Ippl::getNodes(), MPI_UNSIGNED_LONG, MPI_SUM, 0, Ippl::getComm());
4259 }
4260
4263 if (Ippl::myNode() > 0) {
4264 if (totalSendBits > 0) {
4265 msgbuf.reserve(totalSendBits);
4266 const char *buffer;
4267 for (size_t idx = 0; idx < xWrite_m.size(); ++ idx) {
4268 buffer = reinterpret_cast<const char*>(&(xWrite_m[idx]));
4269 msgbuf.insert(msgbuf.end(), buffer, buffer + sizeof(double));
4270 buffer = reinterpret_cast<const char*>(&(pxWrite_m[idx]));
4271 msgbuf.insert(msgbuf.end(), buffer, buffer + sizeof(double));
4272 buffer = reinterpret_cast<const char*>(&(yWrite_m[idx]));
4273 msgbuf.insert(msgbuf.end(), buffer, buffer + sizeof(double));
4274 buffer = reinterpret_cast<const char*>(&(pyWrite_m[idx]));
4275 msgbuf.insert(msgbuf.end(), buffer, buffer + sizeof(double));
4276 buffer = reinterpret_cast<const char*>(&(tOrZWrite_m[idx]));
4277 msgbuf.insert(msgbuf.end(), buffer, buffer + sizeof(double));
4278 buffer = reinterpret_cast<const char*>(&(pzWrite_m[idx]));
4279 msgbuf.insert(msgbuf.end(), buffer, buffer + sizeof(double));
4280 buffer = reinterpret_cast<const char*>(&(binWrite_m[idx]));
4281 msgbuf.insert(msgbuf.end(), buffer, buffer + sizeof(size_t));
4282 }
4283
4284 Ippl::Comm->raw_send(&(msgbuf[0]), totalSendBits, 0, tag);
4285 }
4286 } else {
4287 std::ofstream outputFile(outFilename_m, std::fstream::app);
4288 if (outputFile.bad()) {
4289 ERRORMSG(level1 << "Unable to write to file '" << outFilename_m << "'" << endl);
4290 for (int node = 1; node < Ippl::getNodes(); ++ node) {
4291 if (numberOfBits[node] == 0) continue;
4292 char *recvbuf = new char[numberOfBits[node]];
4293 Ippl::Comm->raw_receive(recvbuf, numberOfBits[node], node, tag);
4294 delete[] recvbuf;
4295 }
4296 } else {
4297
4298 outputFile.precision(9);
4299 outputFile.setf(std::ios::scientific);
4300 outputFile.setf(std::ios::right);
4301
4302 for (size_t partIndex = 0; partIndex < xWrite_m.size(); partIndex++) {
4303
4304 outputFile << std::setw(17) << xWrite_m.at(partIndex)
4305 << std::setw(17) << pxWrite_m.at(partIndex)
4306 << std::setw(17) << yWrite_m.at(partIndex)
4307 << std::setw(17) << pyWrite_m.at(partIndex)
4308 << std::setw(17) << tOrZWrite_m.at(partIndex)
4309 << std::setw(17) << pzWrite_m.at(partIndex)
4310 << std::setw(17) << binWrite_m.at(partIndex) << std::endl;
4311 }
4312
4313 int numSenders = 0;
4314 for (int i = 1; i < Ippl::getNodes(); ++ i) {
4315 if (numberOfBits[i] > 0) numSenders ++;
4316 }
4317
4318 for (int i = 0; i < numSenders; ++ i) {
4319 int node = Communicate::COMM_ANY_NODE;
4320 char *recvbuf;
4321 const int bufsize = Ippl::Comm->raw_probe_receive(recvbuf, node, tag);
4322
4323 int j = 0;
4324 while (j < bufsize) {
4325 const double *dbuffer = reinterpret_cast<const double*>(recvbuf + j);
4326 const size_t *sbuffer = reinterpret_cast<const size_t*>(recvbuf + j + 6 * sizeof(double));
4327 outputFile << std::setw(17) << dbuffer[0]
4328 << std::setw(17) << dbuffer[1]
4329 << std::setw(17) << dbuffer[2]
4330 << std::setw(17) << dbuffer[3]
4331 << std::setw(17) << dbuffer[4]
4332 << std::setw(17) << dbuffer[5]
4333 << std::setw(17) << sbuffer[0]
4334 << std::endl;
4335 j += bitsPerParticle;
4336 }
4337
4338 delete[] recvbuf;
4339
4340 }
4341 }
4342 outputFile.close();
4343
4344 }
4345
4346 // Clear write vectors.
4347 xWrite_m.clear();
4348 pxWrite_m.clear();
4349 yWrite_m.clear();
4350 pyWrite_m.clear();
4351 tOrZWrite_m.clear();
4352 pzWrite_m.clear();
4353 binWrite_m.clear();
4354
4355}
4356
4358
4360 return;
4361
4362 // Nodes take turn writing particles to file.
4363 for (int nodeIndex = 0; nodeIndex < Ippl::getNodes(); nodeIndex++) {
4364
4365 // Write to file if its our turn.
4366 size_t numberOfParticles = 0;
4367 if (Ippl::myNode() == nodeIndex) {
4368 std::ofstream outputFile(outFilename_m, std::fstream::app);
4369 if (outputFile.bad()) {
4370 *gmsg << "Node " << Ippl::myNode() << " unable to write"
4371 << "to file '" << outFilename_m << "'" << endl;
4372 } else {
4373
4374 outputFile.precision(9);
4375 outputFile.setf(std::ios::scientific);
4376 outputFile.setf(std::ios::right);
4377
4378 numberOfParticles = tOrZDist_m.size();
4379 for (size_t partIndex = 0; partIndex < numberOfParticles; partIndex++) {
4380
4381 outputFile.width(17);
4382 outputFile << xDist_m.at(partIndex);
4383 outputFile.width(17);
4384 outputFile << pxDist_m.at(partIndex);
4385 outputFile.width(17);
4386 outputFile << yDist_m.at(partIndex);
4387 outputFile.width(17);
4388 outputFile << pyDist_m.at(partIndex);
4389 outputFile.width(17);
4390 outputFile << tOrZDist_m.at(partIndex);
4391 outputFile.width(17);
4392 outputFile << pzDist_m.at(partIndex);
4393 if (numberOfEnergyBins_m > 0) {
4394 size_t binNumber = findEBin(tOrZDist_m.at(partIndex));
4395 outputFile.width(17);
4396 outputFile << binNumber;
4397 }
4398 outputFile << std::endl;
4399
4400 }
4401 }
4402 outputFile.close();
4403 }
4404
4405 // Wait for writing node before moving on.
4406 reduce(numberOfParticles, numberOfParticles, OpAddAssign());
4407 }
4408}
4409
4411 return 2.0 * std::sqrt(1.0 - std::pow(rand, ami_m));
4412}
4413
4415 return std::sqrt(-2.0 * std::log(rand));
4416}
4417
4418void Distribution::adjustPhaseSpace(double massIneV) {
4420 return;
4421
4424 // Check input momentum units.
4426 deltaPx = Util::convertMomentumEVoverCToBetaGamma(deltaPx, massIneV);
4427 deltaPy = Util::convertMomentumEVoverCToBetaGamma(deltaPy, massIneV);
4428 }
4429
4430 double avrg[6];
4431 avrg[0] = std::accumulate( xDist_m.begin(), xDist_m.end(), 0.0) / totalNumberParticles_m;
4432 avrg[1] = std::accumulate(pxDist_m.begin(), pxDist_m.end(), 0.0) / totalNumberParticles_m;
4433 avrg[2] = std::accumulate( yDist_m.begin(), yDist_m.end(), 0.0) / totalNumberParticles_m;
4434 avrg[3] = std::accumulate(pyDist_m.begin(), pyDist_m.end(), 0.0) / totalNumberParticles_m;
4435 avrg[4] = std::accumulate(tOrZDist_m.begin(), tOrZDist_m.end(), 0.0) / totalNumberParticles_m;
4436 avrg[5] = 0.0;
4437 for (unsigned int i = 0; i < pzDist_m.size(); ++ i) {
4438 // taylor series of sqrt(px*px + py*py + pz*pz) = pz * sqrt(1 + eps*eps) where eps << 1
4439 avrg[5] += (pzDist_m[i] +
4440 (std::pow(pxDist_m[i] + deltaPx, 2) +
4441 std::pow(pyDist_m[i] + deltaPy, 2)) / (2 * pzDist_m[i]));
4442 }
4443 allreduce(&(avrg[0]), 6, std::plus<double>());
4444 avrg[5] /= totalNumberParticles_m;
4445
4446 // solve
4447 // \sum_{i = 0}^{N-1} \sqrt{(pz_i + \eps)^2 + px_i^2 + py_i^2} = N p
4448 double eps = avrgpz_m - avrg[5];
4449 for (unsigned int i = 0; i < pzDist_m.size(); ++ i) {
4450 xDist_m[i] -= avrg[0];
4451 pxDist_m[i] -= avrg[1];
4452 yDist_m[i] -= avrg[2];
4453 pyDist_m[i] -= avrg[3];
4454 tOrZDist_m[i] -= avrg[4];
4455 pzDist_m[i] += eps;
4456 }
4457}
PartBunchBase< T, Dim >::ConstIterator end(PartBunchBase< T, Dim > const &bunch)
Tps< T > log(const Tps< T > &x)
Natural logarithm.
Definition: TpsMath.h:182
Tps< T > cos(const Tps< T > &x)
Cosine.
Definition: TpsMath.h:129
Tps< T > pow(const Tps< T > &x, int y)
Integer power.
Definition: TpsMath.h:76
Tps< T > exp(const Tps< T > &x)
Exponential.
Definition: TpsMath.h:165
Tps< T > sin(const Tps< T > &x)
Sine.
Definition: TpsMath.h:111
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
@ SIZE
Definition: IndexMap.cpp:174
constexpr double SMALLESTCUTOFF
Inform * gmsg
Definition: Main.cpp:61
DistributionType
Definition: Distribution.h:50
void allreduce(const T *input, T *output, int count, Op op)
Definition: GlobalComm.hpp:510
bool reduce(Communicate &, InputIterator, InputIterator, OutputIterator, const ReduceOp &, bool *IncludeVal=0)
Definition: GlobalComm.hpp:55
#define IPPL_APP_CYCLE
Definition: Tags.h:103
#define IPPL_APP_TAG2
Definition: Tags.h:95
T::PETE_Expr_t::PETE_Return_t max(const PETE_Expr< T > &expr, NDIndex< D > &loc)
Definition: ReductionLoc.h:84
T::PETE_Expr_t::PETE_Return_t min(const PETE_Expr< T > &expr, NDIndex< D > &loc)
Definition: ReductionLoc.h:76
#define PM(a, b, c, d)
Definition: fftpack.cpp:96
#define X(arg)
Definition: fftpack.cpp:112
T::PETE_Expr_t::PETE_Return_t sum(const PETE_Expr< T > &expr)
Definition: PETE.h:1111
T::PETE_Expr_t::PETE_Return_t prod(const PETE_Expr< T > &expr)
Definition: PETE.h:1121
PETE_TUTree< FnCeil, typename T::PETE_Expr_t > ceil(const PETE_Expr< T > &l)
Definition: PETE.h:728
PETE_TUTree< FnArcCos, typename T::PETE_Expr_t > acos(const PETE_Expr< T > &l)
Definition: PETE.h:725
PETE_TUTree< FnArcSin, typename T::PETE_Expr_t > asin(const PETE_Expr< T > &l)
Definition: PETE.h:726
std::complex< double > a
PETE_TBTree< FnCopysign, PETE_Scalar< Vektor< T1, Dim > >, typename T2::PETE_Expr_t > copysign(const Vektor< T1, Dim > &l, const PETE_Expr< T2 > &r)
PETE_TUTree< FnAbs, typename T::PETE_Expr_t > abs(const PETE_Expr< T > &l)
PETE_TBTree< FnArcTan2, PETE_Scalar< Vektor< T1, Dim > >, typename T2::PETE_Expr_t > atan2(const Vektor< T1, Dim > &l, const PETE_Expr< T2 > &r)
Inform & level2(Inform &inf)
Definition: Inform.cpp:46
Inform & endl(Inform &inf)
Definition: Inform.cpp:42
Inform & level1(Inform &inf)
Definition: Inform.cpp:45
Inform & level3(Inform &inf)
Definition: Inform.cpp:47
#define PAssert_GE(a, b)
Definition: PAssert.h:109
#define ERRORMSG(msg)
Definition: IpplInfo.h:350
#define INFOMSG(msg)
Definition: IpplInfo.h:348
#define WARNMSG(msg)
Definition: IpplInfo.h:349
const std::string name
Attribute makeBool(const std::string &name, const std::string &help)
Make logical attribute.
Definition: Attributes.cpp:90
double getReal(const Attribute &attr)
Return real value.
Definition: Attributes.cpp:252
Attribute makePredefinedString(const std::string &name, const std::string &help, const std::initializer_list< std::string > &predefinedStrings)
Make predefined string attribute.
Definition: Attributes.cpp:409
Attribute makeReal(const std::string &name, const std::string &help)
Make real attribute.
Definition: Attributes.cpp:240
bool getBool(const Attribute &attr)
Return logical value.
Definition: Attributes.cpp:100
Attribute makeRealArray(const std::string &name, const std::string &help)
Create real array attribute.
Definition: Attributes.cpp:289
std::vector< double > getRealArray(const Attribute &attr)
Get array value.
Definition: Attributes.cpp:294
std::string getString(const Attribute &attr)
Get string value.
Definition: Attributes.cpp:343
Attribute makeString(const std::string &name, const std::string &help)
Make string attribute.
Definition: Attributes.cpp:332
constexpr double kB
Boltzman's constant in eV/K.
Definition: Physics.h:60
constexpr double two_pi
The value of.
Definition: Physics.h:33
constexpr double epsilon_0
The permittivity of vacuum in As/Vm.
Definition: Physics.h:51
constexpr double alpha
The fine structure constant, no dimension.
Definition: Physics.h:72
constexpr double q_e
The elementary charge in As.
Definition: Physics.h:69
constexpr double m_e
The electron rest mass in GeV.
Definition: Physics.h:78
constexpr double e
The value of.
Definition: Physics.h:39
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 GeV2MeV
Definition: Units.h:80
constexpr double A2mA
Definition: Units.h:131
constexpr double rad2mrad
Definition: Units.h:137
constexpr double m2mm
Definition: Units.h:26
constexpr double eV2MeV
Definition: Units.h:77
constexpr double GeV2eV
Definition: Units.h:68
std::string::iterator iterator
Definition: MSLang.h:16
std::string rngtype
random number generator
Definition: Options.cpp:79
bool cloTuneOnly
Do closed orbit and tune calculation only.
Definition: Options.cpp:91
bool cZero
If true create symmetric distribution.
Definition: Options.cpp:77
int seed
The current random seed.
Definition: Options.cpp:37
std::string combineFilePath(std::initializer_list< std::string > ilist)
Definition: Util.cpp:196
double convertMomentumEVoverCToBetaGamma(double p, double mass)
Definition: Util.h:68
double getBetaGamma(double Ekin, double mass)
Definition: Util.h:60
The base class for all OPAL beam lines and sequences.
Definition: BeamSequence.h:32
virtual Beamline * fetchLine() const =0
Return the embedded CLASSIC beam line.
static BeamSequence * find(const std::string &name)
Find a BeamSequence by name.
The base class for all OPAL definitions.
Definition: Definition.h:30
The base class for all OPAL objects.
Definition: Object.h:48
void registerOwnership(const AttributeHandler::OwnerType &itsClass) const
Definition: Object.cpp:191
const std::string & getOpalName() const
Return object name.
Definition: Object.cpp:310
std::vector< Attribute > itsAttr
The object attributes.
Definition: Object.h:216
bool builtin
Built-in flag.
Definition: Object.h:233
double getP() const
void setEnergyBins(int numberOfEnergyBins)
ParticleAttrib< Vector_t > Ef
ParticlePos_t & R
ParticleAttrib< int > Bin
int getSteptoLastInj() const
void setNumBunch(short n)
double getMassPerParticle() const
double getQ() const
Access to reference data.
double getChargePerParticle() const
get the macro particle charge
size_t getLocalNum() const
ParticleAttrib< double > M
size_t getTotalNum() const
ParticleAttrib< Vector_t > P
ParticleAttrib< ParticleType > PType
double getdT() const
ParticleAttrib< ParticleOrigin > POrigin
ParticleAttrib< double > Q
double getInitialGamma() const
ParticleType getPType() const
IpplTimings::TimerRef distrReload_m
timer for IC, can not be in Distribution.h
long long getLocalTrackStep() const
short getNumBunch() const
ParticleAttrib< int > TriID
ParticleAttrib< double > dt
ParticleAttrib< Vector_t > Bf
IpplTimings::TimerRef distrCreate_m
void setPBins(PartBins *pbin)
virtual void boundp()
void create(size_t M)
long long getGlobalTrackStep() const
void setTEmission(double t)
ParticleIndex_t & ID
double get_sPos() const
void iterateEmittedBin(int binNumber)
double getM() const
double getT() const
std::string getInputBasename()
get input file name without extension
Definition: OpalData.cpp:669
double getGlobalPhaseShift()
units: (sec)
Definition: OpalData.cpp:452
Object * find(const std::string &name)
Find entry.
Definition: OpalData.cpp:566
static OpalData * getInstance()
Definition: OpalData.cpp:196
void define(Object *newObject)
Define a new object.
Definition: OpalData.cpp:489
std::string getAuxiliaryOutputDirectory() const
get the name of the the additional data directory
Definition: OpalData.cpp:661
void addProblemCharacteristicValue(const std::string &name, unsigned int value)
Definition: OpalData.cpp:751
virtual double getCyclHarm() const
Definition: Cyclotron.cpp:297
virtual std::string getFieldMapFN() const
Definition: Cyclotron.cpp:183
virtual double getFMHighE() const
Definition: Cyclotron.cpp:365
virtual double getPHIinit() const
Definition: Cyclotron.cpp:143
void setPRinit(double prinit)
Definition: Cyclotron.cpp:131
void setRinit(double rinit)
Definition: Cyclotron.cpp:123
virtual double getFMLowE() const
Definition: Cyclotron.cpp:357
virtual void execute()
Execute the algorithm on the attached beam line.
void setGamma(double gamma)
Definition: PartBins.h:94
void fill(std::vector< double > &p)
Add a particle to the temporary container.
Definition: PartBins.h:50
void sortArray()
Definition: PartBins.cpp:116
An abstract sequence of beam line components.
Definition: Beamline.h:34
void shiftDistCoordinates(double massIneV)
static Distribution * find(const std::string &name)
void setDistParametersBinomial(double massIneV)
std::vector< double > & getBGzDist()
std::vector< double > & getTOrZDist()
void fillEBinHistogram()
std::vector< double > xWrite_m
Definition: Distribution.h:449
Vector_t pmean_m
Total thermal momentum.
Definition: Distribution.h:426
int currentEnergyBin_m
Definition: Distribution.h:412
void generateFlattopT(size_t numberOfParticles)
double cathodeTemp_m
Cathode material Fermi energy (eV).
Definition: Distribution.h:432
void createOpalCycl(PartBunchBase< double, 3 > *beam, size_t numberOfParticles, double current, const Beamline &bl)
void adjustPhaseSpace(double massIneV)
std::vector< double > pxWrite_m
Definition: Distribution.h:450
void writeOutFileHeader()
double getMaxTOrZ()
void checkEmissionParameters()
double getMinTOrZ()
std::string outFilename_m
Definition: Distribution.h:493
double sepPeaks_m
Definition: Distribution.h:473
std::vector< double > tOrZDist_m
Definition: Distribution.h:445
double sigmaTFall_m
Definition: Distribution.h:463
double tPulseLengthFWHM_m
Definition: Distribution.h:464
double getWeight()
int getNumberOfEnergyBins()
virtual void execute()
Execute the command.
gsl_histogram * energyBinHist_m
Distribution energy bins.
Definition: Distribution.h:419
void setupEmissionModelAstra(PartBunchBase< double, 3 > *beam)
void printEnergyBins(Inform &os) const
std::vector< std::vector< double > > additionalRNs_m
Upper limit on emission energy distribution (eV).
Definition: Distribution.h:435
std::vector< double > pxDist_m
Definition: Distribution.h:442
std::vector< double > yWrite_m
Definition: Distribution.h:451
double getEmissionDeltaT()
void printDistBinomial(Inform &os) const
double cathodeFermiEnergy_m
Laser photon energy (eV).
Definition: Distribution.h:431
double emitEnergyUpperLimit_m
Cathode temperature (K).
Definition: Distribution.h:433
void calcPartPerDist(size_t numberOfParticles)
unsigned int numberOfDistributions_m
List of Distribution types.
Definition: Distribution.h:389
void addDistributions()
void initializeBeam(PartBunchBase< double, 3 > *beam)
void doRestartOpalT(PartBunchBase< double, 3 > *p, size_t Np, int restartStep, H5PartWrapper *h5wrapper)
void checkIfEmitted()
void applyEmissModelNonEquil(double eZ, double &px, double &py, double &pz, std::vector< double > &additionalRNs)
std::vector< double > & getBGxDist()
Vector_t cutoffR_m
Definition: Distribution.h:467
void createOpalT(PartBunchBase< double, 3 > *beam, std::vector< Distribution * > addedDistributions, size_t &numberOfParticles)
void generateAstraFlattopT(size_t numberOfParticles)
void fillParticleBins()
size_t getNumberOfParticlesInFile(std::ifstream &inputFile)
void createDistributionBinomial(size_t numberOfParticles, double massIneV)
void setupEmissionModelNone(PartBunchBase< double, 3 > *beam)
virtual bool canReplaceBy(Object *object)
Distribution can only be replaced by another distribution.
std::vector< Distribution * > addedDistributions_m
Vector of distributions to be added to this distribution.
Definition: Distribution.h:398
void doRestartOpalCycl(PartBunchBase< double, 3 > *p, size_t Np, int restartStep, const int specifiedNumBunch, H5PartWrapper *h5wrapper)
std::vector< double > tOrZWrite_m
Definition: Distribution.h:453
std::vector< double > & getXDist()
size_t getNumberOfEmissionSteps()
void injectBeam(PartBunchBase< double, 3 > *beam)
double sigmaRise_m
Definition: Distribution.h:489
std::string laserImageName_m
Definition: Distribution.h:478
Vector_t sigmaP_m
Definition: Distribution.h:466
void create(size_t &numberOfParticles, double massIneV, double charge)
virtual ~Distribution()
std::vector< double > pyDist_m
Definition: Distribution.h:444
int getLastEmittedEnergyBin()
void createDistributionMultiGauss(size_t numberOfParticles, double massIneV)
double laserEnergy_m
Cathode material work function (eV).
Definition: Distribution.h:430
void setEmissionTime(double &maxT, double &minT)
Vector_t mBinomial_m
Definition: Distribution.h:469
gsl_rng * randGen_m
Definition: Distribution.h:422
void setupEnergyBins(double maxTOrZ, double minTOrZ)
void printEmissionModelNone(Inform &os) const
DistributionType distrTypeT_m
Distribution type strings.
Definition: Distribution.h:387
LaserProfile * laserProfile_m
Definition: Distribution.h:480
std::vector< double > yDist_m
Definition: Distribution.h:443
void generateFlattopZ(size_t numberOfParticles)
double cutoff_m
Definition: Distribution.h:491
std::vector< size_t > particlesPerDist_m
Definition: Distribution.h:399
void generateMatchedGauss(const SigmaGenerator::matrix_t &, size_t numberOfParticles, double massIneV)
double tRise_m
time binned distribution with thermal energy
Definition: Distribution.h:487
void setSigmaP_m(double massIneV)
std::string laserProfileFileName_m
Definition: Distribution.h:477
std::vector< double > & getYDist()
void printDistFromFile(Inform &os) const
virtual Distribution * clone(const std::string &name)
Return a clone.
double getEmissionTimeShift() const
void printDistMultiGauss(Inform &os) const
void reflectDistribution(size_t &numberOfParticles)
void checkFileMomentum()
std::vector< double > pzWrite_m
Definition: Distribution.h:454
virtual void update()
Update this object.
double sigmaFall_m
Definition: Distribution.h:490
void eraseTOrZDist()
void setAttributes()
void generateLongFlattopT(size_t numberOfParticles)
double cathodeWorkFunc_m
Definition: Distribution.h:429
void setDistParametersFlattop(double massIneV)
double sigmaTRise_m
Definition: Distribution.h:462
void printDistFlattop(Inform &os) const
void printDistMatchedGauss(Inform &os) const
static const double percentTEmission_m
Definition: Distribution.h:406
void writeOutFileEmission()
PartBins * energyBins_m
Definition: Distribution.h:418
void printEmissionModelAstra(Inform &os) const
void printEmissionModelNonEquil(Inform &os) const
void generateBinomial(size_t numberOfParticles)
std::vector< double > & getBGyDist()
void setupParticleBins(double massIneV, PartBunchBase< double, 3 > *beam)
void sampleUniformDisk(gsl_qrng *quasiRandGen2D, double &x1, double &x2)
double currentEmissionTime_m
Definition: Distribution.h:411
std::vector< size_t > binWrite_m
Definition: Distribution.h:455
double getEnergyBinDeltaT()
void createDistributionFromFile(size_t numberOfParticles, double massIneV)
size_t totalNumberParticles_m
Definition: Distribution.h:437
double pTotThermal_m
Random number generator.
Definition: Distribution.h:425
int numberOfSampleBins_m
Definition: Distribution.h:416
void setDistToEmitted(bool emitted)
void setDistParametersMultiGauss(double massIneV)
void createDistributionFlattop(size_t numberOfParticles, double massIneV)
void chooseInputMomentumUnits(InputMomentumUnits inputMoUnits)
size_t findEBin(double tOrZ)
void scaleDistCoordinates()
std::vector< double > pyWrite_m
Definition: Distribution.h:452
SymTenzor< double, 6 > correlationMatrix_m
Definition: Distribution.h:470
size_t getNumOfLocalParticlesToCreate(size_t n)
std::vector< double > pzDist_m
Definition: Distribution.h:446
InputMomentumUnits inputMoUnits_m
Definition: Distribution.h:461
double getTEmission()
double tEmission_m
Emission parameters.
Definition: Distribution.h:405
void generateFlattopLaserProfile(size_t numberOfParticles)
gsl_qrng * selectRandomGenerator(std::string, unsigned int dimension)
Select and allocate gsl random number generator.
void generateGaussZ(size_t numberOfParticles)
void printDist(Inform &os, size_t numberOfParticles) const
bool getIfDistEmitting()
void applyEmissModelNone(double &pz)
void writeOutFileInjection()
std::string distT_m
Definition: Distribution.h:386
size_t emitParticles(PartBunchBase< double, 3 > *beam, double eZ)
void applyEmissionModel(double lowEnergyLimit, double &px, double &py, double &pz, std::vector< double > &additionalRNs)
void applyEmissModelAstra(double &px, double &py, double &pz, std::vector< double > &additionalRNs)
double tFall_m
Definition: Distribution.h:488
void setupEmissionModelNonEquil()
double avrgpz_m
Definition: Distribution.h:458
void setupEmissionModel(PartBunchBase< double, 3 > *beam)
void printDistGauss(Inform &os) const
int currentSampleBin_m
Definition: Distribution.h:413
unsigned nPeaks_m
Definition: Distribution.h:474
void printEmissionModel(Inform &os) const
void setDistParametersGauss(double massIneV)
Vector_t cutoffP_m
Definition: Distribution.h:468
Inform & printInfo(Inform &os) const
void checkParticleNumber(size_t &numberOfParticles)
void createMatchedGaussDistribution(size_t numberOfParticles, double massIneV, double charge)
void createDistributionGauss(size_t numberOfParticles, double massIneV)
void shiftBeam(double &maxTOrZ, double &minTOrZ)
EmissionModel emissionModel_m
Emission Model.
Definition: Distribution.h:402
Vector_t sigmaR_m
Definition: Distribution.h:465
size_t totalNumberEmittedParticles_m
Definition: Distribution.h:438
std::vector< double > xDist_m
Definition: Distribution.h:441
double laserIntensityCut_m
Definition: Distribution.h:479
int numberOfEnergyBins_m
Definition: Distribution.h:414
void generateTransverseGauss(size_t numberOfParticles)
virtual double get(double rand)
virtual double get(double rand)
void getXY(double &x, double &y)
boost::numeric::ublas::compressed_matrix< double, boost::numeric::ublas::row_major > sparse_matrix_t
Sparse matrix type definition.
boost::numeric::ublas::matrix< double > matrix_t
Dense matrix type definition.
boost::numeric::ublas::vector< double, std::vector< double > > vector_t
Dense vector type definition.
void diagonalize(matrix_t &, sparse_matrix_t &, sparse_matrix_t &)
RealDiracMatrix::matrix_t matrix_t
Type for storing maps.
size_t getNumParticles() const
virtual void readStep(PartBunchBase< double, 3 > *, h5_ssize_t firstParticle, h5_ssize_t lastParticle)=0
virtual bool predecessorIsSameFlavour() const =0
virtual void readHeader()=0
The base class for all OPAL exceptions.
Definition: OpalException.h:28
virtual int raw_probe_receive(char *&, int &, int &)
Definition: Communicate.h:208
void barrier(void)
virtual bool raw_send(void *, int, int, int)
Definition: Communicate.h:192
virtual int raw_receive(char *, int, int &, int &)
Definition: Communicate.h:200
int next_tag(int t, int s=1000)
Definition: TagMaker.h:39
Definition: Inform.h:42
int width() const
Definition: Inform.h:108
FmtFlags_t setf(FmtFlags_t setbits, FmtFlags_t field)
Definition: Inform.h:101
int precision() const
Definition: Inform.h:112
static MPI_Comm getComm()
Definition: IpplInfo.h:152
static int getNodes()
Definition: IpplInfo.cpp:670
static int myNode()
Definition: IpplInfo.cpp:691
static Communicate * Comm
Definition: IpplInfo.h:84
static void stopTimer(TimerRef t)
Definition: IpplTimings.h:192
static void startTimer(TimerRef t)
Definition: IpplTimings.h:187
Vektor< double, 3 > Vector_t
Definition: Vektor.h:6