Grid 0.7.0
IldgIO.h
Go to the documentation of this file.
1/*************************************************************************************
2
3Grid physics library, www.github.com/paboyle/Grid
4
5Source file: ./lib/parallelIO/IldgIO.h
6
7Copyright (C) 2015, 2026
8
9Author: Peter Boyle <paboyle@ph.ed.ac.uk>
10Author: Guido Cossu <guido.cossu@ed.ac.uk>
11Author: Gaurav Ray <gaurav.sinharay@swansea.ac.uk>
12
13This program is free software; you can redistribute it and/or modify
14it under the terms of the GNU General Public License as published by
15the Free Software Foundation; either version 2 of the License, or
16(at your option) any later version.
17
18This program is distributed in the hope that it will be useful,
19but WITHOUT ANY WARRANTY; without even the implied warranty of
20MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21GNU General Public License for more details.
22
23You should have received a copy of the GNU General Public License along
24with this program; if not, write to the Free Software Foundation, Inc.,
2551 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26
27See the full license in the file "LICENSE" in the top level distribution
28directory
29*************************************************************************************/
30/* END LEGAL */
31#pragma once
32
33#ifdef HAVE_LIME
34#include <algorithm>
35#include <fstream>
36#include <iomanip>
37#include <iostream>
38#include <string>
39#include <map>
40
41#include <pwd.h>
42#include <sys/utsname.h>
43#include <unistd.h>
44
45//C-Lime is a must have for this functionality
46extern "C" {
47#include "lime.h"
48}
49
51
52#define GRID_FIELD_NORM "FieldNormMetaData"
53#define GRID_FIELD_NORM_CALC(FieldNormMetaData_, n2ck) \
540.5*fabs(FieldNormMetaData_.norm2 - n2ck)/(FieldNormMetaData_.norm2 + n2ck)
55#define GRID_FIELD_NORM_CHECK(FieldNormMetaData_, n2ck) \
56assert(GRID_FIELD_NORM_CALC(FieldNormMetaData_, n2ck) < 1.0e-5);
57
59 // Encode word types as strings
61 template<class word> inline std::string ScidacWordMnemonic(void){ return std::string("unknown"); }
62 template<> inline std::string ScidacWordMnemonic<double> (void){ return std::string("D"); }
63 template<> inline std::string ScidacWordMnemonic<float> (void){ return std::string("F"); }
64 template<> inline std::string ScidacWordMnemonic< int32_t>(void){ return std::string("I32_t"); }
65 template<> inline std::string ScidacWordMnemonic<uint32_t>(void){ return std::string("U32_t"); }
66 template<> inline std::string ScidacWordMnemonic< int64_t>(void){ return std::string("I64_t"); }
67 template<> inline std::string ScidacWordMnemonic<uint64_t>(void){ return std::string("U64_t"); }
68
70 // Encode a generic tensor as a string
72 template<class vobj> std::string ScidacRecordTypeString(int &colors, int &spins, int & typesize,int &datacount) {
73
74 typedef typename getPrecision<vobj>::real_scalar_type stype;
75
76 int _ColourN = indexRank<ColourIndex,vobj>();
77 int _ColourScalar = isScalar<ColourIndex,vobj>();
78 int _ColourVector = isVector<ColourIndex,vobj>();
79 int _ColourMatrix = isMatrix<ColourIndex,vobj>();
80
81 int _SpinN = indexRank<SpinIndex,vobj>();
82 int _SpinScalar = isScalar<SpinIndex,vobj>();
83 int _SpinVector = isVector<SpinIndex,vobj>();
84 int _SpinMatrix = isMatrix<SpinIndex,vobj>();
85
86 int _LorentzN = indexRank<LorentzIndex,vobj>();
87 int _LorentzScalar = isScalar<LorentzIndex,vobj>();
88 int _LorentzVector = isVector<LorentzIndex,vobj>();
89 int _LorentzMatrix = isMatrix<LorentzIndex,vobj>();
90
91 std::stringstream stream;
92
93 stream << "GRID_";
94 stream << ScidacWordMnemonic<stype>();
95
96 if ( _LorentzVector ) stream << "_LorentzVector"<<_LorentzN;
97 if ( _LorentzMatrix ) stream << "_LorentzMatrix"<<_LorentzN;
98
99 if ( _SpinVector ) stream << "_SpinVector"<<_SpinN;
100 if ( _SpinMatrix ) stream << "_SpinMatrix"<<_SpinN;
101
102 if ( _ColourVector ) stream << "_ColourVector"<<_ColourN;
103 if ( _ColourMatrix ) stream << "_ColourMatrix"<<_ColourN;
104
105 if ( _ColourScalar && _LorentzScalar && _SpinScalar ) stream << "_Complex";
106
107
108 typesize = sizeof(typename vobj::scalar_type);
109
110 if ( _ColourMatrix ) typesize*= _ColourN*_ColourN;
111 else typesize*= _ColourN;
112
113 if ( _SpinMatrix ) typesize*= _SpinN*_SpinN;
114 else typesize*= _SpinN;
115
116 colors = _ColourN;
117 spins = _SpinN;
118 datacount = _LorentzN;
119
120 return stream.str();
121 }
122
123 template<class vobj> std::string ScidacRecordTypeString(Lattice<vobj> & lat,int &colors, int &spins, int & typesize,int &datacount) {
124 return ScidacRecordTypeString<vobj>(colors,spins,typesize,datacount);
125 };
126
127
129 // Helper to fill out metadata
131template<class vobj> void ScidacMetaData(Lattice<vobj> & field,
133 scidacRecord & _scidacRecord,
134 scidacFile & _scidacFile)
135 {
136 typedef typename getPrecision<vobj>::real_scalar_type stype;
137
139 // Pull Grid's metadata
141 PrepareMetaData(field,header);
142
144 // Scidac Private File structure
146 _scidacFile = scidacFile(field.Grid());
147
149 // Scidac Private Record structure
151 scidacRecord sr;
152 sr.datatype = ScidacRecordTypeString(field,sr.colors,sr.spins,sr.typesize,sr.datacount);
153 sr.date = header.creation_date;
154 sr.precision = ScidacWordMnemonic<stype>();
155 sr.recordtype = GRID_IO_FIELD;
156
157 _scidacRecord = sr;
158
159 // std::cout << GridLogMessage << "Build SciDAC datatype " <<sr.datatype<<std::endl;
160 }
161
163 // Scidac checksum
165 static int scidacChecksumVerify(scidacChecksum &scidacChecksum_,uint32_t scidac_csuma,uint32_t scidac_csumb)
166 {
167 uint32_t scidac_checksuma = stoull(scidacChecksum_.suma,0,16);
168 uint32_t scidac_checksumb = stoull(scidacChecksum_.sumb,0,16);
169 std::cout << GridLogMessage << " scidacChecksumVerify computed "<<scidac_csuma<<" expected "<<scidac_checksuma <<std::endl;
170 std::cout << GridLogMessage << " scidacChecksumVerify computed "<<scidac_csumb<<" expected "<<scidac_checksumb <<std::endl;
171 if ( scidac_csuma !=scidac_checksuma) {
172 return 0;
173 };
174 if ( scidac_csumb !=scidac_checksumb) {
175 return 0;
176 };
177 return 1;
178 }
179
181// Lime, ILDG and Scidac I/O classes
183class GridLimeReader : public BinaryIO {
184 public:
186 // FIXME: format for RNG? Now just binary out instead
188
189 FILE *File;
190 LimeReader *LimeR;
191 std::string filename;
192
194 // Open the file
196 void open(const std::string &_filename)
197 {
198 filename= _filename;
199 File = fopen(filename.c_str(), "r");
200 if (File == nullptr)
201 {
202 std::cerr << "cannot open file '" << filename << "'" << std::endl;
203 abort();
204 }
205 LimeR = limeCreateReader(File);
206 }
208 // Close the file
210 void close(void){
211 fclose(File);
212 // limeDestroyReader(LimeR);
213 }
214
216 // Read a generic lattice field and verify checksum
218 template<class vobj>
219 void readLimeLatticeBinaryObject(Lattice<vobj> &field,std::string record_name,int control=BINARYIO_LEXICOGRAPHIC)
220 {
221 typedef typename vobj::scalar_object sobj;
222 scidacChecksum scidacChecksum_;
223 FieldNormMetaData FieldNormMetaData_;
224 uint32_t nersc_csum,scidac_csuma,scidac_csumb;
225
226 std::string format = getFormatString<vobj>();
227
228 while ( limeReaderNextRecord(LimeR) == LIME_SUCCESS ) {
229
230 uint64_t file_bytes =limeReaderBytes(LimeR);
231
232 // std::cout << GridLogMessage << limeReaderType(LimeR) << " "<< file_bytes <<" bytes "<<std::endl;
233 // std::cout << GridLogMessage<< " readLimeObject seeking "<< record_name <<" found record :" <<limeReaderType(LimeR) <<std::endl;
234
235 if ( !strncmp(limeReaderType(LimeR), record_name.c_str(),strlen(record_name.c_str()) ) ) {
236
237 // std::cout << GridLogMessage<< " readLimeLatticeBinaryObject matches ! " <<std::endl;
238
239 uint64_t PayloadSize = sizeof(sobj) * field.Grid()->_gsites;
240
241 // std::cout << "R sizeof(sobj)= " <<sizeof(sobj)<<std::endl;
242 // std::cout << "R Gsites " <<field.Grid()->_gsites<<std::endl;
243 // std::cout << "R Payload expected " <<PayloadSize<<std::endl;
244 // std::cout << "R file size " <<file_bytes <<std::endl;
245
246 assert(PayloadSize == file_bytes);// Must match or user error
247
248 uint64_t offset= ftello(File);
249 // std::cout << " ReadLatticeObject from offset "<<offset << std::endl;
250 BinarySimpleMunger<sobj,sobj> munge;
251 BinaryIO::readLatticeObject< vobj, sobj >(field, filename, munge, offset, format,nersc_csum,scidac_csuma,scidac_csumb,control);
252 std::cout << GridLogMessage << "SciDAC checksum A " << std::hex << scidac_csuma << std::dec << std::endl;
253 std::cout << GridLogMessage << "SciDAC checksum B " << std::hex << scidac_csumb << std::dec << std::endl;
255 // Insist checksum is next record
257 readScidacChecksum(scidacChecksum_,FieldNormMetaData_);
259 // Verify checksums
261 if(FieldNormMetaData_.norm2 != 0.0){
262 RealD n2ck = norm2(field);
263 std::cout << GridLogMessage << "Field norm: metadata= " << FieldNormMetaData_.norm2
264 << " / field= " << n2ck << " / rdiff= " << GRID_FIELD_NORM_CALC(FieldNormMetaData_,n2ck) << std::endl;
265 GRID_FIELD_NORM_CHECK(FieldNormMetaData_,n2ck);
266 }
267 assert(scidacChecksumVerify(scidacChecksum_,scidac_csuma,scidac_csumb)==1);
268
269 // find out if next field is a GridFieldNorm
270 return;
271 }
272 }
273 }
274 void readScidacChecksum(scidacChecksum &scidacChecksum_,
275 FieldNormMetaData &FieldNormMetaData_)
276 {
277 FieldNormMetaData_.norm2 =0.0;
278 std::string scidac_str(SCIDAC_CHECKSUM);
279 std::string field_norm_str(GRID_FIELD_NORM);
280 while ( limeReaderNextRecord(LimeR) == LIME_SUCCESS ) {
281 uint64_t nbytes = limeReaderBytes(LimeR);//size of this record (configuration)
282 std::vector<char> xmlc(nbytes+1,'\0');
283 limeReaderReadData((void *)&xmlc[0], &nbytes, LimeR);
284 std::string xmlstring = std::string(&xmlc[0]);
285 XmlReader RD(xmlstring, true, "");
286 if ( !strncmp(limeReaderType(LimeR), field_norm_str.c_str(),strlen(field_norm_str.c_str()) ) ) {
287 // std::cout << "FieldNormMetaData "<<xmlstring<<std::endl;
288 read(RD,field_norm_str,FieldNormMetaData_);
289 }
290 if ( !strncmp(limeReaderType(LimeR), scidac_str.c_str(),strlen(scidac_str.c_str()) ) ) {
291 // std::cout << SCIDAC_CHECKSUM << " " <<xmlstring<<std::endl;
292 read(RD,std::string("scidacChecksum"),scidacChecksum_);
293 return;
294 }
295 }
296 assert(0);
297 }
299 // Read a generic serialisable object
301 void readLimeObject(std::string &xmlstring,std::string record_name)
302 {
303 // should this be a do while; can we miss a first record??
304 while ( limeReaderNextRecord(LimeR) == LIME_SUCCESS ) {
305
306 // std::cout << GridLogMessage<< " readLimeObject seeking "<< record_name <<" found record :" <<limeReaderType(LimeR) <<std::endl;
307 uint64_t nbytes = limeReaderBytes(LimeR);//size of this record (configuration)
308
309 if ( !strncmp(limeReaderType(LimeR), record_name.c_str(),strlen(record_name.c_str()) ) ) {
310
311 // std::cout << GridLogMessage<< " readLimeObject matches ! " << record_name <<std::endl;
312 std::vector<char> xmlc(nbytes+1,'\0');
313 limeReaderReadData((void *)&xmlc[0], &nbytes, LimeR);
314 // std::cout << GridLogMessage<< " readLimeObject matches XML " << &xmlc[0] <<std::endl;
315
316 xmlstring = std::string(&xmlc[0]);
317 return;
318 }
319
320 }
321 assert(0);
322 }
323
324 template<class serialisable_object>
325 void readLimeObject(serialisable_object &object,std::string object_name,std::string record_name)
326 {
327 std::string xmlstring;
328
329 readLimeObject(xmlstring, record_name);
330 XmlReader RD(xmlstring, true, "");
331 read(RD,object_name,object);
332 }
333};
334
335class GridLimeWriter : public BinaryIO
336{
337 public:
338
340 // FIXME: format for RNG? Now just binary out instead
341 // FIXME: collective calls or not ?
342 // : must know if I am the I/O boss
344 FILE *File;
345 LimeWriter *LimeW;
346 std::string filename;
347 bool boss_node;
348 GridLimeWriter( bool isboss = true) {
349 boss_node = isboss;
350 }
351 void open(const std::string &_filename) {
352 filename= _filename;
353 if ( boss_node ) {
354 File = fopen(filename.c_str(), "w");
355 LimeW = limeCreateWriter(File); assert(LimeW != NULL );
356 }
357 }
359 // Close the file
361 void close(void) {
362 if ( boss_node ) {
363 fclose(File);
364 }
365 // limeDestroyWriter(LimeW);
366 }
368 // Lime utility functions
370 int createLimeRecordHeader(std::string message, int MB, int ME, size_t PayloadSize)
371 {
372 if ( boss_node ) {
373 LimeRecordHeader *h;
374 h = limeCreateHeader(MB, ME, const_cast<char *>(message.c_str()), PayloadSize);
375 assert(limeWriteRecordHeader(h, LimeW) >= 0);
376 limeDestroyHeader(h);
377 }
378 return LIME_SUCCESS;
379 }
381 // Write a generic serialisable object
383 void writeLimeObject(int MB,int ME,XmlWriter &writer,std::string object_name,std::string record_name)
384 {
385 if ( boss_node ) {
386 std::string xmlstring = writer.docString();
387
388 // std::cout << "WriteLimeObject" << record_name <<std::endl;
389 uint64_t nbytes = xmlstring.size();
390 // std::cout << " xmlstring "<< nbytes<< " " << xmlstring <<std::endl;
391 int err;
392 LimeRecordHeader *h = limeCreateHeader(MB, ME,const_cast<char *>(record_name.c_str()), nbytes);
393 assert(h!= NULL);
394
395 err=limeWriteRecordHeader(h, LimeW); assert(err>=0);
396 err=limeWriteRecordData(&xmlstring[0], &nbytes, LimeW); assert(err>=0);
397 err=limeWriterCloseRecord(LimeW); assert(err>=0);
398 limeDestroyHeader(h);
399 }
400 }
401
402 template<class serialisable_object>
403 void writeLimeObject(int MB,int ME,serialisable_object &object,std::string object_name,std::string record_name, const unsigned int scientificPrec = 0)
404 {
405 XmlWriter WR("","");
406
407 if (scientificPrec)
408 {
409 WR.scientificFormat(true);
410 WR.setPrecision(scientificPrec);
411 }
412 write(WR,object_name,object);
413 writeLimeObject(MB, ME, WR, object_name, record_name);
414 }
416 // Write a generic lattice field and csum
417 // This routine is Collectively called by all nodes
418 // in communicator used by the field.Grid()
420 template<class vobj, class group_name=GroupName::SU, MatrixFormat matrix_fmt=MatrixFormat::FULL, FloatingPointFormat fp_fmt=FloatingPointFormat::IEEE64BIG>
421 void writeLimeLatticeBinaryObject(Lattice<vobj> &field,std::string record_name,int control=BINARYIO_LEXICOGRAPHIC)
422 {
424 // NB: FILE and iostream are jointly writing disjoint sequences in the
425 // the same file through different file handles (integer units).
426 //
427 // These are both buffered, so why I think this code is right is as follows.
428 //
429 // i) write record header to FILE *File, telegraphing the size; flush
430 // ii) ftello reads the offset from FILE *File .
431 // iii) iostream / MPI Open independently seek this offset. Write sequence direct to disk.
432 // Closes iostream and flushes.
433 // iv) fseek on FILE * to end of this disjoint section.
434 // v) Continue writing scidac record.
436
437 GridBase *grid = field.Grid();
438 assert(boss_node == field.Grid()->IsBoss() );
439
440 FieldNormMetaData FNMD; FNMD.norm2 = norm2(field);
441
443 // Create record header
445 int err;
446 uint32_t nersc_csum,scidac_csuma,scidac_csumb;
447 typedef typename GaugeUnMunger<vobj, group_name, matrix_fmt, fp_fmt>::out_type sobj;
448 uint64_t PayloadSize = sizeof(sobj) * grid->_gsites;
449 if ( boss_node ) {
450 createLimeRecordHeader(record_name, 0, 0, PayloadSize);
451 fflush(File);
452 }
453
454 // std::cout << "W sizeof(sobj)" <<sizeof(sobj)<<std::endl;
455 // std::cout << "W Gsites " <<field.Grid()->_gsites<<std::endl;
456 // std::cout << "W Payload expected " <<PayloadSize<<std::endl;
457
459 // Check all nodes agree on file position
461 uint64_t offset1;
462 if ( boss_node ) {
463 offset1 = ftello(File);
464 }
465 grid->Broadcast(0,(void *)&offset1,sizeof(offset1));
466
468 // The above is collective. Write by other means into the binary record
470 std::string format = getFormatString<sobj>();
471
472 GaugeUnMunger<vobj, group_name, matrix_fmt, fp_fmt> unmunger;
473
474 BinaryIO::writeLatticeObject<vobj,sobj>(field, filename, unmunger, offset1, format, nersc_csum, scidac_csuma, scidac_csumb, control);
475
477 // Wind forward and close the record
479 if ( boss_node ) {
480 fseek(File,0,SEEK_END);
481 uint64_t offset2 = ftello(File); // std::cout << " now at offset "<<offset2 << std::endl;
482 assert( (offset2-offset1) == PayloadSize);
483 }
484
486 // Check MPI-2 I/O did what we expect to file
488
489 if ( boss_node ) {
490 err=limeWriterCloseRecord(LimeW); assert(err>=0);
491 }
493 // Write checksum element, propagating forward from the BinaryIO
494 // Always pair a checksum with a binary object, and close message
496 scidacChecksum checksum;
497 std::stringstream streama; streama << std::hex << scidac_csuma;
498 std::stringstream streamb; streamb << std::hex << scidac_csumb;
499 checksum.suma= streama.str();
500 checksum.sumb= streamb.str();
501 if ( boss_node ) {
502 writeLimeObject(0,0,FNMD,std::string(GRID_FIELD_NORM),std::string(GRID_FIELD_NORM));
503 writeLimeObject(0,1,checksum,std::string("scidacChecksum"),std::string(SCIDAC_CHECKSUM));
504 }
505 }
506};
507
508class ScidacWriter : public GridLimeWriter {
509 public:
510
511 ScidacWriter(bool isboss =true ) : GridLimeWriter(isboss) { };
512
513 template<class SerialisableUserFile>
514 void writeScidacFileRecord(GridBase *grid,SerialisableUserFile &_userFile)
515 {
516 scidacFile _scidacFile(grid);
517 if ( this->boss_node ) {
518 writeLimeObject(1,0,_scidacFile,_scidacFile.SerialisableClassName(),std::string(SCIDAC_PRIVATE_FILE_XML));
519 writeLimeObject(0,1,_userFile,_userFile.SerialisableClassName(),std::string(SCIDAC_FILE_XML));
520 }
521 }
523 // Write generic lattice field in scidac format
525 template <class vobj, class userRecord>
526 void writeScidacFieldRecord(Lattice<vobj> &field,userRecord _userRecord,
527 const unsigned int recordScientificPrec = 0,
528 int control=BINARYIO_LEXICOGRAPHIC)
529 {
530 GridBase * grid = field.Grid();
531
533 // fill the Grid header
535 FieldMetaData header;
536 scidacRecord _scidacRecord;
537 scidacFile _scidacFile;
538
539 ScidacMetaData(field,header,_scidacRecord,_scidacFile);
540
542 // Fill the Lime file record by record
544 if ( this->boss_node ) {
545 writeLimeObject(1,0,header ,std::string("FieldMetaData"),std::string(GRID_FORMAT)); // Open message
546 writeLimeObject(0,0,_userRecord,_userRecord.SerialisableClassName(),std::string(SCIDAC_RECORD_XML), recordScientificPrec);
547 writeLimeObject(0,0,_scidacRecord,_scidacRecord.SerialisableClassName(),std::string(SCIDAC_PRIVATE_RECORD_XML));
548 }
549 // Collective call
550 writeLimeLatticeBinaryObject(field,std::string(ILDG_BINARY_DATA),control); // Closes message with checksum
551 }
552};
553
554
555class ScidacReader : public GridLimeReader {
556 public:
557
558 template<class SerialisableUserFile>
559 void readScidacFileRecord(GridBase *grid,SerialisableUserFile &_userFile)
560 {
561 scidacFile _scidacFile(grid);
562 readLimeObject(_scidacFile,_scidacFile.SerialisableClassName(),std::string(SCIDAC_PRIVATE_FILE_XML));
563 readLimeObject(_userFile,_userFile.SerialisableClassName(),std::string(SCIDAC_FILE_XML));
564 }
566 // Write generic lattice field in scidac format
568 template <class vobj, class userRecord>
569 void readScidacFieldRecord(Lattice<vobj> &field,userRecord &_userRecord,
570 int control=BINARYIO_LEXICOGRAPHIC)
571 {
572 typedef typename vobj::scalar_object sobj;
573 GridBase * grid = field.Grid();
574
576 // fill the Grid header
578 FieldMetaData header;
579 scidacRecord _scidacRecord;
580 scidacFile _scidacFile;
581
583 // Fill the Lime file record by record
585 readLimeObject(header ,std::string("FieldMetaData"),std::string(GRID_FORMAT)); // Open message
586 readLimeObject(_userRecord,_userRecord.SerialisableClassName(),std::string(SCIDAC_RECORD_XML));
587 readLimeObject(_scidacRecord,_scidacRecord.SerialisableClassName(),std::string(SCIDAC_PRIVATE_RECORD_XML));
588 readLimeLatticeBinaryObject(field,std::string(ILDG_BINARY_DATA),control);
589 }
590 void skipPastBinaryRecord(void) {
591 std::string rec_name(ILDG_BINARY_DATA);
592 while ( limeReaderNextRecord(LimeR) == LIME_SUCCESS ) {
593 if ( !strncmp(limeReaderType(LimeR), rec_name.c_str(),strlen(rec_name.c_str()) ) ) {
594 // in principle should do the line below, but that breaks backard compatibility with old data
595 // skipPastObjectRecord(std::string(GRID_FIELD_NORM));
596 skipPastObjectRecord(std::string(SCIDAC_CHECKSUM));
597 return;
598 }
599 }
600 }
601 void skipPastObjectRecord(std::string rec_name) {
602 while ( limeReaderNextRecord(LimeR) == LIME_SUCCESS ) {
603 if ( !strncmp(limeReaderType(LimeR), rec_name.c_str(),strlen(rec_name.c_str()) ) ) {
604 return;
605 }
606 }
607 }
608 void skipScidacFieldRecord() {
609 skipPastObjectRecord(std::string(GRID_FORMAT));
610 skipPastObjectRecord(std::string(SCIDAC_RECORD_XML));
611 skipPastObjectRecord(std::string(SCIDAC_PRIVATE_RECORD_XML));
612 skipPastBinaryRecord();
613 }
614};
615
616
617class IldgWriter : public ScidacWriter {
618 public:
619
620 IldgWriter(bool isboss) : ScidacWriter(isboss) {};
621
623 // A little helper
625 void writeLimeIldgLFN(std::string &LFN)
626 {
627 uint64_t PayloadSize = LFN.size();
628 int err;
629 createLimeRecordHeader(ILDG_DATA_LFN, 1 , 1, PayloadSize);
630 err=limeWriteRecordData(const_cast<char*>(LFN.c_str()), &PayloadSize,LimeW); assert(err>=0);
631 err=limeWriterCloseRecord(LimeW); assert(err>=0);
632 }
633
635 // Special ILDG operations ; gauge configs only.
636 // Don't require scidac records EXCEPT checksum
637 // Use Grid MetaData object if present.
639 template <class stats = PeriodicGaugeStatistics, class group_name = GroupName::SU, MatrixFormat matrix_fmt = MatrixFormat::FULL, FloatingPointFormat fp_fmt = FloatingPointFormat::IEEE64BIG, class vobj>
640 void writeConfiguration(Lattice<vobj> &Umu, int sequence, std::string LFN, std::string description)
641 {
642 GridBase * grid = Umu.Grid();
643
644 // catch malformed (wrt group_name) template instantiations at compile-time
645 static_assert( std::is_same_v<group_name, GroupName::SU> || (std::is_same_v<group_name, GroupName::Sp> && Nc%2==0), "IldgWriter supports SU(Nc) and Sp(Nc=2k) lattices. For Sp fields Nc must be even" );
646
648 // fill the Grid header
650 FieldMetaData header;
651 scidacRecord _scidacRecord;
652 scidacFile _scidacFile;
653
654 ScidacMetaData(Umu,header,_scidacRecord,_scidacFile);
655
656 stats Stats;
657 Stats(Umu,header);
658
659 header.ensemble_id = description;
660 header.ensemble_label = description;
661 header.sequence_number = sequence;
662 header.ildg_lfn = LFN;
664 // Fill ILDG header data struct
666 ildgFormat ildgfmt ;
667 const std::string stNC = std::to_string( Nc ) ;
668
669 // use the gauge group to populate the 'field' element of ildg header
670 if constexpr ( is_su<group_name>::value ) {
671 std::cout << GridLogMessage << "writing SU(" << stNC << ") field" << std::endl;
672 ildgfmt.field = std::string("su"+stNC+"gauge");
673 } else if constexpr ( is_sp<group_name>::value ) {
674 std::cout << GridLogMessage << "writing Sp(" << stNC << ") field" << std::endl;
675 ildgfmt.field = std::string("sp"+stNC+"gauge");
676 } else {
677 static_assert(1, "Unrecognised group; unable to determine field tag");
678 }
679
680 // populate 'rows' element of ildg header
681 if constexpr( matrix_fmt==MatrixFormat::REDUCED && is_su<group_name>::value ) {
682 ildgfmt.rows = Nc-1 ;
683 } else if constexpr( matrix_fmt==MatrixFormat::REDUCED && is_sp<group_name>::value ) {
684 ildgfmt.rows = Nc/2 ;
685 } else if constexpr( matrix_fmt==MatrixFormat::FULL ) {
686 ildgfmt.rows = Nc;
687 } else {
688 static_assert(1, "Unknown MatrixFormat specified");
689 }
690
691 // set fmt string for single/double precision
692 if constexpr( fp_fmt == FloatingPointFormat::IEEE32BIG ) {
693 header.floating_point = std::string("IEEE32BIG");
694 ildgfmt.precision = 32;
695 } else if constexpr ( fp_fmt == FloatingPointFormat::IEEE64BIG ) {
696 header.floating_point = std::string("IEEE64BIG");
697 ildgfmt.precision = 64;
698 } else {
699 static_assert(1, "Unknown FloatingPointFormat specified");
700 }
701
702 ildgfmt.version = 1.2;
703 ildgfmt.lx = header.dimension[0];
704 ildgfmt.ly = header.dimension[1];
705 ildgfmt.lz = header.dimension[2];
706 ildgfmt.lt = header.dimension[3];
707 assert(header.nd==4);
708 assert(header.nd==header.dimension.size());
709
711 // Field norm tests
713 FieldNormMetaData FieldNormMetaData_;
714 FieldNormMetaData_.norm2 = norm2(Umu);
715
717 // Fill the USQCD info field
719 usqcdInfo info;
720 info.version=1.0;
721 info.plaq = header.plaquette;
722 info.linktr = header.link_trace;
723
724 // std::cout << GridLogMessage << " Writing config; IldgIO n2 "<< FieldNormMetaData_.norm2<<std::endl;
726 // Fill the Lime file record by record
728 writeLimeObject(1,0,header ,std::string("FieldMetaData"),std::string(GRID_FORMAT)); // Open message
729 writeLimeObject(0,0,FieldNormMetaData_,FieldNormMetaData_.SerialisableClassName(),std::string(GRID_FIELD_NORM));
730 writeLimeObject(0,0,_scidacFile,_scidacFile.SerialisableClassName(),std::string(SCIDAC_PRIVATE_FILE_XML));
731 writeLimeObject(0,1,info,info.SerialisableClassName(),std::string(SCIDAC_FILE_XML));
732 writeLimeObject(1,0,_scidacRecord,_scidacRecord.SerialisableClassName(),std::string(SCIDAC_PRIVATE_RECORD_XML));
733 writeLimeObject(0,0,info,info.SerialisableClassName(),std::string(SCIDAC_RECORD_XML));
734 writeLimeObject(0,0,ildgfmt,std::string("ildgFormat") ,std::string(ILDG_FORMAT)); // rec
735 writeLimeLatticeBinaryObject<vobj,group_name,matrix_fmt,fp_fmt>(Umu,std::string(ILDG_BINARY_DATA)); // Closes message with checksum
736 writeLimeIldgLFN(header.ildg_lfn); // rec
737 // limeDestroyWriter(LimeW);
738 }
739};
740
741class IldgReader : public GridLimeReader {
742 public:
743
745 // this helper function wraps the logic for choosing
746 // the correct munger and intermediate lattice data type
747 // when reading a lattice field from disk.
748 // This is a runtime choice as Grid has to first read the
749 // header of the lattice cfg. Therefore templating this function on
750 // FloatingPointFormat etc. does not work. It is a rather
751 // cumbersome if statement with 6 branches,
752 // but the benefit of organising IldgReader this way is it makes
753 // readConfiguration clearer and more readable.
755 template<bool unique_su, class vobj>
756 void readLatticeBinaryObject(Lattice<vobj> &Umu, std::string filename, FloatingPointFormat fp_fmt, MatrixFormat matrix_fmt, bool is_grp_su, bool is_grp_sp, uint64_t &offset, uint32_t &nersc_csum, uint32_t &scidac_csuma, uint32_t &scidac_csumb)
757 {
758
759 // need all the types we could possibly read from
760 // including the intermediate data types for
761 // reduced format lattices and single/double precision
762 typedef typename vobj::scalar_object sobj;
763 typedef LorentzColourMatrixF fobj;
764 typedef LorentzColourMatrixD dobj;
765 typedef LorentzColour2x3F fobjsuR;
766 typedef LorentzColour2x3D dobjsuR;
767 typedef LorentzColourNx2NF fobjspR;
768 typedef LorentzColourNx2ND dobjspR;
769
770 std::string format;
771
772 if ( fp_fmt == FloatingPointFormat::IEEE64BIG ) {
773 format = std::string("IEEE64BIG");
774 if ( is_grp_su && matrix_fmt==MatrixFormat::REDUCED ) {
775 GaugeSUmunger<dobjsuR,sobj,unique_su> munge;
776 BinaryIO::readLatticeObject<vobj,dobjsuR>(Umu, filename, munge, offset, format,nersc_csum,scidac_csuma,scidac_csumb);
777 } else if ( is_grp_sp && matrix_fmt==MatrixFormat::REDUCED ) {
778 GaugeSpmunger<dobjspR,sobj> munge;
779 BinaryIO::readLatticeObject<vobj,dobjspR>(Umu, filename, munge, offset, format,nersc_csum,scidac_csuma,scidac_csumb);
780 } else {
781 GaugeSimpleMunger<dobj,sobj> munge;
782 BinaryIO::readLatticeObject<vobj,dobj>(Umu, filename, munge, offset, format,nersc_csum,scidac_csuma,scidac_csumb);
783 }
784 } else if ( fp_fmt == FloatingPointFormat::IEEE32BIG) {
785 format = std::string("IEEE32BIG");
786 if ( is_grp_su && matrix_fmt==MatrixFormat::REDUCED ) {
787 GaugeSUmunger<fobjsuR,sobj,unique_su> munge;
788 BinaryIO::readLatticeObject<vobj,fobjsuR>(Umu, filename, munge, offset, format,nersc_csum,scidac_csuma,scidac_csumb);
789 } else if ( is_grp_sp && matrix_fmt==MatrixFormat::REDUCED ) {
790 GaugeSpmunger<fobjspR,sobj> munge;
791 BinaryIO::readLatticeObject<vobj,fobjspR>(Umu, filename, munge, offset, format,nersc_csum,scidac_csuma,scidac_csumb);
792 } else {
793 GaugeSimpleMunger<fobj,sobj> munge;
794 BinaryIO::readLatticeObject<vobj,fobj>(Umu, filename, munge, offset, format,nersc_csum,scidac_csuma,scidac_csumb);
795 }
796 } else { assert("Unable to determine which readLatticeObject function template to instantiate."); }
797
798 }
799
801 // Read either Grid/SciDAC/ILDG configuration
802 // Don't require scidac records EXCEPT checksum
803 // Use Grid MetaData object if present.
804 // Else use ILDG MetaData object if present.
805 // Else use SciDAC MetaData object if present.
807 template <class stats = PeriodicGaugeStatistics, bool unique_su = false, class vobj>
808 void readConfiguration(Lattice<vobj> &Umu, FieldMetaData &FieldMetaData_) {
809
810 GridBase *grid = Umu.Grid();
811
812 Coordinate dims = Umu.Grid()->FullDimensions();
813
814 assert(dims.size()==4);
815
816 // Metadata holders
817 ildgFormat ildgFormat_ ;
818 std::string ildgLFN_ ;
819 scidacChecksum scidacChecksum_;
820 usqcdInfo usqcdInfo_ ;
821 FieldNormMetaData FieldNormMetaData_;
822
823 // track what we read from file
824 int found_ildgFormat =0;
825 int found_ildgLFN =0;
826 int found_scidacChecksum=0;
827 int found_usqcdInfo =0;
828 int found_ildgBinary =0;
829 int found_FieldMetaData =0;
830 int found_FieldNormMetaData =0;
831 uint32_t nersc_csum;
832 uint32_t scidac_csuma;
833 uint32_t scidac_csumb;
834
835 // these variables store information about the lattice that is read
836 // from its ildg-format header. if matrix_fmt==MatrixFormat::REDUCED
837 // then Grid will reconstruct the full matrix using the appropriate munger.
838 bool is_grp_su = false;
839 bool is_grp_sp = false;
840 MatrixFormat matrix_fmt;
841 // Binary format
842 std::string format;
843 FloatingPointFormat fp_fmt;
844
846 // Loop over all records
847 // -- Order is poorly guaranteed except ILDG header preceeds binary section.
848 // -- Run like an event loop.
849 // -- Impose trust hierarchy. Grid takes precedence & look for ILDG, and failing
850 // that Scidac.
851 // -- Insist on Scidac checksum record.
853
854 while ( limeReaderNextRecord(LimeR) == LIME_SUCCESS ) {
855
856 uint64_t nbytes = limeReaderBytes(LimeR);//size of this record (configuration)
857
859 // If not BINARY_DATA read a string and parse
861 if ( strncmp(limeReaderType(LimeR), ILDG_BINARY_DATA,strlen(ILDG_BINARY_DATA) ) ) {
862
863 // Copy out the string
864 std::vector<char> xmlc(nbytes+1,'\0');
865 limeReaderReadData((void *)&xmlc[0], &nbytes, LimeR);
866 // std::cout << GridLogMessage<< "Non binary record :" <<limeReaderType(LimeR) <<std::endl; //<<"\n"<<(&xmlc[0])<<std::endl;
867
869 // ILDG format record
870
871 std::string xmlstring(&xmlc[0]);
872 if ( !strncmp(limeReaderType(LimeR), ILDG_FORMAT,strlen(ILDG_FORMAT)) ) {
873
874 // 'older' format ildg lattices don't have a <rows/> element in their header.
875 // Grid's XML parsing doesn't support optional parameters with default values.
876 // Rather than rewrite that, here we explicitly add the <rows/> element with default
877 // value = Nc to the ildg header if it is missing, before passing it to XmlReader."
878 pugi::xml_document doc;
879 doc.load_string(xmlstring.c_str());
880
881 if(doc.child("ildgFormat").child("rows")) {
882 std::string rows = doc.child("ildgFormat").child("rows").child_value();
883 std::cout << GridLogMessage << "<rows/> element present = " << rows << ". So this lattice might be ildg 1.2 compliant." << std::endl;
884 } else {
885 std::cout << GridLogMessage << "<rows/> element not present - adding it after <field>." << std::endl;
886 pugi::xml_node ildgfmt = doc.child("ildgFormat");
887 const std::string stNC = std::to_string(Nc);
888 ildgfmt.insert_child_after("rows", ildgfmt.child("field")).text().set(stNC.c_str());
889
890 // write result back into xmlstring
891 std::ostringstream ss;
892 doc.save(ss);
893 xmlstring = ss.str();
894 }
895
896 XmlReader RD(xmlstring, true, "");
897 read(RD,"ildgFormat",ildgFormat_);
898
899 if ( ildgFormat_.precision == 64 ) { fp_fmt = FloatingPointFormat::IEEE64BIG; }
900 if ( ildgFormat_.precision == 32 ) { fp_fmt = FloatingPointFormat::IEEE32BIG; }
901
902 // set vars to tell Grid if it needs to reconstruct the full field.
903 std::cout << GridLogMessage << "ildgFormat_.rows is " << ildgFormat_.rows << std::endl;
904 matrix_fmt = (ildgFormat_.rows < Nc) ? MatrixFormat::REDUCED : MatrixFormat::FULL;
905 if( !strncmp(ildgFormat_.field.c_str(),"su",2) ) { is_grp_su = true; }
906 if( !strncmp(ildgFormat_.field.c_str(),"sp",2) ) { is_grp_sp = true; }
907 // check if field element corresponds to either su or sp
908 assert( is_grp_su || is_grp_sp );
909
910 // check rows and Nc are related in the way we expect for each gauge group.
911 if (matrix_fmt==MatrixFormat::REDUCED && is_grp_su) {
912 assert( ildgFormat_.rows == Nc-1 );
913 }
914 if (matrix_fmt==MatrixFormat::REDUCED && is_grp_sp) {
915 assert( ildgFormat_.rows == Nc/2 );
916 }
917 if (matrix_fmt==MatrixFormat::FULL) {
918 assert( ildgFormat_.rows == Nc );
919 }
920
921 assert( ildgFormat_.lx == dims[0]);
922 assert( ildgFormat_.ly == dims[1]);
923 assert( ildgFormat_.lz == dims[2]);
924 assert( ildgFormat_.lt == dims[3]);
925
926 found_ildgFormat = 1;
927 }
928
929 if ( !strncmp(limeReaderType(LimeR), ILDG_DATA_LFN,strlen(ILDG_DATA_LFN)) ) {
930 FieldMetaData_.ildg_lfn = xmlstring;
931 found_ildgLFN = 1;
932 }
933
934 if ( !strncmp(limeReaderType(LimeR), GRID_FORMAT,strlen(ILDG_FORMAT)) ) {
935
936 XmlReader RD(xmlstring, true, "");
937 read(RD,"FieldMetaData",FieldMetaData_);
938
939 format = FieldMetaData_.floating_point;
940
941 assert(FieldMetaData_.dimension[0] == dims[0]);
942 assert(FieldMetaData_.dimension[1] == dims[1]);
943 assert(FieldMetaData_.dimension[2] == dims[2]);
944 assert(FieldMetaData_.dimension[3] == dims[3]);
945
946 found_FieldMetaData = 1;
947 }
948
949 if ( !strncmp(limeReaderType(LimeR), SCIDAC_RECORD_XML,strlen(SCIDAC_RECORD_XML)) ) {
950 // is it a USQCD info field
951 if ( xmlstring.find(std::string("usqcdInfo")) != std::string::npos ) {
952 // std::cout << GridLogMessage<<"...found a usqcdInfo field"<<std::endl;
953 XmlReader RD(xmlstring, true, "");
954 read(RD,"usqcdInfo",usqcdInfo_);
955 found_usqcdInfo = 1;
956 }
957 }
958
959 if ( !strncmp(limeReaderType(LimeR), SCIDAC_CHECKSUM,strlen(SCIDAC_CHECKSUM)) ) {
960 XmlReader RD(xmlstring, true, "");
961 read(RD,"scidacChecksum",scidacChecksum_);
962 found_scidacChecksum = 1;
963 }
964
965 if ( !strncmp(limeReaderType(LimeR), GRID_FIELD_NORM,strlen(GRID_FIELD_NORM)) ) {
966 XmlReader RD(xmlstring, true, "");
967 read(RD,GRID_FIELD_NORM,FieldNormMetaData_);
968 found_FieldNormMetaData = 1;
969 }
970
971 } else {
973 // Binary data
975 // std::cout << GridLogMessage << "ILDG Binary record found : " ILDG_BINARY_DATA << std::endl;
976
977 uint64_t offset= ftello(File);
978
979 readLatticeBinaryObject<unique_su>(Umu, filename, fp_fmt, matrix_fmt, is_grp_su, is_grp_sp, offset, nersc_csum, scidac_csuma, scidac_csumb);
980
981 found_ildgBinary = 1;
982 }
983
984 }
985
987 // Minimally must find binary segment and checksum
988 // Since this is an ILDG reader require ILDG format
990 assert(found_ildgLFN);
991 assert(found_ildgBinary);
992 assert(found_ildgFormat);
993 assert(found_scidacChecksum);
994
995 // Must find something with the lattice dimensions
996 assert(found_FieldMetaData||found_ildgFormat);
997
998 if ( found_FieldMetaData ) {
999
1000 std::cout << GridLogMessage<<"Grid MetaData record found: configuration was probably written by Grid ! Yay ! "<<std::endl;
1001
1002 } else {
1003
1004 assert(found_ildgFormat);
1005 const std::string stNC = std::to_string( Nc ) ;
1006 assert ( ildgFormat_.field == std::string("su"+stNC+"gauge") );
1007
1009 // Populate our Grid metadata as best we can
1011
1012 std::ostringstream vers; vers << ildgFormat_.version;
1013 FieldMetaData_.hdr_version = vers.str();
1014 FieldMetaData_.data_type = std::string("4D_SU"+stNC+"_GAUGE_"+stNC+"x"+stNC);
1015
1016 FieldMetaData_.nd=4;
1017 FieldMetaData_.dimension.resize(4);
1018
1019 FieldMetaData_.dimension[0] = ildgFormat_.lx ;
1020 FieldMetaData_.dimension[1] = ildgFormat_.ly ;
1021 FieldMetaData_.dimension[2] = ildgFormat_.lz ;
1022 FieldMetaData_.dimension[3] = ildgFormat_.lt ;
1023
1024 if ( found_usqcdInfo ) {
1025 FieldMetaData_.plaquette = usqcdInfo_.plaq;
1026 FieldMetaData_.link_trace= usqcdInfo_.linktr;
1027 std::cout << GridLogMessage <<"This configuration was probably written by USQCD "<<std::endl;
1028 std::cout << GridLogMessage <<"USQCD xml record Plaquette : "<<FieldMetaData_.plaquette<<std::endl;
1029 std::cout << GridLogMessage <<"USQCD xml record LinkTrace : "<<FieldMetaData_.link_trace<<std::endl;
1030 } else {
1031 FieldMetaData_.plaquette = 0.0;
1032 FieldMetaData_.link_trace= 0.0;
1033 std::cout << GridLogWarning << "This configuration is unsafe with no plaquette records that can verify it !!! "<<std::endl;
1034 }
1035 }
1036
1038 // Really really want to mandate a scidac checksum
1040 if ( found_FieldNormMetaData ) {
1041 RealD nn = norm2(Umu);
1042 GRID_FIELD_NORM_CHECK(FieldNormMetaData_,nn);
1043 std::cout << GridLogMessage<<"FieldNormMetaData matches " << std::endl;
1044 } else {
1045 std::cout << GridLogWarning<<"FieldNormMetaData not found. " << std::endl;
1046 }
1047 if ( found_scidacChecksum ) {
1048 FieldMetaData_.scidac_checksuma = stoull(scidacChecksum_.suma,0,16);
1049 FieldMetaData_.scidac_checksumb = stoull(scidacChecksum_.sumb,0,16);
1050 scidacChecksumVerify(scidacChecksum_,scidac_csuma,scidac_csumb);
1051 assert( scidac_csuma ==FieldMetaData_.scidac_checksuma);
1052 assert( scidac_csumb ==FieldMetaData_.scidac_checksumb);
1053 std::cout << GridLogMessage<<"SciDAC checksums match " << std::endl;
1054 } else {
1055 std::cout << GridLogWarning<<"SciDAC checksums not found. This is unsafe. " << std::endl;
1056 assert(0); // Can I insist always checksum ?
1057 }
1058
1059 if ( found_FieldMetaData || found_usqcdInfo ) {
1060 FieldMetaData checker;
1061 stats Stats;
1062 Stats(Umu,checker);
1063 assert(fabs(checker.plaquette - FieldMetaData_.plaquette )<1.0e-5);
1064 assert(fabs(checker.link_trace - FieldMetaData_.link_trace)<1.0e-5);
1065 std::cout << GridLogMessage<<"Plaquette and link trace match " << std::endl;
1066 }
1067 }
1068 };
1069
1071
1072
1073//HAVE_LIME
1074#endif
1075
AcceleratorVector< int, MaxDims > Coordinate
Definition Coordinate.h:95
RealD norm2(const Lattice< vobj > &arg)
GridLogger GridLogMessage(1, "Message", GridLogColours, "NORMAL")
GridLogger GridLogWarning(1, "Warning", GridLogColours, "YELLOW")
iLorentzColour2x3< ComplexD > LorentzColour2x3D
Definition MetaData.h:351
static std::string getFormatString(void)
Definition MetaData.h:45
void PrepareMetaData(Lattice< vobj > &field, FieldMetaData &header)
Definition MetaData.h:171
iLorentzColourNx2N< ComplexD > LorentzColourNx2ND
Definition MetaData.h:356
iLorentzColour2x3< ComplexF > LorentzColour2x3F
Definition MetaData.h:350
MatrixFormat
Definition MetaData.h:552
iLorentzColourNx2N< ComplexF > LorentzColourNx2NF
Definition MetaData.h:355
FloatingPointFormat
Definition MetaData.h:551
#define NAMESPACE_BEGIN(A)
Definition Namespace.h:35
#define NAMESPACE_END(A)
Definition Namespace.h:36
static constexpr int Nc
Definition QCD.h:50
iLorentzColourMatrix< ComplexD > LorentzColourMatrixD
Definition QCD.h:175
iLorentzColourMatrix< ComplexF > LorentzColourMatrixF
Definition QCD.h:174
#define header
double RealD
Definition Simd.h:61
accelerator_inline int isVector(void)
accelerator_inline int isScalar(void)
accelerator_inline int isMatrix(void)
accelerator_inline int indexRank(void)
accelerator_inline size_type size(void) const
Definition Coordinate.h:52
static void writeLatticeObject(Lattice< vobj > &Umu, std::string file, munger munge, uint64_t offset, const std::string &format, uint32_t &nersc_csum, uint32_t &scidac_csuma, uint32_t &scidac_csumb, int control=BINARYIO_LEXICOGRAPHIC)
Definition BinaryIO.h:580
static void readLatticeObject(Lattice< vobj > &Umu, std::string file, munger munge, uint64_t offset, const std::string &format, uint32_t &nersc_csum, uint32_t &scidac_csuma, uint32_t &scidac_csumb, int control=BINARYIO_LEXICOGRAPHIC)
Definition BinaryIO.h:541
void Broadcast(int root, void *data, int bytes)
const Coordinate & FullDimensions(void)
int64_t _gsites
GridBase * Grid(void) const
GridTypeMapper< scalar_type >::Realified real_scalar_type
void read(Reader< T > &r, const std::string &s, U &output)
Definition BaseIO.h:662
void write(Writer< T > &w, const std::string &s, const U &output)
Definition BaseIO.h:637
static const bool value
Definition GaugeGroup.h:67
static const bool value
Definition GaugeGroup.h:57