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) 2020 QMCPACK developers.
6 //
7 // File developed by: Ken Esler, kpesler@gmail.com, University of Illinois at Urbana-Champaign
8 //                    Jeremy McMinnis, jmcminis@gmail.com, University of Illinois at Urbana-Champaign
9 //                    Jeongnim Kim, jeongnim.kim@gmail.com, University of Illinois at Urbana-Champaign
10 //                    Mark A. Berrill, berrillma@ornl.gov, Oak Ridge National Laboratory
11 //
12 // File created by: Jeongnim Kim, jeongnim.kim@gmail.com, University of Illinois at Urbana-Champaign
13 //////////////////////////////////////////////////////////////////////////////////////
14 
15 
16 #include <string>
17 #include <vector>
18 #include <utility>
19 #include <iostream>
20 #include <fstream>
21 #include "OhmmsData/FileUtility.h"
22 #include "OhmmsData/AttributeSet.h"
23 #include "OhmmsData/ParameterSet.h"
24 #include "ParticleIO/ParticleLayoutIO.h"
25 #include "XMLParticleIO.h"
26 #include "ParticleIO/ParticleIOUtility.h"
27 #include "ParticleBase/RandomSeqGenerator.h"
28 #include "Utilities/ProgressReportEngine.h"
29 namespace qmcplusplus
30 {
31 /** set the property of a SpeciesSet
32  * @param tspecies SpeciesSet
33  * @param sid id of the species whose properties to be set
34  *
35  * Need unit handlers but for now, everything is in AU:
36  * m_e=1.0, hartree and bohr and unit="au"
37  * Example to define C(arbon)
38  * <group name="C">
39  *   <parameter name="mass" unit="amu">12</parameter>
40  *   <parameter name="charge">-6</parameter>
41  * </group>
42  * Note that unit="amu" is given for the mass.
43  * When mass is not given, they are set to the electron mass.
44  */
setSpeciesProperty(SpeciesSet & tspecies,int sid,xmlNodePtr cur)45 void setSpeciesProperty(SpeciesSet& tspecies, int sid, xmlNodePtr cur)
46 {
47   const double proton_mass = 1822.888530063;
48   cur                      = cur->xmlChildrenNode;
49   while (cur != NULL)
50   {
51     std::string cname((const char*)cur->name);
52     if (cname == "parameter")
53     {
54       std::string pname;
55       std::string unit_name("au"); //hartree, bohr & me=1
56       OhmmsAttributeSet pAttrib;
57       pAttrib.add(pname, "name");
58       pAttrib.add(unit_name, "unit");
59       pAttrib.put(cur);
60       if (pname.size())
61       {
62         int iproperty    = tspecies.addAttribute(pname);
63         double ap        = 0.0;
64         double unit_conv = 1.0;
65         putContent(ap, cur);
66         if (pname == "mass")
67         {
68           if (unit_name == "amu")
69             unit_conv = proton_mass;
70         }
71         tspecies(iproperty, sid) = ap * unit_conv;
72       }
73     }
74     cur = cur->next;
75   }
76 }
77 
78 
XMLParticleParser(Particle_t & aptcl,Tensor<int,OHMMS_DIM> & tmat,bool donotresize)79 XMLParticleParser::XMLParticleParser(Particle_t& aptcl, Tensor<int, OHMMS_DIM>& tmat, bool donotresize)
80     : AssignmentOnly(donotresize), ref_(aptcl), TileMatrix(tmat)
81 {
82   //add ref particle attributes
83   ref_.createAttributeList(ref_AttribList);
84 }
85 
86 /**reading particleset node from a file
87  *@param fname_in a file name to open
88  *@param pformat_in the format of the file, not used
89  *@return true, if successful
90  *
91  *Check the type of the external source to work on.
92  *The external source itself can be an xml file.
93  */
put(const std::string & fname_in,const std::string & fext_in)94 bool XMLParticleParser::put(const std::string& fname_in, const std::string& fext_in)
95 {
96   xmlDocPtr doc = NULL;
97   // build an XML tree from a the file;
98   doc = xmlParseFile(fname_in.c_str());
99   if (doc == NULL)
100   {
101     ERRORMSG(fname_in << " does not exist")
102     return false;
103   }
104   ///using XPath instead of recursive search
105   xmlXPathContextPtr context;
106   xmlXPathObjectPtr result;
107   context = xmlXPathNewContext(doc);
108   result  = xmlXPathEvalExpression((const xmlChar*)"//particleset", context);
109   if (xmlXPathNodeSetIsEmpty(result->nodesetval))
110   {
111     app_error() << fname_in << " does not contain any ParticleSet" << std::endl;
112   }
113   else
114   {
115     xmlNodePtr cur = result->nodesetval->nodeTab[0];
116     std::string fname, pformat;
117     OhmmsAttributeSet pAttrib;
118     pAttrib.add(fname, "src");
119     pAttrib.add(fname, "href");
120     pAttrib.add(pformat, "srctype");
121     pAttrib.put(cur);
122     if (fname.size())
123       pformat = getExtension(fname);
124     if (pformat.empty())
125       putSpecial(cur);
126     //else
127     //{
128     //  if(pformat == "h5") {
129     //    HDFParticleParser ahandle(ref_);
130     //    ahandle.put(cur);
131     //  } else {
132     //    app_error() << "  Unknown file extension " << pformat << std::endl;
133     //  }
134     //}
135   }
136   //free local objects
137   xmlXPathFreeObject(result);
138   xmlXPathFreeContext(context);
139   xmlFreeDoc(doc);
140   return true;
141 }
142 
143 /** process xmlnode &lt;particleset/&gt;
144  *@param cur the xmlnode to work on
145  *
146  *If the node has src or href attribute, use an external file.
147  */
put(xmlNodePtr cur)148 bool XMLParticleParser::put(xmlNodePtr cur)
149 {
150   ///process attributes: type or format
151   std::string fname, pformat("xml");
152   OhmmsAttributeSet pAttrib;
153   pAttrib.add(fname, "src");
154   pAttrib.add(fname, "href");
155   pAttrib.add(pformat, "srctype");
156   pAttrib.put(cur);
157   if (fname.empty())
158     return putSpecial(cur);
159   else
160   {
161     //overwrite the format
162     pformat = getExtension(fname);
163     return put(fname, pformat);
164   }
165 }
166 
167 
168 /** process xmlnode &lt;particleset/&gt; which contains everything about the particle set to initialize
169  *@param cur the xmlnode to work on
170  *
171  */
putSpecial(xmlNodePtr cur)172 bool XMLParticleParser::putSpecial(xmlNodePtr cur)
173 {
174   ReportEngine PRE("XMLParticleParser", "putSpecial");
175   std::string pname("none");
176   //xmlDocPtr doc = cur->doc;
177   //the number of particles that are initialized by <attrib/>
178   int nat = 0;
179   std::string randomizeR("no");
180   OhmmsAttributeSet pAttrib;
181   pAttrib.add(randomizeR, "random");
182   pAttrib.add(nat, "size");
183   pAttrib.add(pname, "name");
184   pAttrib.put(cur);
185   ///count the number of atom added one by one
186   xmlNodePtr cur0 = cur->xmlChildrenNode;
187   //total count of the particles to be created
188   int ntot = 0;
189   int ng = 0, ng_in = 0;
190   std::vector<int> nat_group;
191   std::vector<xmlNodePtr> atom_ptr;
192   //pre-process the nodes to count the number of particles to be added
193   while (cur0 != NULL)
194   {
195     std::string cname((const char*)cur0->name);
196     if (cname == "atom")
197     {
198       ntot++;
199       atom_ptr.push_back(cur0);
200     }
201     else if (cname == "group")
202     {
203       int nat_per_group = 0;
204       OhmmsAttributeSet gAttrib;
205       gAttrib.add(nat_per_group, "size");
206       gAttrib.put(cur0);
207       nat_group.push_back(nat_per_group);
208       ng_in += nat_per_group;
209       ntot += nat_per_group;
210       ng++;
211     }
212     else if (cname == attrib_tag)
213     {
214       int size_att = 0;
215       OhmmsAttributeSet aAttrib;
216       aAttrib.add(size_att, "size");
217       aAttrib.put(cur0);
218       if (size_att)
219       {
220         if (size_att != nat)
221         {
222           app_warning() << "\tOverwriting the size of the particle by //particleset/attrib/@size=" << size_att
223                         << std::endl;
224           nat = size_att;
225         }
226       }
227     }
228     cur0 = cur0->next;
229   }
230   ntot += nat;
231   ref_.setName(pname.c_str());
232   int nloc = ref_.getTotalNum();
233   //treat assignment only differently
234   if (AssignmentOnly)
235   {
236     ntot = 0;
237     nloc = 0;
238     for (int iat = 0; iat < ref_.getTotalNum(); iat++)
239       ref_.ID[iat] = iat;
240   }
241   if (ntot)
242   {
243     if (ng_in)
244     {
245       ref_.create(nat_group);
246     }
247     else
248     {
249       ref_.create(ntot);
250     }
251     //assign default ID
252     int nloci = nloc;
253     for (int iat = 0; iat < ntot; iat++, nloci++)
254       ref_.ID[iat] = nloci;
255   }
256   //TinyVector<int,OHMMS_DIM> uc_grid(1);
257   SpeciesSet& tspecies(ref_.getSpeciesSet()); //SpeciesCollection::getSpecies();
258   cur = cur->xmlChildrenNode;
259   //reset the group counter
260   ng = 0;
261   while (cur != NULL)
262   {
263     std::string cname((const char*)(cur->name));
264     if (cname.find("ell") < cname.size()) //accept UnitCell, unitcell, supercell
265     {
266       //if(cname == "UnitCell" || cname == "unitcell") {
267       LatticeParser lat(ref_.Lattice);
268       lat.put(cur);
269       //ParameterSet params;
270       //params.add(uc_grid,"uc_grid");
271       //params.put(cur);
272     }
273     else if (cname == attrib_tag)
274     {
275       getPtclAttrib(cur, nat, nloc);
276     }
277     else if (cname == "group")
278     //found group
279     {
280       std::string sname;
281       OhmmsAttributeSet gAttrib;
282       gAttrib.add(sname, "name");
283       gAttrib.put(cur);
284       if (sname.size()) //only if name is found
285       {
286         int sid = tspecies.addSpecies(sname);
287         setSpeciesProperty(tspecies, sid, cur);
288         xmlNodePtr tcur = cur->xmlChildrenNode;
289         while (tcur != NULL)
290         {
291           std::string tcname((const char*)tcur->name);
292           if (nat_group[ng] && tcname == attrib_tag)
293           {
294             getPtclAttrib(tcur, nat_group[ng], nloc);
295           }
296           tcur = tcur->next;
297         }
298         for (int iat = 0; iat < nat_group[ng]; iat++, nloc++)
299           ref_.GroupID[nloc] = sid;
300         ng++;
301       }
302     }
303     cur = cur->next;
304   }
305 
306   //copy ID -> PCID
307   ref_.PCID = ref_.ID;
308 
309   expandSuperCell(ref_, TileMatrix);
310   if (ref_.Lattice.SuperCellEnum)
311   {
312     if (randomizeR == "yes")
313     {
314       makeUniformRandom(ref_.R);
315       ref_.R.setUnit(PosUnit::Lattice);
316       ref_.convert2Cart(ref_.R);
317 #if !defined(QMC_CUDA)
318       makeUniformRandom(ref_.spins);
319       ref_.spins *= 2 * M_PI;
320 #endif
321     }
322     else // put them [0,1) in the cell
323       ref_.applyBC(ref_.R);
324   }
325 
326   //this sets Mass, Z
327   ref_.resetGroups();
328   ref_.createSK();
329 
330   return true;
331 }
332 
333 /** process xmlnode to reset the properties of a particle set
334  * @param cur current node
335  * @return true, if successful
336  *
337  * This resets or adds new attributes to a particle set.
338  * It cannot modify the size of the particle set.
339  */
reset(xmlNodePtr cur)340 bool XMLParticleParser::reset(xmlNodePtr cur)
341 {
342   ReportEngine PRE("XMLParticleParser", "reset");
343   SpeciesSet& tspecies(ref_.getSpeciesSet());
344   cur = cur->xmlChildrenNode;
345   while (cur != NULL)
346   {
347     std::string cname((const char*)cur->name);
348     if (cname == "group")
349     {
350       std::string sname;
351       OhmmsAttributeSet gAttrib;
352       gAttrib.add(sname, "name");
353       gAttrib.put(cur);
354       if (sname.size())
355       {
356         int sid = tspecies.addSpecies(sname);
357         setSpeciesProperty(tspecies, sid, cur);
358       }
359     }
360     cur = cur->next;
361   }
362   //  //@todo Will add a member function to ParticleSet to handle these
363   //  int massind=tspecies.addAttribute("mass");
364   //  for(int iat=0; iat<ref_.getTotalNum(); iat++)
365   //    ref_.Mass[iat]=tspecies(massind,ref_.GroupID[iat]);
366   //
367   //  int qind=tspecies.addAttribute("charge");
368   //  for(int iat=0; iat<ref_.getTotalNum(); iat++)
369   //    ref_.Z[iat]=tspecies(qind,ref_.GroupID[iat]);
370   //
371   return true;
372 }
373 
374 template<typename PAT>
375 struct ParticleAttribXmlNode
376 {
377   PAT& ref_;
378 
ParticleAttribXmlNodeqmcplusplus::ParticleAttribXmlNode379   inline ParticleAttribXmlNode(PAT& a, PosUnit utype) : ref_(a) { ref_.InUnit = utype; }
380 
putqmcplusplus::ParticleAttribXmlNode381   inline bool put(xmlNodePtr cur, int n_in, int start)
382   {
383     typedef typename PAT::Type_t data_type;
384     std::vector<data_type> data_in(n_in);
385     putContent(data_in, cur);
386     copy(data_in.begin(), data_in.end(), ref_.begin() + start);
387     return true;
388   }
389 };
390 
getPtclAttrib(xmlNodePtr cur,int nat,int nloc)391 void XMLParticleParser::getPtclAttrib(xmlNodePtr cur, int nat, int nloc)
392 {
393   std::string oname, otype;
394   int utype   = 0;
395   int size_in = 0;
396   OhmmsAttributeSet pAttrib;
397   pAttrib.add(otype, datatype_tag);  //datatype
398   pAttrib.add(oname, "name");        //name
399   pAttrib.add(utype, condition_tag); //condition
400   pAttrib.add(size_in, "size");      //size
401   pAttrib.put(cur);
402   if (oname.empty() || otype.empty())
403   {
404     app_error() << "   Missing attrib/@name or attrib/@datatype " << std::endl;
405     app_error() << "     <attrib name=\"aname\"  datatype=\"atype\"/>" << std::endl;
406     return;
407   }
408   int t_id = ref_AttribList.getAttribType(otype);
409 
410   if (oname == ionid_tag)
411   {
412     if (otype == stringtype_tag)
413     {
414       int nloci = nloc;
415       std::vector<std::string> d_in(nat);
416       putContent(d_in, cur);
417       for (int iat = 0; iat < d_in.size(); iat++, nloci++)
418       {
419         ref_.GroupID[nloci] = ref_.getSpeciesSet().addSpecies(d_in[iat]);
420       }
421     }
422     else
423     {
424       ParticleAttribXmlNode<ParticleIndex_t> a(ref_.GroupID, static_cast<PosUnit>(utype));
425       a.put(cur, nat, nloc);
426     }
427   }
428   else
429   {
430     //very permissive in that a unregistered attribute will be created and stored by ParticleSet
431     //cloning is not going to work
432     if (t_id == PA_IndexType)
433     {
434       ParticleIndex_t* obj = nullptr;
435       obj                  = ref_AttribList.getAttribute(otype, oname, obj);
436       ParticleAttribXmlNode<ParticleIndex_t> a(*obj, static_cast<PosUnit>(utype));
437       a.put(cur, nat, nloc);
438     }
439     else if (t_id == PA_ScalarType)
440     {
441       ParticleScalar_t* obj = nullptr;
442       obj                   = ref_AttribList.getAttribute(otype, oname, obj);
443       ParticleAttribXmlNode<ParticleScalar_t> a(*obj, static_cast<PosUnit>(utype));
444       a.put(cur, nat, nloc);
445     }
446     else if (t_id == PA_PositionType)
447     {
448       ParticlePos_t* obj = nullptr;
449       obj                = ref_AttribList.getAttribute(otype, oname, obj);
450       ParticleAttribXmlNode<ParticlePos_t> a(*obj, static_cast<PosUnit>(utype));
451       a.put(cur, nat, nloc);
452     }
453     else if (t_id == PA_TensorType)
454     {
455       ParticleTensor_t* obj = nullptr;
456       obj                   = ref_AttribList.getAttribute(otype, oname, obj);
457       ParticleAttribXmlNode<ParticleTensor_t> a(*obj, static_cast<PosUnit>(utype));
458       a.put(cur, nat, nloc);
459     }
460   }
461 }
462 
463 
XMLSaveParticle(Particle_t & pin)464 XMLSaveParticle::XMLSaveParticle(Particle_t& pin) : ref_(pin) {}
465 
~XMLSaveParticle()466 XMLSaveParticle::~XMLSaveParticle() {}
467 
reset(const char * fileroot,bool append)468 void XMLSaveParticle::reset(const char* fileroot, bool append)
469 {
470   //append is ignored
471   FileRoot = fileroot;
472   FileName = fileroot;
473   FileName.append(".xml");
474   SpeciesName = ref_.getSpeciesSet().speciesName;
475 }
476 
report(int iter)477 void XMLSaveParticle::report(int iter)
478 {
479   // writing a meta file
480   std::ofstream fxml(FileName.c_str()); // always overwrite
481   fxml << "<?xml version=\"1.0\"?>" << std::endl;
482   get(fxml, 1);
483   fxml.close();
484 }
485 
get(std::ostream & fxml,int olevel) const486 void XMLSaveParticle::get(std::ostream& fxml, int olevel) const
487 {
488   ref_.begin_node(fxml);
489   fxml.setf(std::ios::scientific);
490   fxml.precision(15);
491   LatticeXMLWriter latticeout(ref_.Lattice);
492   latticeout.get(fxml);
493   for (int i = 0; i < SpeciesName.size(); i++)
494   {
495     fxml << "<group name=\"" << SpeciesName[i] << "\"/>" << std::endl;
496   }
497   //only write the local particles
498   int nloc = ref_.getTotalNum();
499   if (olevel)
500   {
501     /*
502     Particle_t::PAListIterator it = ref_.first_attrib();
503     while(it != ref_.last_attrib())
504     {
505       OhmmsObject* ooref= (*it).second;
506 //       if(ooref->objName() == ionid_tag) {
507 // 	IonName.begin_node(fxml);
508 //  	for(int iat=0; iat<nloc; iat++) {
509 //  	  fxml << IonName[iat] << " ";
510 //  	  if(iat%20 == 19) fxml << std::endl;
511 //  	}
512 // 	IonName.end_node(fxml);
513 //       } else {
514       int t_id = ref_AttribList.getAttribType(otype);
515       int o_id = ooref->id();
516       ooref->begin_node(fxml);
517       if(t_id == PA_IndexType)
518       {
519         const ParticleIndex_t* itmp=dynamic_cast<ParticleIndex_t*>(ooref);
520         for(int iat=0; iat<nloc; iat++)
521         {
522           fxml << (*itmp)[iat] << " ";
523           if(iat%20 == 19)
524             fxml << std::endl;
525         }
526       }
527       else if(t_id == PA_ScalarType)
528       {
529         fxml.precision(6);
530         const ParticleScalar_t* stmp=dynamic_cast<ParticleScalar_t*>(ooref);
531         for(int iat=0; iat<nloc; iat++)
532         {
533           fxml << (*stmp)[iat] << " ";
534           if(iat%5 == 4)
535             fxml << std::endl;
536         }
537         if(nloc%5 != 0)
538           fxml<< std::endl;
539       }
540       else if (t_id == PA_PositionType)
541       {
542         fxml.precision(15);
543         const ParticlePos_t* rtmp=dynamic_cast<ParticlePos_t*>(ooref);
544         for(int iat=0; iat<nloc; iat++)
545         {
546           fxml << (*rtmp)[iat] << std::endl;
547         }
548       }
549       else if (t_id == PA_TensorType)
550       {
551         fxml.precision(15);
552         const ParticleTensor_t* ttmp=dynamic_cast<ParticleTensor_t*>(ooref);
553         for(int iat=0; iat<nloc; iat++)
554         {
555           fxml << (*ttmp)[iat];
556         }
557       }
558       ooref->end_node(fxml);
559       //      }
560       it++;
561     }
562     */
563   }
564   else
565   {
566     ref_.R.begin_node(fxml);
567     for (int iat = 0; iat < nloc; iat++)
568       fxml << ref_.R[iat] << std::endl;
569     ref_.R.end_node(fxml);
570     ref_.GroupID.begin_node(fxml);
571     for (int iat = 0; iat < nloc; iat++)
572     {
573       fxml << ref_.GroupID[iat] << " ";
574       if (iat % 20 == 19)
575         fxml << std::endl;
576     }
577     if (nloc % 20 != 19)
578       fxml << std::endl;
579     ref_.GroupID.end_node(fxml);
580   }
581   ref_.end_node(fxml);
582 }
583 
put(xmlNodePtr cur)584 bool XMLSaveParticle::put(xmlNodePtr cur) { return true; }
585 
586 /** create particleset node
587  * @param addlattice if true, add unitcell
588  */
createNode(bool addlattice)589 xmlNodePtr XMLSaveParticle::createNode(bool addlattice)
590 {
591   if (SpeciesName.size() != ref_.getSpeciesSet().getTotalNum())
592   {
593     SpeciesName = ref_.getSpeciesSet().speciesName;
594   }
595   //if(addlattice) {
596   //  ref_.Lattice.print(std::cout);
597   //}
598   xmlNodePtr cur = xmlNewNode(NULL, (const xmlChar*)"particleset");
599   xmlNewProp(cur, (const xmlChar*)"name", (const xmlChar*)ref_.getName().c_str());
600   if (ref_.groups() > 1)
601   {
602     const SpeciesSet& mySpecies(ref_.getSpeciesSet());
603     int nitem(mySpecies.numAttributes());
604     int nspecies(mySpecies.getTotalNum());
605     for (int is = 0; is < nspecies; is++)
606     {
607       std::ostringstream ng;
608       ng << ref_.last(is) - ref_.first(is);
609       xmlNodePtr g = xmlNewNode(NULL, (const xmlChar*)"group");
610       xmlNewProp(g, (const xmlChar*)"name", (const xmlChar*)SpeciesName[is].c_str());
611       xmlNewProp(g, (const xmlChar*)"size", (const xmlChar*)ng.str().c_str());
612       for (int item = 0; item < nitem; item++)
613       {
614         std::ostringstream prop;
615         prop << mySpecies(item, is);
616         xmlNodePtr p = xmlNewTextChild(g, NULL, (const xmlChar*)"parameter", (const xmlChar*)prop.str().c_str());
617         xmlNewProp(p, (const xmlChar*)"name", (const xmlChar*)mySpecies.attribName[item].c_str());
618       }
619       std::ostringstream pos;
620       pos.setf(std::ios_base::scientific);
621       pos << "\n";
622       for (int iat = ref_.first(is); iat < ref_.last(is); iat++)
623       {
624         pos << ref_.R[iat] << std::endl;
625       }
626       xmlNodePtr posPtr = xmlNewTextChild(g, NULL, (const xmlChar*)"attrib", (const xmlChar*)pos.str().c_str());
627       xmlNewProp(posPtr, (const xmlChar*)"name", (const xmlChar*)"position");
628       xmlNewProp(posPtr, (const xmlChar*)"datatype", (const xmlChar*)"posArray");
629       xmlAddChild(cur, g);
630     }
631   }
632   else
633   {
634     std::ostringstream nat;
635     nat << ref_.getTotalNum();
636     xmlNewProp(cur, (const xmlChar*)"size", (const xmlChar*)nat.str().c_str());
637     const SpeciesSet& mySpecies(ref_.getSpeciesSet());
638     int nitem(mySpecies.numAttributes());
639     int nspecies(mySpecies.getTotalNum());
640     for (int is = 0; is < nspecies; is++)
641     {
642       xmlNodePtr g = xmlNewNode(NULL, (const xmlChar*)"group");
643       xmlNewProp(g, (const xmlChar*)"name", (const xmlChar*)SpeciesName[is].c_str());
644       for (int item = 0; item < nitem; item++)
645       {
646         std::ostringstream prop;
647         prop << mySpecies(item, is);
648         xmlNodePtr p = xmlNewTextChild(g, NULL, (const xmlChar*)"parameter", (const xmlChar*)prop.str().c_str());
649         xmlNewProp(p, (const xmlChar*)"name", (const xmlChar*)mySpecies.attribName[item].c_str());
650       }
651       xmlAddChild(cur, g);
652     }
653     std::ostringstream pos, gid;
654     pos.setf(std::ios_base::scientific);
655     pos << "\n";
656     for (int iat = 0; iat < ref_.getTotalNum(); iat++)
657     {
658       pos << ref_.R[iat] << std::endl;
659     }
660     xmlNodePtr posPtr = xmlNewTextChild(cur, NULL, (const xmlChar*)"attrib", (const xmlChar*)pos.str().c_str());
661     xmlNewProp(posPtr, (const xmlChar*)"name", (const xmlChar*)"position");
662     xmlNewProp(posPtr, (const xmlChar*)"datatype", (const xmlChar*)"posArray");
663     gid << "\n ";
664     for (int iat = 0; iat < ref_.getTotalNum(); iat++)
665     {
666       gid << SpeciesName[ref_.GroupID[iat]] << " ";
667     }
668     gid << std::endl;
669     xmlNodePtr gPtr = xmlNewTextChild(cur, NULL, (const xmlChar*)"attrib", (const xmlChar*)gid.str().c_str());
670     xmlNewProp(gPtr, (const xmlChar*)"name", (const xmlChar*)"ionid");
671     xmlNewProp(gPtr, (const xmlChar*)"datatype", (const xmlChar*)"stringArray");
672   }
673   return cur;
674 }
675 } // namespace qmcplusplus
676