CHROMA
mdwf_solver.cc
Go to the documentation of this file.
1 // -*- C++ -*-
2 /*! \file
3  * \brief DWF/SSE double-prec solver
4  */
5 
7 
8 extern "C" {
9 #include <qop-mdwf3.h>
10 };
11 
13 #include "io/aniso_io.h"
15 
16 
17 using namespace QDP;
18 namespace Chroma
19 {
20  //! AVP's DWF Solver interface
21  /*!
22  * \ingroup qprop
23  *
24  * @{
25  */
26 
27  /* Utility functions -- stick these in local anonymous namespace */
28 
29  namespace {
30 
31  double gaugeReader(int mu,
32  const int latt_coord[4],
33  int row,
34  int col,
35  int reim,
36  void *env)
37  {
38  START_CODE();
39  /* Translate arg */
40  multi1d<LatticeColorMatrix>& u = *(multi1d<LatticeColorMatrix>*)env;
41 
42  // Get node and index
43  multi1d<int> coord(Nd);
44  coord = latt_coord;
45  int node = Layout::nodeNumber(coord);
46  int linear = Layout::linearSiteIndex(coord);
47 
48  if (node != Layout::nodeNumber()) {
49 
50  QDPIO::cerr << __func__ << ": wrong coordinates for this node" << std::endl;
51  QDP_abort(1);
52  }
53 
54  // Get the value
55  // NOTE: it would be nice to use the "peek" functions, but they will
56  // broadcast to all nodes the value since they are platform independent.
57  // We don't want that, so we poke into the on-node data
58  double val = (reim == 0) ?
59  toDouble(u[mu].elem(linear).elem().elem(row,col).real()) :
60  toDouble(u[mu].elem(linear).elem().elem(row,col).imag());
61 
62 
63  END_CODE();
64  return val;
65 
66  }
67 
68 
69  // Fermion Reader function - user supplied
70  double fermionReader(const int latt_coord[5],
71  int color,
72  int spin,
73  int reim,
74  void *env)
75  {
76  START_CODE();
77 
78  /* Translate arg */
79  multi1d<LatticeFermion>& psi = *(multi1d<LatticeFermion>*)env;
80 
81  // Get node and index
82  int s = latt_coord[Nd];
83  multi1d<int> coord(Nd);
84  coord = latt_coord;
85  int node = Layout::nodeNumber(coord);
86  int linear = Layout::linearSiteIndex(coord);
87 
88  if (node != Layout::nodeNumber()) {
89 
90  QDPIO::cerr << __func__ << ": wrong coordinates for this node" << std::endl;
91  QDP_abort(1);
92  }
93 
94  // Get the value
95  // NOTE: it would be nice to use the "peek" functions, but they will
96  // broadcast to all nodes the value since they are platform independent.
97  // We don't want that, so we poke into the on-node data
98  double val = (reim == 0) ?
99  double(psi[s].elem(linear).elem(spin).elem(color).real()) :
100  double(psi[s].elem(linear).elem(spin).elem(color).imag());
101 
102  // if (spin >= Ns/2)
103  // val *= -1;
104 
105  END_CODE();
106  return val;
107  }
108 
109 
110 
111 
112  // Fermion Writer function - user supplied
113  void fermionWriter(const int latt_coord[5],
114  int color,
115  int spin,
116  int reim,
117  double val,
118  void *env )
119 
120  {
121  START_CODE();
122 
123  /* Translate arg */
124  multi1d<LatticeFermion>& psi = *(multi1d<LatticeFermion>*)env;
125 
126  // Get node and index
127  int s = latt_coord[Nd];
128  multi1d<int> coord(Nd);
129  coord = latt_coord;
130  int node = Layout::nodeNumber(coord);
131  int linear = Layout::linearSiteIndex(coord);
132 
133  if (node != Layout::nodeNumber()) {
134  QDPIO::cerr << __func__ << ": wrong coordinates for this node" << std::endl;
135  QDP_abort(1);
136  }
137 
138  // Rescale
139  // if (spin >= Ns/2)
140  // val *= -1;
141 
142  // val *= -2.0;
143 
144  // Set the value
145  // NOTE: it would be nice to use the "peek" functions, but they will
146  // broadcast to all nodes the value since they are platform independent.
147  // We don't want that, so we poke into the on-node data
148  if (reim == 0)
149  psi[s].elem(linear).elem(spin).elem(color).real() = val;
150  else
151  psi[s].elem(linear).elem(spin).elem(color).imag() = val;
152 
153  END_CODE();
154  return;
155  }
156 
157  // Env is for the interface spec. I ignore it completely
158  void sublattice_func(int lo[],
159  int hi[],
160  const int node[],
161  void *env)
162  {
163  START_CODE();
164  // Given my node coordinates in node[]
165  // produce the lo/hi pair
166 
167  // This is the size on the local subgrid.
168  // For QDP++ they are all the same.
169  const multi1d<int>& local_subgrid=Layout::subgridLattSize();
170  for(int i=0; i <Nd; i++) {
171  // The lowest coordinate is just the node coordinate
172  // times the local subgrid size in that direction
173  lo[i]=node[i]*local_subgrid[i];
174 
175  // The high is the start of the next 'corner'
176  // I would say my hi is hi[i]-1 but am following the
177  // document conventions
178  hi[i]=(node[i]+1)*local_subgrid[i];
179  }
180 
181  END_CODE();
182  return;
183  }
184  } // End of anonymous namespace
185 
186  //! Solver the linear system
187  /*!
188  * \param psi quark propagator ( Modify )
189  * \param chi source ( Read )
190  * \return number of CG iterations
191  */
193  const multi1d<LatticeFermion>& chi) const
194  {
195  START_CODE();
196 
198  res.n_count = 0;
199  int out_iters_single=0;
200  int out_iters_double=0;
201 
202  /* Stuff for flopcounting */
203  double time_sec;
204  long long flops;
205  long long sent; /* Messages? Bytes? Faces? */
206  long long received; /* Messages? Bytes? Receives? */
207 
208 
209  /* Single Precision Branch. */
210  {
211  double out_eps_single;
212 
213  // OK Try to solve for the single precision
214  // Cast to single precision gauge field
215  QOP_F3_MDWF_Gauge *sprec_gauge = NULL;
216  if ( QOP_F3_MDWF_import_gauge(&sprec_gauge,
217  state,
218  gaugeReader,
219  (void *)&u) != 0 ) {
220  QDPIO::cerr << "MDWF Error: "<< QOP_MDWF_error(state) << std::endl;
221  QDP_abort(1);
222  }
223 
224  /* Fermion fields */
225  QOP_F3_MDWF_Fermion *sprec_rhs;
226  QOP_F3_MDWF_Fermion *sprec_x0;
227  QOP_F3_MDWF_Fermion *sprec_soln;
228 
229  if( QOP_F3_MDWF_import_fermion(&sprec_rhs,
230  state,
231  fermionReader,
232  (void *)&chi) != 0 ) {
233  QDPIO::cerr << "MDWF Error: "<< QOP_MDWF_error(state) << std::endl;
234  QDP_abort(1);
235  }
236 
237  if( QOP_F3_MDWF_import_fermion(&sprec_x0,
238  state,
239  fermionReader,
240  (void *)&psi) != 0 ) {
241  QDPIO::cerr << "MDWF Error: "<< QOP_MDWF_error(state) << std::endl;
242  QDP_abort(1);
243  }
244 
245  if( QOP_F3_MDWF_allocate_fermion(&sprec_soln,
246  state) != 0 ) {
247  QDPIO::cerr << "MDWF Error: "<< QOP_MDWF_error(state) << std::endl;
248  QDP_abort(1);
249  }
250 
251  /* Do the solve */
252 
253  double target_epsilon = toDouble(invParam.RsdCG*invParam.RsdCG);
254  int max_iteration = invParam.MaxCG;
255  int status;
256 
257  QDPIO::cout << "MDWFQpropT: Beginning Single Precision Solve" << std::endl;
258  if( ( status=QOP_F3_MDWF_DDW_CG(sprec_soln,
259  &out_iters_single,
260  &out_eps_single,
261  params,
262  sprec_x0,
263  sprec_gauge,
264  sprec_rhs,
265  max_iteration,
266  target_epsilon,
267  QOP_MDWF_LOG_NONE)) != 0 ) {
268  QDPIO::cerr << "MDWF Error: "<< QOP_MDWF_error(state) << std::endl;
269  QDP_abort(1);
270  }
271 
272  /* Get Perf counters before solve */
273  if( QOP_MDWF_performance(&time_sec,
274  &flops,
275  &sent,
276  &received,
277  state) != 0 ) {
278  QDPIO::cerr << "MDWF_Error: "<< QOP_MDWF_error(state) << std::endl;
279  QDP_abort(1);
280  }
281 
282  /* Report status */
283  QDPIO::cout << "MDWFQpropT Single Prec : status=" << status
284  << " iterations=" << out_iters_single
285  << " resulting epsilon=" << sqrt(out_eps_single) << std::endl;
286 
287  /* Report Flops */
288  FlopCounter flopcount_single;
289  flopcount_single.reset();
290  flopcount_single.addFlops(flops);
291  flopcount_single.report("MDWFQpropT_Single_Prec:", time_sec);
292 
293  /* Export the solution */
294  if( QOP_F3_MDWF_export_fermion(fermionWriter,
295  &psi,
296  sprec_soln) != 0 ) {
297  QDPIO::cerr << "MDWF Error: "<< QOP_MDWF_error(state) << std::endl;
298  QDP_abort(1);
299  }
300 
301  /* Now I can free the fermions */
302  QOP_F3_MDWF_free_fermion(&sprec_soln);
303  QOP_F3_MDWF_free_fermion(&sprec_x0);
304  QOP_F3_MDWF_free_fermion(&sprec_rhs);
305  QOP_F3_MDWF_free_gauge(&sprec_gauge);
306 
307  res.n_count = out_iters_single;
308 
309  }
310 
311  /* DoublePrecision Branch. */
312  {
313  double out_eps_double;
314 
315  // OK Try to solve for the single precision
316  // Cast to single precision gauge field
317  QOP_D3_MDWF_Gauge *dprec_gauge = NULL;
318  if ( QOP_D3_MDWF_import_gauge(&dprec_gauge,
319  state,
320  gaugeReader,
321  (void *)&u) != 0 ) {
322  QDPIO::cerr << "MDWF Error: "<< QOP_MDWF_error(state) << std::endl;
323  QDP_abort(1);
324  }
325 
326  /* Fermion fields */
327  QOP_D3_MDWF_Fermion *dprec_rhs; // Right hand side
328  QOP_D3_MDWF_Fermion *dprec_x0; // Guess
329  QOP_D3_MDWF_Fermion *dprec_soln; // result
330 
331  if( QOP_D3_MDWF_import_fermion(&dprec_rhs,
332  state,
333  fermionReader,
334  (void *)&chi) != 0 ) {
335  QDPIO::cerr << "MDWF Error: "<< QOP_MDWF_error(state) << std::endl;
336  QDP_abort(1);
337  }
338 
339  if( QOP_D3_MDWF_import_fermion(&dprec_x0,
340  state,
341  fermionReader,
342  (void *)&psi) != 0 ) {
343  QDPIO::cerr << "MDWF Error: "<< QOP_MDWF_error(state) << std::endl;
344  QDP_abort(1);
345  }
346 
347  if( QOP_D3_MDWF_allocate_fermion(&dprec_soln,
348  state) != 0 ) {
349  QDPIO::cerr << "MDWF Error: "<< QOP_MDWF_error(state) << std::endl;
350  QDP_abort(1);
351  }
352 
353  /* Do the solve */
354  double target_epsilon = toDouble(invParam.RsdCGRestart*invParam.RsdCGRestart);
355  int max_iteration = invParam.MaxCGRestart;
356  int status;
357 
358 
359  QDPIO::cout << "MDWFQpropT: Beginning Double Precision Solve" << std::endl;
360  if( ( status=QOP_D3_MDWF_DDW_CG(dprec_soln,
361  &out_iters_double,
362  &out_eps_double,
363  params,
364  dprec_x0,
365  dprec_gauge,
366  dprec_rhs,
367  max_iteration,
368  target_epsilon,
369  QOP_MDWF_LOG_NONE)) != 0 ) {
370  QDPIO::cerr << "MDWF Error: "<< QOP_MDWF_error(state) << std::endl;
371  QDP_abort(1);
372  }
373 
374  /* Get Perf counters after solve */
375  if( QOP_MDWF_performance(&time_sec,
376  &flops,
377  &sent,
378  &received,
379  state) != 0 ) {
380  QDPIO::cerr << "MDWF_Error: "<< QOP_MDWF_error(state) << std::endl;
381  QDP_abort(1);
382  }
383  /* Reoirt Status */
384  QDPIO::cout << "MDWFQpropT Double Prec: status=" << status
385  << " iterations=" << out_iters_double
386  << " resulting epsilon=" << sqrt(out_eps_double) << std::endl;
387 
388  /* Report Flops */
389  FlopCounter flopcount_double;
390  flopcount_double.reset();
391  flopcount_double.addFlops(flops);
392  flopcount_double.report("MDWFQpropT_Double_Prec:", time_sec);
393 
394  /* Export the solution */
395  if( QOP_D3_MDWF_export_fermion(fermionWriter,
396  &psi,
397  dprec_soln) != 0 ) {
398  QDPIO::cerr << "MDWF Error: "<< QOP_MDWF_error(state) << std::endl;
399  QDP_abort(1);
400  }
401 
402  /* Now I can free the fermions */
403  QOP_D3_MDWF_free_fermion(&dprec_soln);
404  QOP_D3_MDWF_free_fermion(&dprec_x0);
405  QOP_D3_MDWF_free_fermion(&dprec_rhs);
406  QOP_D3_MDWF_free_gauge(&dprec_gauge);
407 
408  /* Add the double prec iteration count onto the global count */
409  res.n_count += out_iters_double;
410  }
411 
412  // Compute actual residual
413  {
414  multi1d<LatticeFermion> r(N5);
415  A->unprecLinOp(r, psi, PLUS);
416  r -= chi;
417  res.resid = sqrt(norm2(r));
418  }
419  QDPIO::cout << "MDWF Final: single_iters=" << out_iters_single << " double_iters=" << out_iters_double << " total_iters=" << res.n_count << std::endl;
420  QDPIO::cout << "MDWF Final: final absolute unprec residuum="<<res.resid<<std::endl;
421 
422  END_CODE();
423  return res;
424  }
425 
426 
427  /* INIT Function */
428  void MDWFQpropT::init(Handle< FermState<T,P,Q> > fermstate, const GroupXML_t& inv)
429  {
430  START_CODE();
431 
432  if( Nd != 4 ) {
433  QDPIO::cout << "This will only work for Nd=4" << std::endl;
434  QDP_abort(1);
435  }
436 
437  if( Nc != 3 ) {
438  QDPIO::cout << "This will only work for Nc=3" << std::endl;
439  QDP_abort(1);
440  }
441 
442  // Read the XML for the CG params
443  try {
444  std::istringstream is(inv.xml);
445  XMLReader paramtop(is);
446  read(paramtop, inv.path, invParam);
447  }
448  catch (const std::string& e) {
449  QDPIO::cerr << "CGDWFQpropT: only support a CG inverter" << std::endl;
450  QDP_abort(1);
451  }
452 
453 
454  // I need to call Andrews init function.
455  // For this I need a function that can tell me the
456  multi1d<int> lattice(5);
457  multi1d<int> network(4);
458  multi1d<int> node_coords(4);
459  int master_p;
460 
461  // Lattice is the 5D lattice size
462  for(int mu=0; mu < Nd; mu++) {
463  lattice[mu] = Layout::lattSize()[mu];
464  }
465  lattice[Nd]=N5;
466 
467  for(int mu=0; mu < Nd; mu++) {
468  network[mu] = Layout::logicalSize()[mu];
469  node_coords[mu] = Layout::nodeCoord()[mu];
470  }
471 
472  // Master_p has to be zero on master node and nonzero
473  // elsewhere. This is odd.
474  if( Layout::primaryNode() ) {
475  master_p = 0;
476  }
477  else {
478  master_p = 1;
479  }
480 
481  // Announce a version just to be nice
482  QDPIO::cout << "MDWFQpropT: Initializing MDWF Library Version " << QOP_MDWF_version() << std::endl;
483 
484  // OK. Let's call Andrew's routine
485  if( QOP_MDWF_init(&state, lattice.slice(), network.slice(),
486  node_coords.slice(), master_p, sublattice_func,
487  NULL) != 0 ) {
488  // Nonzero return value => error
489  QDPIO::cerr << "MDWF Error: " << QOP_MDWF_error(state) << std::endl;
490  QDP_abort(1);
491  }
492 
493  // Set up the masses etc...
494  u.resize(Nd);
495  u = fermstate->getLinks();
496  Real ff = Real(1);
497 
498  if (anisoParam.anisoP) {
499  ff = where(anisoParam.anisoP, anisoParam.nu / anisoParam.xi_0, Real(1));
500  for(int mu=0; mu < u.size(); ++mu) {
501  if (mu != anisoParam.t_dir)
502  u[mu] *= ff;
503  }
504  }
505 
506  // Set the Shamir parameters for now
507  {
508  double a5 = (double)1;
509 
510  // Convention change. Now in the internal code it is 5 - OverMass
511  // To allow using anisotropy, I subtract off the 5 and 'add it back on'
512  // suitable for the anisotropic case
513  double M5 = (double)(-5) + toDouble((double)1 + a5*((double)1 + (double)(Nd-1)*ff - OverMass));
514 
515  double m_f = toDouble(Mass);
516 
517  if( QOP_MDWF_set_Shamir(&params, state, a5, M5 ,m_f) != 0){
518  QDPIO::cerr << "MDWF Error: " << QOP_MDWF_error(state)<< std::endl;
519  QDP_abort(1);
520  }
521 
522  }
523 
524  END_CODE();
525  return;
526  }
527 
528  // Finalize - destructor call
529  void MDWFQpropT::fini(void)
530  {
531  START_CODE();
532  QDPIO::cout << "MDWFQpropT: Finalizing MDWF Library Version " << QOP_MDWF_version() << std::endl;
533 
534  if (params != NULL) {
535  QOP_MDWF_free_parameters(&params);
536  }
537 
538  if (state != NULL) {
539  QOP_MDWF_fini(&state);
540  }
541 
542  END_CODE();
543  return;
544  }
545 
546 
547  /*! @} */ // end of group qprop
548 }
549 
Anisotropy parameters.
#define END_CODE()
Definition: chromabase.h:65
#define START_CODE()
Definition: chromabase.h:64
Support class for fermion actions and linear operators.
Definition: state.h:94
Class for counted reference semantics.
Definition: handle.h:33
int mu
Definition: cool.cc:24
Even-odd const determinant Wilson-like fermact.
void read(XMLReader &xml, const std::string &path, AsqtadFermActParams &param)
Read parameters.
Real Mass
Params params
unsigned s
Definition: ldumul_w.cc:37
unsigned i
Definition: ldumul_w.cc:34
DWF/SSE double-prec solver.
multi1d< int > coord(Nd)
Nd
Definition: meslate.cc:74
double gaugeReader(const void *OuterGauge, void *env, const int latt_coord[4], int mu, int row, int col, int reim)
void init(MesonSpecData_t &data, XMLWriter &xml, const std::string &path, const std::string &id_tag, const Params &params)
Do some initialization.
multi1d< Hadron2PtContraction_t > operator()(const multi1d< LatticeColorMatrix > &u)
Asqtad Staggered-Dirac operator.
Definition: klein_gord.cc:10
static multi1d< LatticeColorMatrix > u
@ PLUS
Definition: chromabase.h:45
A(A, psi, r, Ncb, PLUS)
const WilsonTypeFermAct< multi1d< LatticeFermion > > Handle< const ConnectState > state
Definition: pbg5p_w.cc:28
::std::string string
Definition: gtest.h:1979
multi1d< LatticeFermion > r(Ncb)
chi
Definition: pade_trln_w.cc:24
psi
Definition: pade_trln_w.cc:191
Hold group xml and type id.
Holds return info from SystemSolver call.
Definition: syssolver.h:17
Solve a CG1 system.