1 //////////////////////////////////////////////////////////////////////////////////////
2 // This file is distributed under the University of Illinois/NCSA Open Source License.
3 // See LICENSE file in top directory for details.
4 //
5 // Copyright (c) 2016 Jeongnim Kim and QMCPACK developers.
6 //
7 // File developed by: Jaron T. Krogel, krogeljt@ornl.gov, Oak Ridge National Laboratory
8 //                    Mark A. Berrill, berrillma@ornl.gov, Oak Ridge National Laboratory
9 //
10 // File created by: Jaron T. Krogel, krogeljt@ornl.gov, Oak Ridge National Laboratory
11 //////////////////////////////////////////////////////////////////////////////////////
12 
13 
14 #include "SpaceGrid.h"
15 #include "OhmmsData/AttributeSet.h"
16 #include "Utilities/string_utils.h"
17 #include <cmath>
18 #include "OhmmsPETE/OhmmsArray.h"
19 
20 #include "Message/OpenMP.h"
21 
22 namespace qmcplusplus
23 {
24 using std::acos;
25 using std::atan2;
26 using std::cos;
27 using std::floor;
28 using std::sin;
29 using std::sqrt;
30 
31 
SpaceGrid(int & nvalues)32 SpaceGrid::SpaceGrid(int& nvalues)
33 {
34   nvalues_per_domain = nvalues;
35   Rptcl              = 0;
36   ndparticles        = 0;
37   periodic           = false;
38 }
39 
40 
put(xmlNodePtr cur,std::map<std::string,Point> & points,bool is_periodic,bool abort_on_fail)41 bool SpaceGrid::put(xmlNodePtr cur, std::map<std::string, Point>& points, bool is_periodic, bool abort_on_fail)
42 {
43   periodic       = is_periodic;
44   bool succeeded = true;
45   ndomains       = -1;
46   OhmmsAttributeSet ga;
47   std::string coord;
48   int npundef = -100000;
49   npmin       = npundef;
50   npmax       = npundef;
51   std::string ref("");
52   ga.add(coord, "coord"); //must be cartesian, cylindrical, or spherical
53   ga.add(npmin, "min_part");
54   ga.add(npmax, "max_part");
55   ga.add(ref, "reference");
56   ga.add(periodic,"periodic");
57   ga.put(cur);
58   if (coord == "cartesian")
59     coordinate = cartesian;
60   else if (coord == "cylindrical")
61     coordinate = cylindrical;
62   else if (coord == "spherical")
63     coordinate = spherical;
64   else if (coord == "voronoi")
65     coordinate = voronoi;
66   else
67   {
68     app_log() << "  Coordinate supplied to spacegrid must be cartesian, cylindrical, spherical, or voronoi"
69               << std::endl;
70     app_log() << "  You provided " << coord << std::endl;
71     succeeded = false;
72   }
73   chempot = npmin != npundef && npmax != npundef;
74   if (chempot)
75   {
76     npvalues = npmax - npmin + 1;
77     if (ref == "")
78       reference = noref;
79     else if (ref == "vacuum")
80       reference = vacuum;
81     else if (ref == "neutral")
82       reference = neutral;
83     else
84     {
85       app_log() << ref << " is not a valid reference, choose vacuum or neutral." << std::endl;
86       APP_ABORT("SpaceGrid::put()");
87     }
88     if (coordinate == voronoi)
89     {
90       if (reference == noref)
91         reference = neutral;
92     }
93     else
94     {
95       if (reference == noref)
96         reference = vacuum;
97       else if (reference == neutral)
98       {
99         APP_ABORT("SpaceGrid::put():  A rectilinear grid must be referenced to vacuum");
100       }
101     }
102   }
103   bool init_success;
104   if (coordinate == voronoi)
105     init_success = initialize_voronoi(points);
106   else
107     init_success = initialize_rectilinear(cur, coord, points);
108   succeeded = succeeded && init_success;
109   if (chempot && succeeded)
110   {
111     cellsamples.resize(ndomains, nvalues_per_domain + 1); //+1 is for count
112     std::fill(cellsamples.begin(), cellsamples.end(), 0.0);
113   }
114   if (abort_on_fail && !succeeded)
115   {
116     APP_ABORT("SpaceGrid::put");
117   }
118   return succeeded;
119 }
120 
121 
initialize_voronoi(std::map<std::string,Point> & points)122 bool SpaceGrid::initialize_voronoi(std::map<std::string, Point>& points)
123 {
124   bool succeeded = true;
125   if (Rptcl)
126   {
127     const ParticlePos_t& R = *Rptcl;
128     origin                 = points["center"];
129     ndomains               = R.size();
130     domain_volumes.resize(ndomains, 1);
131     domain_centers.resize(ndomains, DIM);
132     nearcell.resize(ndparticles);
133     for (int i = 0; i < ndomains; i++)
134       domain_volumes(i, 0) = 1.0; //voronoi grid stores values, not densities
135     for (int i = 0; i < ndomains; i++)
136       for (int d = 0; d < DIM; d++)
137         domain_centers(i, d) = R[i][d] - origin[d]; //cell centers are ion positions
138     for (int i = 0; i < ndparticles; i++)
139       nearcell[i].r = std::numeric_limits<RealType>::max();
140     volume = 1.0;
141     if (chempot)
142     {
143       reference_count.resize(ndomains);
144       switch (reference)
145       {
146       case (vacuum):
147         fill(reference_count.begin(), reference_count.end(), 0.0);
148         break;
149       case (neutral):
150       {
151         const std::vector<RealType>& Z = *Zptcl;
152         for (int i = 0; i < ndomains; i++)
153           reference_count[i] = (int)Z[i] + 1; //+1 is for ion itself
154         Zptcl = 0;
155       }
156       break;
157       default:
158         break;
159       }
160     }
161   }
162   else
163   {
164     app_log() << "  Pstatic must be specified for a Voronoi grid" << std::endl;
165     succeeded = false;
166   }
167   return succeeded;
168 }
169 
170 
initialize_rectilinear(xmlNodePtr cur,std::string & coord,std::map<std::string,Point> & points)171 bool SpaceGrid::initialize_rectilinear(xmlNodePtr cur, std::string& coord, std::map<std::string, Point>& points)
172 {
173   bool succeeded                  = true;
174   std::string ax_cartesian[DIM]   = {"x", "y", "z"};
175   std::string ax_cylindrical[DIM] = {"r", "phi", "z"};
176   std::string ax_spherical[DIM]   = {"r", "phi", "theta"};
177   std::map<std::string, int> cmap;
178   switch (coordinate)
179   {
180   case (cartesian):
181     for (int d = 0; d < DIM; d++)
182     {
183       cmap[ax_cartesian[d]] = d;
184       axlabel[d]            = ax_cartesian[d];
185     }
186     break;
187   case (cylindrical):
188     for (int d = 0; d < DIM; d++)
189     {
190       cmap[ax_cylindrical[d]] = d;
191       axlabel[d]              = ax_cylindrical[d];
192       periodic                = false;
193     }
194     break;
195   case (spherical):
196     for (int d = 0; d < DIM; d++)
197     {
198       cmap[ax_spherical[d]] = d;
199       axlabel[d]            = ax_spherical[d];
200       periodic              = false;
201     }
202     break;
203   default:
204     break;
205   }
206   //loop over spacegrid xml elements
207   xmlNodePtr element    = cur->children;
208   std::string undefined = "";
209   int iaxis       = 0;
210   int naxes       = 0;
211   bool has_origin = false;
212   origin          = points["zero"];
213   // variables for loop
214   RealType utol = 1e-5;
215   std::string grid;
216   std::vector<int> ndu_per_interval[DIM];
217   while (element != NULL)
218   {
219     std::string name      = (const char*)element->name;
220     OhmmsAttributeSet* ea = new OhmmsAttributeSet;
221     std::string p1        = undefined;
222     std::string p2        = "zero";
223     std::string fraction  = undefined;
224     RealType frac         = -1.0;
225     std::string scale     = undefined;
226     std::string label     = undefined;
227     grid                  = "0 1";
228     astring agrid;
229     ea->add(p1, "p1");
230     ea->add(p2, "p2");
231     ea->add(fraction, "fraction");
232     ea->add(scale, "scale");
233     ea->add(label, "label");
234     //ea->add(grid,"grid");
235     ea->add(agrid, "grid");
236     ea->put(element);
237     grid = agrid.s;
238     if (name == "origin")
239     {
240       if (p1 == undefined)
241       {
242         app_log() << "      p1 must be defined in spacegrid element " << name << std::endl;
243         succeeded = false;
244       }
245       if (has_origin)
246       {
247         app_log() << "      spacegrid must contain at most one origin element" << std::endl;
248         APP_ABORT("SpaceGrid::put");
249       }
250       else
251         has_origin = true;
252       if (fraction == undefined)
253         frac = 0.0;
254       else
255         frac = string2real(fraction);
256       origin = points[p1] + frac * (points[p2] - points[p1]);
257     }
258     else if (name == "axis")
259     {
260       if (p1 == undefined)
261       {
262         app_log() << "      p1 must be defined in spacegrid element " << name << std::endl;
263         succeeded = false;
264       }
265       naxes++;
266       if (naxes > DIM)
267       {
268         app_log() << "        spacegrid must contain " << DIM << " axes, " << naxes << "provided" << std::endl;
269         APP_ABORT("SpaceGrid::put");
270       }
271       if (cmap.find(label) == cmap.end())
272       {
273         app_log() << "      grid label " << label << " is invalid for " << coord << " coordinates" << std::endl;
274         app_log() << "      valid options are: ";
275         for (int d = 0; d < DIM; d++)
276           app_log() << axlabel[d] << ", ";
277         app_log() << std::endl;
278         APP_ABORT("SpaceGrid::put");
279       }
280       iaxis = cmap[label];
281       if (scale == undefined)
282         frac = 1.0;
283       else
284         frac = string2real(scale);
285       for (int d = 0; d < DIM; d++)
286         axes(d, iaxis) = frac * (points[p1][d] - points[p2][d]);
287       //read in the grid contents
288       //  remove spaces inside of parentheses
289       std::string gtmp;
290       bool inparen = false;
291       char gc;
292       for (int i = 0; i < grid.size(); i++)
293       {
294         gc = grid[i];
295         if (gc == '(')
296         {
297           inparen = true;
298           gtmp += ' ';
299         }
300         if (!(inparen && gc == ' '))
301           gtmp += gc;
302         if (gc == ')')
303         {
304           inparen = false;
305           gtmp += ' ';
306         }
307       }
308       grid = gtmp;
309       //  break into tokens
310       std::vector<std::string> tokens = split(grid, " ");
311       //  count the number of intervals
312       int nintervals = 0;
313       for (int i = 0; i < tokens.size(); i++)
314         if (tokens[i][0] != '(')
315           nintervals++;
316       nintervals--;
317       //  allocate temporary interval variables
318       std::vector<int> ndom_int, ndu_int;
319       std::vector<RealType> du_int;
320       ndom_int.resize(nintervals);
321       du_int.resize(nintervals);
322       ndu_int.resize(nintervals);
323       //  determine number of domains in each interval and the width of each domain
324       RealType u1 = string2real(tokens[0]);
325       umin[iaxis] = u1;
326       if (std::abs(u1) > 1.0000001)
327       {
328         app_log() << "  interval endparticles cannot be greater than " << 1 << std::endl;
329         app_log() << "  endpoint provided: " << u1 << std::endl;
330         succeeded = false;
331       }
332       bool is_int        = false;
333       bool has_paren_val = false;
334       RealType du_i;
335       int ndom_i   = 1;
336       int interval = -1;
337       for (int i = 1; i < tokens.size(); i++)
338       {
339         if (tokens[i][0] != '(')
340         {
341           RealType u2 = string2real(tokens[i]);
342           umax[iaxis] = u2;
343           if (!has_paren_val)
344             du_i = u2 - u1;
345           has_paren_val = false;
346           interval++;
347           if (u2 < u1)
348           {
349             app_log() << "  interval (" << u1 << "," << u2 << ") is negative" << std::endl;
350             succeeded = false;
351           }
352           if (std::abs(u2) > 1.0000001)
353           {
354             app_log() << "  interval endparticles cannot be greater than " << 1 << std::endl;
355             app_log() << "  endpoint provided: " << u2 << std::endl;
356             succeeded = false;
357           }
358           if (is_int)
359           {
360             du_int[interval]   = (u2 - u1) / ndom_i;
361             ndom_int[interval] = ndom_i;
362           }
363           else
364           {
365             du_int[interval]   = du_i;
366             ndom_int[interval] = floor((u2 - u1) / du_i + .5);
367             if (std::abs(u2 - u1 - du_i * ndom_int[interval]) > utol)
368             {
369               app_log() << "  interval (" << u1 << "," << u2 << ") not divisible by du=" << du_i << std::endl;
370               succeeded = false;
371             }
372           }
373           u1 = u2;
374         }
375         else
376         {
377           has_paren_val         = true;
378           std::string paren_val = tokens[i].substr(1, tokens[i].length() - 2);
379           is_int                = tokens[i].find(".") == std::string::npos;
380           if (is_int)
381           {
382             ndom_i = string2int(paren_val);
383             du_i   = -1.0;
384           }
385           else
386           {
387             ndom_i = 0;
388             du_i   = string2real(paren_val);
389           }
390         }
391       }
392       // find the smallest domain width
393       RealType du_min = 1.0;
394       for (int i = 0; i < du_int.size(); i++)
395         du_min = std::min(du_min, du_int[i]);
396       odu[iaxis] = 1.0 / du_min;
397       // make sure it divides into all other domain widths
398       for (int i = 0; i < du_int.size(); i++)
399       {
400         ndu_int[i] = floor(du_int[i] / du_min + .5);
401         if (std::abs(du_int[i] - ndu_int[i] * du_min) > utol)
402         {
403           app_log() << "interval " << i + 1 << " of axis " << iaxis + 1 << " is not divisible by smallest subinterval "
404                     << du_min << std::endl;
405           succeeded = false;
406         }
407       }
408       // set up the interval map such that gmap[u/du]==domain index
409       gmap[iaxis].resize(floor((umax[iaxis] - umin[iaxis]) * odu[iaxis] + .5));
410       int n  = 0;
411       int nd = -1;
412       for (int i = 0; i < ndom_int.size(); i++)
413         for (int j = 0; j < ndom_int[i]; j++)
414         {
415           nd++;
416           for (int k = 0; k < ndu_int[i]; k++)
417           {
418             //app_log()<<"        accessing gmap "<<iaxis<<" "<<n<< std::endl;
419             gmap[iaxis][n] = nd;
420             n++;
421           }
422         }
423       dimensions[iaxis] = nd + 1;
424       //end read in the grid contents
425       //save interval width information
426       int ndom_tot = 0;
427       for (int i = 0; i < ndom_int.size(); i++)
428         ndom_tot += ndom_int[i];
429       ndu_per_interval[iaxis].resize(ndom_tot);
430       int idom = 0;
431       for (int i = 0; i < ndom_int.size(); i++)
432         for (int ii = 0; ii < ndom_int[i]; ii++)
433         {
434           ndu_per_interval[iaxis][idom] = ndu_int[i];
435           idom++;
436         }
437     }
438     delete ea;
439     element = element->next;
440   }
441   if (naxes != DIM)
442   {
443     app_log() << "  spacegrid must contain " << DIM << " axes, " << iaxis + 1 << "provided" << std::endl;
444     succeeded = false;
445   }
446   axinv = inverse(axes);
447   //check that all axis grid values fall in the allowed intervals
448   std::map<std::string, int> cartmap;
449   for (int d = 0; d < DIM; d++)
450   {
451     cartmap[ax_cartesian[d]] = d;
452   }
453   for (int d = 0; d < DIM; d++)
454   {
455     if (cartmap.find(axlabel[d]) != cartmap.end())
456     {
457       if (umin[d] < -1.0 || umax[d] > 1.0)
458       {
459         app_log() << "  grid values for " << axlabel[d] << " must fall in [-1,1]" << std::endl;
460         app_log() << "  interval provided: [" << umin[d] << "," << umax[d] << "]" << std::endl;
461         succeeded = false;
462       }
463     }
464     else if (axlabel[d] == "phi")
465     {
466       if (std::abs(umin[d]) + std::abs(umax[d]) > 1.0)
467       {
468         app_log() << "  phi interval cannot be longer than 1" << std::endl;
469         app_log() << "  interval length provided: " << std::abs(umin[d]) + std::abs(umax[d]) << std::endl;
470         succeeded = false;
471       }
472     }
473     else
474     {
475       if (umin[d] < 0.0 || umax[d] > 1.0)
476       {
477         app_log() << "  grid values for " << axlabel[d] << " must fall in [0,1]" << std::endl;
478         app_log() << "  interval provided: [" << umin[d] << "," << umax[d] << "]" << std::endl;
479         succeeded = false;
480       }
481     }
482   }
483   //set grid dimensions
484   // C/Python style indexing
485   dm[0] = dimensions[1] * dimensions[2];
486   dm[1] = dimensions[2];
487   dm[2] = 1;
488   // Fortran/Matlab style indexing
489   //dm[0] = 1;
490   //dm[1] = dimensions[0];
491   //dm[2] = dimensions[0]*dimensions[1];
492   ndomains = dimensions[0] * dimensions[1] * dimensions[2];
493   volume   = std::abs(det(axes)) * 8.0; //axes span only one octant
494   //compute domain volumes, centers, and widths
495   domain_volumes.resize(ndomains, 1);
496   domain_centers.resize(ndomains, DIM);
497   domain_uwidths.resize(ndomains, DIM);
498   std::vector<RealType> interval_centers[DIM];
499   std::vector<RealType> interval_widths[DIM];
500   for (int d = 0; d < DIM; d++)
501   {
502     int nintervals = ndu_per_interval[d].size();
503     app_log() << "nintervals " << d << " " << nintervals << std::endl;
504     interval_centers[d].resize(nintervals);
505     interval_widths[d].resize(nintervals);
506     interval_widths[d][0]  = ndu_per_interval[d][0] / odu[d];
507     interval_centers[d][0] = interval_widths[d][0] / 2.0 + umin[d];
508     for (int i = 1; i < nintervals; i++)
509     {
510       interval_widths[d][i]  = ndu_per_interval[d][i] / odu[d];
511       interval_centers[d][i] = interval_centers[d][i - 1] + .5 * (interval_widths[d][i] + interval_widths[d][i - 1]);
512     }
513     //app_log()<<"  interval widths"<< std::endl;
514     //for(int i=0;i<nintervals;i++)
515     //  app_log()<<"    "<<i<<" "<<interval_widths[d][i]<< std::endl;
516     //app_log()<<"  interval centers"<< std::endl;
517     //for(int i=0;i<nintervals;i++)
518     //  app_log()<<"    "<<i<<" "<<interval_centers[d][i]<< std::endl;
519   }
520   Point du, uc, ubc, rc;
521   RealType vol = 0.0;
522   RealType vol_tot = 0.0;
523   RealType vscale  = std::abs(det(axes));
524   for (int i = 0; i < dimensions[0]; i++)
525   {
526     for (int j = 0; j < dimensions[1]; j++)
527     {
528       for (int k = 0; k < dimensions[2]; k++)
529       {
530         int idomain = dm[0] * i + dm[1] * j + dm[2] * k;
531         du[0]       = interval_widths[0][i];
532         du[1]       = interval_widths[1][j];
533         du[2]       = interval_widths[2][k];
534         uc[0]       = interval_centers[0][i];
535         uc[1]       = interval_centers[1][j];
536         uc[2]       = interval_centers[2][k];
537         switch (coordinate)
538         {
539         case (cartesian):
540           vol = du[0] * du[1] * du[2];
541           ubc = uc;
542           break;
543         case (cylindrical):
544           uc[1]  = 2.0 * M_PI * uc[1] - M_PI;
545           du[1]  = 2.0 * M_PI * du[1];
546           vol    = uc[0] * du[0] * du[1] * du[2];
547           ubc[0] = uc[0] * cos(uc[1]);
548           ubc[1] = uc[0] * sin(uc[1]);
549           ubc[2] = uc[2];
550           break;
551         case (spherical):
552           uc[1] = 2.0 * M_PI * uc[1] - M_PI;
553           du[1] = 2.0 * M_PI * du[1];
554           uc[2] = M_PI * uc[2];
555           du[2] = M_PI * du[2];
556           vol   = (uc[0] * uc[0] + du[0] * du[0] / 12.0) * du[0] //r
557               * du[1]                                            //theta
558               * 2.0 * sin(uc[2]) * sin(.5 * du[2]);              //phi
559           ubc[0] = uc[0] * sin(uc[2]) * cos(uc[1]);
560           ubc[1] = uc[0] * sin(uc[2]) * sin(uc[1]);
561           ubc[2] = uc[0] * cos(uc[2]);
562           break;
563         default:
564           break;
565         }
566         vol *= vscale;
567         vol_tot += vol;
568         rc = dot(axes, ubc) + origin;
569         //app_log()<< std::endl;
570         //app_log()<<"umin "<<uc-du/2<< std::endl;
571         //app_log()<<"uc "<<uc<< std::endl;
572         //app_log()<<"umax "<<uc+du/2<< std::endl;
573         //app_log()<<"rc "<<rc<< std::endl;
574         domain_volumes(idomain, 0) = vol;
575         for (int d = 0; d < DIM; d++)
576         {
577           domain_uwidths(idomain, d) = du[d];
578           domain_centers(idomain, d) = rc[d];
579         }
580       }
581     }
582   }
583   ////the following check is only valid if grid spans maximum amount
584   ////check that the amount of space the grid takes up is correct
585   //RealType vfrac;
586   //switch(coordinate){
587   //case(cartesian):
588   //  vfrac=1.0;
589   //  break;
590   //case(cylindrical):
591   //  vfrac=M_PI/4.0;
592   //  break;
593   //case(spherical):
594   //  vfrac=M_PI/6.0;
595   //  break;
596   //default:
597   //  vfrac=vol_tot/volume;
598   //}
599   //if(std::abs(vol_tot/volume-vfrac)>1e-6){
600   //  app_log()<<"  "<<coord<<" relative volume"<< std::endl;
601   //  app_log()<<"  spacegrid volume fraction "<<vol_tot/volume<< std::endl;
602   //  app_log()<<"                  should be "<<vfrac<< std::endl;
603   //  succeeded=false;
604   //}
605   //find the actual volume of the grid
606   for (int d = 0; d < DIM; d++)
607   {
608     du[d] = umax[d] - umin[d];
609     uc[d] = .5 * (umax[d] + umin[d]);
610   }
611   switch (coordinate)
612   {
613   case (cartesian):
614     vol = du[0] * du[1] * du[2];
615     break;
616   case (cylindrical):
617     uc[1] = 2.0 * M_PI * uc[1] - M_PI;
618     du[1] = 2.0 * M_PI * du[1];
619     vol   = uc[0] * du[0] * du[1] * du[2];
620     break;
621   case (spherical):
622     uc[1] = 2.0 * M_PI * uc[1] - M_PI;
623     du[1] = 2.0 * M_PI * du[1];
624     uc[2] = M_PI * uc[2];
625     du[2] = M_PI * du[2];
626     vol   = (uc[0] * uc[0] + du[0] * du[0] / 12.0) * du[0] //r
627         * du[1]                                            //theta
628         * 2.0 * sin(uc[2]) * sin(.5 * du[2]);              //phi
629     break;
630   default:
631     break;
632   }
633   volume = vol * det(axes);
634   if (chempot)
635   {
636     reference_count.resize(ndomains);
637     //rectilinear is referenced to vacuum for now
638     fill(reference_count.begin(), reference_count.end(), 0.0);
639   }
640   succeeded = succeeded && check_grid();
641   return succeeded;
642 }
643 
644 
write_description(std::ostream & os,std::string & indent)645 void SpaceGrid::write_description(std::ostream& os, std::string& indent)
646 {
647   os << indent + "SpaceGrid" << std::endl;
648   std::string s;
649   switch (coordinate)
650   {
651   case cartesian:
652     s = "cartesian";
653     break;
654   case cylindrical:
655     s = "cylindrical";
656     break;
657   case spherical:
658     s = "spherical";
659     break;
660   default:
661     break;
662   }
663   os << indent + "  coordinates   = " + s << std::endl;
664   os << indent + "  buffer_offset = " << buffer_offset << std::endl;
665   os << indent + "  ndomains      = " << ndomains << std::endl;
666   os << indent + "  axes  = " << axes << std::endl;
667   os << indent + "  axinv = " << axinv << std::endl;
668   for (int d = 0; d < DIM; d++)
669   {
670     os << indent + "  axis " << axlabel[d] << ":" << std::endl;
671     os << indent + "    umin = " << umin[d] << std::endl;
672     os << indent + "    umax = " << umax[d] << std::endl;
673     os << indent + "    du   = " << 1.0 / odu[d] << std::endl;
674     os << indent + "    dm   = " << dm[d] << std::endl;
675     os << indent + "    gmap = ";
676     for (int g = 0; g < gmap[d].size(); g++)
677     {
678       os << gmap[d][g] << " ";
679     }
680     os << std::endl;
681   }
682   os << indent + "end SpaceGrid" << std::endl;
683 }
684 
685 
allocate_buffer_space(BufferType & buf)686 int SpaceGrid::allocate_buffer_space(BufferType& buf)
687 {
688   buffer_offset = buf.size();
689   if (!chempot)
690   {
691     std::vector<RealType> tmp(nvalues_per_domain * ndomains);
692     buf.add(tmp.begin(), tmp.end());
693     buffer_start = buffer_offset;
694     buffer_end   = buffer_start + nvalues_per_domain * ndomains - 1;
695   }
696   else
697   {
698     std::vector<RealType> tmp(nvalues_per_domain * npvalues * ndomains);
699     buf.add(tmp.begin(), tmp.end());
700     buffer_start = buffer_offset;
701     buffer_end   = buffer_start + nvalues_per_domain * npvalues * ndomains - 1;
702   }
703   return buffer_offset;
704 }
705 
706 
registerCollectables(std::vector<observable_helper * > & h5desc,hid_t gid,int grid_index) const707 void SpaceGrid::registerCollectables(std::vector<observable_helper*>& h5desc, hid_t gid, int grid_index) const
708 {
709   typedef Matrix<int> iMatrix;
710   iMatrix imat;
711   std::vector<int> ng(1);
712   int cshift = 1;
713   std::stringstream ss;
714   ss << grid_index + cshift;
715   std::string name      = "spacegrid" + ss.str();
716   observable_helper* oh = new observable_helper(name);
717   if (!chempot)
718     ng[0] = nvalues_per_domain * ndomains;
719   else
720     ng[0] = nvalues_per_domain * npvalues * ndomains;
721   oh->set_dimensions(ng, buffer_offset);
722   oh->open(gid);
723   int coord = (int)coordinate;
724   oh->addProperty(const_cast<int&>(coord), "coordinate");
725   oh->addProperty(const_cast<int&>(ndomains), "ndomains");
726   oh->addProperty(const_cast<int&>(nvalues_per_domain), "nvalues_per_domain");
727   oh->addProperty(const_cast<RealType&>(volume), "volume");
728   oh->addProperty(const_cast<Matrix_t&>(domain_volumes), "domain_volumes");
729   oh->addProperty(const_cast<Matrix_t&>(domain_centers), "domain_centers");
730   if (chempot)
731   {
732     oh->addProperty(const_cast<int&>(npmin), "min_part");
733     oh->addProperty(const_cast<int&>(npmax), "max_part");
734     int ref = (int)reference;
735     oh->addProperty(const_cast<int&>(ref), "reference");
736     imat.resize(reference_count.size(), 1);
737     for (int i = 0; i < reference_count.size(); i++)
738       imat(i, 0) = reference_count[i];
739     oh->addProperty(const_cast<iMatrix&>(imat), "reference_count");
740   }
741   if (coordinate != voronoi)
742   {
743     oh->addProperty(const_cast<Point&>(origin), "origin");
744     oh->addProperty(const_cast<Tensor_t&>(axes), "axes");
745     oh->addProperty(const_cast<Tensor_t&>(axinv), "axinv");
746     oh->addProperty(const_cast<Matrix_t&>(domain_uwidths), "domain_uwidths");
747     //add dimensioned quantities
748     std::map<std::string, int> axtmap;
749     axtmap["x"]     = 0;
750     axtmap["y"]     = 1;
751     axtmap["z"]     = 2;
752     axtmap["r"]     = 3;
753     axtmap["phi"]   = 4;
754     axtmap["theta"] = 5;
755     int axtypes[DIM];
756     for (int d = 0; d < DIM; d++)
757     {
758       axtypes[d] = axtmap[axlabel[d]];
759     }
760     int n;
761     const int ni = 3;
762     int* ivar[ni];
763     std::string iname[ni];
764     n        = 0;
765     ivar[n]  = (int*)axtypes;
766     iname[n] = "axtypes";
767     n++;
768     ivar[n]  = (int*)dimensions;
769     iname[n] = "dimensions";
770     n++;
771     ivar[n]  = (int*)dm;
772     iname[n] = "dm";
773     n++;
774     const int nr = 3;
775     RealType* rvar[nr];
776     std::string rname[nr];
777     n        = 0;
778     rvar[n]  = (RealType*)odu;
779     rname[n] = "odu";
780     n++;
781     rvar[n]  = (RealType*)umin;
782     rname[n] = "umin";
783     n++;
784     rvar[n]  = (RealType*)umax;
785     rname[n] = "umax";
786     n++;
787     imat.resize(DIM, 1);
788     for (int i = 0; i < ni; i++)
789     {
790       for (int d = 0; d < DIM; d++)
791         imat(d, 0) = ivar[i][d];
792       oh->addProperty(const_cast<iMatrix&>(imat), iname[i]);
793     }
794     Matrix_t rmat;
795     rmat.resize(DIM, 1);
796     for (int i = 0; i < nr; i++)
797     {
798       for (int d = 0; d < DIM; d++)
799         rmat(d, 0) = rvar[i][d];
800       oh->addProperty(const_cast<Matrix_t&>(rmat), rname[i]);
801     }
802     for (int d = 0; d < DIM; d++)
803     {
804       int gsize = gmap[d].size();
805       imat.resize(gsize, 1);
806       for (int i = 0; i < gsize; i++)
807       {
808         int gval   = gmap[d][i];
809         imat(i, 0) = gval;
810       }
811       int ival           = d + 1;
812       std::string gmname = "gmap" + int2string(ival);
813       oh->addProperty(const_cast<iMatrix&>(imat), gmname);
814     }
815   }
816   h5desc.push_back(oh);
817   return;
818 }
819 
820 
821 #define SPACEGRID_CHECK
822 
823 
evaluate(const ParticlePos_t & R,const Matrix<RealType> & values,BufferType & buf,std::vector<bool> & particles_outside,const DistanceTableData & dtab)824 void SpaceGrid::evaluate(const ParticlePos_t& R,
825                          const Matrix<RealType>& values,
826                          BufferType& buf,
827                          std::vector<bool>& particles_outside,
828                          const DistanceTableData& dtab)
829 {
830   int p, v;
831   int nparticles = values.size1();
832   int nvalues    = values.size2();
833   int iu[DIM];
834   int buf_index;
835   const RealType o2pi = 1.0 / (2.0 * M_PI);
836   if (!chempot)
837   {
838     switch (coordinate)
839     {
840     case cartesian:
841       if (periodic)
842       {
843         for (p = 0; p < nparticles; p++)
844         {
845           particles_outside[p] = false;
846           u = dot(axinv, (R[p] - origin));
847           for (int d=0; d<DIM; ++d)
848             iu[d] = gmap[d][floor((u[d] - umin[d]) * odu[d])];
849           buf_index = buffer_offset;
850           for (int d=0; d<DIM; ++d)
851             buf_index += nvalues*dm[d]*iu[d];
852           for (v = 0; v < nvalues; v++, buf_index++)
853             buf[buf_index] += values(p, v);
854         }
855       }
856       else
857       {
858         for (p = 0; p < nparticles; p++)
859         {
860           u = dot(axinv, (R[p] - origin));
861           if (u[0] > umin[0] && u[0] < umax[0] && u[1] > umin[1] && u[1] < umax[1] && u[2] > umin[2] && u[2] < umax[2])
862           {
863             particles_outside[p] = false;
864             iu[0]                = gmap[0][floor((u[0] - umin[0]) * odu[0])];
865             iu[1]                = gmap[1][floor((u[1] - umin[1]) * odu[1])];
866             iu[2]                = gmap[2][floor((u[2] - umin[2]) * odu[2])];
867             buf_index            = buffer_offset + nvalues * (dm[0] * iu[0] + dm[1] * iu[1] + dm[2] * iu[2]);
868             for (v = 0; v < nvalues; v++, buf_index++)
869               buf[buf_index] += values(p, v);
870           }
871         }
872       }
873       break;
874     case cylindrical:
875       for (p = 0; p < nparticles; p++)
876       {
877         ub   = dot(axinv, (R[p] - origin));
878         u[0] = sqrt(ub[0] * ub[0] + ub[1] * ub[1]);
879         u[1] = atan2(ub[1], ub[0]) * o2pi + .5;
880         u[2] = ub[2];
881         if (u[0] > umin[0] && u[0] < umax[0] && u[1] > umin[1] && u[1] < umax[1] && u[2] > umin[2] && u[2] < umax[2])
882         {
883           particles_outside[p] = false;
884           iu[0]                = gmap[0][floor((u[0] - umin[0]) * odu[0])];
885           iu[1]                = gmap[1][floor((u[1] - umin[1]) * odu[1])];
886           iu[2]                = gmap[2][floor((u[2] - umin[2]) * odu[2])];
887           buf_index            = buffer_offset + nvalues * (dm[0] * iu[0] + dm[1] * iu[1] + dm[2] * iu[2]);
888           for (v = 0; v < nvalues; v++, buf_index++)
889             buf[buf_index] += values(p, v);
890         }
891       }
892       break;
893     case spherical:
894       for (p = 0; p < nparticles; p++)
895       {
896         ub   = dot(axinv, (R[p] - origin));
897         u[0] = sqrt(ub[0] * ub[0] + ub[1] * ub[1] + ub[2] * ub[2]);
898         u[1] = atan2(ub[1], ub[0]) * o2pi + .5;
899         u[2] = acos(ub[2] / u[0]) * o2pi * 2.0;
900         if (u[0] > umin[0] && u[0] < umax[0] && u[1] > umin[1] && u[1] < umax[1] && u[2] > umin[2] && u[2] < umax[2])
901         {
902           particles_outside[p] = false;
903           iu[0]                = gmap[0][floor((u[0] - umin[0]) * odu[0])];
904           iu[1]                = gmap[1][floor((u[1] - umin[1]) * odu[1])];
905           iu[2]                = gmap[2][floor((u[2] - umin[2]) * odu[2])];
906           buf_index            = buffer_offset + nvalues * (dm[0] * iu[0] + dm[1] * iu[1] + dm[2] * iu[2]);
907           for (v = 0; v < nvalues; v++, buf_index++)
908             buf[buf_index] += values(p, v);
909         }
910       }
911       break;
912     case voronoi:
913       //find cell center nearest to each dynamic particle
914       int nd, nn;
915       RealType dist;
916       for (p = 0; p < ndparticles; p++)
917       {
918         const auto& dist = dtab.getDistRow(p);
919         for (nd = 0; nd < ndomains; nd++)
920               if (dist[nd] < nearcell[p].r)
921               {
922                 nearcell[p].r = dist[nd];
923                 nearcell[p].i = nd;
924               }
925       }
926       //accumulate values for each dynamic particle
927       for (p = 0; p < ndparticles; p++)
928       {
929         buf_index = buffer_offset + nvalues * nearcell[p].i;
930         for (v = 0; v < nvalues; v++, buf_index++)
931           buf[buf_index] += values(p, v);
932       }
933       //accumulate values for static particles (static particles == cell centers)
934       buf_index = buffer_offset;
935       for (p = ndparticles; p < nparticles; p++)
936         for (v = 0; v < nvalues; v++, buf_index++)
937           buf[buf_index] += values(p, v);
938       //each particle belongs to some voronoi cell
939       for (p = 0; p < nparticles; p++)
940         particles_outside[p] = false;
941       //reset distances
942       for (p = 0; p < ndparticles; p++)
943         nearcell[p].r = std::numeric_limits<RealType>::max();
944       break;
945     default:
946       app_log() << "  coordinate type must be cartesian, cylindrical, spherical, or voronoi" << std::endl;
947       APP_ABORT("SpaceGrid::evaluate");
948     }
949   }
950   else
951   //chempot: sort values by particle count in volumes
952   {
953     int cell_index;
954     int nd;
955     std::fill(cellsamples.begin(), cellsamples.end(), 0.0);
956     switch (coordinate)
957     {
958     case cartesian:
959       for (p = 0; p < nparticles; p++)
960       {
961         u = dot(axinv, (R[p] - origin));
962         if (u[0] > umin[0] && u[0] < umax[0] && u[1] > umin[1] && u[1] < umax[1] && u[2] > umin[2] && u[2] < umax[2])
963         {
964           particles_outside[p] = false;
965           iu[0]                = gmap[0][floor((u[0] - umin[0]) * odu[0])];
966           iu[1]                = gmap[1][floor((u[1] - umin[1]) * odu[1])];
967           iu[2]                = gmap[2][floor((u[2] - umin[2]) * odu[2])];
968           cell_index           = dm[0] * iu[0] + dm[1] * iu[1] + dm[2] * iu[2];
969           for (v = 0; v < nvalues; v++)
970             cellsamples(cell_index, v) += values(p, v);
971           cellsamples(cell_index, nvalues) += 1.0;
972         }
973       }
974       break;
975     case cylindrical:
976       for (p = 0; p < nparticles; p++)
977       {
978         ub   = dot(axinv, (R[p] - origin));
979         u[0] = sqrt(ub[0] * ub[0] + ub[1] * ub[1]);
980         u[1] = atan2(ub[1], ub[0]) * o2pi + .5;
981         u[2] = ub[2];
982         if (u[0] > umin[0] && u[0] < umax[0] && u[1] > umin[1] && u[1] < umax[1] && u[2] > umin[2] && u[2] < umax[2])
983         {
984           particles_outside[p] = false;
985           iu[0]                = gmap[0][floor((u[0] - umin[0]) * odu[0])];
986           iu[1]                = gmap[1][floor((u[1] - umin[1]) * odu[1])];
987           iu[2]                = gmap[2][floor((u[2] - umin[2]) * odu[2])];
988           cell_index           = dm[0] * iu[0] + dm[1] * iu[1] + dm[2] * iu[2];
989           for (v = 0; v < nvalues; v++)
990             cellsamples(cell_index, v) += values(p, v);
991           cellsamples(cell_index, nvalues) += 1.0;
992         }
993       }
994       break;
995     case spherical:
996       for (p = 0; p < nparticles; p++)
997       {
998         ub   = dot(axinv, (R[p] - origin));
999         u[0] = sqrt(ub[0] * ub[0] + ub[1] * ub[1] + ub[2] * ub[2]);
1000         u[1] = atan2(ub[1], ub[0]) * o2pi + .5;
1001         u[2] = acos(ub[2] / u[0]) * o2pi * 2.0;
1002         if (u[0] > umin[0] && u[0] < umax[0] && u[1] > umin[1] && u[1] < umax[1] && u[2] > umin[2] && u[2] < umax[2])
1003         {
1004           particles_outside[p] = false;
1005           iu[0]                = gmap[0][floor((u[0] - umin[0]) * odu[0])];
1006           iu[1]                = gmap[1][floor((u[1] - umin[1]) * odu[1])];
1007           iu[2]                = gmap[2][floor((u[2] - umin[2]) * odu[2])];
1008           cell_index           = dm[0] * iu[0] + dm[1] * iu[1] + dm[2] * iu[2];
1009           for (v = 0; v < nvalues; v++)
1010             cellsamples(cell_index, v) += values(p, v);
1011           cellsamples(cell_index, nvalues) += 1.0;
1012         }
1013       }
1014       break;
1015     case voronoi:
1016       //find cell center nearest to each dynamic particle
1017       int nn;
1018       RealType dist;
1019       APP_ABORT("SoA transformation needed for Voronoi grids")
1020       //for (nd = 0; nd < ndomains; nd++)
1021       //  for (nn = dtab.M[nd], p = 0; nn < dtab.M[nd + 1]; ++nn, ++p)
1022       //  {
1023       //    dist = dtab.r(nn);
1024       //    if (dist < nearcell[p].r)
1025       //    {
1026       //      nearcell[p].r = dist;
1027       //      nearcell[p].i = nd;
1028       //    }
1029       //  }
1030       //accumulate values for each dynamic particle
1031       for (p = 0; p < ndparticles; p++)
1032       {
1033         cell_index = nearcell[p].i;
1034         for (v = 0; v < nvalues; v++)
1035           cellsamples(cell_index, v) += values(p, v);
1036         cellsamples(cell_index, nvalues) += 1.0;
1037       }
1038       //accumulate values for static particles (static particles == cell centers)
1039       for (p = ndparticles, cell_index = 0; p < nparticles; p++, cell_index++)
1040       {
1041         for (v = 0; v < nvalues; v++)
1042           cellsamples(cell_index, v) += values(p, v);
1043         cellsamples(cell_index, nvalues) += 1.0;
1044       }
1045       //each particle belongs to some voronoi cell
1046       for (p = 0; p < nparticles; p++)
1047         particles_outside[p] = false;
1048       //reset distances
1049       for (p = 0; p < ndparticles; p++)
1050         nearcell[p].r = std::numeric_limits<RealType>::max();
1051       break;
1052     default:
1053       app_log() << "  coordinate type must be cartesian, cylindrical, spherical, or voronoi" << std::endl;
1054       APP_ABORT("SpaceGrid::evaluate");
1055     }
1056     //now place samples in the buffer according to how
1057     // many particles are in each cell
1058     int nincell;
1059     buf_index = buffer_offset;
1060     for (nd = 0; nd < ndomains; nd++)
1061     {
1062       nincell = cellsamples(nd, nvalues) - reference_count[nd];
1063       if (nincell >= npmin && nincell <= npmax)
1064       {
1065         buf_index = buffer_offset + (nd * npvalues + nincell - npmin) * nvalues;
1066         for (v = 0; v < nvalues; v++, buf_index++)
1067           buf[buf_index] += cellsamples(nd, v);
1068       }
1069     }
1070   }
1071 }
1072 
1073 
sum(const BufferType & buf,RealType * vals)1074 void SpaceGrid::sum(const BufferType& buf, RealType* vals)
1075 {
1076   for (int v = 0; v < nvalues_per_domain; v++)
1077   {
1078     vals[v] = 0.0;
1079   }
1080   for (int i = 0, n = buffer_offset; i < ndomains; i++, n += nvalues_per_domain)
1081   {
1082     for (int v = 0; v < nvalues_per_domain; v++)
1083     {
1084       vals[v] += buf[n + v];
1085     }
1086   }
1087 }
1088 
1089 
check_grid(void)1090 bool SpaceGrid::check_grid(void)
1091 {
1092   app_log() << "SpaceGrid::check_grid" << std::endl;
1093   const RealType o2pi = 1.0 / (2.0 * M_PI);
1094   int iu[DIM];
1095   int idomain;
1096   bool ok = true;
1097   Point dc;
1098   for (int i = 0; i < ndomains; i++)
1099   {
1100     for (int d = 0; d < DIM; d++)
1101       dc[d] = domain_centers(i, d);
1102     ub = dot(axinv, (dc - origin));
1103     switch (coordinate)
1104     {
1105     case cartesian:
1106       u = ub;
1107       break;
1108     case cylindrical:
1109       u[0] = sqrt(ub[0] * ub[0] + ub[1] * ub[1]);
1110       u[1] = atan2(ub[1], ub[0]) * o2pi + .5;
1111       u[2] = ub[2];
1112       break;
1113     case spherical:
1114       u[0] = sqrt(ub[0] * ub[0] + ub[1] * ub[1] + ub[2] * ub[2]);
1115       u[1] = atan2(ub[1], ub[0]) * o2pi + .5;
1116       u[2] = acos(ub[2] / u[0]) * o2pi * 2.0;
1117       break;
1118     default:
1119       break;
1120     }
1121     iu[0]   = gmap[0][floor((u[0] - umin[0]) * odu[0])];
1122     iu[1]   = gmap[1][floor((u[1] - umin[1]) * odu[1])];
1123     iu[2]   = gmap[2][floor((u[2] - umin[2]) * odu[2])];
1124     idomain = dm[0] * iu[0] + dm[1] * iu[1] + dm[2] * iu[2];
1125     if (idomain != i)
1126     {
1127       app_log() << "  cell mismatch " << i << " " << idomain << std::endl;
1128       ok = false;
1129     }
1130   }
1131   if (!ok)
1132   {
1133     app_log() << "  SpaceGrid cells do not map onto themselves" << std::endl;
1134   }
1135   app_log() << "end SpaceGrid::check_grid" << std::endl;
1136   return ok;
1137 }
1138 
1139 } // namespace qmcplusplus
1140