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