OPAL (Object Oriented Parallel Accelerator Library)  2.2.0
OPAL
DragtFinnNormalForm.h
Go to the documentation of this file.
1 #ifndef MAD_DragtFinnNormalForm_HH
2 #define MAD_DragtFinnNormalForm_HH
3 
4 // ------------------------------------------------------------------------
5 // $RCSfile: DragtFinnNormalForm.h,v $
6 // ------------------------------------------------------------------------
7 // $Revision: 1.1.1.1 $
8 // ------------------------------------------------------------------------
9 // Copyright: see Copyright.readme
10 // ------------------------------------------------------------------------
11 //
12 // Class: DragtFinnNormalForm
13 //
14 // ------------------------------------------------------------------------
15 // Class category: FixedAlgebra
16 // ------------------------------------------------------------------------
17 //
18 // $Date: 2000/03/27 09:32:37 $
19 // $Author: fci $
20 //
21 // ------------------------------------------------------------------------
22 
23 #include "FixedAlgebra/FVector.h"
24 #include <algorithm>
25 #include <complex>
26 
27 template <int> class DragtFinnMap;
28 template <class T, int> class FLieGenerator;
29 template <class T, int, int> class FMatrix;
30 
31 
32 // Tolerance for accepting an eigenvalue.
33 namespace {
34  const double tol = 1.0e-8;
35 }
36 
37 
38 // Class DragtFinnNormalForm
39 // ------------------------------------------------------------------------
41 // Compute from representation of a map which can be a nil-potent,
42 // static or dynamic symplectic map. Implementation of an algorithm
43 // described in
44 // [center]
45 // M. Berz, E. Forest and J. Irwin,[br]
46 // Particle Accelerators, 1989, Vol. 24, pp. 91-107.
47 // [/center]
48 
49 template <int N>
51 
52 public:
53 
55  // Perform normal-form analysis of [b]map[/b].
56  explicit DragtFinnNormalForm(const DragtFinnMap<N> &map);
57 
61 
63  int degreesOfFreedom() const;
64 
66  const DragtFinnMap<N> &normalForm() const;
67 
69  const DragtFinnMap<N> &normalisingMap() const;
70 
72  const FVector<std::complex<double>, 2 * N> &eigenValues() const;
73 
76 
79 
82 
83 protected:
84 
85  // Order the modes of the map and associate them to the planes.
86  void orderModes(FVector<std::complex<double>, 2 * N>, FMatrix<double, 2 * N, 2 * N>);
87 
88 private:
89 
90  // Not implemented.
91  void operator=(const DragtFinnNormalForm &);
92 
93  // Representation of the normal form analysis results.
94  // ----------------------------------------------------------------------
95  // Number of degrees of freedom.
96  int freedom;
97 
98  // The factorised normalising map.
100 
101  // The factorised normal form map/
103 
104  // The vector of eigenvalues.
106 
107  // The matrix of eigenvectors.
109 };
110 
111 
113 #include "FixedAlgebra/FMatrix.h"
114 #include "FixedAlgebra/FLUMatrix.h"
115 #include "FixedAlgebra/FMonomial.h"
117 #include "Physics/Physics.h"
118 #include "Utilities/LogicalError.h"
119 #include <functional>
120 
121 
122 // Class DragtFinnNormalForm
123 // ------------------------------------------------------------------------
124 
125 template <int N>
127  freedom(0), A_scr(), N_scr(), lambda(), V()
128 {}
129 
130 
131 template <int N>
133  freedom(form.freedom), A_scr(form.A_scr), N_scr(form.N_scr),
134  lambda(form.lambda), V(form.V)
135 {}
136 
137 
138 template <int N>
140  freedom(N), A_scr(), N_scr(), lambda(), V() {
141  // Establish Jacobian matrix of map,
142  // and find its eigenvalues and eigenvectors.
143  // Order eigenvectors, attach modes to planes and find inverse of V.
144  DragtFinnMap<N> M_scr(map);
146  {
148  FDoubleEigen<2 * N> eigen(M, true);
149  orderModes(eigen.eigenValues(), eigen.packedEigenVectors());
151  V_inv = lu.inverse();
152  }
153 
154  // Initialise.
155  FMatrix<double, 2 * N, 2 * N> Rot, R_dir, I_dir, R_inv;
156 
157  for(int i = 0; i < N; ++i) {
158  Rot(i, i) = R_dir(i, i) = I_dir(i, i) = R_inv(i, i) = 1.0;
159  }
160 
161  FMonomial<2 * N> pows;
162 
163  for(int i = 0; i < 2 * freedom; i += 2) {
164  // Set up the auxiliary matrices.
165  R_dir(i, i) = R_dir(i, i + 1) = R_dir(i + 1, i) = 0.5;
166  R_dir(i + 1, i + 1) = -0.5;
167  R_inv(i, i) = R_inv(i, i + 1) = R_inv(i + 1, i) = 1.0;
168  R_inv(i + 1, i + 1) = -1.0;
169 
170  if(std::abs(std::imag(lambda[i])) > tol) {
171  // Complex eigenvalue pair.
172  Rot(i, i) = Rot(i + 1, i + 1) = std::real(lambda[i]);
173  Rot(i, i + 1) = std::imag(lambda[i]);
174  Rot(i + 1, i) = - std::imag(lambda[i]);
175 
176  I_dir(i, i) = I_dir(i + 1, i + 1) = 0.0;
177  I_dir(i, i + 1) = I_dir(i + 1, i) = 1.0;
178  } else {
179  // Real eigenvalue pair.
180  Rot(i, i) = Rot(i + 1, i + 1) = std::real(lambda[i] + lambda[i+1]) * 0.5;
181  Rot(i, i + 1) = Rot(i + 1, i) = std::real(lambda[i] - lambda[i+1]) * 0.5;
182  }
183  }
184 
185  // Initialise the factored map.
187  N_scr.assign(V_inv * M_scr.getMatrix() * V);
188  N_scr.assign(M_scr.getGenerators().substitute(V));
189 
190  // Remove non-resonant terms order by order.
191  int maxOrder = M_scr.getOrder();
192 
193  for(int omega = 2; omega < maxOrder; omega++) {
194  // Compute the terms to be removed and store in f.
195  FLieGenerator<double, N> f = N_scr.getGenerator(omega + 1);
196 
197  // Set up the "phi" transformations.
198  FLieGenerator<double, N> a(omega + 1);
199  FLieGenerator<double, N> b(omega + 1);
200  FLieGenerator<double, N> pi(omega + 1);
201  FLieGenerator<double, N> t(omega + 1);
202 
203  for(int m = f.getBottomIndex(); m < f.getTopIndex(); ++m) {
205  std::complex<double> factor = 1.0;
206  int count = 0;
207 
208  for(int j = 0; j < 2 * freedom; j += 2) {
209  if(std::abs(std::imag(lambda[j])) > tol) count += index[j+1];
210  factor *= (std::pow(lambda[j], index[j]) * std::pow(lambda[j+1], index[j+1]));
211  }
212 
213  if(std::abs(1.0 - factor) < tol) {
214  // Term cannot be removed.
215  t[m] = 0.5;
216  } else {
217  // Term can be removed.
218  factor = 1.0 / (1.0 - factor);
219  a[m] = std::real(factor);
220  b[m] = std::imag(factor);
221  }
222 
223  pi[m] = std::pow(-1.0, int(count + 1) / 2);
224  }
225 
226  // Compute cal_T^(-1) * f and T_omega.
227  FLieGenerator<double, N> f1 = pi.scale(f).transform(R_dir);
228  FLieGenerator<double, N> f2 = f1.transform(I_dir);
229 
230  // Remove terms of order "omega" and accumulate map script(A).
231  FLieGenerator<double, N> F_omega =
232  (a.scale(f1) + b.scale(f2)).transform(R_inv).scale(pi);
233  A_scr.assign(F_omega);
234 
235  // Accumulate map script(N).
236  N_scr = N_scr.transform(- F_omega, maxOrder);
237  }
238 }
239 
240 
241 template <int N>
243 {}
244 
245 
246 template <int N>
248  return freedom;
249 }
250 
251 
252 template <int N>
254  FTps<double, 2 * N> N_tps = N_scr.getGenerators();
256  using Physics::pi;
257 
258  for(int mode1 = 0; mode1 < freedom; mode1++) {
259  {
260  FMonomial<2 * N> power1, power2, power3;
261  power1[2*mode1] = power3[2*mode1+1] = 4;
262  power2[2*mode1] = power2[2*mode1+1] = 2;
263  QQ(mode1, mode1) = (- 3.0 / (4.0 * pi)) *
264  (N_tps[power1] + N_tps[power3] + N_tps[power2]);
265  }
266 
267  for(int mode2 = mode1 + 1; mode2 < freedom; mode2++) {
268  FMonomial<2 * N> power1, power2, power3, power4;
269  power1[2*mode1] = power1[2*mode2] = 2;
270  power2[2*mode1+1] = power2[2*mode2] = 2;
271  power3[2*mode1] = power3[2*mode2+1] = 2;
272  power4[2*mode1+1] = power4[2*mode2+1] = 2;
273  QQ(mode1, mode2) = QQ(mode2, mode1) = (- 1.0 / (4.0 * pi)) *
274  (N_tps[power1] + N_tps[power2] + N_tps[power3] + N_tps[power4]);
275  }
276  }
277 
278  return QQ;
279 }
280 
281 
282 template <int N>
284  FLieGenerator<double, N> a(1, 1);
285  FLieGenerator<double, N> b(1, 1);
286 
287  for(int j = 0; j < 2 * N; j += 2) {
288  a[j+1] = - V(j + 1, 2 * mode);
289  a[j+2] = V(j, 2 * mode);
290  b[j+1] = - V(j + 1, 2 * mode + 1);
291  b[j+2] = V(j, 2 * mode + 1);
292  }
293 
294  if(std::abs(std::imag(lambda[2*mode])) > tol) {
295  return (a * a + b * b);
296  } else {
297  return (a * a - b * b);
298  }
299 }
300 
301 
302 template <int N>
304  return N_scr;
305 }
306 
307 
308 template <int N>
310  return A_scr;
311 }
312 
313 
314 template <int N>
316  return lambda;
317 }
318 
319 
320 template <int N>
322  return V;
323 }
324 
325 
326 template <int N>
328 (FVector<std::complex<double>, 2 * N> tlam, FMatrix<double, 2 * N, 2 * N> tmat) {
329  // Static constant.
330  int nDim = 2 * N;
331  int n_c = 0;
332  int n_r = 0;
333 
334  for(int i = 0; i < 2 * N;) {
335  if(std::abs(tlam[i] - 1.0) < tol) {
336  // Collect "coasting" modes in upper indices of V.
337  nDim--;
338  lambda[nDim] = 1.0;
339  std::fill(V.col_begin(nDim), V.col_end(nDim), 0.0);
340  V(nDim, nDim) = 1.0;
341  i++;
342  } else if(std::abs(std::imag(tlam[i])) < tol) {
343  // Collect "unstable" modes in lower indices of tmat.
344  if(n_r != i) {
345  tlam[n_r] = tlam[i];
346  std::copy(tmat.col_begin(i), tmat.col_end(i), tmat.col_begin(n_r));
347  }
348  n_r++;
349  i++;
350  } else {
351  // Collect "stable" modes in lower indices of V.
352  double pb = 0.0;
353  for(int j = 0; j < 2 * N; j += 2) {
354  pb += tmat(j, i) * tmat(j + 1, i + 1) - tmat(j + 1, i) * tmat(j, i + 1);
355  }
356 
357  int i1 = i;
358  int i2 = i;
359  if(pb > 0.0) {
360  ++i2;
361  } else {
362  ++i1;
363  }
364 
365  lambda[n_c] = tlam[i1];
366  lambda[n_c+1] = tlam[i2];
367 
368  double fact = sqrt(std::abs(pb));
369 
370  for(int j = 0; j < 2 * N; j++) {
371  V(j, n_c) = tmat(j, i1) / fact;
372  V(j, n_c + 1) = tmat(j, i2) / fact;
373  }
374 
375  i += 2;
376  n_c += 2;
377  }
378  }
379 
380  // Order and copy "unstable" modes.
381  for(int i = 0; i < n_r;) {
382  int m;
383  for(m = i + 1; m < n_r; ++m) {
384  if(std::abs(tlam[i] * tlam[m] - 1.0) < tol) break;
385  }
386 
387  if(m >= n_r) {
388  throw LogicalError("DragtFinnNormalForm::orderModes()",
389  "Cannot find pair of real eigenvalues.");
390  }
391 
392  // Swap values to make pair.
393  if(m != i + 1) {
394  std::swap(tlam[m], tlam[i+1]);
395  tmat.swapColumns(m, i + 1);
396  }
397 
398  // Normalise this real pair and convert it to Sinh/Cosh form.
399  double pb = 0.0;
400  for(int j = 0; j < 2 * N; j += 2) {
401  pb += tmat(j, i) * tmat(j + 1, i + 1) - tmat(j + 1, i) * tmat(j, i + 1);
402  }
403 
404  int i1 = i;
405  int i2 = i;
406  if(pb > 0.0) {
407  ++i2;
408  } else {
409  ++i1;
410  }
411 
412  lambda[n_c] = tlam[i1];
413  lambda[n_c+1] = tlam[i2];
414 
415  double fact = sqrt(std::abs(2.0 * pb));
416 
417  for(int j = 0; j < 2 * N; ++j) {
418  V(j, n_c) = (tmat(j, i1) + tmat(j, i2)) / fact;
419  V(j, n_c + 1) = (tmat(j, i1) - tmat(j, i2)) / fact;
420  }
421 
422  n_c += 2;
423  i += 2;
424  }
425 
426  freedom = nDim / 2;
427  if(nDim != 2 * freedom) {
428  throw LogicalError("DragtFinnNormalForm::orderModes()",
429  "Map dimension is odd.");
430  }
431 
432  // Re-order eigenvector pairs by their main components.
433  for(int i = 0; i < nDim; i += 2) {
434  // Find eigenvector pair with larges component in direction i/2.
435  double big = 0.0;
436  int k = i;
437  for(int j = i; j < 2 * N; j += 2) {
438  double c = std::abs(V(i, j) * V(i + 1, j + 1) - V(i, j + 1) * V(i + 1, j));
439 
440  if(c > big) {
441  big = c;
442  k = j;
443  }
444  }
445 
446  if(k != i) {
447  // Move eigenvector pair to its place.
448  std::swap(lambda[i], lambda[k]);
449  std::swap(lambda[i+1], lambda[k+1]);
450  V.swapColumns(i, k);
451  V.swapColumns(i + 1, k + 1);
452  }
453 
454  if(std::abs(std::imag(lambda[i])) > tol) {
455  // Rotate complex eigenvectors to make their main components real.
456  double re = V(i, i) / sqrt(V(i, i) * V(i, i) + V(i, i + 1) * V(i, i + 1));
457  double im = V(i, i + 1) / sqrt(V(i, i) * V(i, i) + V(i, i + 1) * V(i, i + 1));
458 
459  for(int j = 0; j < 2 * N; j++) {
460  double real_part = re * V(j, i) + im * V(j, i + 1);
461  double imag_part = re * V(j, i + 1) - im * V(j, i);
462  V(j, i) = real_part;
463  V(j, i + 1) = imag_part;
464  }
465  }
466  }
467 }
468 
469 #endif // MAD_DragtFinnNormalForm_HH
FVector< std::complex< double >, 2 *N > lambda
PETE_TUTree< FnAbs, typename T::PETE_Expr_t > abs(const PETE_Expr< T > &l)
col_iterator col_end(int c)
Get column iterator.
Definition: FArray2D.h:352
A templated representation for matrices.
Definition: IdealMapper.h:26
FLieGenerator< double, N > invariant(int i) const
Get invariant polynomial for mode i.
void swapColumns(int c1, int c2)
Exchange columns.
Definition: FArray2D.h:403
A templated representation for vectors.
Definition: PartBunchBase.h:26
FLieGenerator< U, N > transform(const FMatrix< U, 2 *N, 2 *N > &) const
Substitute matrix in Lie generator.
Eigenvalues and eigenvectors for a real general matrix.
Definition: FDoubleEigen.h:35
FMatrix< double, 2 *N, 2 *N > V
Representation of the exponents for a monomial with fixed dimension.
Definition: FMonomial.h:32
FLieGenerator scale(const FLieGenerator &) const
Scale monomial-wise.
DragtFinnMap< N > A_scr
const FMatrix< double, 2 *N, 2 *N > & getMatrix() const
Return the matrix representing the linear transform.
Definition: DragtFinnMap.h:671
void orderModes(FVector< std::complex< double >, 2 *N >, FMatrix< double, 2 *N, 2 *N >)
FLieGenerator< T, N > real(const FLieGenerator< std::complex< T >, N > &)
Take real part of a complex generator.
int degreesOfFreedom() const
Get number of stable degrees of freedom.
void assign(const FMatrix< double, 2 *N, 2 *N > &)
Assign the matrix representing the linear transform.
Definition: DragtFinnMap.h:395
const FTps< double, 2 *N > & getGenerators() const
Return the complete set of generators.
Definition: DragtFinnMap.h:685
const DragtFinnMap< N > & normalisingMap() const
Get normalising map as a Lie transform.
A templated representation of a LU-decomposition.
Definition: FLUMatrix.h:42
constexpr double pi
The value of .
Definition: Physics.h:31
Normal form of a truncated Taylor series map.
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 >, 2 *N > & eigenValues() const
Get eigenvalues of the linear part as a complex vector.
FMatrix< double, N, N > anharmonicity() const
Get anharmonicities as a matrix.
col_iterator col_begin(int c)
Get column iterator.
Definition: FArray2D.h:343
DragtFinnMap transform(const FLieGenerator< double, N > &g, int topOrder)
Build the terminating exponential series.
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
FTps substitute(const FMatrix< T, N, N > &M, int n) const
Substitute.
Definition: FTps.hpp:1075
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
A representation for a homogeneous polynomial, used as a Lie generator.
Definition: DragtFinnMap.h:29
static const FMonomial< N > & getExponents(int index)
Definition: FTpsData.h:160
MMatrix< double > re(MMatrix< m_complex > mc)
Definition: MMatrix.cpp:389
const FLieGenerator< double, N > getGenerator(int) const
Return the generator for a selected order.
Definition: DragtFinnMap.h:699
A Lie algebraic map, factored according to Dragt and Finn.
Definition: DragtFinnMap.h:43
int getBottomIndex() const
Return bottom index of this generator.
FLieGenerator< T, N > imag(const FLieGenerator< std::complex< T >, N > &)
Take imaginary part of a complex generator.
int getTopIndex() const
Return top index of this generator.
int getOrder() const
Return the order of the generators.
Definition: DragtFinnMap.h:714
Logical error exception.
Definition: LogicalError.h:33
const FMatrix< double, 2 *N, 2 *N > & eigenVectors() const
Get eigenvectors of the linear part in packed form.
FVector< std::complex< double >, N > eigenValues() const
Get eigenvalues.
Definition: FDoubleEigen.h:183
void operator=(const DragtFinnNormalForm &)
const DragtFinnMap< N > & normalForm() const
Get normal-form map as a Lie transform.
DragtFinnMap< N > N_scr