OPAL (Object Oriented Parallel Accelerator Library)  2.2.0
OPAL
NormalForm.cpp
Go to the documentation of this file.
1 // ------------------------------------------------------------------------
2 // $RCSfile: NormalForm.cpp,v $
3 // ------------------------------------------------------------------------
4 // $Revision: 1.1.1.1 $
5 // ------------------------------------------------------------------------
6 // Copyright: see Copyright.readme
7 // ------------------------------------------------------------------------
8 //
9 // Class: NormalForm
10 // Find normal form of a truncated Taylor series map.
11 // Compute form representation of a map which can be a nil-potent,
12 // static or dynamic symplectic map.
13 //
14 // Implementation of an algorithm described in
15 // M. Berz, E. Forest and J. Irwin,
16 // Particle Accelerators, 1989, Vol. 24, pp. 91-107.
17 //
18 // ------------------------------------------------------------------------
19 // Class category: Algebra
20 // ------------------------------------------------------------------------
21 //
22 // $Date: 2000/03/27 09:32:32 $
23 // $Author: fci $
24 //
25 // ------------------------------------------------------------------------
26 
27 #include "Algebra/NormalForm.h"
28 #include "Algebra/DoubleEigen.h"
29 #include "Algebra/LieMap.h"
30 #include "Algebra/LUMatrix.h"
31 #include "Algebra/TpsMonomial.h"
32 #include "Physics/Physics.h"
33 #include "Utilities/LogicalError.h"
34 #include "Utilities/SizeError.h"
35 #include <algorithm>
36 
37 using std::abs;
38 using std::arg;
39 using std::imag;
40 using std::pow;
41 using std::real;
42 using std::swap;
43 
44 
45 // Tolerance for accepting an eigenvalue.
46 namespace {
47  const double tol = 1.0e-8;
48 }
49 
50 
51 // Class NormalForm
52 // ------------------------------------------------------------------------
53 
55  dimension(0),
56  freedom(0),
57  A_Lie(),
58  N_Lie(),
59  lambda(),
60  V()
61 {}
62 
63 
65  dimension(form.dimension),
66  freedom(form.freedom),
67  A_Lie(form.A_Lie),
68  N_Lie(form.N_Lie),
69  lambda(form.lambda),
70  V(form.V)
71 {}
72 
73 
75  dimension(M_scr.getDimension()),
76  freedom(dimension / 2),
77  A_Lie(0.0),
78  N_Lie(2, dimension), // Reserve space for second-order.
79  lambda(dimension),
80  V(dimension, dimension) {
81  int nVar = M_scr.getVariables();
82  if(nVar != dimension) {
83  throw SizeError("NormalForm::NormalForm()", "Map is not R^n --> R^n.");
84  }
85  if(dimension % 2 != 0) {
86  throw LogicalError("NormalForm::NormalForm()", "Map dimension is odd.");
87  }
88 
89  // Establish Jacobian matrix of map,
90  // and find its eigenvalues and eigenvectors.
91  // Order eigenvectors, attach modes to planes and find inverse of V.
92  Matrix<double> V_inv;
93  {
94  Matrix<double> M = M_scr.linearTerms();
95  DoubleEigen eigen(M, true);
96  orderModes(eigen.eigenValues(), eigen.packedEigenVectors());
97  LUMatrix<double> lu(V);
98  V_inv = lu.inverse();
99  }
100 
101  // Initialise.
102  Matrix<double> Rot_inv(dimension, dimension, 1.0);
103  Matrix<double> R_dir(dimension, dimension, 1.0);
104  Matrix<double> I_dir(dimension, dimension, 1.0);
105  Matrix<double> R_inv(dimension, dimension, 1.0);
106  TpsMonomial pows(dimension);
107 
108  for(int i = 0; i < 2 * freedom; i += 2) {
109  // Store linear part of normal form.
110  double c1, c2;
111 
112  if(imag(lambda(i)) != 0.0) {
113  c1 = c2 = arg(lambda(i)) / 2.0;
114  } else {
115  c1 = log(std::abs(lambda(i))) / 2.0;
116  c2 = - c1;
117  }
118 
119  pows[i] = 2;
120  N_Lie.setCoefficient(pows, c1);
121  pows[i] = 0;
122 
123  pows[i+1] = 2;
124  N_Lie.setCoefficient(pows, c2);
125  pows[i+1] = 0;
126 
127  // Set up the auxiliary matrices.
128  R_dir(i, i) = R_dir(i, i + 1) = R_dir(i + 1, i) = 0.5;
129  R_dir(i + 1, i + 1) = -0.5;
130  R_inv(i, i) = R_inv(i, i + 1) = R_inv(i + 1, i) = 1.0;
131  R_inv(i + 1, i + 1) = -1.0;
132 
133  if(imag(lambda(i)) != 0.0) {
134  // Complex eigenvalue pair.
135  Rot_inv(i, i) = Rot_inv(i + 1, i + 1) = real(lambda(i));
136  Rot_inv(i, i + 1) = -imag(lambda(i));
137  Rot_inv(i + 1, i) = imag(lambda(i));
138 
139  I_dir(i, i) = I_dir(i + 1, i + 1) = 0.0;
140  I_dir(i, i + 1) = I_dir(i + 1, i) = 1.0;
141  } else {
142  // Real eigenvalue pair.
143  Rot_inv(i, i) = Rot_inv(i + 1, i + 1) = real(lambda(i) + lambda(i + 1)) * 0.5;
144  Rot_inv(i, i + 1) = Rot_inv(i + 1, i) = real(lambda(i) - lambda(i + 1)) * 0.5;
145  }
146  }
147 
148  // Fill in the values for the coasting modes.
149  // Note that the matrices already have ones on the main diagonal.
150  for(int i = 2 * freedom; i < dimension; i += 2) {
151  // Store linear part of normal form.
152  pows[i+1] = 2;
153  N_Lie.setCoefficient(pows, 0.5);
154  pows[i+1] = 0;
155  }
156 
157  // Initialise the factored map.
158  VpsInvMap<double> N_scr = V_inv * M_scr.substitute(V * Rot_inv);
159  VpsInvMap<double> N_acc(dimension);
160 
161  // Remove non-resonant terms order by order.
162  int maxOrder = M_scr.getTruncOrder();
163 
164  for(int omega = 2; omega < maxOrder; omega++) {
165  // Compute the terms to be removed and store in f.
166  VpsInvMap<double> temp = N_scr.substitute(N_acc).filter(omega, omega);
167  Tps<double> f(omega + 1, dimension);
168 
169  for(int i = 0; i < dimension; i += 2) {
170  f += temp[i+1].multiplyVariable(i) - temp[i].multiplyVariable(i + 1);
171  }
172 
173  f /= double(omega + 1);
174 
175  // Set up the "phi" transformations.
176  Tps<double> a(omega + 1, dimension);
177  Tps<double> b(omega + 1, dimension);
178  Tps<double> pi(omega + 1, dimension);
179  Tps<double> t(omega + 1, dimension);
180 
181  for(int m = f.getSize(omega); m < f.getSize(omega + 1); m++) {
182  const TpsMonomial &index = f.getExponents(m);
183  complex<double> factor = 1.0;
184  bool reject = true;
185  int count = 0;
186 
187  for(int j = 0; j < 2 * freedom; j += 2) {
188  if(index[j] != index[j+1]) reject = false;
189  if(imag(lambda(j)) != 0.0) count += index[j+1];
190  factor *= pow(lambda(j), int(index[j] - index[j+1]));
191  }
192 
193  if(reject || std::abs(1.0 - factor) < tol) {
194  // Term cannot be removed.
195  t.setCoefficient(m, 0.5);
196  } else {
197  // Term can be removed.
198  factor = 1.0 / (1.0 - factor);
199  a.setCoefficient(m, real(factor));
200  b.setCoefficient(m, imag(factor));
201  }
202 
203  pi.setCoefficient(m, pow(-1.0, int(count + 1) / 2));
204  }
205 
206  // Compute cal_T^(-1) * f and T_omega.
207  Tps<double> f1 = pi.scaleMonomials(f).substitute(R_dir);
208  Tps<double> f2 = f1.substitute(I_dir);
209 
210  // Remove terms of order "omega" and accumulate map script(A).
211  Tps<double> F_omega =
212  (a.scaleMonomials(f1).substitute(R_inv) +
213  b.scaleMonomials(f2).substitute(R_inv)).scaleMonomials(pi);
214 
215  if(F_omega != 0.0) {
216  LieMap<double> A_omega =
217  LieMap<double>::ExpMap(F_omega.substitute(Rot_inv));
218  LieMap<double> A_invert = LieMap<double>::ExpMap(- F_omega);
219  N_scr = A_invert.substitute(N_scr).substitute(A_omega);
220  A_Lie -= F_omega;
221  }
222 
223  // Accumulate map script(N).
224  Tps<double> T_omega = t.scaleMonomials(f1 + f2).
225  substitute(R_inv).scaleMonomials(pi);
226 
227  if(T_omega != 0.0) {
228  N_acc = LieMap<double>::ExpMap(- T_omega, N_acc);
229  N_Lie -= T_omega;
230  }
231  }
232 }
233 
234 
236 {}
237 
238 
240  return freedom;
241 }
242 
243 
246  using Physics::pi;
247 
248  for(int mode1 = 0; mode1 < freedom; mode1++) {
249  {
250  TpsMonomial power1(dimension), power2(dimension), power3(dimension);
251  power1[2*mode1] = power3[2*mode1+1] = 4;
252  power2[2*mode1] = power2[2*mode1+1] = 2;
253  QQ(mode1, mode1) =
254  - (3.0 * (N_Lie.getCoefficient(power1) +
255  N_Lie.getCoefficient(power3)) +
256  N_Lie.getCoefficient(power2)) / (4.0 * pi);
257  }
258 
259  for(int mode2 = mode1 + 1; mode2 < freedom; mode2++) {
260  TpsMonomial power1(dimension), power2(dimension),
261  power3(dimension), power4(dimension);
262  power1[2*mode1] = power1[2*mode2] = 2;
263  power2[2*mode1+1] = power2[2*mode2] = 2;
264  power3[2*mode1] = power3[2*mode2+1] = 2;
265  power4[2*mode1+1] = power4[2*mode2+1] = 2;
266  QQ(mode1, mode2) = QQ(mode2, mode1) =
267  - (N_Lie.getCoefficient(power1) +
268  N_Lie.getCoefficient(power2) +
269  N_Lie.getCoefficient(power3) +
270  N_Lie.getCoefficient(power4)) / (4.0 * pi);
271  }
272  }
273 
274  return QQ;
275 }
276 
277 
279  Tps<double> a(1, V.nrows());
280  Tps<double> b(1, V.nrows());
281 
282  for(int j = 0; j < V.nrows(); j += 2) {
283  a.setCoefficient(j + 1, - V(j + 1, 2 * mode));
284  a.setCoefficient(j + 2, V(j, 2 * mode));
285  b.setCoefficient(j + 1, - V(j + 1, 2 * mode + 1));
286  b.setCoefficient(j + 2, V(j, 2 * mode + 1));
287  }
288 
289  if(imag(lambda(2 * mode)) != 0.0) {
290  return (a * a + b * b);
291  } else {
292  return (a * a - b * b);
293  }
294 }
295 
296 
298  return N_Lie;
299 }
300 
301 
303  return A_Lie;
304 }
305 
306 
308  return lambda;
309 }
310 
311 
313  return V;
314 }
315 
316 
317 void NormalForm::orderModes(Vector<complex<double> > tlam, Matrix<double> tmat) {
318  // Copy complex eigenvalue pairs.
319  int nDim = dimension;
320  int n_c = 0;
321  int n_r = 0;
322 
323  for(int i = 0; i < dimension;) {
324  if(imag(tlam(i)) != 0.0) {
325  // Normalise this complex pair.
326  double pb = 0.0;
327 
328  for(int j = 0; j < dimension; j += 2) {
329  pb += tmat(j, i) * tmat(j + 1, i + 1) - tmat(j + 1, i) * tmat(j, i + 1);
330  }
331 
332  lambda(n_c) = tlam(i);
333  lambda(n_c + 1) = tlam(i + 1);
334  if(pb < 0.0) swap(lambda(i), lambda(i + 1));
335  double fact1 = sqrt(std::abs(pb));
336  double fact2 = pb / fact1;
337 
338  for(int j = 0; j < dimension; j++) {
339  V(j, n_c) = tmat(j, i) / fact1;
340  V(j, n_c + 1) = tmat(j, i + 1) / fact2;
341  }
342 
343  i += 2;
344  n_c += 2;
345  } else {
346  if(std::abs(real(tlam(i)) - 1.0) < tol) {
347  // Move unit eigenvalues to upper end.
348  for(int j = 0; j < dimension; j++) {
349  V(j, nDim - 1) = 0.0;
350  }
351 
352  lambda(nDim - 1) = 1.0;
353  V(nDim - 1, nDim - 1) = 1.0;
354  nDim--;
355  } else {
356  // Compact storage of remaining real eigenvalues.
357  if(n_r != i) {
358  tlam(n_r) = tlam(i);
359  std::copy(tmat.col_begin(i), tmat.col_end(i), tmat.col_begin(n_r));
360  }
361 
362  n_r++;
363  }
364 
365  i++;
366  }
367  }
368 
369  // Copy remaining real eigenvalue pairs.
370  for(int i = 0; i < n_r;) {
371  int m;
372  for(m = i + 1; m < n_r; m++) {
373  if(std::abs(tlam(i) * tlam(m) - 1.0) < tol) break;
374  }
375 
376  if(m >= n_r) {
377  throw LogicalError("FNormalForm::orderModes()",
378  "Cannot find pair of real eigenvalues.");
379  }
380 
381  // Swap values to make pair.
382  if(m != i + 1) {
383  swap(tlam(m), tlam(i + 1));
384  tmat.swapColumns(m, i + 1);
385  }
386 
387  // Normalise this real pair and convert it to Sinh/Cosh form.
388  double pb = 0.0;
389 
390  for(int j = 0; j < dimension; j += 2) {
391  pb += tmat(j, i) * tmat(j + 1, i + 1) - tmat(j + 1, i) * tmat(j, i + 1);
392  }
393 
394 
395  pb = sqrt(2.0 * std::abs(pb));
396  lambda(n_c) = tlam(i);
397  lambda(n_c + 1) = tlam(i + 1);
398 
399  for(int j = 0; j < dimension; j++) {
400  V(j, n_c) = (tmat(j, i) + tmat(j, i + 1)) / pb;
401  V(j, n_c + 1) = (tmat(j, i) - tmat(j, i + 1)) / pb;
402  }
403 
404  if(pb < 0.0) {
405  swap(lambda(i), lambda(i + 1));
406  V.swapColumns(i, i + 1);
407  }
408 
409  n_c += 2;
410  i += 2;
411  }
412 
413  freedom = nDim / 2;
414  if(nDim != 2 * freedom) {
415  throw LogicalError("NormalForm::orderModes()", "Map dimension is odd.");
416  }
417 
418  // Re-order eigenvector pairs by their main components.
419  for(int i = 0; i < nDim; i += 2) {
420  // Find eigenvector pair with larges component in direction i/2.
421  double big = 0.0;
422  int k = i;
423  for(int j = i; j < dimension; j += 2) {
424  double c = std::abs(V(j, i) * V(j + 1, i + 1) - V(j, i + 1) * V(j + 1, i));
425 
426  if(c > big) {
427  big = c;
428  k = j;
429  }
430  }
431 
432  if(k != i) {
433  // Move eigenvector pair to its place.
434  swap(lambda(i), lambda(k));
435  swap(lambda(i + 1), lambda(k + 1));
436  V.swapColumns(i, k);
437  V.swapColumns(i + 1, k + 1);
438  }
439 
440  if(imag(lambda(i)) != 0.0) {
441  // Rotate complex eigenvectors to make their main components real.
442  double re = V(i, i) / sqrt(V(i, i) * V(i, i) + V(i, i + 1) * V(i, i + 1));
443  double im = V(i, i + 1) / sqrt(V(i, i) * V(i, i) + V(i, i + 1) * V(i, i + 1));
444 
445  for(int j = 0; j < dimension; j++) {
446  double real_part = re * V(j, i) + im * V(j, i + 1);
447  double imag_part = re * V(j, i + 1) - im * V(j, i);
448  V(j, i) = real_part;
449  V(j, i + 1) = imag_part;
450  }
451  }
452  }
453 }
void swapColumns(int c1, int c2)
Exchange columns.
Definition: Array2D.h:459
const Tps< double > & normalForm() const
Get normal-form.
Definition: NormalForm.cpp:297
Tps< T > substitute(const Matrix< T > &M) const
Substitute.
Definition: Tps.hpp:614
PETE_TUTree< FnAbs, typename T::PETE_Expr_t > abs(const PETE_Expr< T > &l)
Triangle decomposition of a matrix.
Definition: LUMatrix.h:44
Double eigenvector routines.
Definition: DoubleEigen.h:36
Tps< double > N_Lie
Definition: NormalForm.h:102
int degreesOfFreedom() const
Get number of stable degrees of freedom.
Definition: NormalForm.cpp:239
const Matrix< double > & eigenVectors() const
Get eigenvectors.
Definition: NormalForm.cpp:312
col_iterator col_begin(int c)
Get column iterator.
Definition: Array2D.h:391
Size error exception.
Definition: SizeError.h:33
int getSize() const
Get number of coefficients.
Definition: Tps.hpp:1049
int dimension
Definition: NormalForm.h:93
Matrix< double > anharmonicity() const
Get anharmonicities.
Definition: NormalForm.cpp:244
Matrix< double > packedEigenVectors() const
Get eigenvectors.
static LieMap< T > ExpMap(const Tps< T > &H)
Lie series.
Definition: LieMap.h:182
const TpsMonomial & getExponents(int index) const
Get exponents.
Definition: Tps.hpp:1073
col_iterator col_end(int c)
Get column iterator.
Definition: Array2D.h:400
Tps< T > scaleMonomials(const Tps< T > &y) const
Multiply monomial-wise.
Definition: Tps.hpp:992
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.
int getTruncOrder() const
Get lowest truncation order in any component.
Definition: Vps.hpp:266
int getVariables() const
Get number of variables (the same in all components).
Definition: Vps.hpp:279
Vps< T > filter(int lowOrder, int highOrder) const
Extract range of orders, set others to zero.
Definition: Vps.hpp:286
Lie algebraic map.
Definition: LieMap.h:40
void orderModes(Vector< complex< double > >, Matrix< double >)
Definition: NormalForm.cpp:317
constexpr double pi
The value of .
Definition: Physics.h:31
const double pi
Definition: fftpack.cpp:894
constexpr double c
The velocity of light in m/s.
Definition: Physics.h:52
Tps< double > A_Lie
Definition: NormalForm.h:99
const Tps< double > & normalisingMap() const
Get normalising map.
Definition: NormalForm.cpp:302
VpsMap< T > substitute(const VpsMap< T > &vv) const
Substitute.
Definition: VpsMap.h:221
Matrix< T > linearTerms(const Vector< T > &y) const
Extract linear terms at point.
Definition: VpsMap.h:274
Tps< T > pow(const Tps< T > &x, int y)
Integer power.
Definition: TpsMath.h:76
MMatrix< double > im(MMatrix< m_complex > mc)
Definition: MMatrix.cpp:398
arg(a))
Vector< complex< double > > lambda
Definition: NormalForm.h:105
Tps< T > sqrt(const Tps< T > &x)
Square root.
Definition: TpsMath.h:91
MMatrix< double > re(MMatrix< m_complex > mc)
Definition: MMatrix.cpp:389
void setCoefficient(int index, const T &value)
Set coefficient.
Definition: Tps.hpp:337
int freedom
Definition: NormalForm.h:96
Exponent array for Tps&lt;T&gt;.
Definition: TpsMonomial.h:31
Matrix< double > V
Definition: NormalForm.h:108
const T getCoefficient(int index) const
Get coefficient.
Definition: Tps.hpp:327
Vector.
FLieGenerator< T, N > imag(const FLieGenerator< std::complex< T >, N > &)
Take imaginary part of a complex generator.
int nrows() const
Get number of rows.
Definition: Array2D.h:301
Resonance-free normal form.
Definition: NormalForm.h:41
Vector< complex< double > > eigenValues() const
Get eigenvalues.
Tps< double > invariant(int i) const
Get invariant polynomial.
Definition: NormalForm.cpp:278
Logical error exception.
Definition: LogicalError.h:33
const Vector< complex< double > > & eigenValues() const
Get eigenvalues.
Definition: NormalForm.cpp:307