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