FLAME  devel
 All Classes Functions Variables Typedefs Enumerations Pages
moment.cpp
1 
2 #include <fstream>
3 
4 #include <limits>
5 
6 #include <boost/lexical_cast.hpp>
7 
8 #include <boost/numeric/ublas/vector_proxy.hpp>
9 #include <boost/numeric/ublas/matrix_proxy.hpp>
10 
11 #include "flame/constants.h"
12 
13 #include "flame/moment.h"
14 #include "flame/moment_sup.h"
15 #include "flame/rf_cavity.h"
16 #include "flame/chg_stripper.h"
17 
18 #include "flame/h5loader.h"
19 
20 #define sqr(x) ((x)*(x))
21 #define cube(x) ((x)*(x)*(x))
22 
23 namespace ub = boost::numeric::ublas;
24 
25 namespace {
26 
27 // ARR should be an array-like object (std::vector or or ublas vector or matrix storage)
28 // fill 'to' using config.get<>(name)
29 // 'T' selects throw error (true) or return boolean (false)
30 template<typename ARR>
31 bool load_storage(ARR& to, const Config& conf, const std::string& name, bool T=true)
32 {
33  try{
34  const std::vector<double>& val(conf.get<std::vector<double> >(name));
35  if(to.size()!=val.size()) {
36  throw std::invalid_argument(SB()<<"Array "<<name<<" must have "<<to.size()<<" elements, not "<<val.size());
37  }
38  std::copy(val.begin(), val.end(), to.begin());
39 
40  return true;
41  }catch(std::exception&){
42  if(T)
43  throw std::invalid_argument(name+" not defined or has wrong type (must be vector)");
44  else
45  return false;
46  // default to identity
47  }
48 }
49 
50 } // namespace
51 
52 std::ostream& operator<<(std::ostream& strm, const Particle& P)
53 {
54  strm
55  <<"IonZ="<<std::scientific << std::setprecision(10)<<P.IonZ
56  <<" IonQ="<<P.IonQ
57  <<" IonEs="<<P.IonEs
58  <<" IonEk="<<P.IonEk
59  <<" SampleIonK="<<P.SampleIonK
60  <<" phis="<<P.phis
61  <<" IonW="<<P.IonW
62  <<" gamma="<<P.gamma
63  <<" beta="<<P.beta
64  <<" bg="<<P.bg
65  ;
66  return strm;
67 }
68 
69 MomentState::MomentState(const Config& c)
70  :StateBase(c)
71  ,ref()
72  ,real()
73  ,moment0_env(maxsize, 0e0)
74  ,moment0_rms(maxsize, 0e0)
75  ,moment1_env(boost::numeric::ublas::identity_matrix<double>(maxsize))
76 {
77  // hack. getArray() promises that returned pointers will remain valid for our lifetime.
78  // This may not be true if std::vectors are resized.
79  // Reserve for up to 10 states and hope for the best...
80  // either need to provide real limit to max. states, or change getArray() iface
81  real.reserve(10);
82 
83  double icstate_f = 0.0;
84  bool have_cstate = c.tryGet<double>("cstate", icstate_f);
85  size_t icstate = (size_t)icstate_f;
86 
87  std::string vectorname(c.get<std::string>("vector_variable", "moment0"));
88  std::string matrixname(c.get<std::string>("matrix_variable", "initial"));
89 
90  std::vector<double> ics, nchg;
91  bool have_ics = c.tryGet<std::vector<double> >("IonChargeStates", ics);
92 
93  ref.IonEs = c.get<double>("IonEs", 0e0),
94  ref.IonEk = c.get<double>("IonEk", 0e0);
95  ref.recalc();
96 
97  if(!have_ics) {
98  ref.IonZ = c.get<double>("IonZ", 0e0);
99  ref.IonQ = c.get<double>("IonQ", 1e0);
100 
101  ics.push_back(ref.IonZ);
102  nchg.push_back(ref.IonQ);
103 
104  } else {
105  if(ics.empty())
106  throw std::invalid_argument("IonChargeStates w/ length 0");
107  if(icstate>=ics.size())
108  throw std::invalid_argument("IonChargeStates[cstate] is out of bounds");
109 
110  nchg = c.get<std::vector<double> >("NCharge");
111  if(nchg.size()!=ics.size())
112  throw std::invalid_argument("NCharge[] and IonChargeStates[] must have equal length");
113 
114  ref.IonZ = ics[0];
115  ref.IonQ = nchg[0];
116  }
117 
118  /* Possible configurations
119  * 1. Neither 'cstate' nor 'IonChargeStates' defined (empty Config).
120  * No charge states, must go through source element to be useful
121  * 2. 'IonChargeStates' defined, but not 'cstate'.
122  * Load all charge states
123  * 3. 'cstate' and 'IonChargeStates' defined.
124  * Load a single charge state
125  */
126  if(!have_cstate && !have_ics) {
127  // no-op
128  } else if(!have_cstate && have_ics) {
129  // many charge states
130  icstate = 0;
131 
132  } else if(have_cstate && have_ics) {
133  // single charge state
134 
135  // drop other than selected state
136  ics[0] = ics[icstate];
137  nchg[0] = nchg[icstate];
138  ics.resize(1);
139  nchg.resize(1);
140 
141  } else {
142  throw std::invalid_argument("MomentState: must define IonChargeStates and NCharge when cstate is set");
143  }
144 
145  if(have_ics) {
146  real.resize(ics.size());
147  moment0.resize(ics.size());
148  moment1.resize(ics.size());
149 
150  for(size_t i=0; i<ics.size(); i++) {
151  std::string num(boost::lexical_cast<std::string>(icstate+i));
152 
153  moment0[i].resize(maxsize);
154  moment1[i].resize(maxsize, maxsize);
155  moment1[i] = boost::numeric::ublas::identity_matrix<double>(maxsize);
156 
157  load_storage(moment0[i].data(), c, vectorname+num);
158  load_storage(moment1[i].data(), c, matrixname+num);
159 
160  real[i] = ref;
161 
162  real[i].IonZ = ics[i];
163  real[i].IonQ = nchg[i];
164 
165  real[i].phis = moment0[i][PS_S];
166  real[i].IonEk += moment0[i][PS_PS]*MeVtoeV;
167 
168  real[i].recalc();
169  }
170  } else {
171  real.resize(1); // hack, ensure at least one element so getArray() can return some pointer
172  real[0] = ref;
173 
174  moment0.resize(1);
175  moment1.resize(1);
176  moment0[0].resize(maxsize);
177  moment1[0].resize(maxsize, maxsize);
178 
179  load_storage(moment0[0].data(), c, vectorname, false);
180  load_storage(moment1[0].data(), c, matrixname, false);
181  }
182 
183  calc_rms();
184 }
185 
186 MomentState::~MomentState() {}
187 
188 void MomentState::calc_rms()
189 {
190  assert(real.size()>0);
191  assert(moment0_env.size()==maxsize);
192  assert(moment0_rms.size()==maxsize);
193  assert(moment1_env.size1()==maxsize);
194  assert(moment1_env.size2()==maxsize);
195 
196  double totQ = 0.0;
197  for(size_t n=0; n<real.size(); n++) {
198  totQ += real[n].IonQ;
199  if(n==0)
200  noalias(moment0_env) = moment0[n]*real[n].IonQ;
201  else
202  noalias(moment0_env) += moment0[n]*real[n].IonQ;
203  }
204  moment0_env /= totQ;
205 
206  // Zero orbit terms.
207  moment1_env = ub::zero_matrix<double>(state_t::maxsize);
208  boost::numeric::ublas::slice S(0, 1, 6);
209  for(size_t n=0; n<real.size(); n++) {
210  const double Q = real[n].IonQ;
211  state_t::vector_t m0diff(moment0[n]-moment0_env);
212 
213  ub::project(moment1_env, S, S) += ub::project(Q*(moment1[n]+ub::outer_prod(m0diff, m0diff)), S, S);
214  }
215  moment1_env /= totQ;
216 
217  for(size_t j=0; j<maxsize; j++) {
218  moment0_rms[j] = sqrt(moment1_env(j,j));
219  }
220 }
221 
222 MomentState::MomentState(const MomentState& o, clone_tag t)
223  :StateBase(o, t)
224  ,ref(o.ref)
225  ,real(o.real)
226  ,moment0(o.moment0)
227  ,moment1(o.moment1)
228  ,moment0_env(o.moment0_env)
229  ,moment0_rms(o.moment0_rms)
230  ,moment1_env(o.moment1_env)
231 {}
232 
233 void MomentState::assign(const StateBase& other)
234 {
235  const MomentState *O = dynamic_cast<const MomentState*>(&other);
236  if(!O)
237  throw std::invalid_argument("Can't assign State: incompatible types");
238  ref = O->ref;
239  real = O->real;
240  moment0 = O->moment0;
241  moment1 = O->moment1;
242  moment0_env = O->moment0_env;
243  moment0_rms = O->moment0_rms;
244  moment1_env = O->moment1_env;
245  StateBase::assign(other);
246 }
247 
248 void MomentState::show(std::ostream& strm, int level) const
249 {
250  if(real.empty()) {
251  strm<<"State: empty";
252  return;
253  }
254 
255  if(level<=0) {
256  strm<<"State: moment0 mean="<<moment0_env;
257  }
258  if(level>=1) {
259  strm << std::scientific << std::setprecision(8)
260  << "\nState:\n energy [eV] =\n" << std::setw(20) << real[0].IonEk << "\n moment0 mean =\n ";
261  for (size_t k = 0; k < MomentState::maxsize; k++)
262  strm << std::scientific << std::setprecision(10) << std::setw(18) << moment0_env(k) << ",";
263  strm << std::scientific << std::setprecision(10)
264  << "\n moment0 rms =\n ";
265  for (size_t k = 0; k < MomentState::maxsize; k++)
266  strm << std::scientific << std::setprecision(10) << std::setw(18) << moment0_rms(k) << ",";
267  strm << "\n moment1 mean =\n";
268  for (size_t j = 0; j < MomentState::maxsize; j++) {
269  strm << " ";
270  for (size_t k = 0; k < MomentState::maxsize; k++) {
271  strm << std::scientific << std::setprecision(10) << std::setw(18) << moment1_env(j, k) << ",";
272  }
273  if (j < MomentState::maxsize-1) strm << "\n";
274  }
275  }
276  if(level>=2) {
277  strm<< "\n Reference state:\n "<<ref<<"\n";
278  for(size_t k=0; k<size(); k++) {
279  strm<<" Charge state "<<k<<"\n"
280  " "<<real[k]<<"\n"
281  " moment0 "<<moment0[k]<<"\n";
282  if(level>=3)
283  strm<<
284  " moment1 "<<moment1[k]<<"\n";
285  }
286  }
287 }
288 
289 bool MomentState::getArray(unsigned idx, ArrayInfo& Info) {
290  unsigned I=0;
291  if(idx==I++) {
292  Info.name = "moment1_env";
293  Info.ptr = &moment1_env(0,0);
294  Info.type = ArrayInfo::Double;
295  Info.ndim = 2;
296  Info.dim[0] = moment1_env.size1();
297  Info.dim[1] = moment1_env.size2();
298  Info.stride[0] = sizeof(double)*moment1_env.size2();
299  Info.stride[1] = sizeof(double);
300  return true;
301  } else if(idx==I++) {
302  /* Slight evilness here
303  * moment0 is vector of ublas::matrix
304  * We assume ublax::matrix uses storage bounded_array<>, and that this storage
305  * is really a C array, which means that everything is part of one big allocation.
306  * Further we assume that all entries in the vector have the same shape.
307  * If this isn't the case, then SIGSEGV here we come...
308  */
309  static_assert(sizeof(moment1[0])>=sizeof(double)*maxsize*maxsize,
310  "storage assumption violated");
311  Info.name = "moment1";
312  Info.ptr = &moment1[0](0,0);
313  Info.type = ArrayInfo::Double;
314  Info.ndim = 3;
315  Info.dim[0] = moment1[0].size1();
316  Info.dim[1] = moment1[0].size2();
317  Info.dim[2] = moment1.size();
318  Info.stride[0] = sizeof(double)*moment1_env.size2();
319  Info.stride[1] = sizeof(double);
320  Info.stride[2] = sizeof(moment1[0]);
321  return true;
322  } else if(idx==I++) {
323  Info.name = "moment0_env";
324  Info.ptr = &moment0_env(0);
325  Info.type = ArrayInfo::Double;
326  Info.ndim = 1;
327  Info.dim[0] = moment0_env.size();
328  Info.stride[0] = sizeof(double);
329  return true;
330  } else if(idx==I++) {
331  Info.name = "moment0_rms";
332  Info.ptr = &moment0_rms(0);
333  Info.type = ArrayInfo::Double;
334  Info.ndim = 1;
335  Info.dim[0] = moment0_rms.size();
336  Info.stride[0] = sizeof(double);
337  return true;
338  } else if(idx==I++) {
339  // more evilness here, see above
340  static_assert(sizeof(moment0[0])>=sizeof(double)*maxsize,
341  "storage assumption violated");
342  Info.name = "moment0";
343  Info.ptr = &moment0[0][0];
344  Info.type = ArrayInfo::Double;
345  Info.ndim = 2;
346  Info.dim[0] = moment0[0].size();
347  Info.dim[1] = moment0.size();
348  Info.stride[0] = sizeof(double);
349  Info.stride[1] = sizeof(moment0[0]);
350  return true;
351  } else if(idx==I++) {
352  Info.name = "ref_IonZ";
353  Info.ptr = &ref.IonZ;
354  Info.type = ArrayInfo::Double;
355  Info.ndim = 0;
356  return true;
357  } else if(idx==I++) {
358  Info.name = "ref_IonQ";
359  Info.ptr = &ref.IonQ;
360  Info.type = ArrayInfo::Double;
361  Info.ndim = 0;
362  return true;
363  } else if(idx==I++) {
364  Info.name = "ref_IonEs";
365  Info.ptr = &ref.IonEs;
366  Info.type = ArrayInfo::Double;
367  Info.ndim = 0;
368  return true;
369  } else if(idx==I++) {
370  Info.name = "ref_IonW";
371  Info.ptr = &ref.IonW;
372  Info.type = ArrayInfo::Double;
373  Info.ndim = 0;
374  return true;
375  } else if(idx==I++) {
376  Info.name = "ref_gamma";
377  Info.ptr = &ref.gamma;
378  Info.type = ArrayInfo::Double;
379  Info.ndim = 0;
380  return true;
381  } else if(idx==I++) {
382  Info.name = "ref_beta";
383  Info.ptr = &ref.beta;
384  Info.type = ArrayInfo::Double;
385  Info.ndim = 0;
386  return true;
387  } else if(idx==I++) {
388  Info.name = "ref_bg";
389  Info.ptr = &ref.bg;
390  Info.type = ArrayInfo::Double;
391  Info.ndim = 0;
392  return true;
393  } else if(idx==I++) {
394  Info.name = "ref_SampleIonK";
395  Info.ptr = &ref.SampleIonK;
396  Info.type = ArrayInfo::Double;
397  Info.ndim = 0;
398  return true;
399  } else if(idx==I++) {
400  Info.name = "ref_phis";
401  Info.ptr = &ref.phis;
402  Info.type = ArrayInfo::Double;
403  Info.ndim = 0;
404  return true;
405  } else if(idx==I++) {
406  Info.name = "ref_IonEk";
407  Info.ptr = &ref.IonEk;
408  Info.type = ArrayInfo::Double;
409  Info.ndim = 0;
410  return true;
411  } else if(idx==I++) {
412  Info.name = "IonZ";
413  Info.ptr = &real[0].IonZ;
414  Info.type = ArrayInfo::Double;
415  Info.ndim = 1;
416  Info.dim [0] = real.size();
417  Info.stride[0] = sizeof(real[0]);
418  // Note: this array is discontigious as we reference a single member from a Particle[]
419  return true;
420  } else if(idx==I++) {
421  Info.name = "IonEs";
422  Info.ptr = &real[0].IonEs;
423  Info.type = ArrayInfo::Double;
424  Info.ndim = 1;
425  Info.dim [0] = real.size();
426  Info.stride[0] = sizeof(real[0]);
427  return true;
428  } else if(idx==I++) {
429  Info.name = "IonW";
430  Info.ptr = &real[0].IonW;
431  Info.type = ArrayInfo::Double;
432  Info.ndim = 1;
433  Info.dim [0] = real.size();
434  Info.stride[0] = sizeof(real[0]);
435  return true;
436  } else if(idx==I++) {
437  Info.name = "gamma";
438  Info.ptr = &real[0].gamma;
439  Info.type = ArrayInfo::Double;
440  Info.ndim = 1;
441  Info.dim [0] = real.size();
442  Info.stride[0] = sizeof(real[0]);
443  return true;
444  } else if(idx==I++) {
445  Info.name = "beta";
446  Info.ptr = &real[0].beta;
447  Info.type = ArrayInfo::Double;
448  Info.ndim = 1;
449  Info.dim [0] = real.size();
450  Info.stride[0] = sizeof(real[0]);
451  return true;
452  } else if(idx==I++) {
453  Info.name = "bg";
454  Info.ptr = &real[0].bg;
455  Info.type = ArrayInfo::Double;
456  Info.ndim = 1;
457  Info.dim [0] = real.size();
458  Info.stride[0] = sizeof(real[0]);
459  return true;
460  } else if(idx==I++) {
461  Info.name = "SampleIonK";
462  Info.ptr = &real[0].SampleIonK;
463  Info.type = ArrayInfo::Double;
464  Info.ndim = 1;
465  Info.dim [0] = real.size();
466  Info.stride[0] = sizeof(real[0]);
467  return true;
468  } else if(idx==I++) {
469  Info.name = "phis";
470  Info.ptr = &real[0].phis;
471  Info.type = ArrayInfo::Double;
472  Info.ndim = 1;
473  Info.dim [0] = real.size();
474  Info.stride[0] = sizeof(real[0]);
475  return true;
476  } else if(idx==I++) {
477  Info.name = "IonEk";
478  Info.ptr = &real[0].IonEk;
479  Info.type = ArrayInfo::Double;
480  Info.ndim = 1;
481  Info.dim [0] = real.size();
482  Info.stride[0] = sizeof(real[0]);
483  return true;
484  } else if(idx==I++) {
485  Info.name = "IonQ";
486  Info.ptr = &real[0].IonQ;
487  Info.type = ArrayInfo::Double;
488  Info.ndim = 1;
489  Info.dim [0] = real.size();
490  Info.stride[0] = sizeof(real[0]);
491  // Note: this array is discontigious as we reference a single member from a Particle[]
492  return true;
493  }
494  return StateBase::getArray(idx-I, Info);
495 }
496 
497 MomentElementBase::MomentElementBase(const Config& c)
498  :ElementVoid(c)
499  ,dx (c.get<double>("dx", 0e0)*MtoMM)
500  ,dy (c.get<double>("dy", 0e0)*MtoMM)
501  ,pitch(c.get<double>("pitch", 0e0))
502  ,yaw (c.get<double>("yaw", 0e0))
503  ,roll (c.get<double>("roll", 0e0))
504  ,skipcache(c.get<double>("skipcache", 0.0)!=0.0)
505  ,scratch(state_t::maxsize, state_t::maxsize)
506 {
507 }
508 
509 MomentElementBase::~MomentElementBase() {}
510 
512 {
513  const MomentElementBase *O = static_cast<const MomentElementBase*>(other);
514  last_real_in = O->last_real_in;
515  last_real_out= O->last_real_out;
516  last_ref_in = O->last_ref_in;
517  last_ref_out = O->last_ref_out;
518  transfer = O->transfer;
519  misalign = O->misalign;
520  misalign_inv = O->misalign_inv;
521  dx = O->dx;
522  dy = O->dy;
523  pitch = O->pitch;
524  yaw = O->yaw;
525  roll = O->roll;
526  skipcache = O->skipcache;
527  ElementVoid::assign(other);
528 }
529 
530 void MomentElementBase::show(std::ostream& strm, int level) const
531 {
532  using namespace boost::numeric::ublas;
533  ElementVoid::show(strm, level);
534  /*
535  strm<<"Length "<<length<<"\n"
536  "Transfer: "<<transfer<<"\n"
537  "Mis-align: "<<misalign<<"\n";
538  */
539 }
540 
541 void MomentElementBase::get_misalign(const state_t &ST, const Particle &real, value_t &M, value_t &IM) const
542 {
543  state_t::matrix_t R,
544  scl = boost::numeric::ublas::identity_matrix<double>(state_t::maxsize),
545  scl_inv = scl,
546  T = scl,
547  T_inv = scl,
548  R_inv = scl;
549 
550  scl(state_t::PS_S, state_t::PS_S) /= -real.SampleIonK;
551  scl(state_t::PS_PS, state_t::PS_PS) /= sqr(real.beta)*real.gamma*ST.ref.IonEs/MeVtoeV;
552 
553  inverse(scl_inv, scl);
554 
555  // Translate to center of element.
556  T(state_t::PS_S, 6) = -length/2e0*MtoMM;
557  T(state_t::PS_PS, 6) = 1e0;
558  inverse(T_inv, T);
559 
560  RotMat(dx, dy, pitch, yaw, roll, R);
561 
562  M = prod(T, scl);
563  M = prod(R, M);
564  M = prod(T_inv, M);
565  M = prod(scl_inv, M);
566 
567  inverse(R_inv, R);
568 
569  // Translate to center of element.
570  T(state_t::PS_S, 6) = length/2e0*MtoMM;
571  T(state_t::PS_PS, 6) = 1e0;
572  inverse(T_inv, T);
573 
574  IM = prod(T, scl);
575  IM = prod(R_inv, IM);
576  IM = prod(T_inv, IM);
577  IM = prod(scl_inv, IM);
578 }
579 
581 {
582  state_t& ST = static_cast<state_t&>(s);
583  using namespace boost::numeric::ublas;
584 
585  // IonEk is Es + E_state; the latter is set by user.
586  ST.recalc();
587 
588  if(!check_cache(ST))
589  {
590  // need to re-calculate energy dependent terms
591 
592  last_ref_in = ST.ref;
593  last_real_in = ST.real;
594  resize_cache(ST);
595 
596  recompute_matrix(ST); // updates transfer and last_Kenergy_out
597 
598  ST.recalc();
599 
600  for(size_t k=0; k<last_real_in.size(); k++)
601  ST.real[k].phis += ST.real[k].SampleIonK*length*MtoMM;
602  ST.ref.phis += ST.ref.SampleIonK*length*MtoMM;
603 
604  last_ref_out = ST.ref;
605  last_real_out = ST.real;
606  } else {
607  ST.ref = last_ref_out;
608  assert(last_real_out.size()==ST.real.size()); // should be true if check_cache() -> true
609  std::copy(last_real_out.begin(),
610  last_real_out.end(),
611  ST.real.begin());
612  }
613 
614  ST.pos += length;
615 
616  for(size_t k=0; k<last_real_in.size(); k++) {
617 
618  ST.moment0[k] = prod(transfer[k], ST.moment0[k]);
619 
620  scratch = prod(transfer[k], ST.moment1[k]);
621  ST.moment1[k] = prod(scratch, trans(transfer[k]));
622  }
623 
624  ST.calc_rms();
625 }
626 
628 {
629  return !skipcache
630  && last_real_in.size()==ST.size()
631  && last_ref_in==ST.ref
632  && std::equal(last_real_in.begin(),
633  last_real_in.end(),
634  ST.real.begin());
635 }
636 
638 {
639  transfer.resize(ST.real.size(), boost::numeric::ublas::identity_matrix<double>(state_t::maxsize));
640  misalign.resize(ST.real.size(), boost::numeric::ublas::identity_matrix<double>(state_t::maxsize));
641  misalign_inv.resize(ST.real.size(), boost::numeric::ublas::identity_matrix<double>(state_t::maxsize));
642 }
643 
645 {
646  // Default, initialize as no-op
647 
648  for(size_t k=0; k<last_real_in.size(); k++) {
649  transfer[k] = boost::numeric::ublas::identity_matrix<double>(state_t::maxsize);
650  }
651 }
652 
653 namespace {
654 
655 struct ElementSource : public MomentElementBase
656 {
657  typedef ElementSource self_t;
658  typedef MomentElementBase base_t;
659  typedef typename base_t::state_t state_t;
660 
661  ElementSource(const Config& c): base_t(c), istate(c) {}
662 
663  virtual void advance(StateBase& s)
664  {
665  state_t& ST = static_cast<state_t&>(s);
666  // Replace state with our initial values
667  ST.assign(istate);
668  }
669 
670  virtual void show(std::ostream& strm, int level) const
671  {
672  ElementVoid::show(strm, level);
673  strm<<"Initial: "<<istate.moment0_env<<"\n";
674  }
675 
676  state_t istate;
677  // note that 'transfer' is not used by this element type
678 
679  virtual ~ElementSource() {}
680 
681  virtual const char* type_name() const {return "source";}
682 
683  virtual void assign(const ElementVoid *other) {
684  base_t::assign(other);
685  const self_t* O=static_cast<const self_t*>(other);
686  istate.assign(O->istate);
687  }
688 };
689 
690 struct ElementMark : public MomentElementBase
691 {
692  // Transport (identity) matrix for Marker.
693  typedef ElementMark self_t;
694  typedef MomentElementBase base_t;
695  typedef typename base_t::state_t state_t;
696 
697  ElementMark(const Config& c): base_t(c) {length = 0e0;}
698  virtual ~ElementMark() {}
699  virtual const char* type_name() const {return "marker";}
700 
701  virtual void assign(const ElementVoid *other) { base_t::assign(other); }
702 };
703 
704 struct ElementBPM : public MomentElementBase
705 {
706  // Transport (identity) matrix for BPM.
707  typedef ElementBPM self_t;
708  typedef MomentElementBase base_t;
709  typedef typename base_t::state_t state_t;
710 
711  ElementBPM(const Config& c): base_t(c) {length = 0e0;}
712  virtual ~ElementBPM() {}
713  virtual const char* type_name() const {return "bpm";}
714 
715  virtual void assign(const ElementVoid *other) { base_t::assign(other); }
716 };
717 
718 struct ElementDrift : public MomentElementBase
719 {
720  // Transport matrix for Drift.
721  typedef ElementDrift self_t;
722  typedef MomentElementBase base_t;
723  typedef typename base_t::state_t state_t;
724 
725  ElementDrift(const Config& c) : base_t(c) {}
726  virtual ~ElementDrift() {}
727  virtual const char* type_name() const {return "drift";}
728 
729  virtual void assign(const ElementVoid *other) { base_t::assign(other); }
730 
731  virtual void recompute_matrix(state_t& ST)
732  {
733  // Re-initialize transport matrix.
734 
735  const double L = length*MtoMM; // Convert from [m] to [mm].
736 
737  for(size_t i=0; i<last_real_in.size(); i++) {
738  transfer[i] = boost::numeric::ublas::identity_matrix<double>(state_t::maxsize);
739  transfer[i](state_t::PS_X, state_t::PS_PX) = L;
740  transfer[i](state_t::PS_Y, state_t::PS_PY) = L;
741  transfer[i](state_t::PS_S, state_t::PS_PS) =
742  -2e0*M_PI/(SampleLambda*ST.real[i].IonEs/MeVtoeV*cube(ST.real[i].bg))*L;
743  }
744  }
745 };
746 
747 struct ElementOrbTrim : public MomentElementBase
748 {
749  // Transport matrix for Orbit Trim.
750  typedef ElementOrbTrim self_t;
751  typedef MomentElementBase base_t;
752  typedef typename base_t::state_t state_t;
753 
754  ElementOrbTrim(const Config& c) : base_t(c) {length = 0e0;}
755  virtual ~ElementOrbTrim() {}
756  virtual const char* type_name() const {return "orbtrim";}
757 
758  virtual void assign(const ElementVoid *other) { base_t::assign(other); }
759 
760  virtual void recompute_matrix(state_t& ST)
761  {
762  // Re-initialize transport matrix.
763  double theta_x = conf().get<double>("theta_x", 0e0),
764  theta_y = conf().get<double>("theta_y", 0e0);
765 
766  for(size_t i=0; i<last_real_in.size(); i++) {
767  transfer[i] = boost::numeric::ublas::identity_matrix<double>(state_t::maxsize);
768  transfer[i](state_t::PS_PX, 6) = theta_x*ST.real[i].IonZ/ST.ref.IonZ;
769  transfer[i](state_t::PS_PY, 6) = theta_y*ST.real[i].IonZ/ST.ref.IonZ;
770 
771  get_misalign(ST, ST.real[i], misalign[i], misalign_inv[i]);
772 
773  noalias(scratch) = prod(transfer[i], misalign[i]);
774  noalias(transfer[i]) = prod(misalign_inv[i], scratch);
775  }
776  }
777 };
778 
779 struct ElementSBend : public MomentElementBase
780 {
781  // Transport matrix for Gradient Sector Bend; with edge focusing (cylindrical coordinates).
782  // Note, TLM only includes energy offset for the orbit; not the transport matrix.
783 
784  typedef ElementSBend self_t;
785  typedef MomentElementBase base_t;
786  typedef typename base_t::state_t state_t;
787 
788  unsigned HdipoleFitMode;
789 
790  ElementSBend(const Config& c) : base_t(c), HdipoleFitMode(0) {
791 
792  std::istringstream strm(c.get<std::string>("HdipoleFitMode", "1"));
793  strm>>HdipoleFitMode;
794  if(!strm.eof() && strm.fail())
795  throw std::runtime_error("HdipoleFitMode must be an integer");
796  }
797  virtual ~ElementSBend() {}
798  virtual const char* type_name() const {return "sbend";}
799 
800  virtual void assign(const ElementVoid *other) {
801  base_t::assign(other);
802  const self_t* O=static_cast<const self_t*>(other);
803  HdipoleFitMode = O->HdipoleFitMode;
804  }
805 
806  virtual void advance(StateBase& s)
807  {
808  state_t& ST = static_cast<state_t&>(s);
809  using namespace boost::numeric::ublas;
810 
811  // IonEk is Es + E_state; the latter is set by user.
812  ST.recalc();
813 
814  if(!check_cache(ST)) {
815  // need to re-calculate energy dependent terms
816  last_ref_in = ST.ref;
817  last_real_in = ST.real;
818  resize_cache(ST);
819 
820  recompute_matrix(ST); // updates transfer and last_Kenergy_out
821 
822  ST.recalc();
823  last_ref_out = ST.ref;
824  last_real_out = ST.real;
825  } else {
826  ST.ref = last_ref_out;
827  assert(last_real_out.size()==ST.real.size()); // should be true if check_cache() -> true
828  std::copy(last_real_out.begin(),
829  last_real_out.end(),
830  ST.real.begin());
831  }
832 
833  ST.pos += length;
834 
835  ST.ref.phis += ST.ref.SampleIonK*length*MtoMM;
836 
837  for(size_t i=0; i<last_real_in.size(); i++) {
838  double phis_temp = ST.moment0[i][state_t::PS_S];
839 
840  ST.moment0[i] = prod(transfer[i], ST.moment0[i]);
841 
842  noalias(scratch) = prod(transfer[i], ST.moment1[i]);
843  noalias(ST.moment1[i]) = prod(scratch, trans(transfer[i]));
844 
845  double dphis_temp = ST.moment0[i][state_t::PS_S] - phis_temp;
846 
847  if (HdipoleFitMode != 1) {
848  /*
849  double di_bg, Ek00, beta00, gamma00, IonK_Bend;
850  di_bg = conf().get<double>("bg");
851  // Dipole reference energy.
852  Ek00 = (sqrt(sqr(di_bg)+1e0)-1e0)*ST.ref.IonEs;
853  gamma00 = (Ek00+ST.ref.IonEs)/ST.ref.IonEs;
854  beta00 = sqrt(1e0-1e0/sqr(gamma00));
855  IonK_Bend = 2e0*M_PI/(beta00*SampleLambda);
856  */
857 
858  // J.B.: this is odd.
859  // ST.real.phis += IonK_Bend*length*MtoMM + dphis_temp;
860  ST.real[i].phis += ST.real[i].SampleIonK*length*MtoMM + dphis_temp;
861  } else
862  ST.real[i].phis += ST.real[i].SampleIonK*length*MtoMM + dphis_temp;
863  }
864 
865  ST.calc_rms();
866  }
867 
868  virtual void recompute_matrix(state_t& ST)
869  {
870  // Re-initialize transport matrix.
871 
872  double L = conf().get<double>("L")*MtoMM,
873  phi = conf().get<double>("phi")*M_PI/180e0,
874  phi1 = conf().get<double>("phi1")*M_PI/180e0,
875  phi2 = conf().get<double>("phi2")*M_PI/180e0,
876  K = conf().get<double>("K", 0e0)/sqr(MtoMM);
877 
878  for(size_t i=0; i<last_real_in.size(); i++) {
879  double qmrel = (ST.real[i].IonZ-ST.ref.IonZ)/ST.ref.IonZ;
880 
881  transfer[i] = boost::numeric::ublas::identity_matrix<double>(state_t::maxsize);
882 
883  if (HdipoleFitMode != 1) {
884  double dip_bg = conf().get<double>("bg"),
885  // Dipole reference energy.
886  dip_Ek = (sqrt(sqr(dip_bg)+1e0)-1e0)*ST.ref.IonEs,
887  dip_gamma = (dip_Ek+ST.ref.IonEs)/ST.ref.IonEs,
888  dip_beta = sqrt(1e0-1e0/sqr(dip_gamma)),
889  d = (ST.ref.gamma-dip_gamma)/(sqr(dip_beta)*dip_gamma) - qmrel,
890  dip_IonK = 2e0*M_PI/(dip_beta*SampleLambda);
891 
892  GetSBendMatrix(L, phi, phi1, phi2, K, ST.ref.IonEs, ST.ref.gamma, qmrel,
893  dip_beta, dip_gamma, d, dip_IonK, transfer[i]);
894  } else
895  GetSBendMatrix(L, phi, phi1, phi2, K, ST.ref.IonEs, ST.ref.gamma, qmrel,
896  ST.ref.beta, ST.ref.gamma, - qmrel, ST.ref.SampleIonK, transfer[i]);
897 
898  get_misalign(ST, ST.real[i], misalign[i], misalign_inv[i]);
899 
900  noalias(scratch) = prod(transfer[i], misalign[i]);
901  noalias(transfer[i]) = prod(misalign_inv[i], scratch);
902  }
903  }
904 };
905 
906 struct ElementQuad : public MomentElementBase
907 {
908  // Transport matrix for Quadrupole; K = B2/Brho.
909  typedef ElementQuad self_t;
910  typedef MomentElementBase base_t;
911  typedef typename base_t::state_t state_t;
912 
913  ElementQuad(const Config& c) : base_t(c) {}
914  virtual ~ElementQuad() {}
915  virtual const char* type_name() const {return "quadrupole";}
916 
917  virtual void assign(const ElementVoid *other) { base_t::assign(other); }
918 
919  virtual void recompute_matrix(state_t& ST)
920  {
921  const double B2= conf().get<double>("B2"),
922  L = conf().get<double>("L")*MtoMM;
923 
924  for(size_t i=0; i<last_real_in.size(); i++) {
925  // Re-initialize transport matrix.
926  transfer[i] = boost::numeric::ublas::identity_matrix<double>(state_t::maxsize);
927 
928  double Brho = ST.real[i].beta*(ST.real[i].IonEk+ST.real[i].IonEs)/(C0*ST.real[i].IonZ),
929  K = B2/Brho/sqr(MtoMM);
930 
931  // Horizontal plane.
932  GetQuadMatrix(L, K, (unsigned)state_t::PS_X, transfer[i]);
933  // Vertical plane.
934  GetQuadMatrix(L, -K, (unsigned)state_t::PS_Y, transfer[i]);
935  // Longitudinal plane.
936  // transfer(state_t::PS_S, state_t::PS_S) = L;
937 
938  transfer[i](state_t::PS_S, state_t::PS_PS) =
939  -2e0*M_PI/(SampleLambda*ST.real[i].IonEs/MeVtoeV*cube(ST.real[i].bg))*L;
940 
941  get_misalign(ST, ST.real[i], misalign[i], misalign_inv[i]);
942 
943  noalias(scratch) = prod(transfer[i], misalign[i]);
944  noalias(transfer[i]) = prod(misalign_inv[i], scratch);
945  }
946  }
947 };
948 
949 struct ElementSolenoid : public MomentElementBase
950 {
951  // Transport (identity) matrix for a Solenoid; K = B/(2 Brho).
952  typedef ElementSolenoid self_t;
953  typedef MomentElementBase base_t;
954  typedef typename base_t::state_t state_t;
955 
956  ElementSolenoid(const Config& c) : base_t(c) {}
957  virtual ~ElementSolenoid() {}
958  virtual const char* type_name() const {return "solenoid";}
959 
960  virtual void assign(const ElementVoid *other) { base_t::assign(other); }
961 
962  virtual void recompute_matrix(state_t& ST)
963  {
964  const double B = conf().get<double>("B"),
965  L = conf().get<double>("L")*MtoMM; // Convert from [m] to [mm].
966 
967  for(size_t i=0; i<last_real_in.size(); i++) {
968  // Re-initialize transport matrix.
969  transfer[i] = boost::numeric::ublas::identity_matrix<double>(state_t::maxsize);
970 
971  double Brho = ST.real[i].beta*(ST.real[i].IonEk+ST.real[i].IonEs)/(C0*ST.real[i].IonZ),
972  K = B/(2e0*Brho)/MtoMM;
973 
974  GetSolMatrix(L, K, transfer[i]);
975 
976  transfer[i](state_t::PS_S, state_t::PS_PS) =
977  -2e0*M_PI/(SampleLambda*ST.real[i].IonEs/MeVtoeV*cube(ST.real[i].bg))*L;
978 
979  get_misalign(ST, ST.real[i], misalign[i], misalign_inv[i]);
980 
981  noalias(scratch) = prod(transfer[i], misalign[i]);
982  noalias(transfer[i]) = prod(misalign_inv[i], scratch);
983  }
984  }
985 };
986 
987 struct ElementEDipole : public MomentElementBase
988 {
989  // Transport matrix for Electrostatic Dipole with edge focusing.
990  typedef ElementEDipole self_t;
991  typedef MomentElementBase base_t;
992  typedef typename base_t::state_t state_t;
993 
994  ElementEDipole(const Config& c) : base_t(c) {}
995  virtual ~ElementEDipole() {}
996  virtual const char* type_name() const {return "edipole";}
997 
998  virtual void assign(const ElementVoid *other) { base_t::assign(other); }
999 
1000  virtual void recompute_matrix(state_t& ST)
1001  {
1002  // Re-initialize transport matrix.
1003 
1004  //value_mat R;
1005 
1006  bool ver = conf().get<double>("ver") == 1.0;
1007  double L = conf().get<double>("L")*MtoMM,
1008  phi = conf().get<double>("phi")*M_PI/180e0,
1009  // fit to TLM unit.
1010  fringe_x = conf().get<double>("fringe_x", 0e0)/MtoMM,
1011  fringe_y = conf().get<double>("fringe_y", 0e0)/MtoMM,
1012  kappa = conf().get<double>("asym_fac", 0e0),
1013  // spher: cylindrical - 0, spherical - 1.
1014  spher = conf().get<double>("spher"),
1015  rho = L/phi,
1016  // magnetic - 0, electrostatic - 1.
1017  h = 1e0,
1018  Ky = spher/sqr(rho),
1019  dip_beta = conf().get<double>("beta"),
1020  dip_gamma = 1e0/sqrt(1e0-sqr(dip_beta));
1021 
1022  for(size_t i=0; i<last_real_in.size(); i++) {
1023  double eta0 = (ST.real[i].gamma-1e0)/2e0,
1024  Kx = (1e0-spher+sqr(1e0+2e0*eta0))/sqr(rho),
1025  delta_KZ = ST.real[i].IonZ/ST.ref.IonZ - 1e0,
1026  SampleIonK = 2e0*M_PI/(ST.real[i].beta*SampleLambda);
1027 
1028  transfer[i] = boost::numeric::ublas::identity_matrix<double>(state_t::maxsize);
1029 
1030  GetEBendMatrix(L, phi, fringe_x, fringe_y, kappa, Kx, Ky, ST.ref.IonEs, ST.ref.beta, ST.real[i].gamma,
1031  eta0, h, dip_beta, dip_gamma, delta_KZ, SampleIonK, transfer[i]);
1032 
1033  if (ver) {
1034  // Rotate transport matrix by 90 degrees.
1035  value_t
1036  R = boost::numeric::ublas::identity_matrix<double>(state_t::maxsize);
1037  R(state_t::PS_X, state_t::PS_X) = 0e0;
1038  R(state_t::PS_PX, state_t::PS_PX) = 0e0;
1039  R(state_t::PS_Y, state_t::PS_Y) = 0e0;
1040  R(state_t::PS_PY, state_t::PS_PY) = 0e0;
1041  R(state_t::PS_X, state_t::PS_Y) = -1e0;
1042  R(state_t::PS_PX, state_t::PS_PY) = -1e0;
1043  R(state_t::PS_Y, state_t::PS_X) = 1e0;
1044  R(state_t::PS_PY, state_t::PS_PX) = 1e0;
1045 
1046  noalias(scratch) = prod(R, transfer[i]);
1047  noalias(transfer[i]) = prod(scratch, trans(R));
1048  //TODO: no-op code? results are unconditionally overwritten
1049  }
1050 
1051  get_misalign(ST, ST.real[i], misalign[i], misalign_inv[i]);
1052 
1053  noalias(scratch) = prod(transfer[i], misalign[i]);
1054  noalias(transfer[i]) = prod(misalign_inv[i], scratch);
1055  }
1056  }
1057 };
1058 
1059 struct ElementEQuad : public MomentElementBase
1060 {
1061  // Transport matrix for Electrostatic Quadrupole.
1062  typedef ElementEQuad self_t;
1063  typedef MomentElementBase base_t;
1064  typedef typename base_t::state_t state_t;
1065 
1066  ElementEQuad(const Config& c) : base_t(c) {}
1067  virtual ~ElementEQuad() {}
1068  virtual const char* type_name() const {return "equad";}
1069 
1070  virtual void assign(const ElementVoid *other) { base_t::assign(other); }
1071 
1072  virtual void recompute_matrix(state_t& ST)
1073  {
1074  const double V0 = conf().get<double>("V"),
1075  R = conf().get<double>("radius"),
1076  L = conf().get<double>("L")*MtoMM;
1077 
1078  for(size_t i=0; i<last_real_in.size(); i++) {
1079  // Re-initialize transport matrix.
1080  // V0 [V] electrode voltage and R [m] electrode half-distance.
1081  transfer[i] = boost::numeric::ublas::identity_matrix<double>(state_t::maxsize);
1082 
1083  double Brho = ST.real[i].beta*(ST.real[i].IonEk+ST.real[i].IonEs)/(C0*ST.real[i].IonZ),
1084  K = 2e0*V0/(C0*ST.real[i].beta*sqr(R))/Brho/sqr(MtoMM);
1085 
1086  // Horizontal plane.
1087  GetQuadMatrix(L, K, (unsigned)state_t::PS_X, transfer[i]);
1088  // Vertical plane.
1089  GetQuadMatrix(L, -K, (unsigned)state_t::PS_Y, transfer[i]);
1090  // Longitudinal plane.
1091  // transfer(state_t::PS_S, state_t::PS_S) = L;
1092 
1093  transfer[i](state_t::PS_S, state_t::PS_PS) =
1094  -2e0*M_PI/(SampleLambda*ST.real[i].IonEs/MeVtoeV*cube(ST.real[i].bg))*L;
1095 
1096  get_misalign(ST, ST.real[i], misalign[i], misalign_inv[i]);
1097 
1098  noalias(scratch) = prod(transfer[i], misalign[i]);
1099  noalias(transfer[i]) = prod(misalign_inv[i], scratch);
1100  }
1101  }
1102 };
1103 
1104 } // namespace
1105 
1106 void registerMoment()
1107 {
1108  Machine::registerState<MomentState>("MomentMatrix");
1109 
1110  Machine::registerElement<ElementSource >("MomentMatrix", "source");
1111 
1112  Machine::registerElement<ElementMark >("MomentMatrix", "marker");
1113 
1114  Machine::registerElement<ElementBPM >("MomentMatrix", "bpm");
1115 
1116  Machine::registerElement<ElementDrift >("MomentMatrix", "drift");
1117 
1118  Machine::registerElement<ElementOrbTrim >("MomentMatrix", "orbtrim");
1119 
1120  Machine::registerElement<ElementSBend >("MomentMatrix", "sbend");
1121 
1122  Machine::registerElement<ElementQuad >("MomentMatrix", "quadrupole");
1123 
1124  Machine::registerElement<ElementSolenoid >("MomentMatrix", "solenoid");
1125 
1126  Machine::registerElement<ElementRFCavity >("MomentMatrix", "rfcavity");
1127 
1128  Machine::registerElement<ElementStripper >("MomentMatrix", "stripper");
1129 
1130  Machine::registerElement<ElementEDipole >("MomentMatrix", "edipole");
1131 
1132  Machine::registerElement<ElementEQuad >("MomentMatrix", "equad");
1133 }
double gamma
Gamma for ion. (dependent)
Definition: moment.h:24
double IonQ
Ion Charge.
Definition: moment.h:24
virtual bool check_cache(const state_t &S) const
Definition: moment.cpp:627
virtual bool getArray(unsigned idx, ArrayInfo &Info)
Introspect named parameter of the derived class.
Definition: moment.cpp:289
Base class for all simulated elements.
Definition: base.h:166
double length
Longitudual length of this element (added to StateBase::pos)
Definition: base.h:196
double IonW
Total energy. (dependent)
Definition: moment.h:24
double IonEk
Kinetic energy.
Definition: moment.h:24
virtual void show(std::ostream &strm, int level) const
Definition: moment.cpp:530
virtual const char * type_name() const =0
double IonZ
Charge state.
Definition: moment.h:24
The abstract base class for all simulation state objects.
Definition: base.h:28
virtual void advance(StateBase &s)
Propogate the given State through this Element.
Definition: moment.cpp:580
double phis
Absolute synchrotron phase [rad].
Definition: moment.h:24
virtual void assign(const ElementVoid *other)=0
Definition: base.cpp:72
double IonEs
Rest energy.
Definition: moment.h:24
Associative configuration container.
Definition: config.h:66
virtual void recompute_matrix(state_t &ST)
recalculate 'transfer' taking into consideration the provided input state
Definition: moment.cpp:644
Used with StateBase::getArray() to describe a single parameter.
Definition: base.h:48
virtual void assign(const StateBase &other)=0
Definition: base.cpp:30
double pos
absolute longitudinal position at end of Element
Definition: base.h:36
virtual void assign(const ElementVoid *other)=0
Definition: moment.cpp:511
virtual void assign(const StateBase &other)
Definition: moment.cpp:233
size_t dim[maxdims]
Array dimensions in elements.
Definition: base.h:66
const Config & conf() const
The Config used to construct this element.
Definition: base.h:191
double dx
constituents of misalign
Definition: moment.h:164
bool skipcache
If set, check_cache() will always return false.
Definition: moment.h:167
size_t size() const
of charge states
Definition: moment.h:123
size_t stride[maxdims]
Array strides in bytes.
Definition: base.h:68
std::vector< value_t > transfer
final transfer matricies
Definition: moment.h:160
double beta
Beta for ion. (dependent)
Definition: moment.h:24
double bg
Beta*gamma. (dependent)
Definition: moment.h:24
virtual void show(std::ostream &strm, int level) const
Definition: moment.cpp:248
bool tryGet(const std::string &name, T &val) const
Definition: config.h:156
virtual bool getArray(unsigned index, ArrayInfo &Info)
Introspect named parameter of the derived class.
Definition: base.cpp:35
detail::RT< T >::type get(const std::string &name) const
Definition: config.h:123
double SampleIonK
Sample rate; different RF Cavity due to RF frequenies. (dependent)
Definition: moment.h:24
virtual void show(std::ostream &, int level) const
Definition: base.cpp:67
void resize_cache(const state_t &ST)
Definition: moment.cpp:637
const char * name
The parameter name.
Definition: base.h:52
An Element which propagates the statistical moments of a bunch.
Definition: moment.h:131
unsigned ndim
Definition: base.h:64