Bridge++  Ver. 1.3.x
fieldIO_LIME_MPIIO.cpp
Go to the documentation of this file.
1 
14 // this code only makes sense in MPI environment.
15 #ifdef USE_MPI
16 
17 #include "fieldIO_LIME_MPIIO.h"
18 #include <list>
19 
20 using Bridge::vout;
21 
22 const std::string FieldIO_LIME_MPIIO::class_name = "FieldIO_LIME_MPIIO";
23 
24 //====================================================================
25 // private definitions
26 
27 namespace {
28 // ILDG metadata
29  const char ildg_metadata_template[] =
30  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
31  "<ildgFormat xmlns=\"http://www.lqcd.org/ildg\"\n"
32  " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
33  " xsi:schemaLocation=\"http://www.lqcd.org/ildg http://www.lqcd.org/ildg/filefmt.xsd\">\n"
34  " <version> 1.0 </version>\n"
35  " <field> su3gauge </field>\n"
36  " <precision> %zu </precision>\n"
37  " <lx> %d </lx> <ly> %d </ly> <lz> %d </lz> <lt> %d </lt>\n"
38  "</ildgFormat>";
39 
40 // LIME header format
41 
42 #define LIME_MAGIC ((uint32_t)0x456789ab)
43 #define MB_MASK ((uint16_t)0x8000)
44 #define ME_MASK ((uint16_t)0x4000)
45 
46  struct LIME_header
47  {
48  uint32_t magic;
49  uint16_t version;
50  uint16_t bitfield;
51  uint64_t length;
52  char type[128];
53  };
54 
55 // local header info
56  struct LIME_record_info
57  {
58  uint64_t offset;
59  uint64_t length;
60  char type[128];
61  };
62 
63  typedef std::list<LIME_record_info> LIME_message_info;
64  typedef std::list<LIME_message_info> LIME_file_info;
65 
66  int traverse(MPI_File& fh, LIME_file_info& file_info);
67  int read_lime_header(MPI_File& fh, LIME_header& header);
68  int read_lime_content(MPI_File& fh, const MPI_Offset offset, char *buf, const size_t length);
69  int find_record_offset(const LIME_file_info& file_info, const char *type, MPI_Offset& pos);
70 
71  int report_file_info(const LIME_file_info& file_info);
72 
73  size_t write_lime_header(MPI_File& fh, const char *type, const size_t length, const uint16_t flag);
74  size_t write_lime_record(MPI_File& fh, const char *type, const char *content, const size_t length, const uint16_t flag);
75 
77 }
78 
79 //====================================================================
80 void FieldIO_LIME_MPIIO::read_file(Field *u, string filename)
81 {
82  static const char _function_name[] = "FieldIO_LIME_MPIIO::read_file";
83 
84  initialize();
85 
86  MPI_File fh;
87  int ret;
88 
89  int nin_file = m_format->nin();
90  int nex_file = m_format->nex();
91 
92 // Field::element_type *buf = new Field::element_type [m_nvol*m_nvector];
93  double *buf = new double [nin_file * nex_file * m_nvol];
94  if (!buf) {
95  vout.crucial(m_vl, "%s: allocate buffer failed.\n", _function_name);
96  exit(EXIT_FAILURE);
97  }
98 
99 #ifdef USE_BGNET
100  ret = MPI_File_open(MPI_COMM_WORLD, const_cast<char *>(filename.c_str()), MPI_MODE_RDONLY, MPI_INFO_NULL, &fh);
101 #else
102  ret = MPI_File_open(Communicator_impl::world(), const_cast<char *>(filename.c_str()), MPI_MODE_RDONLY, MPI_INFO_NULL, &fh);
103 #endif
104  if (ret) {
105  vout.crucial(m_vl, "%s: MPI_File_open failed.\n", _function_name);
106  exit(EXIT_FAILURE);
107  }
108 
109  MPI_Offset pos = 0;
110 
111  // process records
112  if (Communicator::is_primary()) {
113  LIME_file_info file_info;
114 
115  traverse(fh, file_info);
116 
117  report_file_info(file_info);
118 
119  if (!find_record_offset(file_info, "ildg-binary-data", pos)) {
120  vout.crucial(m_vl, "binary data record not found.\n");
121  exit(EXIT_FAILURE);
122  }
123  }
124 
125  // It would be better to use MPI communicator here (even when BGNET is available),
126  // since the mixture of MPI/BGNET is buggy in BG/Q
127 #ifdef USE_BGNET
128  MPI_Bcast(&pos, sizeof(off_t), MPI_BYTE, 0, MPI_COMM_WORLD);
129 #else
130  Communicator::Base::broadcast(sizeof(off_t), &pos, 0);
131 #endif
132 
133  ret = MPI_File_set_view(fh, pos, m_type_vector, m_type_tiled, const_cast<char *>("native"), MPI_INFO_NULL);
134  if (ret) {
135  vout.crucial(m_vl, "%s: MPI_File_set_view failed.\n", _function_name);
136  exit(EXIT_FAILURE);
137  }
138 
139  ret = MPI_File_read_all(fh, (void *)buf, m_nvol * nex_file, m_type_vector, MPI_STATUS_IGNORE);
140  if (ret) {
141  vout.crucial(m_vl, "%s: MPI_File_read_all failed.\n", _function_name);
142  exit(EXIT_FAILURE);
143  }
144 
145  ret = MPI_File_close(&fh);
146  if (ret) {
147  vout.crucial(m_vl, "%s: MPI_File_close failed.\n", _function_name);
148  exit(EXIT_FAILURE);
149  }
150 
151  if (!is_bigendian()) {
152 // convert_endian(buf, sizeof(Field::element_type), m_nvol*m_nvector);
153  convert_endian(buf, sizeof(double), m_nvol * nin_file * nex_file);
154  }
155 
156  // unpack buffer
157  double *p = buf;
158 
159  for (int j = 0; j < nex_file; ++j) {
160  for (int isite = 0; isite < m_nvol; ++isite) {
161  for (int i = 0; i < nin_file; ++i) {
162  int s, t;
163  m_format->file_to_field(s, t, i, j);
164 
165  u->set(s, isite, t, *p++);
166  }
167  }
168  }
169 
170  delete [] buf;
171 
172  finalize();
173 }
174 
175 
176 //====================================================================
177 void FieldIO_LIME_MPIIO::write_file(Field *u, string filename)
178 {
179  static const char _function_name[] = "FieldIO_LIME_MPIIO::write_file";
180 
181  initialize();
182 
183  int nin_file = m_format->nin();
184  int nex_file = m_format->nex();
185 
186  // Field::element_type *buf = new Field::element_type [m_nvol*m_nvector];
187  double *buf = new double [nin_file * nex_file * m_nvol];
188  if (!buf) {
189  vout.crucial(m_vl, "%s: allocate buffer failed.\n", _function_name);
190  exit(EXIT_FAILURE);
191  }
192 
193  size_t data_length = sizeof(double) * nin_file * nex_file * CommonParameters::Lvol();
194 
195  // pack buffer
196  double *p = buf;
197 
198  for (int j = 0; j < nex_file; ++j) {
199  for (int isite = 0; isite < m_nvol; ++isite) {
200  for (int i = 0; i < nin_file; ++i) {
201  int s, t;
202  m_format->file_to_field(s, t, i, j);
203 
204  *p++ = u->cmp(s, isite, t);
205  }
206  }
207  }
208 
209  if (!is_bigendian()) {
210  // convert_endian(buf, sizeof(Field::element_type), m_nvol*m_nvector);
211  convert_endian(buf, sizeof(double), nin_file * nex_file * m_nvol);
212  }
213 
214  MPI_File fh;
215  int ret;
216 
217 #ifdef USE_BGNET
218  ret = MPI_File_open(MPI_COMM_WORLD, const_cast<char *>(filename.c_str()), MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &fh);
219 #else
220  ret = MPI_File_open(Communicator_impl::world(), const_cast<char *>(filename.c_str()), MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &fh);
221 #endif
222  if (ret) {
223  vout.crucial(m_vl, "%s: MPI_File_open failed.\n", _function_name);
224  exit(EXIT_FAILURE);
225  }
226 
227  MPI_Offset pos = 0;
228 
229  if (Communicator::is_primary()) {
230  // metadata
231  char metadata[2048];
232  sprintf(metadata, ildg_metadata_template,
233  sizeof(double) * 8, /* bit width */
238 
239  pos += write_lime_record(fh, "ildg-format", metadata, strlen(metadata), MB_MASK);
240 
241  // content: write header
242  pos += write_lime_header(fh, "ildg-binary-data", data_length, ME_MASK);
243  }
244 
245  // It would be better to use MPI communicator here (even when BGNET is available),
246  // since the mixture of MPI/BGNET is buggy in BG/Q
247 #ifdef USE_BGNET
248  MPI_Bcast(&pos, sizeof(off_t), MPI_BYTE, 0, MPI_COMM_WORLD);
249 #else
250  Communicator::Base::broadcast(sizeof(off_t), &pos, 0);
251 #endif
252 
253  // content: write data
254 
255  ret = MPI_File_set_view(fh, pos, m_type_vector, m_type_tiled, const_cast<char *>("native"), MPI_INFO_NULL);
256  if (ret) {
257  vout.crucial(m_vl, "%s: MPI_File_set_view failed.\n", _function_name);
258  exit(EXIT_FAILURE);
259  }
260 
261  ret = MPI_File_write_all(fh, (void *)buf, m_nvol * nex_file, m_type_vector, MPI_STATUS_IGNORE);
262  if (ret) {
263  vout.crucial(m_vl, "%s: MPI_File_write_all failed.\n", _function_name);
264  exit(EXIT_FAILURE);
265  }
266 
267  // content: padding if needed
268  if (data_length % 8 > 0) {
269  size_t padding_size = (8 - data_length % 8) % 8;
270 
271  const char blank[8] = "";
272  ret = MPI_File_write_at(fh, pos + data_length, const_cast<char *>(blank), padding_size, MPI_BYTE, MPI_STATUS_IGNORE);
273  if (ret) {
274  vout.crucial(vl, "%s: write padding failed.\n", _function_name);
275  exit(EXIT_FAILURE);
276  }
277 
278  vout.general(m_vl, "%s: padding %lu bytes added.\n", _function_name, padding_size);
279  }
280 
281  ret = MPI_File_close(&fh);
282  if (ret) {
283  vout.crucial(m_vl, "%s: MPI_File_close failed.\n", _function_name);
284  exit(EXIT_FAILURE);
285  }
286 
287  delete [] buf;
288 
289  finalize();
290 }
291 
292 
293 //====================================================================
294 int FieldIO_LIME_MPIIO::initialize()
295 {
296  static const char _function_name[] = "FieldIO_LIME_MPIIO::initialize";
297 
298  // store verbose level to private parameter.
299  vl = m_vl;
300 
301  if (m_is_initialized) return EXIT_SUCCESS;
302 
303  int nin_file = m_format->nin();
304  int nex_file = m_format->nex();
305 
306  const int ndim = CommonParameters::Ndim();
307 
308  int *global_dims = new int[ndim];
309  global_dims[0] = CommonParameters::Lx();
310  global_dims[1] = CommonParameters::Ly();
311  global_dims[2] = CommonParameters::Lz();
312  global_dims[3] = CommonParameters::Lt();
313 
314  int *local_dims = new int[ndim];
315  local_dims[0] = CommonParameters::Nx();
316  local_dims[1] = CommonParameters::Ny();
317  local_dims[2] = CommonParameters::Nz();
318  local_dims[3] = CommonParameters::Nt();
319 
320  m_nvol = 1;
321  for (int i = 0; i < ndim; ++i) {
322  m_nvol *= local_dims[i];
323  }
324 
325  int *grid_pos = new int[ndim];
326  for (int i = 0; i < ndim; ++i) {
327  grid_pos[i] = Communicator::ipe(i);
328  }
329 
330  int *starts = new int[ndim];
331  for (int i = 0; i < ndim; ++i) {
332  starts[i] = local_dims[i] * grid_pos[i];
333  }
334 
335  int ret = 0;
336 
337 // MPI_Datatype m_type_vector;
338 // ret = MPI_Type_contiguous(sizeof(Field::element_type)*nin_file, MPI_BYTE, &m_type_vector);
339  ret = MPI_Type_contiguous(sizeof(double) * nin_file, MPI_BYTE, &m_type_vector);
340  if (ret) {
341  vout.general(m_vl, "%s: MPI_Type_Contiguous failed.\n", _function_name);
342  return EXIT_FAILURE;
343  }
344 
345  ret = MPI_Type_commit(&m_type_vector);
346  if (ret) {
347  vout.general(m_vl, "%s: MPI_Type_commit failed.\n", _function_name);
348  return EXIT_FAILURE;
349  }
350 
351 // MPI_Datatype m_type_tiled;
352  ret = MPI_Type_create_subarray(ndim, global_dims, local_dims, starts, MPI_ORDER_FORTRAN, m_type_vector, &m_type_tiled);
353  if (ret) {
354  vout.general(m_vl, "%s: MPI_Type_create_subarray failed.\n", _function_name);
355  return EXIT_FAILURE;
356  }
357 
358  ret = MPI_Type_commit(&m_type_tiled);
359  if (ret) {
360  vout.general(m_vl, "%s: MPI_Type_commit failed.\n", _function_name);
361  return EXIT_FAILURE;
362  }
363 
364  m_is_initialized = true;
365 
366  delete [] starts;
367  delete [] grid_pos;
368  delete [] local_dims;
369  delete [] global_dims;
370 
371  vout.detailed(m_vl, "FieldIO_LIME_MPIIO via MPI I/O initialize done.\n");
372 
373  return EXIT_SUCCESS;
374 }
375 
376 
377 //====================================================================
378 int FieldIO_LIME_MPIIO::finalize()
379 {
380  static const char _function_name[] = "FieldIO_LIME_MPIIO::finalize";
381 
382  if (!m_is_initialized) return EXIT_SUCCESS;
383 
384  int ret;
385 
386  ret = MPI_Type_free(&m_type_tiled);
387  if (ret) {
388  vout.general(m_vl, "%s: MPI_Type_free failed.\n", _function_name);
389  return EXIT_FAILURE;
390  }
391 
392  ret = MPI_Type_free(&m_type_vector);
393  if (ret) {
394  vout.general(m_vl, "%s: MPI_Type_free failed.\n", _function_name);
395  return EXIT_FAILURE;
396  }
397 
398  m_is_initialized = false;
399 
400  vout.detailed(m_vl, "%s via MPI I/O finalize done.\n", class_name.c_str());
401 
402  return EXIT_SUCCESS;
403 }
404 
405 
406 //====================================================================
407 namespace {
408 //--------------------------------------------------------------------
409  int read_lime_header(MPI_File& fh, LIME_header& header)
410  {
411  MPI_Status status;
412 
413  int ret = MPI_File_read(fh, (void *)&header, sizeof(LIME_header), MPI_BYTE, &status);
414 
415  int count;
416 
417  MPI_Get_count(&status, MPI_BYTE, &count);
418  if (count != sizeof(LIME_header)) { // data length short. end of file.
419  return 1;
420  }
421 
422  if (ret) {
423  vout.crucial(vl, "%s: io error.\n", __func__);
424  return -1;
425  }
426 
427  if (FieldIO::is_bigendian() == false) {
428  FieldIO::convert_endian(&header.magic, 4, 1);
429  FieldIO::convert_endian(&header.version, 2, 1);
430  FieldIO::convert_endian(&header.bitfield, 2, 1);
431  FieldIO::convert_endian(&header.length, 8, 1);
432  }
433 
434  if (header.magic != LIME_MAGIC) {
435  vout.crucial(vl, "not lime header.\n");
436  return -1;
437  }
438 
439  return 0;
440  }
441 
442 
443 //====================================================================
444  int traverse(MPI_File& fh, LIME_file_info& file_info)
445  {
446  // go to the beginning of the file
447  MPI_File_seek(fh, 0, MPI_SEEK_SET);
448 
449  MPI_Offset pos = 0;
450 
451  LIME_message_info message_info;
452 
453  while (true)
454  {
455  LIME_header header;
456  int stat = read_lime_header(fh, header);
457 
458 // if (stat == -1) { // bad input
459 // return -1;
460 // } else if (stat == 1) { // end of file
461 // break;
462 // } else if (stat != 0) {
463 // // unknown status.
464 // return -1;
465 // }
466 
467 // vout.detailed(vl, "read_lime_header: stat = %d, pos = %lu\n", stat, pos);
468 
469  if (stat != 0) {
470  break;
471  }
472 
473  // read ok
474  pos += sizeof(LIME_header);
475 
476  LIME_record_info record_info;
477 
478  memcpy((void *)&record_info, (void *)&header, sizeof(LIME_record_info));
479  record_info.offset = pos;
480 
481  // padding (0-7)
482  size_t padding_size = (8 - header.length % 8) % 8;
483 
484  // seek to next record
485  MPI_File_seek(fh, header.length + padding_size, MPI_SEEK_CUR);
486  pos += header.length + padding_size;
487 
488 
489  // store record info
490  if ((header.bitfield & MB_MASK) == MB_MASK) {
491  message_info.clear();
492  }
493 
494  message_info.push_back(record_info);
495 
496  if ((header.bitfield & ME_MASK) == ME_MASK) {
497  file_info.push_back(message_info);
498 // message_info.clear();
499  }
500  }
501 
502  return 0;
503  }
504 
505 
506 //====================================================================
507  int find_record_offset(const LIME_file_info& file_info, const char *type, MPI_Offset& pos)
508  {
509  bool is_found = false;
510 
511  for (LIME_file_info::const_iterator p = file_info.begin(); p != file_info.end(); ++p) {
512  for (LIME_message_info::const_iterator q = p->begin(); q != p->end(); ++q) {
513  if (strncmp(q->type, type, strlen(type)) == 0) {
514  is_found = true;
515  pos = q->offset;
516  break;
517  }
518  }
519  }
520 
521  return is_found ? 1 : 0;
522  }
523 
524 
525 //====================================================================
526  int read_record_content(MPI_File& fh, const LIME_file_info& file_info, const char *type, std::string& content)
527  {
528  bool is_found = false;
529  LIME_record_info info;
530 
531  for (LIME_file_info::const_iterator p = file_info.begin(); p != file_info.end(); ++p) {
532  for (LIME_message_info::const_iterator q = p->begin(); q != p->end(); ++q) {
533  if (strncmp(q->type, type, strlen(type)) == 0) {
534  is_found = true;
535  info = *q;
536  break;
537  }
538  }
539  }
540 
541  if (!is_found) {
542  return 0;
543  }
544 
545  MPI_Status status;
546  char *buf = new char [info.length + 1];
547  MPI_File_read_at(fh, info.offset, buf, info.length, MPI_BYTE, &status);
548 
549  int count;
550  MPI_Get_count(&status, MPI_BYTE, &count);
551 
552  if (count != info.length) {
553  vout.crucial(vl, "%s: read error. content length mismatch.\n", __func__);
554  exit(EXIT_FAILURE);
555  }
556 
557  content = std::string(buf);
558 
559  return 1;
560  }
561 
562 
563 //====================================================================
564  int report_file_info(const LIME_file_info& file_info)
565  {
566  Bridge::VerboseLevel vlo = vl;
567 
569 
570  for (LIME_file_info::const_iterator p = file_info.begin(); p != file_info.end(); ++p) {
571  vout.detailed(vl, "Message:\n");
572 
573  for (LIME_message_info::const_iterator q = p->begin(); q != p->end(); ++q) {
574  vout.detailed(vl, "\tRecord:\n");
575  vout.detailed(vl, "\t\toffset = %lu\n", q->offset);
576  vout.detailed(vl, "\t\tsize = %lu\n", q->length);
577  vout.detailed(vl, "\t\ttype = %s\n", q->type);
578  }
579  }
580 
581  vl = vlo;
582 
583  return 0;
584  }
585 
586 
587 //====================================================================
588  size_t write_lime_header(MPI_File& fh, const char *type, const size_t length, const uint16_t flag)
589  {
590  LIME_header header;
591 
592  memset(&header, 0, sizeof(LIME_header));
593 
594  header.magic = LIME_MAGIC;
595  header.version = (uint16_t)1;
596  header.bitfield = flag;
597  strncpy(header.type, type, 128);
598  header.length = length;
599 
600  if (FieldIO::is_bigendian() == false) {
601  FieldIO::convert_endian(&header.magic, 4, 1);
602  FieldIO::convert_endian(&header.version, 2, 1);
603  FieldIO::convert_endian(&header.bitfield, 2, 1);
604  FieldIO::convert_endian(&header.length, 8, 1);
605  }
606 
607  MPI_Status status;
608  int ret = MPI_File_write(fh, (void *)&header, sizeof(LIME_header), MPI_BYTE, &status);
609 
610  if (ret) {
611  vout.crucial(vl, "%s: write header failed.\n", __func__);
612  return 0;
613  }
614 
615  return sizeof(LIME_header); // length written.
616  }
617 
618 
619 //====================================================================
620  size_t write_lime_record(MPI_File& fh, const char *type, const char *content, const size_t length, const uint16_t flag)
621  {
622  const char blank[8] = "";
623 
624  if (write_lime_header(fh, type, length, flag) == 0) {
625  return 0;
626  }
627 
628  size_t padding_size = (8 - length % 8) % 8;
629 
630  MPI_Status status;
631  int ret = MPI_File_write(fh, const_cast<char *>(content), length, MPI_BYTE, &status);
632  if (ret) {
633  vout.crucial(vl, "%s: write content failed.\n", __func__);
634  return 0;
635  }
636 
637  if (padding_size > 0) {
638  ret = MPI_File_write(fh, const_cast<char *>(blank), padding_size, MPI_BYTE, &status);
639  if (ret) {
640  vout.crucial(vl, "%s: write padding failed.\n", __func__);
641  return 0;
642  }
643  }
644 
645  return sizeof(LIME_header) + length + padding_size;
646  }
647 
648 
649 //--------------------------------------------------------------------
650 } // unnamed namespace
651 #endif /* USE_MPI */
652 
653 //====================================================================
654 //============================================================END=====
BridgeIO vout
Definition: bridgeIO.cpp:278
static const std::string class_name
Definition: fieldIO.h:56
void detailed(const char *format,...)
Definition: bridgeIO.cpp:82
virtual void file_to_field(int &s, int &t, const int i, const int j) const =0
void set(const int jin, const int site, const int jex, double v)
Definition: field.h:155
virtual int nex() const =0
void general(const char *format,...)
Definition: bridgeIO.cpp:65
Container of Field-type object.
Definition: field.h:39
double cmp(const int jin, const int site, const int jex) const
Definition: field.h:123
void read_file(Field *v, std::string filename)
read data from file.
static int broadcast(size_t size, void *data, int sender)
static int ipe(const int dir)
logical coordinate of current proc.
static int Lvol()
static bool is_bigendian()
Definition: fieldIO.cpp:203
void write_file(Field *v, std::string filename)
write data to file.
const IO_Format::Format * m_format
Definition: fieldIO.h:62
void crucial(const char *format,...)
Definition: bridgeIO.cpp:48
Bridge::VerboseLevel vl
Definition: checker.cpp:18
VerboseLevel
Definition: bridgeIO.h:39
virtual int nin() const =0
static void convert_endian(void *buf, size_t size, size_t nmemb)
check if machine byte order is big-endian.
Definition: fieldIO.cpp:225
static bool is_primary()
check if the present node is primary in small communicator.
Bridge::VerboseLevel m_vl
Definition: fieldIO.h:64