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."),
83 distrTypeT_m(DistributionType::NODIST),
84 numberOfDistributions_m(1),
85 emitting_m(false),
86 emissionModel_m(EmissionModel::NONE),
87 tEmission_m(0.0),
88 tBin_m(0.0),
89 currentEmissionTime_m(0.0),
90 currentEnergyBin_m(1),
91 currentSampleBin_m(0),
92 numberOfEnergyBins_m(0),
93 numberOfSampleBins_m(0),
94 energyBins_m(nullptr),
95 energyBinHist_m(nullptr),
96 randGen_m(nullptr),
97 pTotThermal_m(0.0),
98 pmean_m(0.0),
99 cathodeWorkFunc_m(0.0),
100 laserEnergy_m(0.0),
101 cathodeFermiEnergy_m(0.0),
102 cathodeTemp_m(0.0),
103 emitEnergyUpperLimit_m(0.0),
104 totalNumberParticles_m(0),
105 totalNumberEmittedParticles_m(0),
106 avrgpz_m(0.0),
107 inputMoUnits_m(InputMomentumUnits::NONE),
108 sigmaTRise_m(0.0),
109 sigmaTFall_m(0.0),
110 tPulseLengthFWHM_m(0.0),
111 correlationMatrix_m(getUnit6x6()),
112 sepPeaks_m(0.0),
113 nPeaks_m(1),
114 laserProfileFileName_m(""),
115 laserImageName_m(""),
116 laserIntensityCut_m(0.0),
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}
141Distribution::Distribution(const std::string &name, Distribution *parent):
142 Definition(name, parent),
143 distT_m(parent->distT_m),
144 distrTypeT_m(DistributionType::NODIST),
145 numberOfDistributions_m(parent->numberOfDistributions_m),
146 emitting_m(parent->emitting_m),
147 particleRefData_m(parent->particleRefData_m),
148 addedDistributions_m(parent->addedDistributions_m),
149 particlesPerDist_m(parent->particlesPerDist_m),
150 emissionModel_m(parent->emissionModel_m),
151 tEmission_m(parent->tEmission_m),
152 tBin_m(parent->tBin_m),
153 currentEmissionTime_m(parent->currentEmissionTime_m),
154 currentEnergyBin_m(parent->currentEnergyBin_m),
155 currentSampleBin_m(parent->currentSampleBin_m),
156 numberOfEnergyBins_m(parent->numberOfEnergyBins_m),
157 numberOfSampleBins_m(parent->numberOfSampleBins_m),
158 energyBins_m(nullptr),
159 energyBinHist_m(nullptr),
160 randGen_m(nullptr),
161 pTotThermal_m(parent->pTotThermal_m),
162 pmean_m(parent->pmean_m),
163 cathodeWorkFunc_m(parent->cathodeWorkFunc_m),
164 laserEnergy_m(parent->laserEnergy_m),
165 cathodeFermiEnergy_m(parent->cathodeFermiEnergy_m),
166 cathodeTemp_m(parent->cathodeTemp_m),
167 emitEnergyUpperLimit_m(parent->emitEnergyUpperLimit_m),
168 totalNumberParticles_m(parent->totalNumberParticles_m),
169 totalNumberEmittedParticles_m(parent->totalNumberEmittedParticles_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),
183 inputMoUnits_m(parent->inputMoUnits_m),
184 sigmaTRise_m(parent->sigmaTRise_m),
185 sigmaTFall_m(parent->sigmaTFall_m),
186 tPulseLengthFWHM_m(parent->tPulseLengthFWHM_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),
191 correlationMatrix_m(parent->correlationMatrix_m),
192 sepPeaks_m(parent->sepPeaks_m),
193 nPeaks_m(parent->nPeaks_m),
194 laserProfileFileName_m(parent->laserProfileFileName_m),
195 laserImageName_m(parent->laserImageName_m),
196 laserIntensityCut_m(parent->laserIntensityCut_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
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
250}
251
253}
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 }
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;
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->
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()
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
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
1853 int numberOfEnergyBins
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
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 {
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
2804 return emitting_m;
2805}
2806
2808 return currentEnergyBin_m;
2809}
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
2842 return numberOfEnergyBins_m;
2843}
2844
2847}
2848
2850 return tBin_m;
2851}
2852
2855}
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
3634 // SIGMAZ overrides SIGMAT
3637 // SIGMAR overrides SIGMAX/Y
3641 }
3642}
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
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
3840
3841 setSigmaR_m();
3843
3844 if (!emitting_m)
3845 setSigmaP_m(massIneV);
3846
3850
3853}
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)
3953 if (cutoffP_m[i] < SMALLESTCUTOFF)
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
4000 pTotThermal_m = Util::getBetaGamma(wThermal, beam->getM());
4001 pmean_m = Vector_t(0.0, 0.0, 0.5 * pTotThermal_m);
4002}
4003
4005
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
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
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
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 > log(const Tps< T > &x)
Natural logarithm.
Definition: TpsMath.h:182
Tps< T > cos(const Tps< T > &x)
Cosine.
Definition: TpsMath.h:129
Tps< T > pow(const Tps< T > &x, int y)
Integer power.
Definition: TpsMath.h:76
Tps< T > exp(const Tps< T > &x)
Exponential.
Definition: TpsMath.h:165
Tps< T > sin(const Tps< T > &x)
Sine.
Definition: TpsMath.h:111
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
@ SIZE
Definition: IndexMap.cpp:174
boost::numeric::ublas::matrix< double > matrix_t
Definition: BoostMatrix.h:23
constexpr double SMALLESTCUTOFF
Inform * gmsg
Definition: Main.cpp:70
DistributionType
Definition: Distribution.h:50
void allreduce(const T *input, T *output, int count, Op op)
Definition: GlobalComm.hpp:510
bool reduce(Communicate &, InputIterator, InputIterator, OutputIterator, const ReduceOp &, bool *IncludeVal=0)
Definition: GlobalComm.hpp:55
#define IPPL_APP_CYCLE
Definition: Tags.h:103
#define IPPL_APP_TAG2
Definition: Tags.h:95
T::PETE_Expr_t::PETE_Return_t max(const PETE_Expr< T > &expr, NDIndex< D > &loc)
Definition: ReductionLoc.h:84
T::PETE_Expr_t::PETE_Return_t min(const PETE_Expr< T > &expr, NDIndex< D > &loc)
Definition: ReductionLoc.h:76
#define PM(a, b, c, d)
Definition: fftpack.cpp:96
#define X(arg)
Definition: fftpack.cpp:112
T::PETE_Expr_t::PETE_Return_t sum(const PETE_Expr< T > &expr)
Definition: PETE.h:1111
T::PETE_Expr_t::PETE_Return_t prod(const PETE_Expr< T > &expr)
Definition: PETE.h:1121
PETE_TUTree< FnCeil, typename T::PETE_Expr_t > ceil(const PETE_Expr< T > &l)
Definition: PETE.h:728
PETE_TUTree< FnArcCos, typename T::PETE_Expr_t > acos(const PETE_Expr< T > &l)
Definition: PETE.h:725
PETE_TUTree< FnArcSin, typename T::PETE_Expr_t > asin(const PETE_Expr< T > &l)
Definition: PETE.h:726
std::complex< double > a
PETE_TBTree< FnCopysign, PETE_Scalar< Vektor< T1, Dim > >, typename T2::PETE_Expr_t > copysign(const Vektor< T1, Dim > &l, const PETE_Expr< T2 > &r)
PETE_TUTree< FnAbs, typename T::PETE_Expr_t > abs(const PETE_Expr< T > &l)
PETE_TBTree< FnArcTan2, PETE_Scalar< Vektor< T1, Dim > >, typename T2::PETE_Expr_t > atan2(const Vektor< T1, Dim > &l, const PETE_Expr< T2 > &r)
Inform & level2(Inform &inf)
Definition: Inform.cpp:46
Inform & endl(Inform &inf)
Definition: Inform.cpp:42
Inform & level1(Inform &inf)
Definition: Inform.cpp:45
Inform & level3(Inform &inf)
Definition: Inform.cpp:47
#define PAssert_GE(a, b)
Definition: PAssert.h:109
#define ERRORMSG(msg)
Definition: IpplInfo.h:350
#define INFOMSG(msg)
Definition: IpplInfo.h:348
#define WARNMSG(msg)
Definition: IpplInfo.h:349
const std::string name
Attribute makeBool(const std::string &name, const std::string &help)
Make logical attribute.
Definition: Attributes.cpp:90
double getReal(const Attribute &attr)
Return real value.
Definition: Attributes.cpp:252
Attribute makePredefinedString(const std::string &name, const std::string &help, const std::initializer_list< std::string > &predefinedStrings)
Make predefined string attribute.
Definition: Attributes.cpp:409
Attribute makeReal(const std::string &name, const std::string &help)
Make real attribute.
Definition: Attributes.cpp:240
bool getBool(const Attribute &attr)
Return logical value.
Definition: Attributes.cpp:100
Attribute makeRealArray(const std::string &name, const std::string &help)
Create real array attribute.
Definition: Attributes.cpp:289
std::vector< double > getRealArray(const Attribute &attr)
Get array value.
Definition: Attributes.cpp:294
std::string getString(const Attribute &attr)
Get string value.
Definition: Attributes.cpp:343
Attribute makeString(const std::string &name, const std::string &help)
Make string attribute.
Definition: Attributes.cpp:332
constexpr double kB
Boltzman's constant in eV/K.
Definition: Physics.h:60
constexpr double two_pi
The value of.
Definition: Physics.h:33
constexpr double epsilon_0
The permittivity of vacuum in As/Vm.
Definition: Physics.h:51
constexpr double alpha
The fine structure constant, no dimension.
Definition: Physics.h:72
constexpr double q_e
The elementary charge in As.
Definition: Physics.h:69
constexpr double m_e
The electron rest mass in GeV.
Definition: Physics.h:78
constexpr double e
The value of.
Definition: Physics.h:39
constexpr double c
The velocity of light in m/s.
Definition: Physics.h:45
constexpr double pi
The value of.
Definition: Physics.h:30
constexpr double GeV2MeV
Definition: Units.h:80
constexpr double A2mA
Definition: Units.h:131
constexpr double rad2mrad
Definition: Units.h:137
constexpr double m2mm
Definition: Units.h:26
constexpr double eV2MeV
Definition: Units.h:77
constexpr double GeV2eV
Definition: Units.h:68
constexpr double rad2deg
Definition: Units.h:146
std::string::iterator iterator
Definition: MSLang.h:15
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.
Definition: BeamSequence.h:32
virtual Beamline * fetchLine() const =0
Return the embedded CLASSIC beam line.
static BeamSequence * find(const std::string &name)
Find a BeamSequence by name.
The base class for all OPAL definitions.
Definition: Definition.h:30
The base class for all OPAL objects.
Definition: Object.h:48
void registerOwnership(const AttributeHandler::OwnerType &itsClass) const
Definition: Object.cpp:191
const std::string & getOpalName() const
Return object name.
Definition: Object.cpp:310
std::vector< Attribute > itsAttr
The object attributes.
Definition: Object.h:216
bool builtin
Built-in flag.
Definition: Object.h:233
double getP() const
void setEnergyBins(int numberOfEnergyBins)
ParticleAttrib< Vector_t > Ef
ParticlePos_t & R
ParticleAttrib< int > Bin
int getSteptoLastInj() const
void setNumBunch(short n)
double getMassPerParticle() const
double getQ() const
Access to reference data.
double getChargePerParticle() const
get the macro particle charge
size_t getLocalNum() const
ParticleAttrib< double > M
size_t getTotalNum() const
ParticleAttrib< Vector_t > P
ParticleAttrib< ParticleType > PType
double getdT() const
ParticleAttrib< ParticleOrigin > POrigin
ParticleAttrib< double > Q
double getInitialGamma() const
ParticleType getPType() const
IpplTimings::TimerRef distrReload_m
timer for IC, can not be in Distribution.h
long long getLocalTrackStep() const
short getNumBunch() const
ParticleAttrib< int > TriID
ParticleAttrib< double > dt
ParticleAttrib< Vector_t > Bf
IpplTimings::TimerRef distrCreate_m
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
get the spos of the primary beam
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
Definition: Cyclotron.cpp:300
virtual std::string getFieldMapFN() const
Definition: Cyclotron.cpp:183
virtual double getFMHighE() const
Definition: Cyclotron.cpp:375
virtual double getPHIinit() const
Definition: Cyclotron.cpp:143
void setPRinit(double prinit)
Definition: Cyclotron.cpp:131
void setRinit(double rinit)
Definition: Cyclotron.cpp:123
virtual double getFMLowE() const
Definition: Cyclotron.cpp:367
virtual void execute()
Execute the algorithm on the attached beam line.
void setGamma(double gamma)
Definition: PartBins.h:94
void fill(std::vector< double > &p)
Add a particle to the temporary container.
Definition: PartBins.h:50
void sortArray()
sort the vector of particles such that positions of the particles decrease with increasing index.
Definition: PartBins.cpp:116
An abstract sequence of beam line components.
Definition: Beamline.h:34
void shiftDistCoordinates(double massIneV)
static Distribution * find(const std::string &name)
void setDistParametersBinomial(double massIneV)
std::vector< double > & getBGzDist()
std::vector< double > & getTOrZDist()
void fillEBinHistogram()
std::vector< double > xWrite_m
Definition: Distribution.h:449
Vector_t pmean_m
Total thermal momentum.
Definition: Distribution.h:426
int currentEnergyBin_m
Definition: Distribution.h:412
void generateFlattopT(size_t numberOfParticles)
double cathodeTemp_m
Cathode material Fermi energy (eV).
Definition: Distribution.h:432
void createOpalCycl(PartBunchBase< double, 3 > *beam, size_t numberOfParticles, double current, const Beamline &bl)
void adjustPhaseSpace(double massIneV)
std::vector< double > pxWrite_m
Definition: Distribution.h:450
void writeOutFileHeader()
double getMaxTOrZ()
void checkEmissionParameters()
double getMinTOrZ()
std::string outFilename_m
Definition: Distribution.h:493
double sepPeaks_m
Definition: Distribution.h:473
std::vector< double > tOrZDist_m
Definition: Distribution.h:445
double sigmaTFall_m
Definition: Distribution.h:463
double tPulseLengthFWHM_m
Definition: Distribution.h:464
double getWeight()
int getNumberOfEnergyBins()
virtual void execute()
Execute the command.
gsl_histogram * energyBinHist_m
Distribution energy bins.
Definition: Distribution.h:419
void setupEmissionModelAstra(PartBunchBase< double, 3 > *beam)
void printEnergyBins(Inform &os) const
std::vector< std::vector< double > > additionalRNs_m
Upper limit on emission energy distribution (eV).
Definition: Distribution.h:435
std::vector< double > pxDist_m
Definition: Distribution.h:442
std::vector< double > yWrite_m
Definition: Distribution.h:451
double getEmissionDeltaT()
void printDistBinomial(Inform &os) const
double cathodeFermiEnergy_m
Laser photon energy (eV).
Definition: Distribution.h:431
double emitEnergyUpperLimit_m
Cathode temperature (K).
Definition: Distribution.h:433
void calcPartPerDist(size_t numberOfParticles)
unsigned int numberOfDistributions_m
List of Distribution types.
Definition: Distribution.h:389
void addDistributions()
void initializeBeam(PartBunchBase< double, 3 > *beam)
void doRestartOpalT(PartBunchBase< double, 3 > *p, size_t Np, int restartStep, H5PartWrapper *h5wrapper)
void checkIfEmitted()
void applyEmissModelNonEquil(double eZ, double &px, double &py, double &pz, std::vector< double > &additionalRNs)
std::vector< double > & getBGxDist()
Vector_t cutoffR_m
Definition: Distribution.h:467
void createOpalT(PartBunchBase< double, 3 > *beam, std::vector< Distribution * > addedDistributions, size_t &numberOfParticles)
void generateAstraFlattopT(size_t numberOfParticles)
void fillParticleBins()
size_t getNumberOfParticlesInFile(std::ifstream &inputFile)
void createDistributionBinomial(size_t numberOfParticles, double massIneV)
void setupEmissionModelNone(PartBunchBase< double, 3 > *beam)
virtual bool canReplaceBy(Object *object)
Distribution can only be replaced by another distribution.
std::vector< Distribution * > addedDistributions_m
Reference data for particle type (charge, mass etc.)
Definition: Distribution.h:398
void doRestartOpalCycl(PartBunchBase< double, 3 > *p, size_t Np, int restartStep, const int specifiedNumBunch, H5PartWrapper *h5wrapper)
std::vector< double > tOrZWrite_m
Definition: Distribution.h:453
std::vector< double > & getXDist()
size_t getNumberOfEmissionSteps()
void injectBeam(PartBunchBase< double, 3 > *beam)
double sigmaRise_m
Definition: Distribution.h:489
std::string laserImageName_m
Definition: Distribution.h:478
Vector_t sigmaP_m
Definition: Distribution.h:466
void create(size_t &numberOfParticles, double massIneV, double charge)
virtual ~Distribution()
std::vector< double > pyDist_m
Definition: Distribution.h:444
int getLastEmittedEnergyBin()
void createDistributionMultiGauss(size_t numberOfParticles, double massIneV)
double laserEnergy_m
Cathode material work function (eV).
Definition: Distribution.h:430
void setEmissionTime(double &maxT, double &minT)
Vector_t mBinomial_m
Definition: Distribution.h:469
matrix_t correlationMatrix_m
Definition: Distribution.h:470
gsl_rng * randGen_m
GSL histogram used to define energy bin structure.
Definition: Distribution.h:422
void setupEnergyBins(double maxTOrZ, double minTOrZ)
void printEmissionModelNone(Inform &os) const
DistributionType distrTypeT_m
Distribution type strings.
Definition: Distribution.h:387
LaserProfile * laserProfile_m
Definition: Distribution.h:480
std::vector< double > yDist_m
Definition: Distribution.h:443
void generateFlattopZ(size_t numberOfParticles)
double cutoff_m
Definition: Distribution.h:491
std::vector< size_t > particlesPerDist_m
Definition: Distribution.h:399
void generateMatchedGauss(const SigmaGenerator::matrix_t &, size_t numberOfParticles, double massIneV)
double tRise_m
time binned distribution with thermal energy
Definition: Distribution.h:487
void setSigmaP_m(double massIneV)
std::string laserProfileFileName_m
Definition: Distribution.h:477
std::vector< double > & getYDist()
void printDistFromFile(Inform &os) const
virtual Distribution * clone(const std::string &name)
Return a clone.
double getEmissionTimeShift() const
void printDistMultiGauss(Inform &os) const
void reflectDistribution(size_t &numberOfParticles)
std::vector< double > pzWrite_m
Definition: Distribution.h:454
virtual void update()
Update this object.
double sigmaFall_m
Definition: Distribution.h:490
void eraseTOrZDist()
void setAttributes()
void generateLongFlattopT(size_t numberOfParticles)
double cathodeWorkFunc_m
Definition: Distribution.h:429
void setDistParametersFlattop(double massIneV)
double sigmaTRise_m
Definition: Distribution.h:462
void printDistFlattop(Inform &os) const
void printDistMatchedGauss(Inform &os) const
static const double percentTEmission_m
Definition: Distribution.h:406
void writeOutFileEmission()
PartBins * energyBins_m
Number of samples to use per energy bin when emitting beam.
Definition: Distribution.h:418
void printEmissionModelAstra(Inform &os) const
void printEmissionModelNonEquil(Inform &os) const
void generateBinomial(size_t numberOfParticles)
std::vector< double > & getBGyDist()
void setupParticleBins(double massIneV, PartBunchBase< double, 3 > *beam)
void sampleUniformDisk(gsl_qrng *quasiRandGen2D, double &x1, double &x2)
double currentEmissionTime_m
Definition: Distribution.h:411
std::vector< size_t > binWrite_m
Definition: Distribution.h:455
double getEnergyBinDeltaT()
void createDistributionFromFile(size_t numberOfParticles, double massIneV)
size_t totalNumberParticles_m
Definition: Distribution.h:437
double pTotThermal_m
Random number generator.
Definition: Distribution.h:425
int numberOfSampleBins_m
Number of energy bins the distribution is broken into.
Definition: Distribution.h:416
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
Definition: Distribution.h:452
size_t getNumOfLocalParticlesToCreate(size_t n)
Calculate the local number of particles evenly and adjust node 0 such that n is matched exactly.
std::vector< double > pzDist_m
Definition: Distribution.h:446
InputMomentumUnits inputMoUnits_m
Definition: Distribution.h:461
double getTEmission()
double tEmission_m
Emission parameters.
Definition: Distribution.h:405
void generateFlattopLaserProfile(size_t numberOfParticles)
gsl_qrng * selectRandomGenerator(std::string, unsigned int dimension)
Select and allocate gsl random number generator.
void generateGaussZ(size_t numberOfParticles)
void printDist(Inform &os, size_t numberOfParticles) const
bool getIfDistEmitting()
void applyEmissModelNone(double &pz)
void writeOutFileInjection()
std::string distT_m
Definition: Distribution.h:386
size_t emitParticles(PartBunchBase< double, 3 > *beam, double eZ)
Here we emit particles from the cathode.
void applyEmissionModel(double lowEnergyLimit, double &px, double &py, double &pz, std::vector< double > &additionalRNs)
void applyEmissModelAstra(double &px, double &py, double &pz, std::vector< double > &additionalRNs)
double tFall_m
Definition: Distribution.h:488
void setupEmissionModelNonEquil()
double avrgpz_m
Definition: Distribution.h:458
void setupEmissionModel(PartBunchBase< double, 3 > *beam)
void printDistGauss(Inform &os) const
int currentSampleBin_m
Definition: Distribution.h:413
unsigned nPeaks_m
Definition: Distribution.h:474
void printEmissionModel(Inform &os) const
void setDistParametersGauss(double massIneV)
Vector_t cutoffP_m
Definition: Distribution.h:468
Inform & printInfo(Inform &os) const
void checkParticleNumber(size_t &numberOfParticles)
void createMatchedGaussDistribution(size_t numberOfParticles, double massIneV, double charge)
void createDistributionGauss(size_t numberOfParticles, double massIneV)
void shiftBeam(double &maxTOrZ, double &minTOrZ)
EmissionModel emissionModel_m
Emission Model.
Definition: Distribution.h:402
Vector_t sigmaR_m
Definition: Distribution.h:465
size_t totalNumberEmittedParticles_m
Definition: Distribution.h:438
std::vector< double > xDist_m
Definition: Distribution.h:441
double tBin_m
Increase tEmission_m by twice this percentage to ensure that no particles fall on the leading edge of...
Definition: Distribution.h:410
double laserIntensityCut_m
Definition: Distribution.h:479
int numberOfEnergyBins_m
Definition: Distribution.h:414
void generateTransverseGauss(size_t numberOfParticles)
virtual double get(double rand)
virtual double get(double rand)
void getXY(double &x, double &y)
boost::numeric::ublas::compressed_matrix< double, boost::numeric::ublas::row_major > sparse_matrix_t
Sparse matrix type definition.
boost::numeric::ublas::matrix< double > matrix_t
Dense matrix type definition.
boost::numeric::ublas::vector< double, std::vector< double > > vector_t
Dense vector type definition.
void diagonalize(matrix_t &, sparse_matrix_t &, sparse_matrix_t &)
RealDiracMatrix::matrix_t matrix_t
Type for storing maps.
size_t getNumParticles() const
virtual void readStep(PartBunchBase< double, 3 > *, h5_ssize_t firstParticle, h5_ssize_t lastParticle)=0
virtual bool predecessorIsSameFlavour() const =0
virtual void readHeader()=0
The base class for all OPAL exceptions.
Definition: OpalException.h:28
virtual int raw_probe_receive(char *&, int &, int &)
Definition: Communicate.h:208
void barrier(void)
virtual bool raw_send(void *, int, int, int)
Definition: Communicate.h:192
virtual int raw_receive(char *, int, int &, int &)
Definition: Communicate.h:200
int next_tag(int t, int s=1000)
Definition: TagMaker.h:39
Definition: Inform.h:42
int width() const
Definition: Inform.h:108
FmtFlags_t setf(FmtFlags_t setbits, FmtFlags_t field)
Definition: Inform.h:101
int precision() const
Definition: Inform.h:112
static MPI_Comm getComm()
Definition: IpplInfo.h:152
static int getNodes()
Definition: IpplInfo.cpp:670
static int myNode()
Definition: IpplInfo.cpp:691
static Communicate * Comm
Definition: IpplInfo.h:84
static void stopTimer(TimerRef t)
Definition: IpplTimings.h:192
static void startTimer(TimerRef t)
Definition: IpplTimings.h:187
Vektor< double, 3 > Vector_t