OPAL (Object Oriented Parallel Accelerator Library)  2.2.0
OPAL
FNormalForm.h
Go to the documentation of this file.
1 #ifndef MAD_FNormalForm_HH
2 #define MAD_FNormalForm_HH
3 
4 // ------------------------------------------------------------------------
5 // $RCSfile: FNormalForm.h,v $
6 // ------------------------------------------------------------------------
7 // $Revision: 1.1.1.1.2.1 $
8 // ------------------------------------------------------------------------
9 // Copyright: see Copyright.readme
10 // ------------------------------------------------------------------------
11 //
12 // Class: FNormalForm
13 //
14 // ------------------------------------------------------------------------
15 // Class category: FixedAlgebra
16 // ------------------------------------------------------------------------
17 //
18 // $Date: 2002/03/25 20:44:17 $
19 // $Author: dbruhwil $
20 //
21 // ------------------------------------------------------------------------
22 
23 #include "FixedAlgebra/FMatrix.h"
24 #include "FixedAlgebra/FVector.h"
25 #include <algorithm>
26 #include <complex>
27 
28 template <class T, int, int> class FMatrix;
29 template <class T, int> class FTps;
30 template <class T, int> class FVps;
31 
32 
33 // Tolerance for accepting an eigenvalue.
34 namespace {
35  const double tol = 1.0e-8;
36 }
37 
38 
39 // Class FNormalForm
40 // ------------------------------------------------------------------------
42 // Compute from representation of a map which can be a nil-potent,
43 // static or dynamic symplectic map. Implementation of an algorithm
44 // described in
45 // [center]
46 // M. Berz, E. Forest and J. Irwin,[br]
47 // Particle Accelerators, 1989, Vol. 24, pp. 91-107.
48 // [/center]
49 
50 template <int N>
51 class FNormalForm {
52 
53 public:
54 
56  // Perform normal-form analysis of [b]map[/b].
57  explicit FNormalForm(const FVps<double, N> &map);
58 
59  FNormalForm();
60  FNormalForm(const FNormalForm &);
61  ~FNormalForm();
62 
64  int degreesOfFreedom() const;
65 
67  const FTps<double, N> &normalForm() const;
68 
70  const FTps<double, N> &normalisingMap() const;
71 
73  const FVector<std::complex<double>, N> &eigenValues() const;
74 
76  const FMatrix<double, N, N> &eigenVectors() const;
77 
79  FMatrix < double, N / 2, N / 2 > anharmonicity() const;
80 
82  FTps<double, N> invariant(int i) const;
83 
84 protected:
85 
86  // Order the modes of the map and associate them to the planes.
87  void orderModes(FVector<std::complex<double>, N>, FMatrix<double, N, N>);
88 
89 private:
90 
91  // Not implemented.
92  void operator=(const FNormalForm &);
93 
94  // Representation of the normal form analysis results.
95  // ----------------------------------------------------------------------
96  // Number of degrees of freedom.
97  int freedom;
98 
99  // The factorised normalising map.
101 
102  // The factorised normal form map/
104 
105  // The vector of eigenvalues.
107 
108  // The matrix of eigenvectors.
110 };
111 
112 
114 #include "FixedAlgebra/FMatrix.h"
115 #include "FixedAlgebra/FLUMatrix.h"
116 #include "FixedAlgebra/FMonomial.h"
117 #include "FixedAlgebra/FVps.h"
118 #include "Physics/Physics.h"
119 #include "Utilities/LogicalError.h"
120 #include <functional>
121 
122 
123 
124 
125 // Class FNormalForm
126 // ------------------------------------------------------------------------
127 
128 template <int N>
130  freedom(0), A_Lie(), N_Lie(), lambda(), V()
131 {}
132 
133 
134 template <int N>
136  freedom(form.freedom), A_Lie(form.A_Lie), N_Lie(form.N_Lie),
137  lambda(form.lambda), V(form.V)
138 {}
139 
140 
141 template <int N>
143  freedom(N / 2), A_Lie(0.0),
144  N_Lie(0, 2, M_scr.getTopOrder() + 1), // Reserve space for second-order.
145  lambda(), V() {
146  if(N % 2 != 0) {
147  throw LogicalError("FNormalForm::FNormalForm()", "Map dimension is odd.");
148  }
149 
150  // Establish Jacobian matrix of map,
151  // and find its eigenvalues and eigenvectors.
152  // Order eigenvectors, attach modes to planes and find inverse of V.
153  FMatrix<double, N, N> V_inv;
154  {
156  FDoubleEigen<N> eigen(M, true);
157  orderModes(eigen.eigenValues(), eigen.packedEigenVectors());
159  V_inv = lu.inverse();
160  }
161 
162  // Initialise.
163  FMatrix<double, N, N> Rot, R_dir, I_dir, R_inv;
164 
165  for(int i = 0; i < N; ++i) {
166  Rot(i, i) = R_dir(i, i) = I_dir(i, i) = R_inv(i, i) = 1.0;
167  }
168 
169  FMonomial<N> pows;
170 
171  for(int i = 0; i < 2 * freedom; i += 2) {
172  // Store linear part of normal form.
173  double c1, c2;
174 
175  if(std::abs(std::imag(lambda[i])) > tol) {
176  c1 = c2 = std::arg(lambda[i]) / 2.0;
177  } else {
178  c1 = log(std::abs(lambda[i])) / 2.0;
179  c2 = - c1;
180  }
181 
182  pows[i] = 2;
183  N_Lie.setCoefficient(pows, c1);
184  pows[i] = 0;
185 
186  pows[i+1] = 2;
187  N_Lie.setCoefficient(pows, c2);
188  pows[i+1] = 0;
189 
190  // Set up the auxiliary matrices.
191  R_dir(i, i) = R_dir(i, i + 1) = R_dir(i + 1, i) = 0.5;
192  R_dir(i + 1, i + 1) = -0.5;
193  R_inv(i, i) = R_inv(i, i + 1) = R_inv(i + 1, i) = 1.0;
194  R_inv(i + 1, i + 1) = -1.0;
195 
196  if(std::abs(std::imag(lambda[i])) > tol) {
197  // Complex eigenvalue pair.
198  Rot(i, i) = Rot(i + 1, i + 1) = std::real(lambda[i]);
199  Rot(i, i + 1) = std::imag(lambda[i]);
200  Rot(i + 1, i) = - std::imag(lambda[i]);
201 
202  I_dir(i, i) = I_dir(i + 1, i + 1) = 0.0;
203  I_dir(i, i + 1) = I_dir(i + 1, i) = 1.0;
204  } else {
205  // Real eigenvalue pair.
206  Rot(i, i) = Rot(i + 1, i + 1) = std::real(lambda[i] + lambda[i+1]) * 0.5;
207  Rot(i, i + 1) = Rot(i + 1, i) = std::real(lambda[i] - lambda[i+1]) * 0.5;
208  }
209  }
210 
211  // Fill in the values for the coasting modes.
212  // Note that the matrices already have ones on the main diagonal.
213  for(int i = 2 * freedom; i < N; i += 2) {
214  // Store linear part of normal form.
215  pows[i+1] = 2;
216  N_Lie.setCoefficient(pows, 0.5);
217  pows[i+1] = 0;
218  }
219 
220  // Initialise the factored map.
221  FVps<double, N> N_scr = V_inv * M_scr.substitute(V * Rot);
222  FVps<double, N> N_acc;
223 
224  // Remove non-resonant terms order by order.
225  int maxOrder = M_scr.getTopOrder();
226 
227  for(int omega = 2; omega < maxOrder; omega++) {
228  // Compute the terms to be removed and store in f.
229  FVps<double, N> temp = N_scr.substitute(N_acc).filter(omega, omega);
230  FTps<double, N> f(0, omega + 1, omega + 1);
231 
232  for(int i = 0; i < N; i += 2) {
233  f += temp[i+1].multiplyVariable(i) - temp[i].multiplyVariable(i + 1);
234  }
235 
236  f /= double(omega + 1);
237 
238  // Set up the "phi" transformations.
239  FTps<double, N> a(0, omega + 1, omega + 1);
240  FTps<double, N> b(0, omega + 1, omega + 1);
241  FTps<double, N> pi(0, omega + 1, omega + 1);
242  FTps<double, N> t(0, omega + 1, omega + 1);
243 
244  for(int m = FTps<double, N>::getSize(omega);
245  m < FTps<double, N>::getSize(omega + 1); m++) {
246  const FMonomial<N> &index = FTpsData<N>::getExponents(m);
247  std::complex<double> factor = 1.0;
248  int count = 0;
249 
250  for(int j = 0; j < 2 * freedom; j += 2) {
251  if(std::abs(std::imag(lambda[j])) > tol) count += index[j+1];
252  factor *= (std::pow(lambda[j], index[j]) * std::pow(lambda[j+1], index[j+1]));
253  }
254 
255  if(std::abs(1.0 - factor) < tol) {
256  // Term cannot be removed.
257  t.setCoefficient(m, 0.5);
258  } else {
259  // Term can be removed.
260  factor = 1.0 / (1.0 - factor);
261  a.setCoefficient(m, std::real(factor));
262  b.setCoefficient(m, std::imag(factor));
263  }
264 
265  pi.setCoefficient(m, std::pow(-1.0, int(count + 1) / 2));
266  }
267 
268  // Compute cal_T^(-1) * f and T_omega.
269  FTps<double, N> f1 = pi.scaleMonomials(f).substitute(R_dir);
270  FTps<double, N> f2 = f1.substitute(I_dir);
271 
272  // Remove terms of order "omega" and accumulate map script(A).
273  FTps<double, N> F_omega =
274  (a.scaleMonomials(f1) + b.scaleMonomials(f2)).
275  substitute(R_inv).scaleMonomials(pi);
276 
277  if(F_omega != 0.0) {
278  FVps<double, N> A_omega = ExpMap(F_omega.substitute(Rot));
279  FVps<double, N> A_invert = ExpMap(- F_omega);
280  N_scr = A_invert.substitute(N_scr).substitute(A_omega);
281  A_Lie -= F_omega;
282  }
283 
284  // Accumulate map script(N).
285  FTps<double, N> T_omega = t.scaleMonomials(f1 + f2).
286  substitute(R_inv).scaleMonomials(pi);
287 
288  if(T_omega != 0.0) {
289  N_acc = ExpMap(- T_omega, N_acc);
290  N_Lie -= T_omega;
291  }
292  }
293 }
294 
295 
296 template <int N>
298 {}
299 
300 
301 template <int N>
303  return freedom;
304 }
305 
306 
307 template <int N>
308 FMatrix < double, N / 2, N / 2 > FNormalForm<N>::anharmonicity() const {
309  FMatrix < double, N / 2, N / 2 > QQ;
310  using Physics::pi;
311 
312  for(int mode1 = 0; mode1 < freedom; mode1++) {
313  {
314  FMonomial<N> power1, power2, power3;
315  power1[2*mode1] = power3[2*mode1+1] = 4;
316  power2[2*mode1] = power2[2*mode1+1] = 2;
317  QQ(mode1, mode1) =
318  - (3.0 * (N_Lie.getCoefficient(power1) +
319  N_Lie.getCoefficient(power3)) +
320  N_Lie.getCoefficient(power2)) / (4.0 * pi);
321  }
322 
323  for(int mode2 = mode1 + 1; mode2 < freedom; mode2++) {
324  FMonomial<N> power1, power2, power3, power4;
325  power1[2*mode1] = power1[2*mode2] = 2;
326  power2[2*mode1+1] = power2[2*mode2] = 2;
327  power3[2*mode1] = power3[2*mode2+1] = 2;
328  power4[2*mode1+1] = power4[2*mode2+1] = 2;
329  QQ(mode1, mode2) = QQ(mode2, mode1) =
330  - (N_Lie.getCoefficient(power1) +
331  N_Lie.getCoefficient(power2) +
332  N_Lie.getCoefficient(power3) +
333  N_Lie.getCoefficient(power4)) / (4.0 * pi);
334  }
335  }
336 
337  return QQ;
338 }
339 
340 
341 template <int N>
343  FTps<double, N> a(1, 1);
344  FTps<double, N> b(1, 1);
345 
346  for(int j = 0; j < N; j += 2) {
347  a.setCoefficient(j + 1, - V(j + 1, 2 * mode));
348  a.setCoefficient(j + 2, V(j, 2 * mode));
349  b.setCoefficient(j + 1, - V(j + 1, 2 * mode + 1));
350  b.setCoefficient(j + 2, V(j, 2 * mode + 1));
351  }
352 
353  if(std::abs(std::imag(lambda[2*mode])) > tol) {
354  return (a * a + b * b);
355  } else {
356  return (a * a - b * b);
357  }
358 }
359 
360 
361 template <int N>
363  return N_Lie;
364 }
365 
366 
367 template <int N>
369  return A_Lie;
370 }
371 
372 
373 template <int N>
375  return lambda;
376 }
377 
378 
379 template <int N>
381  return V;
382 }
383 
384 
385 template <int N>
386 void FNormalForm<N>::orderModes(FVector<std::complex<double>, N> tlam,
387  FMatrix<double, N, N> tmat) {
388  // Static constant.
389  int nDim = N;
390  int n_c = 0;
391  int n_r = 0;
392 
393  for(int i = 0; i < N;) {
394  if(std::abs(tlam[i] - 1.0) < tol) {
395  // Collect "coasting" modes in upper indices of V.
396  nDim--;
397  lambda[nDim] = 1.0;
398  std::fill(V.col_begin(nDim), V.col_end(nDim), 0.0);
399  V(nDim, nDim) = 1.0;
400  i++;
401  } else if(std::abs(std::imag(tlam[i])) < tol) {
402  // Collect "unstable" modes in lower indices of tmat.
403  if(n_r != i) {
404  tlam[n_r] = tlam[i];
405  std::copy(tmat.col_begin(i), tmat.col_end(i), tmat.col_begin(n_r));
406  }
407  n_r++;
408  i++;
409  } else {
410  // Collect "stable" modes in lower indices of V.
411  double pb = 0.0;
412  for(int j = 0; j < N; j += 2) {
413  pb += tmat(j, i) * tmat(j + 1, i + 1) - tmat(j + 1, i) * tmat(j, i + 1);
414  }
415 
416  int i1 = i;
417  int i2 = i;
418  if(pb > 0.0) {
419  ++i2;
420  } else {
421  ++i1;
422  }
423 
424  lambda[n_c] = tlam[i1];
425  lambda[n_c+1] = tlam[i2];
426 
427  double fact = sqrt(std::abs(pb));
428 
429  for(int j = 0; j < N; j++) {
430  V(j, n_c) = tmat(j, i1) / fact;
431  V(j, n_c + 1) = tmat(j, i2) / fact;
432  }
433 
434  i += 2;
435  n_c += 2;
436  }
437  }
438 
439  // Order and copy "unstable" modes.
440  for(int i = 0; i < n_r;) {
441  int m;
442  for(m = i + 1; m < n_r; ++m) {
443  if(std::abs(tlam[i] * tlam[m] - 1.0) < tol) break;
444  }
445 
446  if(m >= n_r) {
447  throw LogicalError("FNormalForm::orderModes()",
448  "Cannot find pair of real eigenvalues.");
449  }
450 
451  // Swap values to make pair.
452  if(m != i + 1) {
453  std::swap(tlam[m], tlam[i+1]);
454  tmat.swapColumns(m, i + 1);
455  }
456 
457  // Normalise this real pair and convert it to Sinh/Cosh form.
458  double pb = 0.0;
459  for(int j = 0; j < N; j += 2) {
460  pb += tmat(j, i) * tmat(j + 1, i + 1) - tmat(j + 1, i) * tmat(j, i + 1);
461  }
462 
463  int i1 = i;
464  int i2 = i;
465  if(pb > 0.0) {
466  ++i2;
467  } else {
468  ++i1;
469  }
470 
471  lambda[n_c] = tlam[i1];
472  lambda[n_c+1] = tlam[i2];
473 
474  double fact = sqrt(std::abs(2.0 * pb));
475 
476  for(int j = 0; j < N; ++j) {
477  V(j, n_c) = (tmat(j, i1) + tmat(j, i2)) / fact;
478  V(j, n_c + 1) = (tmat(j, i1) - tmat(j, i2)) / fact;
479  }
480 
481  n_c += 2;
482  i += 2;
483  }
484 
485  freedom = nDim / 2;
486  if(nDim != 2 * freedom) {
487  throw LogicalError("FNormalForm::orderModes()", "Map dimension is odd.");
488  }
489 
490  // Re-order eigenvector pairs by their main components.
491  for(int i = 0; i < nDim; i += 2) {
492  // Find eigenvector pair with larges component in direction i/2.
493  double big = 0.0;
494  int k = i;
495  for(int j = i; j < N; j += 2) {
496  double c = std::abs(V(i, j) * V(i + 1, j + 1) - V(i, j + 1) * V(i + 1, j));
497 
498  if(c > big) {
499  big = c;
500  k = j;
501  }
502  }
503 
504  if(k != i) {
505  // Move eigenvector pair to its place.
506  std::swap(lambda[i], lambda[k]);
507  std::swap(lambda[i+1], lambda[k+1]);
508  V.swapColumns(i, k);
509  V.swapColumns(i + 1, k + 1);
510  }
511 
512  if(std::abs(std::imag(lambda[i])) > tol) {
513  // Rotate complex eigenvectors to make their main components real.
514  double re = V(i, i) / sqrt(V(i, i) * V(i, i) + V(i, i + 1) * V(i, i + 1));
515  double im = V(i, i + 1) / sqrt(V(i, i) * V(i, i) + V(i, i + 1) * V(i, i + 1));
516 
517  for(int j = 0; j < N; j++) {
518  double real_part = re * V(j, i) + im * V(j, i + 1);
519  double imag_part = re * V(j, i + 1) - im * V(j, i);
520  V(j, i) = real_part;
521  V(j, i + 1) = imag_part;
522  }
523  }
524  }
525 }
526 
527 #endif // MAD_FNormalForm_HH
int degreesOfFreedom() const
Get number of stable degrees of freedom.
Definition: FNormalForm.h:302
PETE_TUTree< FnAbs, typename T::PETE_Expr_t > abs(const PETE_Expr< T > &l)
int getTopOrder() const
Get highest order contained in any component.
Definition: FVps.hpp:197
col_iterator col_end(int c)
Get column iterator.
Definition: FArray2D.h:352
A templated representation for matrices.
Definition: IdealMapper.h:26
FVector< std::complex< double >, N > lambda
Definition: FNormalForm.h:106
void swapColumns(int c1, int c2)
Exchange columns.
Definition: FArray2D.h:403
A templated representation for vectors.
Definition: PartBunchBase.h:26
Eigenvalues and eigenvectors for a real general matrix.
Definition: FDoubleEigen.h:35
Representation of the exponents for a monomial with fixed dimension.
Definition: FMonomial.h:32
void orderModes(FVector< std::complex< double >, N >, FMatrix< double, N, N >)
Definition: FNormalForm.h:386
FTps< double, N > invariant(int i) const
Get invariant polynomial for mode i.
Definition: FNormalForm.h:342
const FMatrix< double, N, N > & eigenVectors() const
Get eigenvectors of the linear part in packed form.
Definition: FNormalForm.h:380
Tps< T > log(const Tps< T > &x)
Natural logarithm.
Definition: TpsMath.h:182
FLieGenerator< T, N > real(const FLieGenerator< std::complex< T >, N > &)
Take real part of a complex generator.
A templated representation of a LU-decomposition.
Definition: FLUMatrix.h:42
constexpr double pi
The value of .
Definition: Physics.h:31
FTps< double, N > A_Lie
Definition: FNormalForm.h:100
const double pi
Definition: fftpack.cpp:894
constexpr double c
The velocity of light in m/s.
Definition: Physics.h:52
const FVector< std::complex< double >, N > & eigenValues() const
Get eigenvalues of the linear part as a complex vector.
Definition: FNormalForm.h:374
col_iterator col_begin(int c)
Get column iterator.
Definition: FArray2D.h:343
const FTps< double, N > & normalisingMap() const
Get normalising map as a Lie transform.
Definition: FNormalForm.h:368
FMatrix< double, N/2, N/2 > anharmonicity() const
Get anharmonicities as a matrix.
Definition: FNormalForm.h:308
FVps< T, N > ExpMap(const FTps< T, N > &H, int trunc=FTps< T, N >::EXACT)
Build the exponential series.
Definition: FTps.hpp:1994
Tps< T > pow(const Tps< T > &x, int y)
Integer power.
Definition: TpsMath.h:76
FMatrix< double, N, N > packedEigenVectors() const
Get eigenvectors.
Definition: FDoubleEigen.h:217
MMatrix< double > im(MMatrix< m_complex > mc)
Definition: MMatrix.cpp:398
Normal form of a truncated Taylor series map.
Definition: FNormalForm.h:51
FTps substitute(const FMatrix< T, N, N > &M, int n) const
Substitute.
Definition: FTps.hpp:1075
FVps filter(int minOrder, int maxOrder, int trcOrder=(FTps< T, N >::EXACT)) const
Extract given range of orders, with truncation.
Definition: FVps.hpp:223
FTps scaleMonomials(const FTps &y) const
Scale monomial coefficients by coefficients in [b]y[/b].
Definition: FTps.hpp:589
arg(a))
FMatrix< T, N, N > linearTerms() const
Extract the linear part of the map.
Definition: FVps.hpp:561
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
static const FMonomial< N > & getExponents(int index)
Definition: FTpsData.h:160
MMatrix< double > re(MMatrix< m_complex > mc)
Definition: MMatrix.cpp:389
FMatrix< double, N, N > V
Definition: FNormalForm.h:109
void operator=(const FNormalForm &)
const FTps< double, N > & normalForm() const
Get normal-form map as a Lie transform.
Definition: FNormalForm.h:362
Truncated power series in N variables of type T.
FVps substitute(const FMatrix< T, N, N > &M, int n) const
Substitute.
Definition: FVps.hpp:608
FLieGenerator< T, N > imag(const FLieGenerator< std::complex< T >, N > &)
Take imaginary part of a complex generator.
Logical error exception.
Definition: LogicalError.h:33
FVector< std::complex< double >, N > eigenValues() const
Get eigenvalues.
Definition: FDoubleEigen.h:183
Vector truncated power series in n variables.
void setCoefficient(int index, const T &value)
Set coefficient.
Definition: FTps.hpp:236
FTps< double, N > N_Lie
Definition: FNormalForm.h:103