OPAL (Object Oriented Parallel Accelerator Library)  2024.1
OPAL
Distribution.cpp
Go to the documentation of this file.
1 //
2 // Class Distribution
3 // This class defines the initial beam that is injected or emitted into the simulation.
4 //
5 // Copyright (c) 2008 - 2022, Paul Scherrer Institut, Villigen PSI, Switzerland
6 // All rights reserved
7 //
8 // This file is part of OPAL.
9 //
10 // OPAL is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with OPAL. If not, see <https://www.gnu.org/licenses/>.
17 //
19 
24 #include "Algorithms/PartBins.h"
25 #include "Algorithms/PartBinsCyc.h"
27 #include "BasicActions/Option.h"
28 #include "DataSource/DataConnect.h"
31 #include "Elements/OpalBeamline.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 
59 extern Inform *gmsg;
60 
61 constexpr 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. */
67 const double Distribution::percentTEmission_m = 0.0005;
68 
69 namespace {
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 {
121  setAttributes();
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 }
141 Distribution::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 
245 Distribution *Distribution::clone(const std::string &name) {
246  return new Distribution(name, this);
247 }
248 
250 }
251 
253 }
254 
255 void 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();
334  const bool scalable = Attributes::getBool(itsAttr[Attrib::Distribution::SCALABLE]);
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 
376 void 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 
492 Distribution *Distribution::find(const std::string &name) {
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;
586  printEmissionModel(os);
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 
645 void Distribution::applyEmissionModel(double lowEnergyLimit, double &px, double &py, double &pz, std::vector<double> &additionalRNs) {
646 
647  switch (emissionModel_m) {
648 
649  case EmissionModel::NONE:
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 
663 void 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 
678 void 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 
735 void 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.
824  currentEnergyBin_m = 1;
825  currentSampleBin_m = 0;
826 
827 }
828 
830 
832 
833  switch (distrTypeT_m) {
834 
837  emitting_m = true;
838  break;
839  default:
840  break;
841  }
842 }
843 
844 void 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 
875 void 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 
900  const std::string inputUnits = Attributes::getString(itsAttr[Attrib::Distribution::INPUTMOUNITS]);
901  if (inputUnits.empty()) {
902  inputMoUnits_m = inputMoUnits;
903  } else {
904  inputMoUnits_m = stringInputMomentumUnits_s.at(inputUnits);
905  }
906 }
907 
908 void Distribution::createDistributionBinomial(size_t numberOfParticles, double massIneV) {
909 
910  setDistParametersBinomial(massIneV);
911  generateBinomial(numberOfParticles);
912 }
913 
914 void 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 
927 void Distribution::createDistributionMultiGauss(size_t numberOfParticles, double massIneV) {
928 
929  gsl_qrng *quasiRandGen2D = selectRandomGenerator(Options::rngtype,2);
930 
931  setDistParametersMultiGauss(massIneV);
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 
1004 size_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 
1034 void 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 
1166 void Distribution::createMatchedGaussDistribution(size_t numberOfParticles,
1167  double massIneV,
1168  double charge)
1169 {
1170 
1171  /*
1172  ToDo:
1173  - eliminate physics and error
1174  */
1175 
1176  std::string lineName = Attributes::getString(itsAttr[Attrib::Distribution::LINE]);
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,
1278  Attributes::getReal(itsAttr[Attrib::Distribution::EX]) * Units::m2mm * Units::rad2mrad,
1279  Attributes::getReal(itsAttr[Attrib::Distribution::EY]) * Units::m2mm * Units::rad2mrad,
1280  Attributes::getReal(itsAttr[Attrib::Distribution::ET]) * Units::m2mm * Units::rad2mrad,
1281  E_m * Units::eV2MeV,
1282  massIneV * Units::eV2MeV,
1283  charge,
1284  CyclotronElement,
1285  Nint,
1286  Nsectors,
1287  Attributes::getReal(itsAttr[Attrib::Distribution::ORDERMAPS]),
1288  writeMap));
1289 
1290  if (siggen->match(accuracy,
1291  Attributes::getReal(itsAttr[Attrib::Distribution::MAXSTEPSSI]),
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 
1334 void 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) {
1393  fillParticleBins();
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->
1451  checkIfEmitted();
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.
1473  addDistributions();
1474 
1476  setupEmissionModelNone(beam);
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);
1711  currentSampleBin_m = 0;
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 
1770 void 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 
1828 size_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 
1841 void 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 
1952 void 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 {
1985  gamma(index) = std::sqrt(std::numeric_limits<double>::max());
1986  }
1987  alpha(index) = -correlationMatrix_m(2 * index + 1, 2 * index)
1988  * std::sqrt(beta(index) * gamma(index));
1989  }
1990 
1991  Vector_t M, PM, L, PL, X, PX;
1992  Vector_t CHI, COSCHI, SINCHI(0.0);
1993  Vector_t AMI;
1995  for (unsigned int index = 0; index < 3; index++) {
1996  // gamma(index) *= cutoffR_m[index];
1997  // beta(index) *= cutoffP_m[index];
1998  COSCHI[index] = std::sqrt(1.0 / (1.0 + std::pow(alpha(index), 2)));
1999  SINCHI[index] = -alpha(index) * COSCHI[index];
2000  CHI[index] = std::atan2(SINCHI[index], COSCHI[index]);
2001  AMI[index] = 1.0 / mBinomial_m[index];
2002  M[index] = std::sqrt(emittance(index) * beta(index));
2003  PM[index] = std::sqrt(emittance(index) * gamma(index));
2004  L[index] = std::sqrt((mBinomial_m[index] + 1.0) / 2.0) * M[index];
2005  PL[index] = std::sqrt((mBinomial_m[index] + 1.0) / 2.0) * PM[index];
2006 
2007  if (mBinomial_m[index] < 10000) {
2008  X[index] = L[index];
2009  PX[index] = PL[index];
2010  splitter[index] = new MDependentBehavior(mBinomial_m[index]);
2011  } else {
2012  X[index] = M[index];
2013  PX[index] = PM[index];
2014  splitter[index] = new GaussianLikeBehavior();
2015  }
2016  }
2017 
2018  int saveProcessor = -1;
2019  const int myNode = Ippl::myNode();
2020  const int numNodes = Ippl::getNodes();
2022 
2023  double temp = 1.0 - std::pow(correlationMatrix_m(1, 0), 2);
2024  const double tempa = std::copysign(std::sqrt(std::abs(temp)), temp);
2025  const double l32 = (correlationMatrix_m(4, 1) -
2026  correlationMatrix_m(1, 0) * correlationMatrix_m(4, 0)) / tempa;
2027  temp = 1 - std::pow(correlationMatrix_m(4, 0), 2) - l32 * l32;
2028  const double l33 = std::copysign(std::sqrt(std::abs(temp)), temp);
2029 
2030  const double l42 = (correlationMatrix_m(5, 1) -
2031  correlationMatrix_m(1, 0) * correlationMatrix_m(5, 0)) / tempa;
2032  const double l43 = (correlationMatrix_m(5, 4) -
2033  correlationMatrix_m(4, 0) * correlationMatrix_m(5, 0) - l42 * l32) / l33;
2034  temp = 1 - std::pow(correlationMatrix_m(5, 0), 2) - l42 * l42 - l43 * l43;
2035  const double l44 = std::copysign(std::sqrt(std::abs(temp)), temp);
2036 
2037  Vector_t x = Vector_t(0.0);
2038  Vector_t p = Vector_t(0.0);
2039  for (size_t partIndex = 0; partIndex < numberOfParticles; partIndex++) {
2040 
2041  double A = 0.0;
2042  double AL = 0.0;
2043  double Ux = 0.0, U = 0.0;
2044  double Vx = 0.0, V = 0.0;
2045 
2046  A = splitter[0]->get(gsl_rng_uniform(randGen_m));
2047  AL = Physics::two_pi * gsl_rng_uniform(randGen_m);
2048  Ux = A * std::cos(AL);
2049  Vx = A * std::sin(AL);
2050  x[0] = X[0] * Ux;
2051  p[0] = PX[0] * (Ux * SINCHI[0] + Vx * COSCHI[0]);
2052 
2053  A = splitter[1]->get(gsl_rng_uniform(randGen_m));
2054  AL = Physics::two_pi * gsl_rng_uniform(randGen_m);
2055  U = A * std::cos(AL);
2056  V = A * std::sin(AL);
2057  x[1] = X[1] * U;
2058  p[1] = PX[1] * (U * SINCHI[1] + V * COSCHI[1]);
2059 
2060  A = splitter[2]->get(gsl_rng_uniform(randGen_m));
2061  AL = Physics::two_pi * gsl_rng_uniform(randGen_m);
2062  U = A * std::cos(AL);
2063  V = A * std::sin(AL);
2064  x[2] = X[2] * (Ux * correlationMatrix_m(4, 0) + Vx * l32 + U * l33);
2065  p[2] = PX[2] * (Ux * correlationMatrix_m(5, 0) + Vx * l42 + U * l43 + V * l44);
2066 
2067  // Save to each processor in turn.
2068  saveProcessor++;
2069  if (saveProcessor >= numNodes)
2070  saveProcessor = 0;
2071 
2072  if (scalable || myNode == saveProcessor) {
2073  xDist_m.push_back(x[0]);
2074  pxDist_m.push_back(p[0]);
2075  yDist_m.push_back(x[1]);
2076  pyDist_m.push_back(p[1]);
2077  tOrZDist_m.push_back(x[2]);
2078  pzDist_m.push_back(avrgpz_m + p[2]);
2079  }
2080  }
2081 
2082  for (unsigned int index = 0; index < 3; index++) {
2083  delete splitter[index];
2084  }
2085 }
2086 
2087 void 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 
2123 void 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 
2165 void 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 
2214 void 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
2423  RealDiracMatrix::matrix_t A(4, 4);
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 
2500 void 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 
2642 void 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  */
2731  beam->setTEmission(tEmission_m);
2732  beam->setNumBunch(1);
2733  if (numberOfEnergyBins_m > 0)
2735 }
2736 
2738 
2740 
2741  std::vector<double> id1 = Attributes::getRealArray(itsAttr[Attrib::Distribution::ID1]);
2742  std::vector<double> id2 = Attributes::getRealArray(itsAttr[Attrib::Distribution::ID2]);
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 
2846  return tBin_m / numberOfSampleBins_m;
2847 }
2848 
2850  return tBin_m;
2851 }
2852 
2855 }
2856 
2857 std::vector<double>& Distribution::getXDist() {
2858  return xDist_m;
2859 }
2860 
2861 std::vector<double>& Distribution::getBGxDist() {
2862  return pxDist_m;
2863 }
2864 
2865 std::vector<double>& Distribution::getYDist() {
2866  return yDist_m;
2867 }
2868 
2869 std::vector<double>& Distribution::getBGyDist() {
2870  return pyDist_m;
2871 }
2872 
2873 std::vector<double>& Distribution::getTOrZDist() {
2874  return tOrZDist_m;
2875 }
2876 
2877 std::vector<double>& Distribution::getBGzDist() {
2878  return pzDist_m;
2879 }
2880 
2881 void 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) {
2894  case InputMomentumUnits::NONE: {
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) {
2909  printDistFromFile(os);
2910  break;
2912  printDistGauss(os);
2913  break;
2915  printDistBinomial(os);
2916  break;
2920  printDistFlattop(os);
2921  break;
2923  printDistMultiGauss(os);
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 
3155  case EmissionModel::NONE:
3157  break;
3158  case EmissionModel::ASTRA:
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 
3223 void 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 
3274 gsl_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 ", "");
3308  itsAttr[Attrib::Distribution::EX]
3309  = Attributes::makeReal("EX", "Projected normalized emittance EX (m-rad), used to create matched distribution ", 1E-6);
3310  itsAttr[Attrib::Distribution::EY]
3311  = Attributes::makeReal("EY", "Projected normalized emittance EY (m-rad) used to create matched distribution ", 1E-6);
3312  itsAttr[Attrib::Distribution::ET]
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);
3374  itsAttr[Attrib::Distribution::W]
3375  = Attributes::makeReal("W", "Workfunction of photocathode material (eV)."
3376  " (Default atomically clean copper.)", 4.31);
3377  itsAttr[Attrib::Distribution::FE]
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);
3436  itsAttr[Attrib::Distribution::MX]
3437  = Attributes::makeReal("MX", "Defines the binomial distribution in x, "
3438  "0.0 ... infinity.", 10001.0);
3439  itsAttr[Attrib::Distribution::MY]
3440  = Attributes::makeReal("MY", "Defines the binomial distribution in y, "
3441  "0.0 ... infinity.", 10001.0);
3442  itsAttr[Attrib::Distribution::MZ]
3443  = Attributes::makeReal("MZ", "Defines the binomial distribution in z, "
3444  "0.0 ... infinity.", 10001.0);
3445  itsAttr[Attrib::Distribution::MT]
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 
3492  itsAttr[Attrib::Distribution::R51]
3493  = Attributes::makeReal("R51", "x/z correlation, (R51 in transport "
3494  "notation).", 0.0);
3495  itsAttr[Attrib::Distribution::R52]
3496  = Attributes::makeReal("R52", "xp/z correlation, (R52 in transport "
3497  "notation).", 0.0);
3498 
3499  itsAttr[Attrib::Distribution::R61]
3500  = Attributes::makeReal("R61", "x/pz correlation, (R61 in transport "
3501  "notation).", 0.0);
3502  itsAttr[Attrib::Distribution::R62]
3503  = Attributes::makeReal("R62", "xp/pz correlation, (R62 in transport "
3504  "notation).", 0.0);
3505 
3506  itsAttr[Attrib::Distribution::R]
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 
3534  itsAttr[Attrib::Distribution::ID1]
3535  = Attributes::makeRealArray("ID1", "User defined particle with ID=1");
3536  itsAttr[Attrib::Distribution::ID2]
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  */
3561  itsAttr[Attrib::Legacy::Distribution::SIGMAPT] = Attributes::makeReal("SIGMAPT", "SIGMApt", 0.0);
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
3636  sigmaR_m[2] = std::abs(Attributes::getReal(itsAttr[Attrib::Distribution::SIGMAZ]));
3637  // SIGMAR overrides SIGMAX/Y
3639  sigmaR_m[0] = std::abs(Attributes::getReal(itsAttr[Attrib::Distribution::SIGMAR]));
3640  sigmaR_m[1] = std::abs(Attributes::getReal(itsAttr[Attrib::Distribution::SIGMAR]));
3641  }
3642 }
3643 
3644 void Distribution::setSigmaP_m(double massIneV) {
3648 
3649  // SIGMAPZ overrides SIGMAPT. SIGMAPT is left for legacy compatibility.
3651  sigmaP_m[2] = std::abs(Attributes::getReal(itsAttr[Attrib::Legacy::Distribution::SIGMAPT]));
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 
3664 void Distribution::setEmissionTime(double &maxT, double &minT) {
3665 
3666  if (addedDistributions_m.empty()) {
3667 
3668  switch (distrTypeT_m) {
3669 
3674  * (sigmaTRise_m + sigmaTFall_m);
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
3755  mBinomial_m[2] = std::abs(Attributes::getReal(itsAttr[Attrib::Distribution::MZ]));
3756 
3757  if (emitting_m) {
3758  mBinomial_m[2] = std::abs(Attributes::getReal(itsAttr[Attrib::Distribution::MT]));
3759  correlationMatrix_m(5, 4) = std::abs(Attributes::getReal(itsAttr[Attrib::Distribution::CORRT]));
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.
3781  correlationMatrix_m(5, 4) = Attributes::getReal(itsAttr[Attrib::Distribution::CORRZ]);
3782 
3783  setSigmaR_m();
3784  if (emitting_m) {
3785  sigmaR_m[2] = 0.0;
3786 
3788  sigmaTFall_m = std::abs(Attributes::getReal(itsAttr[Attrib::Distribution::SIGMAT]));
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 
3801  if (std::abs(Attributes::getReal(itsAttr[Attrib::Distribution::TRISE])) > 0.0)
3802  sigmaTRise_m = std::abs(Attributes::getReal(itsAttr[Attrib::Distribution::TRISE]))
3803  / timeRatio;
3804 
3805  if (std::abs(Attributes::getReal(itsAttr[Attrib::Distribution::TFALL])) > 0.0)
3806  sigmaTFall_m = std::abs(Attributes::getReal(itsAttr[Attrib::Distribution::TFALL]))
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 
3875  cutoffR_m[1] = Attributes::getReal(itsAttr[Attrib::Distribution::CUTOFFR]);
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.
3910  correlationMatrix_m(5, 4) = Attributes::getReal(itsAttr[Attrib::Distribution::CORRZ]);
3911  }
3912  }
3913 
3915  setSigmaR_m();
3916 
3917  if (emitting_m) {
3918  sigmaR_m[2] = 0.0;
3919 
3921  sigmaTFall_m = std::abs(Attributes::getReal(itsAttr[Attrib::Distribution::SIGMAT]));
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 
3934  if (std::abs(Attributes::getReal(itsAttr[Attrib::Distribution::TRISE])) > 0.0)
3935  sigmaTRise_m = std::abs(Attributes::getReal(itsAttr[Attrib::Distribution::TRISE]))
3936  / timeRatio;
3937 
3938  if (std::abs(Attributes::getReal(itsAttr[Attrib::Distribution::TFALL])) > 0.0)
3939  sigmaTFall_m = std::abs(Attributes::getReal(itsAttr[Attrib::Distribution::TFALL]))
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  }
3987  case EmissionModel::NONEQUIL: {
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
4032  pmean_m = Vector_t(0, 0, std::sqrt(std::pow(0.5 * emitEnergyUpperLimit_m / (Physics::m_e * Units::GeV2eV) + 1.0, 2) - 1.0));
4033 }
4034 
4035 void 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 
4071 void 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 
4107  } else if (distrTypeT_m != DistributionType::FROMFILE) {
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 
4127 void Distribution::shiftDistCoordinates(double massIneV) {
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  */
4142  if (currDist->itsAttr[Attrib::Legacy::Distribution::T]) {
4143  throw OpalException("Distribution::shiftDistCoordinates",
4144  "Attribute T isn't supported anymore; use OFFSETZ instead");
4145  }
4146 
4147  double deltaTOrZ = 0.0;
4148  if (!emitting_m)
4151 
4152  double deltaPx = Attributes::getReal(currDist->itsAttr[Attrib::Distribution::OFFSETPX]);
4153  double deltaPy = Attributes::getReal(currDist->itsAttr[Attrib::Distribution::OFFSETPY]);
4154  double deltaPz = Attributes::getReal(currDist->itsAttr[Attrib::Distribution::OFFSETPZ]);
4155 
4157  WARNMSG("PT & PZ are obsolete and will be ignored. The moments of the beam is defined with PC" << endl);
4158 
4159  // Check input momentum units.
4161  deltaPx = Util::convertMomentumEVoverCToBetaGamma(deltaPx, massIneV);
4162  deltaPy = Util::convertMomentumEVoverCToBetaGamma(deltaPy, massIneV);
4163  deltaPz = Util::convertMomentumEVoverCToBetaGamma(deltaPz, massIneV);
4164  }
4165 
4166  size_t endIdx = startIdx + particlesPerDist_m[i];
4167  for (size_t particleIndex = startIdx; particleIndex < endIdx; ++ particleIndex) {
4168  xDist_m.at(particleIndex) += deltaX;
4169  pxDist_m.at(particleIndex) += deltaPx;
4170  yDist_m.at(particleIndex) += deltaY;
4171  pyDist_m.at(particleIndex) += deltaPy;
4172  tOrZDist_m.at(particleIndex) += deltaTOrZ;
4173  pzDist_m.at(particleIndex) += deltaPz;
4174  }
4175 
4176  startIdx = endIdx;
4177  }
4178 }
4179 
4181 
4183  return;
4184  }
4185 
4186  unsigned int totalNum = tOrZDist_m.size();
4187  reduce(totalNum, totalNum, OpAddAssign());
4188  if (Ippl::myNode() != 0)
4189  return;
4190 
4193  OpalData::getInstance()->getInputBasename() + "_" + getOpalName() + ".dat"
4194  });
4195 
4196  std::ofstream outputFile(outFilename_m);
4197  if (outputFile.bad()) {
4198  *gmsg << "Unable to open output file '" << outFilename_m << "'" << endl;
4199  } else {
4200  outputFile.setf(std::ios::left);
4201  outputFile << "# ";
4202  outputFile.width(17);
4203  outputFile << "x [m]";
4204  outputFile.width(17);
4205  outputFile << "px [betax gamma]";
4206  outputFile.width(17);
4207  outputFile << "y [m]";
4208  outputFile.width(17);
4209  outputFile << "py [betay gamma]";
4210  if (emitting_m) {
4211  outputFile.width(17);
4212  outputFile << "t [s]";
4213  } else {
4214  outputFile.width(17);
4215  outputFile << "z [m]";
4216  }
4217  outputFile.width(17);
4218  outputFile << "pz [betaz gamma]" ;
4219  if (emitting_m) {
4220  outputFile.width(17);
4221  outputFile << "Bin Number" << std::endl;
4222  } else {
4223  if (numberOfEnergyBins_m > 0) {
4224  outputFile.width(17);
4225  outputFile << "Bin Number";
4226  }
4227  outputFile << std::endl;
4228 
4229  outputFile << "# " << totalNum << std::endl;
4230  }
4231  }
4232  outputFile.close();
4233 }
4234 
4236 
4238  xWrite_m.clear();
4239  pxWrite_m.clear();
4240  yWrite_m.clear();
4241  pyWrite_m.clear();
4242  tOrZWrite_m.clear();
4243  pzWrite_m.clear();
4244  binWrite_m.clear();
4245 
4246  return;
4247  }
4248 
4249  // Gather particles to be written onto node 0.
4250  std::vector<char> msgbuf;
4251  const size_t bitsPerParticle = (6 * sizeof(double) + sizeof(size_t));
4252  size_t totalSendBits = xWrite_m.size() * bitsPerParticle;
4253 
4254  std::vector<unsigned long> numberOfBits(Ippl::getNodes(), 0);
4255  numberOfBits[Ippl::myNode()] = totalSendBits;
4256 
4257  if (Ippl::myNode() == 0) {
4258  MPI_Reduce(MPI_IN_PLACE, &(numberOfBits[0]), Ippl::getNodes(), MPI_UNSIGNED_LONG, MPI_SUM, 0, Ippl::getComm());
4259  } else {
4260  MPI_Reduce(&(numberOfBits[0]), nullptr, Ippl::getNodes(), MPI_UNSIGNED_LONG, MPI_SUM, 0, Ippl::getComm());
4261  }
4262 
4263  Ippl::Comm->barrier();
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 
4420 void Distribution::adjustPhaseSpace(double massIneV) {
4421  if (emitting_m || distrTypeT_m == DistributionType::FROMFILE || OpalData::getInstance()->isInOPALCyclMode())
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 }
std::string laserProfileFileName_m
Definition: Distribution.h:477
#define PAssert_GE(a, b)
Definition: PAssert.h:109
double getMinTOrZ()
Attribute makeReal(const std::string &name, const std::string &help)
Make real attribute.
Definition: Attributes.cpp:240
virtual double get(double rand)
double cathodeFermiEnergy_m
Laser photon energy (eV).
Definition: Distribution.h:431
static OpalData * getInstance()
Definition: OpalData.cpp:196
boost::numeric::ublas::matrix< double > matrix_t
Definition: BoostMatrix.h:23
void applyEmissModelNonEquil(double eZ, double &px, double &py, double &pz, std::vector< double > &additionalRNs)
void setupEnergyBins(double maxTOrZ, double minTOrZ)
bool builtin
Built-in flag.
Definition: Object.h:233
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
std::vector< double > & getBGzDist()
constexpr double c
The velocity of light in m/s.
Definition: Physics.h:45
void generateBinomial(size_t numberOfParticles)
double laserEnergy_m
Cathode material work function (eV).
Definition: Distribution.h:430
void generateGaussZ(size_t numberOfParticles)
virtual double getFMHighE() const
Definition: Cyclotron.cpp:372
int seed
The current random seed.
Definition: Options.cpp:37
size_t getNumberOfEmissionSteps()
and give any other recipients of the Program a copy of this License along with the Program You may charge a fee for the physical act of transferring a copy
Definition: LICENSE:87
double convertMomentumEVoverCToBetaGamma(double p, double mass)
Definition: Util.h:69
std::vector< double > pyWrite_m
Definition: Distribution.h:452
size_t getNumParticles() const
void setDistToEmitted(bool emitted)
#define PM(a, b, c, d)
Definition: fftpack.cpp:96
ParticleAttrib< ParticleOrigin > POrigin
The base class for all OPAL objects.
Definition: Object.h:48
double tPulseLengthFWHM_m
Definition: Distribution.h:464
double getGlobalPhaseShift()
units: (sec)
Definition: OpalData.cpp:452
void applyEmissModelNone(double &pz)
boost::numeric::ublas::vector< double, std::vector< double > > vector_t
Dense vector type definition.
virtual bool raw_send(void *, int, int, int)
Definition: Communicate.h:192
double getEnergyBinDeltaT()
void writeOutFileEmission()
void setPBins(PartBins *pbin)
Vector_t mBinomial_m
Definition: Distribution.h:469
ParticleAttrib< int > TriID
and that you know you can do these things To protect your we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights These restrictions translate to certain responsibilities for you if you distribute copies of the or if you modify it For if you distribute copies of such a whether gratis or for a you must give the recipients all the rights that you have You must make sure that receive or can get the source code And you must show them these terms so they know their rights We protect your rights with two distribute and or modify the software for each author s protection and we want to make certain that everyone understands that there is no warranty for this free software If the software is modified by someone else and passed we want its recipients to know that what they have is not the so that any problems introduced by others will not reflect on the original authors reputations any free program is threatened constantly by software patents We wish to avoid the danger that redistributors of a free program will individually obtain patent in effect making the program proprietary To prevent we have made it clear that any patent must be licensed for everyone s free use or not licensed at all The precise terms and conditions for distribution and modification follow GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR DISTRIBUTION AND MODIFICATION This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License The refers to any such program or and a work based on the Program means either the Program or any derivative work under copyright a work containing the Program or a portion of it
Definition: LICENSE:43
PETE_TBTree< FnCopysign, PETE_Scalar< Vektor< T1, Dim > >, typename T2::PETE_Expr_t > copysign(const Vektor< T1, Dim > &l, const PETE_Expr< T2 > &r)
void barrier(void)
std::string getString(const Attribute &attr)
Get string value.
Definition: Attributes.cpp:343
constexpr double two_pi
The value of .
Definition: Physics.h:33
void reflectDistribution(size_t &numberOfParticles)
constexpr double m2mm
Definition: Units.h:26
std::string rngtype
random number generator
Definition: Options.cpp:79
void printDistMatchedGauss(Inform &os) const
double sigmaTRise_m
Definition: Distribution.h:462
void printEmissionModel(Inform &os) const
double tRise_m
time binned distribution with thermal energy
Definition: Distribution.h:487
std::vector< double > & getYDist()
void setPRinit(double prinit)
Definition: Cyclotron.cpp:131
PETE_TUTree< FnAbs, typename T::PETE_Expr_t > abs(const PETE_Expr< T > &l)
virtual void execute()
Execute the algorithm on the attached beam line.
void define(Object *newObject)
Define a new object.
Definition: OpalData.cpp:489
ParticleAttrib< Vector_t > P
size_t emitParticles(PartBunchBase< double, 3 > *beam, double eZ)
constexpr double epsilon_0
The permittivity of vacuum in As/Vm.
Definition: Physics.h:51
unsigned nPeaks_m
Definition: Distribution.h:474
ParticleAttrib< ParticleType > PType
std::vector< double > & getXDist()
static MPI_Comm getComm()
Definition: IpplInfo.h:152
static int myNode()
Definition: IpplInfo.cpp:691
gsl_rng * randGen_m
Definition: Distribution.h:422
void generateAstraFlattopT(size_t numberOfParticles)
virtual std::string getFieldMapFN() const
Definition: Cyclotron.cpp:183
DistributionType distrTypeT_m
Distribution type strings.
Definition: Distribution.h:387
bool cZero
If true create symmetric distribution.
Definition: Options.cpp:77
void fillParticleBins()
boost::numeric::ublas::matrix< double > matrix_t
Dense matrix type definition.
Vektor< double, 3 > Vector_t
Definition: Vektor.h:6
bool cloTuneOnly
Do closed orbit and tune calculation only.
Definition: Options.cpp:91
int getLastEmittedEnergyBin()
IpplTimings::TimerRef distrCreate_m
void setDistParametersBinomial(double massIneV)
void setGamma(double gamma)
Definition: PartBins.h:94
ParticleAttrib< Vector_t > Ef
#define ERRORMSG(msg)
Definition: IpplInfo.h:350
static BeamSequence * find(const std::string &name)
Find a BeamSequence by name.
void printEmissionModelNonEquil(Inform &os) const
RealDiracMatrix::matrix_t matrix_t
Type for storing maps.
std::vector< double > & getBGyDist()
int next_tag(int t, int s=1000)
Definition: TagMaker.h:39
std::vector< double > pzWrite_m
Definition: Distribution.h:454
ParticleAttrib< double > M
#define WARNMSG(msg)
Definition: IpplInfo.h:349
void printDistFlattop(Inform &os) const
long long getLocalTrackStep() const
std::vector< double > pzDist_m
Definition: Distribution.h:446
void adjustPhaseSpace(double massIneV)
void diagonalize(matrix_t &, sparse_matrix_t &, sparse_matrix_t &)
void generateFlattopT(size_t numberOfParticles)
int numberOfEnergyBins_m
Definition: Distribution.h:414
LaserProfile * laserProfile_m
Definition: Distribution.h:480
constexpr double SMALLESTCUTOFF
An abstract sequence of beam line components.
Definition: Beamline.h:34
FmtFlags_t setf(FmtFlags_t setbits, FmtFlags_t field)
Definition: Inform.h:101
void setAttributes()
void createOpalT(PartBunchBase< double, 3 > *beam, std::vector< Distribution * > addedDistributions, size_t &numberOfParticles)
Tps< T > exp(const Tps< T > &x)
Exponential.
Definition: TpsMath.h:165
double get_sPos() const
void generateTransverseGauss(size_t numberOfParticles)
constexpr double rad2mrad
Definition: Units.h:137
void printEmissionModelAstra(Inform &os) const
void printEnergyBins(Inform &os) const
std::vector< double > getRealArray(const Attribute &attr)
Get array value.
Definition: Attributes.cpp:294
void chooseInputMomentumUnits(InputMomentumUnits inputMoUnits)
void generateFlattopLaserProfile(size_t numberOfParticles)
int precision() const
Definition: Inform.h:112
double getQ() const
Access to reference data.
constexpr double pi
The value of .
Definition: Physics.h:30
Attribute makeRealArray(const std::string &name, const std::string &help)
Create real array attribute.
Definition: Attributes.cpp:289
virtual Beamline * fetchLine() const =0
Return the embedded CLASSIC beam line.
size_t getNumberOfParticlesInFile(std::ifstream &inputFile)
#define INFOMSG(msg)
Definition: IpplInfo.h:348
Inform & endl(Inform &inf)
Definition: Inform.cpp:42
virtual void readStep(PartBunchBase< double, 3 > *, h5_ssize_t firstParticle, h5_ssize_t lastParticle)=0
double getdT() const
std::vector< size_t > binWrite_m
Definition: Distribution.h:455
double getM() const
PETE_TBTree< FnArcTan2, PETE_Scalar< Vektor< T1, Dim > >, typename T2::PETE_Expr_t > atan2(const Vektor< T1, Dim > &l, const PETE_Expr< T2 > &r)
T::PETE_Expr_t::PETE_Return_t min(const PETE_Expr< T > &expr, NDIndex< D > &loc)
Definition: ReductionLoc.h:76
double getEmissionDeltaT()
std::vector< double > & getBGxDist()
Vector_t sigmaP_m
Definition: Distribution.h:466
virtual void boundp()
T::PETE_Expr_t::PETE_Return_t max(const PETE_Expr< T > &expr, NDIndex< D > &loc)
Definition: ReductionLoc.h:84
void setTEmission(double t)
void printDist(Inform &os, size_t numberOfParticles) const
InputMomentumUnits inputMoUnits_m
Definition: Distribution.h:461
virtual ~Distribution()
void createDistributionGauss(size_t numberOfParticles, double massIneV)
ParticleAttrib< Vector_t > Bf
Vector_t sigmaR_m
Definition: Distribution.h:465
void writeOutFileInjection()
clearpage the user may choose between constant or variable radius This model includes fringe fields L
Definition: multipole_t.tex:7
void checkIfEmitted()
void createMatchedGaussDistribution(size_t numberOfParticles, double massIneV, double charge)
double getEmissionTimeShift() const
bool getBool(const Attribute &attr)
Return logical value.
Definition: Attributes.cpp:100
static Distribution * find(const std::string &name)
size_t totalNumberEmittedParticles_m
Definition: Distribution.h:438
static void startTimer(TimerRef t)
Definition: IpplTimings.h:187
std::string::iterator iterator
Definition: MSLang.h:15
void createDistributionMultiGauss(size_t numberOfParticles, double massIneV)
void printDistMultiGauss(Inform &os) const
Attribute makeBool(const std::string &name, const std::string &help)
Make logical attribute.
Definition: Attributes.cpp:90
std::vector< double > xWrite_m
Definition: Distribution.h:449
void shiftDistCoordinates(double massIneV)
double getInitialGamma() const
static int getNodes()
Definition: IpplInfo.cpp:670
int currentEnergyBin_m
Definition: Distribution.h:412
double getT() const
std::vector< double > tOrZWrite_m
Definition: Distribution.h:453
void registerOwnership(const AttributeHandler::OwnerType &itsClass) const
Definition: Object.cpp:191
T::PETE_Expr_t::PETE_Return_t sum(const PETE_Expr< T > &expr)
Definition: PETE.h:1111
size_t getTotalNum() const
void initializeBeam(PartBunchBase< double, 3 > *beam)
static Communicate * Comm
Definition: IpplInfo.h:84
The base class for all OPAL exceptions.
Definition: OpalException.h:28
std::string getAuxiliaryOutputDirectory() const
get the name of the the additional data directory
Definition: OpalData.cpp:666
int numberOfSampleBins_m
Definition: Distribution.h:416
void fillEBinHistogram()
std::string laserImageName_m
Definition: Distribution.h:478
Vector_t cutoffP_m
Definition: Distribution.h:468
void setupParticleBins(double massIneV, PartBunchBase< double, 3 > *beam)
void createDistributionBinomial(size_t numberOfParticles, double massIneV)
void scaleDistCoordinates()
void setEnergyBins(int numberOfEnergyBins)
ParticleType getPType() const
size_t getLocalNum() const
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
int getNumberOfEnergyBins()
T::PETE_Expr_t::PETE_Return_t prod(const PETE_Expr< T > &expr)
Definition: PETE.h:1121
The base class for all OPAL beam lines and sequences.
Definition: BeamSequence.h:32
Definition: Inform.h:42
std::vector< double > yDist_m
Definition: Distribution.h:443
void generateLongFlattopT(size_t numberOfParticles)
void setDistParametersGauss(double massIneV)
IpplTimings::TimerRef distrReload_m
timer for IC, can not be in Distribution.h
std::vector< std::vector< double > > additionalRNs_m
Upper limit on emission energy distribution (eV).
Definition: Distribution.h:435
std::string outFilename_m
Definition: Distribution.h:493
void checkFileMomentum(double momentumTol)
double sigmaFall_m
Definition: Distribution.h:490
void generateMatchedGauss(const SigmaGenerator::matrix_t &, size_t numberOfParticles, double massIneV)
void printDistBinomial(Inform &os) const
virtual int raw_receive(char *, int, int &, int &)
Definition: Communicate.h:200
double currentEmissionTime_m
Definition: Distribution.h:411
Attribute makeString(const std::string &name, const std::string &help)
Make string attribute.
Definition: Attributes.cpp:332
void allreduce(const T *input, T *output, int count, Op op)
Definition: GlobalComm.hpp:510
long long getGlobalTrackStep() const
void calcPartPerDist(size_t numberOfParticles)
double avrgpz_m
Definition: Distribution.h:458
double sigmaRise_m
Definition: Distribution.h:489
boost::numeric::ublas::compressed_matrix< double, boost::numeric::ublas::row_major > sparse_matrix_t
Sparse matrix type definition.
unsigned int numberOfDistributions_m
List of Distribution types.
Definition: Distribution.h:389
gsl_histogram * energyBinHist_m
Distribution energy bins.
Definition: Distribution.h:419
std::vector< double > pxDist_m
Definition: Distribution.h:442
ParticleAttrib< double > Q
std::string distT_m
Definition: Distribution.h:386
virtual Distribution * clone(const std::string &name)
Return a clone.
DistributionType
Definition: Distribution.h:50
void fill(std::vector< double > &p)
Add a particle to the temporary container.
Definition: PartBins.h:50
#define X(arg)
Definition: fftpack.cpp:112
constexpr double kB
Boltzman&#39;s constant in eV/K.
Definition: Physics.h:60
void doRestartOpalT(PartBunchBase< double, 3 > *p, size_t Np, int restartStep, H5PartWrapper *h5wrapper)
void injectBeam(PartBunchBase< double, 3 > *beam)
virtual int raw_probe_receive(char *&, int &, int &)
Definition: Communicate.h:208
double tFall_m
Definition: Distribution.h:488
constexpr double q_e
The elementary charge in As.
Definition: Physics.h:69
void create(size_t &numberOfParticles, double massIneV, double charge)
size_t getNumOfLocalParticlesToCreate(size_t n)
void applyEmissModelAstra(double &px, double &py, double &pz, std::vector< double > &additionalRNs)
void setupEmissionModelAstra(PartBunchBase< double, 3 > *beam)
Vector_t cutoffR_m
Definition: Distribution.h:467
#define IPPL_APP_TAG2
Definition: Tags.h:95
constexpr double GeV2MeV
Definition: Units.h:80
#define IPPL_APP_CYCLE
Definition: Tags.h:103
void eraseTOrZDist()
ParticleAttrib< double > dt
const std::string & getOpalName() const
Return object name.
Definition: Object.cpp:310
void printDistGauss(Inform &os) const
virtual void readHeader()=0
void sampleUniformDisk(gsl_qrng *quasiRandGen2D, double &x1, double &x2)
constexpr double rad2deg
Definition: Units.h:146
void setDistParametersFlattop(double massIneV)
std::vector< Attribute > itsAttr
The object attributes.
Definition: Object.h:216
double getChargePerParticle() const
get the macro particle charge
double getMassPerParticle() const
Tps< T > cos(const Tps< T > &x)
Cosine.
Definition: TpsMath.h:129
PETE_TUTree< FnArcCos, typename T::PETE_Expr_t > acos(const PETE_Expr< T > &l)
Definition: PETE.h:725
std::vector< double > yWrite_m
Definition: Distribution.h:451
constexpr double alpha
The fine structure constant, no dimension.
Definition: Physics.h:72
constexpr double m_e
The electron rest mass in GeV.
Definition: Physics.h:78
Tps< T > log(const Tps< T > &x)
Natural logarithm.
Definition: TpsMath.h:182
constexpr double A2mA
Definition: Units.h:131
size_t totalNumberParticles_m
Definition: Distribution.h:437
const std::string name
ParticlePos_t & R
std::string combineFilePath(std::initializer_list< std::string > ilist)
Definition: Util.cpp:197
void generateFlattopZ(size_t numberOfParticles)
double sigmaTFall_m
Definition: Distribution.h:463
void setNumBunch(short n)
void setRinit(double rinit)
Definition: Cyclotron.cpp:123
double cathodeWorkFunc_m
Definition: Distribution.h:429
std::vector< double > xDist_m
Definition: Distribution.h:441
double getTEmission()
size_t findEBin(double tOrZ)
std::vector< double > pyDist_m
Definition: Distribution.h:444
double getReal(const Attribute &attr)
Return real value.
Definition: Attributes.cpp:252
ParticleIndex_t & ID
void checkParticleNumber(size_t &numberOfParticles)
void setupEmissionModelNonEquil()
void addProblemCharacteristicValue(const std::string &name, unsigned int value)
Definition: OpalData.cpp:756
double laserIntensityCut_m
Definition: Distribution.h:479
virtual double getCyclHarm() const
Definition: Cyclotron.cpp:297
void getXY(double &x, double &y)
void addDistributions()
std::string getInputBasename()
get input file name without extension
Definition: OpalData.cpp:674
void createOpalCycl(PartBunchBase< double, 3 > *beam, size_t numberOfParticles, double current, const Beamline &bl)
void setEmissionTime(double &maxT, double &minT)
The base class for all OPAL definitions.
Definition: Definition.h:30
void createDistributionFromFile(size_t numberOfParticles, double massIneV)
constexpr double GeV2eV
Definition: Units.h:68
void createDistributionFlattop(size_t numberOfParticles, double massIneV)
double getP() const
Inform & level3(Inform &inf)
Definition: Inform.cpp:47
bool getIfDistEmitting()
Object * find(const std::string &name)
Find entry.
Definition: OpalData.cpp:571
virtual double getPHIinit() const
Definition: Cyclotron.cpp:143
constexpr double e
The value of .
Definition: Physics.h:39
void iterateEmittedBin(int binNumber)
Inform & level2(Inform &inf)
Definition: Inform.cpp:46
void create(size_t M)
PETE_TUTree< FnCeil, typename T::PETE_Expr_t > ceil(const PETE_Expr< T > &l)
Definition: PETE.h:728
void shiftBeam(double &maxTOrZ, double &minTOrZ)
Vector_t pmean_m
Total thermal momentum.
Definition: Distribution.h:426
void setDistParametersMultiGauss(double massIneV)
ParticleAttrib< int > Bin
void doRestartOpalCycl(PartBunchBase< double, 3 > *p, size_t Np, int restartStep, const int specifiedNumBunch, H5PartWrapper *h5wrapper)
short getNumBunch() const
double getMaxTOrZ()
PETE_TUTree< FnArcSin, typename T::PETE_Expr_t > asin(const PETE_Expr< T > &l)
Definition: PETE.h:726
void setSigmaP_m(double massIneV)
void printDistFromFile(Inform &os) const
int currentSampleBin_m
Definition: Distribution.h:413
double emitEnergyUpperLimit_m
Cathode temperature (K).
Definition: Distribution.h:433
double getWeight()
void applyEmissionModel(double lowEnergyLimit, double &px, double &py, double &pz, std::vector< double > &additionalRNs)
Tps< T > pow(const Tps< T > &x, int y)
Integer power.
Definition: TpsMath.h:76
virtual void update()
Update this object.
Tps< T > sin(const Tps< T > &x)
Sine.
Definition: TpsMath.h:111
virtual void execute()
Execute the command.
static void stopTimer(TimerRef t)
Definition: IpplTimings.h:192
gsl_qrng * selectRandomGenerator(std::string, unsigned int dimension)
Select and allocate gsl random number generator.
std::vector< size_t > particlesPerDist_m
Definition: Distribution.h:399
void writeOutFileHeader()
EmissionModel emissionModel_m
Emission Model.
Definition: Distribution.h:402
void printEmissionModelNone(Inform &os) const
void setupEmissionModelNone(PartBunchBase< double, 3 > *beam)
void checkEmissionParameters()
void sortArray()
Definition: PartBins.cpp:116
matrix_t correlationMatrix_m
Definition: Distribution.h:470
Inform & level1(Inform &inf)
Definition: Inform.cpp:45
virtual bool predecessorIsSameFlavour() const =0
static const double percentTEmission_m
Definition: Distribution.h:406
double pTotThermal_m
Random number generator.
Definition: Distribution.h:425
double sepPeaks_m
Definition: Distribution.h:473
Inform * gmsg
Definition: Main.cpp:70
std::vector< double > tOrZDist_m
Definition: Distribution.h:445
double cutoff_m
Definition: Distribution.h:491
end
Definition: multipole_t.tex:9
std::vector< double > pxWrite_m
Definition: Distribution.h:450
void setupEmissionModel(PartBunchBase< double, 3 > *beam)
double getBetaGamma(double Ekin, double mass)
Definition: Util.h:61
std::vector< double > & getTOrZDist()
int width() const
Definition: Inform.h:108
double cathodeTemp_m
Cathode material Fermi energy (eV).
Definition: Distribution.h:432
std::vector< Distribution * > addedDistributions_m
Vector of distributions to be added to this distribution.
Definition: Distribution.h:398
constexpr double eV2MeV
Definition: Units.h:77
double getMomentumTolerance() const
PartBins * energyBins_m
Definition: Distribution.h:418
bool reduce(Communicate &, InputIterator, InputIterator, OutputIterator, const ReduceOp &, bool *IncludeVal=0)
Definition: GlobalComm.hpp:55
int getSteptoLastInj() const
virtual bool canReplaceBy(Object *object)
Distribution can only be replaced by another distribution.
virtual double get(double rand)
virtual double getFMLowE() const
Definition: Cyclotron.cpp:364
Inform & printInfo(Inform &os) const
double tEmission_m
Emission parameters.
Definition: Distribution.h:405