CHROMA
multipole_w.cc
Go to the documentation of this file.
1 /*! \file
2  * \brief Multipole moments
3  *
4  * Compute multipole moment within two hadron states using spherical
5  * harmonic expansion
6  */
7 
8 #include "chromabase.h"
9 #include "util/ft/sftmom.h"
11 
12 namespace Chroma
13 {
14 
15  // Write a Multipole_t
16  void write(XMLWriter& xml, const std::string& path, const Multipole_t::ElecMag_t& pole)
17  {
18  push(xml, path);
19 
20  write(xml, "L", pole.L);
21  write(xml, "M", pole.M);
22  write(xml, "electric", pole.electric);
23  write(xml, "magnetic", pole.magnetic);
24 
25  pop(xml);
26  }
27 
28 
29  // Write a Multipole_t
30  void write(XMLWriter& xml, const std::string& path, const Multipole_t& pole)
31  {
32  push(xml, path);
33 
34  int version = 1;
35  write(xml, "version", version);
36  write(xml, "j_decay", pole.j_decay);
37  write(xml, "Harmonic", pole.corr);
38 
39  pop(xml);
40  }
41 
42  // Anonymous namespace
43  namespace
44  {
45 
46  //! Dumb factorial function
47  int factorial(int m)
48  {
49  return (m <= 0) ? 1 : m*factorial(m-1);
50  }
51 
52  //! Normalization for Y_{LM}
53  Real normConst(int l, int m)
54  {
55  Real cnst = sqrt((2*l+1)*factorial(l-m)/(2*twopi*factorial(l+m)));
56  return cnst;
57  }
58 
59  // I'm lazy and want a function here. I don't remember if QDP does this
60  int sign_m(int m)
61  {
62  return ((-m) & 1 == 1) ? -1 : 1;
63  }
64 
65  //! Compute the P_{lm} Legendre polynomial
66  LatticeReal plgndr(int l, int m, const LatticeReal& x)
67  {
68 // if (m < 0 || m > l || fabs(x) > 1.0)
69  if (m < 0 || l < 0)
70  QDP_error_exit("Bad arguments in plgndr");
71 
72  if (m > l)
73  {
74  return LatticeReal(zero);
75  }
76 
77  LatticeReal pmm=1.0;
78  if (m > 0)
79  {
80  LatticeReal somx2 = sqrt((1.0-x)*(1.0+x));
81  LatticeReal fact = 1.0;
82  for (int i=1; i <= m; i++)
83  {
84  pmm *= -fact*somx2;
85  fact += 2.0;
86  }
87  }
88  if (l == m)
89  return pmm;
90  else
91  {
92  LatticeReal pmmp1 = x*(2*m+1)*pmm;
93  if (l == (m+1))
94  return pmmp1;
95  else
96  {
97  LatticeReal pll;
98  for (int ll=m+2; ll <= l; ll++)
99  {
100  pll = (x*(2*ll-1)*pmmp1-(ll+m-1)*pmm)/(ll-m);
101  pmm = pmmp1;
102  pmmp1 = pll;
103  }
104  return pll;
105  }
106  }
107  }
108 
109 
110  //! Compute r^L*Y_{LM}
111  LatticeComplex rYLM(const multi1d<LatticeReal>& x, int l, int m)
112  {
113  if (l < 0)
114  QDP_error_exit("Bad arguments in rYLM");
115 
116  if (x.size() != Nd-1)
117  QDP_error_exit("Bad arguments in rYLM");
118 
119  LatticeComplex ylm;
120 
121  if (m < -l || m > l)
122  {
123  ylm = zero;
124  return ylm;
125  }
126 
127  if (m < 0)
128  {
129  // Y_{l,-m} = (-1)^m * conj(Y_{l,m})
130  ylm = sign_m(m) * conj(rYLM(x,l,-m));
131  }
132  else
133  {
134  // Build radius squared
135  LatticeReal r_sq = zero;
136  for(int j=0; j < x.size(); ++j)
137  r_sq += x[j]*x[j];
138 
139  LatticeReal r = where(r_sq > fuzz, sqrt(r_sq), LatticeReal(1));
140 
141  // z = r*cos(theta)
142  LatticeReal cos_theta = x[2] / r;
143 
144  // y = r*sin(phi), x = r*cos(phi), y/x = tan(phi)
145  LatticeReal phi = atan2(x[1], x[0]);
146 
147  // Ylm = const*P_{LM}(cos(theta))*exp(i*m*phi)
148  ylm = normConst(l,m) * plgndr(l,m,cos_theta) * cmplx(cos(m*phi),sin(m*phi));
149  }
150 
151  return ylm;
152  }
153 
154 
155  //! Compute the grad(r^L*Y_{LM},x_k)
156  multi1d<LatticeComplex> deriv_rYLM(const multi1d<LatticeReal>& x, int l, int m)
157  {
158  if (l < 0)
159  QDP_error_exit("Bad arguments in deriv_rYLM");
160 
161  if (x.size() != Nd-1)
162  QDP_error_exit("Bad arguments in deriv_rYLM");
163 
164  if (Nd != 4)
165  QDP_error_exit("Bad arguments in deriv_rYLM");
166 
167  multi1d<LatticeComplex> ylm(Nd-1);
168 
169  // Special cases. NOTE: l=0 always gives zero
170  if (m < -l || m > l || l == 0)
171  {
172  ylm = zero;
173  return ylm;
174  }
175 
176  // Use -m relations
177  if (m < 0)
178  {
179  // Y_{l,-m} = (-1)^m * conj(Y_{l,m})
180  int ss = sign_m(m);
181  multi1d<LatticeComplex> foo = deriv_rYLM(x,l,-m);
182  for(int k=0; k < Nd-1; ++k)
183  ylm[k] = ss*conj(foo[k]);
184  return ylm;
185  }
186 
187  // NOTE: automate this by using a recurrence relation for the deriv.
188  // (1-x^2)d_x(P_l^m(x)) = -m*x*P_l^m(x) - (1-x^2)^{1/2}*P_l^{m+1)(x)
189  // where
190  // P_l^m(x) = (-1)^m * (1-x^2)^{m/2} * d^m P_l(x)/d x^m , note the P_l(x)
191  // are standard Legendre polynomials
192  //
193  // So here with x=cos(theta)
194  //
195  // d_theta P_l^m(cos(theta)) = + m P_l^m(cos(theta))/sin(theta) + P_l^{m+1}(cos(theta))
196  //
197  // Use gradient in coord space, but with deriv. in spherical polar space
198  // grad = \hat{x}*d_x + \hat{y}*d_y + \hat{z}*d_z
199  // d_x = sin(theta)*cos(phi)*d_r + (cos(theta)*cos(phi)/r)*d_theta
200  // - sin(phi)/(r*sin(theta)*d_phi
201  // d_y = sin(theta)*sin(phi)*d_r + (cos(theta)*sin(phi)/r)*d_theta
202  // + cos(phi)/(r*sin(theta)*d_phi
203  // d_z = cos(theta)*d_r - (sin(theta)/r)*d_theta
204 
205  // Build radius squared
206  LatticeReal r_sq = zero;
207  for(int j=0; j < x.size(); ++j)
208  r_sq += x[j]*x[j];
209 
210  LatticeReal r = where(r_sq > fuzz, sqrt(r_sq), LatticeReal(1));
211 
212  // z = r*cos(theta)
213  LatticeReal cos_theta = x[2] / r;
214  LatticeReal sin_theta = sin(acos(cos_theta));
215 
216  // y = r*sin(phi), x = r*cos(phi), y/x = tan(phi)
217  LatticeReal phi = atan2(x[1], x[0]);
218 
219  // This pops up a bunch
220  LatticeReal P_lm = plgndr(l,m,cos_theta);
221 
222  // We seem to always need the phase here even though it should
223  // cancel
224  LatticeComplex phase = cmplx(cos(m*phi),sin(m*phi));
225 
226  // r^{l-1}
227  LatticeReal rlm1;
228  if (l > 1)
229  rlm1 = pow(r,l-1);
230  else
231  rlm1 = 1;
232 
233  // ylm = normConst(l,m) * plgndr(l,m,cos_theta) * cmplx(cos(m*phi),sin(m*phi));
234 
235  // Handle limiting case of P_l^m(cos(theta))/sin(theta)
236  LatticeReal P_div_sintheta = where(sin_theta > fuzz,
237  P_lm/sin_theta,
238  Real(0));
239 
240  // Deriv of P_l^m(cos(theta))
241  LatticeReal deriv_P = m*cos_theta*P_div_sintheta + plgndr(l,m+1,cos_theta);
242 
243  // The pieces need for the derivatives with respect to x,y,z in terms of
244  // polar angles
245  LatticeComplex d_r = l*rlm1* P_lm * phase;
246  LatticeComplex d_theta = rlm1 * deriv_P * phase;
247  LatticeComplex d_phi = rlm1 * P_div_sintheta * timesI(Real(m)) * phase;
248 
249  // Finally, add the pieces together to make the derivatives
250  ylm[0] = sin_theta*cos(phi)*d_r + cos_theta*cos(phi)*d_theta - sin(phi)*d_phi;
251  ylm[1] = sin_theta*sin(phi)*d_r + cos_theta*sin(phi)*d_theta + cos(phi)*d_phi;
252  ylm[2] = cos_theta*d_r - sin_theta*d_theta;
253 
254  // Multiply in normalization
255  Real cnst = normConst(l,m) * sign_m(m);
256  for(int k=0; k < ylm.size(); ++k)
257  ylm[k] *= cnst;
258 
259  return ylm;
260  }
261 
262 
263  //! Compute the electric density
264  LatticeSpinMatrix elec_dens(int L, int M, int j_decay)
265  {
266  SpinMatrix g_one = 1.0;
267 
268  multi1d<LatticeReal> x(Nd-1);
269  for(int mu=0, j=0; mu < Nd; ++mu)
270  {
271  if (mu == j_decay) continue;
272 
273  x[j++] = Layout::latticeCoordinate(mu);
274  }
275 
276  // Compute r^L*Y_{LM}(x)*J_4(x)
277  return LatticeSpinMatrix((Gamma(1 << j_decay) * g_one) * rYLM(x,L,M));
278  }
279 
280 
281  //! Compute the magnetic density
282  LatticeSpinMatrix mag_dens(int L, int M, int j_decay)
283  {
284  LatticeSpinMatrix dens;
285  SpinMatrix g_one = 1.0;
286 
287  multi1d<LatticeReal> x(Nd-1);
288  multi1d<int> g(Nd-1);
289  for(int mu=0, j=0; mu < Nd; ++mu)
290  {
291  if (mu == j_decay) continue;
292 
293  x[j] = Layout::latticeCoordinate(mu);
294  g[j] = 1 << mu;
295  j++;
296  }
297 
298  // The density is (\vec{r} \cross \vec{J}(x)) \dot div(r^L*Y_{LM}(x))
299  // Here, use local current for \vec{J} so insertion is gamma_k
300  multi1d<LatticeComplex> deriv = deriv_rYLM(x,L,M);
301 
302  dens = (x[1]*(Gamma(g[2])*g_one) - x[2]*(Gamma(g[1])*g_one)) * deriv[0];
303  dens += (x[2]*(Gamma(g[0])*g_one) - x[0]*(Gamma(g[2])*g_one)) * deriv[1];
304  dens += (x[0]*(Gamma(g[1])*g_one) - x[1]*(Gamma(g[0])*g_one)) * deriv[2];
305 
306  return dens;
307  }
308  } // end anonymous namespace
309 
310 
311  //! Compute contractions for multipole moments
312  /*!
313  * \ingroup hadron
314  *
315  * \param quark_propagator quark propagator ( Read )
316  * \param seq_quark_prop sequential quark propagator ( Read )
317  * \param GammaInsertion extra gamma matrix insertion ( Read )
318  * \param max_power max value of L ( Read )
319  * \param j_decay direction of decay ( Read )
320  * \param t0 cartesian coordinates of the source ( Read )
321  * \param xml xml file object ( Write )
322  * \param xml_group std::string used for writing xml data ( Read )
323  */
324 
325  void multipole(const LatticePropagator& quark_propagator,
326  const LatticePropagator& seq_quark_prop,
327  int GammaInsertion,
328  int max_power,
329  int j_decay,
330  int t0,
331  XMLWriter& xml,
332  const std::string& xml_group)
333  {
334  START_CODE();
335 
336  if (Nd != 4)
337  QDP_error_exit("%s only designed and meaningful for Nd=4",__func__);
338 
339  if (j_decay < 0 || j_decay >= Nd)
340  QDP_error_exit("Invalid args to multipole");
341 
342  // Big struct of data
343  Multipole_t pole;
344  pole.j_decay = j_decay;
345 
346  // Initialize the slow Fourier transform phases
347  SftMom phases(0, true, j_decay);
348 
349  // Length of lattice in j_decay direction and 3pt correlations fcns
350  int length = phases.numSubsets();
351 
352  int G5 = Ns*Ns-1;
353 
354  // Construct the anti-quark propagator from the seq. quark prop.
355  LatticePropagator anti_quark_prop = Gamma(G5) * seq_quark_prop * Gamma(G5);
356 
357  pole.corr.resize(max_power+1);
358 
359  // Loop over possible "L" values
360  for(int L = 0; L < pole.corr.size(); ++L)
361  {
362  pole.corr[L].resize(2*L+1);
363 
364  // Loop over possible "M" values
365  for(int M = -L, MM = 0; MM < pole.corr[L].size(); ++M, ++MM)
366  {
367  // The electric multipole moment
368  // The density is r^L*Y_{LM}(x)*J_4(x)
369  // Here, use local current for J_4 so insertion is gamma_{j_decay}
370  multi1d<DComplex> hsum_elec;
371  {
372  LatticeComplex corr_fn =
373  trace(adj(anti_quark_prop) * elec_dens(L,M,j_decay) *
374  quark_propagator * Gamma(GammaInsertion));
375 
376  hsum_elec = sumMulti(corr_fn, phases.getSet());
377  }
378 
379  // The magnetic multipole moment
380  // The density is (\vec{r} \times \vec{J}(x)) \dot div(r^L*Y_{LM}(x))
381  // Here, use local current for \vec{J} so insertion is gamma_k
382  multi1d<DComplex> hsum_mag;
383  {
384  LatticeComplex corr_fn =
385  trace(adj(anti_quark_prop) * mag_dens(L,M,j_decay) *
386  quark_propagator * Gamma(GammaInsertion));
387 
388  hsum_mag = sumMulti(corr_fn, phases.getSet());
389  }
390 
391  multi1d<Complex> elec_corr(length);
392  multi1d<Complex> mag_corr(length);
393 
394  for (int t=0; t < length; ++t)
395  {
396  int t_eff = (t - t0 + length) % length;
397  elec_corr[t_eff] = Complex(hsum_elec[t]);
398  mag_corr[t_eff] = Complex(hsum_mag[t]);
399  } // end for(t)
400 
401  pole.corr[L][MM].L = L;
402  pole.corr[L][MM].M = M;
403  pole.corr[L][MM].electric = elec_corr;
404  pole.corr[L][MM].magnetic = mag_corr;
405 
406  } // end for(M)
407  } // end for(L)
408 
409  // Write the pole struct
410  write(xml, xml_group, pole);
411 
412  END_CODE();
413  }
414 
415 } // end namespace Chroma
Primary include file for CHROMA library code.
Fourier transform phase factor support.
Definition: sftmom.h:35
int numSubsets() const
Number of subsets - length in decay direction.
Definition: sftmom.h:63
const Set & getSet() const
The set to be used in sumMulti.
Definition: sftmom.h:57
int mu
Definition: cool.cc:24
void write(XMLWriter &xml, const std::string &path, const AsqtadFermActParams &param)
Writer parameters.
void multipole(const LatticePropagator &quark_propagator, const LatticePropagator &seq_quark_prop, int GammaInsertion, int max_power, int j_decay, int t0, XMLWriter &xml, const std::string &xml_group)
Compute contractions for multipole moments.
Definition: multipole_w.cc:325
unsigned j
Definition: ldumul_w.cc:35
static int m[4]
Definition: make_seeds.cc:16
int j_decay
Definition: meslate.cc:22
int x
Definition: meslate.cc:34
int t
Definition: meslate.cc:37
Nd
Definition: meslate.cc:74
LatticeReal phi
Definition: mesq.cc:27
Multipole moments.
Asqtad Staggered-Dirac operator.
Definition: klein_gord.cc:10
QDP_error_exit("too many BiCG iterations", n_count, rsd_sq, cp, c, re_rvr, im_rvr, re_a, im_a, re_b, im_b)
int G5
Definition: pbg5p_w.cc:57
push(xml_out,"Condensates")
int i
Definition: pbg5p_w.cc:55
const Real twopi
Definition: chromabase.h:55
pop(xml_out)
START_CODE()
Double zero
Definition: invbicg.cc:106
int k
Definition: invbicg.cc:119
::std::string string
Definition: gtest.h:1979
int l
Definition: pade_trln_w.cc:111
Fourier transform phase factor support.
multi1d< Complex > electric
Definition: multipole_w.h:27
multi1d< Complex > magnetic
Definition: multipole_w.h:28
Storage structure to hold electric and magnetic multipole moments.
Definition: multipole_w.h:22
multi1d< multi1d< ElecMag_t > > corr
Definition: multipole_w.h:32
multi1d< LatticeColorMatrix > deriv(const EvenOddPrecLinearOperator< LatticeFermion, multi1d< LatticeColorMatrix >, multi1d< LatticeColorMatrix > > &AP, const LatticeFermion &chi, const LatticeFermion &psi, enum PlusMinus isign)
Apply the operator onto a source std::vector.