1 // clang-format off
2 /* ----------------------------------------------------------------------
3    LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
4    https://www.lammps.org/, Sandia National Laboratories
5    Steve Plimpton, sjplimp@sandia.gov
6 
7    Copyright (2003) Sandia Corporation.  Under the terms of Contract
8    DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
9    certain rights in this software.  This software is distributed under
10    the GNU General Public License.
11 
12    See the README file in the top-level LAMMPS directory.
13 ------------------------------------------------------------------------- */
14 
15 #include "write_restart.h"
16 
17 #include "angle.h"
18 #include "atom.h"
19 #include "atom_vec.h"
20 #include "bond.h"
21 #include "comm.h"
22 #include "dihedral.h"
23 #include "domain.h"
24 #include "error.h"
25 #include "fix.h"
26 #include "force.h"
27 #include "group.h"
28 #include "improper.h"
29 #include "memory.h"
30 #include "modify.h"
31 #include "mpiio.h"
32 #include "neighbor.h"
33 #include "output.h"
34 #include "pair.h"
35 #include "thermo.h"
36 #include "update.h"
37 
38 #include <cstring>
39 
40 #include "lmprestart.h"
41 
42 using namespace LAMMPS_NS;
43 
44 /* ---------------------------------------------------------------------- */
45 
WriteRestart(LAMMPS * lmp)46 WriteRestart::WriteRestart(LAMMPS *lmp) : Command(lmp)
47 {
48   MPI_Comm_rank(world,&me);
49   MPI_Comm_size(world,&nprocs);
50   multiproc = 0;
51   noinit = 0;
52   fp = nullptr;
53 }
54 
55 /* ----------------------------------------------------------------------
56    called as write_restart command in input script
57 ------------------------------------------------------------------------- */
58 
command(int narg,char ** arg)59 void WriteRestart::command(int narg, char **arg)
60 {
61   if (domain->box_exist == 0)
62     error->all(FLERR,"Write_restart command before simulation box is defined");
63   if (narg < 1) error->all(FLERR,"Illegal write_restart command");
64 
65   // if filename contains a "*", replace with current timestep
66 
67   std::string file = arg[0];
68   std::size_t found = file.find('*');
69   if (found != std::string::npos)
70     file.replace(found,1,fmt::format("{}",update->ntimestep));
71 
72   // check for multiproc output and an MPI-IO filename
73 
74   if (strchr(arg[0],'%')) multiproc = nprocs;
75   else multiproc = 0;
76   if (strstr(arg[0],".mpiio")) mpiioflag = 1;
77   else mpiioflag = 0;
78 
79   // setup output style and process optional args
80   // also called by Output class for periodic restart files
81 
82   multiproc_options(multiproc,mpiioflag,narg-1,&arg[1]);
83 
84   // init entire system since comm->exchange is done
85   // comm::init needs neighbor::init needs pair::init needs kspace::init, etc
86 
87   if (noinit == 0) {
88     if (comm->me == 0) utils::logmesg(lmp,"System init for write_restart ...\n");
89     lmp->init();
90 
91     // move atoms to new processors before writing file
92     // enforce PBC in case atoms are outside box
93     // call borders() to rebuild atom map since exchange() destroys map
94     // NOTE: removed call to setup_pre_exchange
95     //   used to be needed by fixShearHistory for granular
96     //   to move history info from neigh list to atoms between runs
97     //   but now that is done via FIx::post_run()
98     //   don't think any other fix needs this or should do it
99     //   e.g. fix evaporate should not delete more atoms
100 
101     // modify->setup_pre_exchange();
102     if (domain->triclinic) domain->x2lamda(atom->nlocal);
103     domain->pbc();
104     domain->reset_box();
105     comm->setup();
106     comm->exchange();
107     comm->borders();
108     if (domain->triclinic) domain->lamda2x(atom->nlocal+atom->nghost);
109   }
110 
111   // write single restart file
112 
113   write(file);
114 }
115 
116 /* ---------------------------------------------------------------------- */
117 
multiproc_options(int multiproc_caller,int mpiioflag_caller,int narg,char ** arg)118 void WriteRestart::multiproc_options(int multiproc_caller, int mpiioflag_caller,
119                                      int narg, char **arg)
120 {
121   multiproc = multiproc_caller;
122   mpiioflag = mpiioflag_caller;
123 
124   // error checks
125 
126   if (multiproc && mpiioflag)
127     error->all(FLERR,
128                "Restart file MPI-IO output not allowed with % in filename");
129 
130   if (mpiioflag) {
131     mpiio = new RestartMPIIO(lmp);
132     if (!mpiio->mpiio_exists)
133       error->all(FLERR,"Writing to MPI-IO filename when "
134                  "MPIIO package is not installed");
135   }
136 
137   // defaults for multiproc file writing
138 
139   nclusterprocs = nprocs;
140   filewriter = 0;
141   if (me == 0) filewriter = 1;
142   fileproc = 0;
143 
144   if (multiproc) {
145     nclusterprocs = 1;
146     filewriter = 1;
147     fileproc = me;
148     icluster = me;
149   }
150 
151   // optional args
152 
153   int iarg = 0;
154   while (iarg < narg) {
155     if (strcmp(arg[iarg],"fileper") == 0) {
156       if (iarg+2 > narg) error->all(FLERR,"Illegal write_restart command");
157       if (!multiproc)
158         error->all(FLERR,"Cannot use write_restart fileper "
159                    "without % in restart file name");
160       int nper = utils::inumeric(FLERR,arg[iarg+1],false,lmp);
161       if (nper <= 0) error->all(FLERR,"Illegal write_restart command");
162 
163       multiproc = nprocs/nper;
164       if (nprocs % nper) multiproc++;
165       fileproc = me/nper * nper;
166       int fileprocnext = MIN(fileproc+nper,nprocs);
167       nclusterprocs = fileprocnext - fileproc;
168       if (me == fileproc) filewriter = 1;
169       else filewriter = 0;
170       icluster = fileproc/nper;
171       iarg += 2;
172 
173     } else if (strcmp(arg[iarg],"nfile") == 0) {
174       if (iarg+2 > narg) error->all(FLERR,"Illegal write_restart command");
175       if (!multiproc)
176         error->all(FLERR,"Cannot use write_restart nfile "
177                    "without % in restart file name");
178       int nfile = utils::inumeric(FLERR,arg[iarg+1],false,lmp);
179       if (nfile <= 0) error->all(FLERR,"Illegal write_restart command");
180       nfile = MIN(nfile,nprocs);
181 
182       multiproc = nfile;
183       icluster = static_cast<int> ((bigint) me * nfile/nprocs);
184       fileproc = static_cast<int> ((bigint) icluster * nprocs/nfile);
185       int fcluster = static_cast<int> ((bigint) fileproc * nfile/nprocs);
186       if (fcluster < icluster) fileproc++;
187       int fileprocnext =
188         static_cast<int> ((bigint) (icluster+1) * nprocs/nfile);
189       fcluster = static_cast<int> ((bigint) fileprocnext * nfile/nprocs);
190       if (fcluster < icluster+1) fileprocnext++;
191       nclusterprocs = fileprocnext - fileproc;
192       if (me == fileproc) filewriter = 1;
193       else filewriter = 0;
194       iarg += 2;
195 
196     } else if (strcmp(arg[iarg],"noinit") == 0) {
197       noinit = 1;
198       iarg++;
199     } else error->all(FLERR,"Illegal write_restart command");
200   }
201 }
202 
203 /* ----------------------------------------------------------------------
204    called from command() and directly from output within run/minimize loop
205    file = final file name to write, except may contain a "%"
206 ------------------------------------------------------------------------- */
207 
write(const std::string & file)208 void WriteRestart::write(const std::string &file)
209 {
210   // special case where reneighboring is not done in integrator
211   //   on timestep restart file is written (due to build_once being set)
212   // if box is changing, must be reset, else restart file will have
213   //   wrong box size and atoms will be lost when restart file is read
214   // other calls to pbc and domain and comm are not made,
215   //   b/c they only make sense if reneighboring is actually performed
216 
217   if (neighbor->build_once) domain->reset_box();
218 
219   // natoms = sum of nlocal = value to write into restart file
220   // if unequal and thermo lostflag is "error", don't write restart file
221 
222   bigint nblocal = atom->nlocal;
223   MPI_Allreduce(&nblocal,&natoms,1,MPI_LMP_BIGINT,MPI_SUM,world);
224   if (natoms != atom->natoms && output->thermo->lostflag == Thermo::ERROR)
225     error->all(FLERR,"Atom count is inconsistent, cannot write restart file");
226 
227   // open single restart file or base file for multiproc case
228 
229   if (me == 0) {
230     std::string base = file;
231     if (multiproc) base.replace(base.find('%'),1,"base");
232 
233     fp = fopen(base.c_str(),"wb");
234     if (fp == nullptr)
235       error->one(FLERR, "Cannot open restart file {}: {}",
236                                     base, utils::getsyserror());
237   }
238 
239   // proc 0 writes magic string, endian flag, numeric version
240 
241   if (me == 0) {
242     magic_string();
243     endian();
244     version_numeric();
245   }
246 
247   // proc 0 writes header, groups, pertype info, force field info
248 
249   if (me == 0) {
250     header();
251     group->write_restart(fp);
252     type_arrays();
253     force_fields();
254   }
255 
256   // all procs write fix info
257 
258   modify->write_restart(fp);
259 
260   // communication buffer for my atom info
261   // max_size = largest buffer needed by any proc
262   // NOTE: are assuming size_restart() returns 32-bit int
263   //   for a huge one-proc problem, nlocal could be 32-bit
264   //   but nlocal * doubles-peratom could overflow
265 
266   int max_size;
267   int send_size = atom->avec->size_restart();
268   MPI_Allreduce(&send_size,&max_size,1,MPI_INT,MPI_MAX,world);
269 
270   double *buf;
271   memory->create(buf,max_size,"write_restart:buf");
272   memset(buf,0,max_size*sizeof(buf));
273 
274   // all procs write file layout info which may include per-proc sizes
275 
276   file_layout(send_size);
277 
278   // header info is complete
279   // if multiproc output:
280   //   close header file, open multiname file on each writing proc,
281   //   write PROCSPERFILE into new file
282 
283   int io_error = 0;
284   if (multiproc) {
285     if (me == 0 && fp) {
286       magic_string();
287       if (ferror(fp)) io_error = 1;
288       fclose(fp);
289       fp = nullptr;
290     }
291 
292     std::string multiname = file;
293     multiname.replace(multiname.find('%'),1,fmt::format("{}",icluster));
294 
295     if (filewriter) {
296       fp = fopen(multiname.c_str(),"wb");
297       if (fp == nullptr)
298         error->one(FLERR, "Cannot open restart file {}: {}",
299                                       multiname, utils::getsyserror());
300       write_int(PROCSPERFILE,nclusterprocs);
301     }
302   }
303 
304   // pack my atom data into buf
305 
306   AtomVec *avec = atom->avec;
307   int n = 0;
308   for (int i = 0; i < atom->nlocal; i++) n += avec->pack_restart(i,&buf[n]);
309 
310   // if any fix requires it, remap each atom's coords via PBC
311   // is because fix changes atom coords (excepting an integrate fix)
312   // just remap in buffer, not actual atoms
313 
314   if (modify->restart_pbc_any) {
315     int triclinic = domain->triclinic;
316     double *lo,*hi,*period;
317 
318     if (triclinic == 0) {
319       lo = domain->boxlo;
320       hi = domain->boxhi;
321       period = domain->prd;
322     } else {
323       lo = domain->boxlo_lamda;
324       hi = domain->boxhi_lamda;
325       period = domain->prd_lamda;
326     }
327 
328     int xperiodic = domain->xperiodic;
329     int yperiodic = domain->yperiodic;
330     int zperiodic = domain->zperiodic;
331 
332     double *x;
333     int m = 0;
334     for (int i = 0; i < atom->nlocal; i++) {
335       x = &buf[m+1];
336       if (triclinic) domain->x2lamda(x,x);
337 
338       if (xperiodic) {
339         if (x[0] < lo[0]) x[0] += period[0];
340         if (x[0] >= hi[0]) x[0] -= period[0];
341         x[0] = MAX(x[0],lo[0]);
342       }
343       if (yperiodic) {
344         if (x[1] < lo[1]) x[1] += period[1];
345         if (x[1] >= hi[1]) x[1] -= period[1];
346         x[1] = MAX(x[1],lo[1]);
347       }
348       if (zperiodic) {
349         if (x[2] < lo[2]) x[2] += period[2];
350         if (x[2] >= hi[2]) x[2] -= period[2];
351         x[2] = MAX(x[2],lo[2]);
352       }
353 
354       if (triclinic) domain->lamda2x(x,x);
355       m += static_cast<int> (buf[m]);
356     }
357   }
358 
359   // MPI-IO output to single file
360 
361   if (mpiioflag) {
362     if (me == 0 && fp) {
363       magic_string();
364       if (ferror(fp)) io_error = 1;
365       fclose(fp);
366       fp = nullptr;
367     }
368     mpiio->openForWrite(file.c_str());
369     mpiio->write(headerOffset,send_size,buf);
370     mpiio->close();
371   } else {
372 
373     // output of one or more native files
374     // filewriter = 1 = this proc writes to file
375     // ping each proc in my cluster, receive its data, write data to file
376     // else wait for ping from fileproc, send my data to fileproc
377 
378     int tmp,recv_size;
379 
380     if (filewriter) {
381       MPI_Status status;
382       MPI_Request request;
383       for (int iproc = 0; iproc < nclusterprocs; iproc++) {
384         if (iproc) {
385           MPI_Irecv(buf,max_size,MPI_DOUBLE,me+iproc,0,world,&request);
386           MPI_Send(&tmp,0,MPI_INT,me+iproc,0,world);
387           MPI_Wait(&request,&status);
388           MPI_Get_count(&status,MPI_DOUBLE,&recv_size);
389         } else recv_size = send_size;
390 
391         write_double_vec(PERPROC,recv_size,buf);
392       }
393       magic_string();
394       if (ferror(fp)) io_error = 1;
395       fclose(fp);
396       fp = nullptr;
397 
398     } else {
399       MPI_Recv(&tmp,0,MPI_INT,fileproc,0,world,MPI_STATUS_IGNORE);
400       MPI_Rsend(buf,send_size,MPI_DOUBLE,fileproc,0,world);
401     }
402   }
403 
404   // Check for I/O error status
405 
406   int io_all = 0;
407   MPI_Allreduce(&io_error,&io_all,1,MPI_INT,MPI_MAX,world);
408   if (io_all) error->all(FLERR,"I/O error while writing restart");
409 
410   // clean up
411 
412   memory->destroy(buf);
413 
414   // invoke any fixes that write their own restart file
415 
416   for (int ifix = 0; ifix < modify->nfix; ifix++)
417     if (modify->fix[ifix]->restart_file)
418       modify->fix[ifix]->write_restart_file(file.c_str());
419 }
420 
421 /* ----------------------------------------------------------------------
422    proc 0 writes out problem description
423 ------------------------------------------------------------------------- */
424 
header()425 void WriteRestart::header()
426 {
427   write_string(VERSION,lmp->version);
428   write_int(SMALLINT,sizeof(smallint));
429   write_int(IMAGEINT,sizeof(imageint));
430   write_int(TAGINT,sizeof(tagint));
431   write_int(BIGINT,sizeof(bigint));
432   write_string(UNITS,update->unit_style);
433   write_bigint(NTIMESTEP,update->ntimestep);
434   write_int(DIMENSION,domain->dimension);
435   write_int(NPROCS,nprocs);
436   write_int_vec(PROCGRID,3,comm->procgrid);
437   write_int(NEWTON_PAIR,force->newton_pair);
438   write_int(NEWTON_BOND,force->newton_bond);
439   write_int(XPERIODIC,domain->xperiodic);
440   write_int(YPERIODIC,domain->yperiodic);
441   write_int(ZPERIODIC,domain->zperiodic);
442   write_int_vec(BOUNDARY,6,&domain->boundary[0][0]);
443 
444   // added field for shrink-wrap boundaries with minimum - 2 Jul 2015
445 
446   double minbound[6];
447   minbound[0] = domain->minxlo; minbound[1] = domain->minxhi;
448   minbound[2] = domain->minylo; minbound[3] = domain->minyhi;
449   minbound[4] = domain->minzlo; minbound[5] = domain->minzhi;
450   write_double_vec(BOUNDMIN,6,minbound);
451 
452   // write atom_style and its args
453 
454   write_string(ATOM_STYLE,atom->atom_style);
455   fwrite(&atom->avec->nargcopy,sizeof(int),1,fp);
456   for (int i = 0; i < atom->avec->nargcopy; i++) {
457     int n = strlen(atom->avec->argcopy[i]) + 1;
458     fwrite(&n,sizeof(int),1,fp);
459     fwrite(atom->avec->argcopy[i],sizeof(char),n,fp);
460   }
461 
462   write_bigint(NATOMS,natoms);
463   write_int(NTYPES,atom->ntypes);
464   write_bigint(NBONDS,atom->nbonds);
465   write_int(NBONDTYPES,atom->nbondtypes);
466   write_int(BOND_PER_ATOM,atom->bond_per_atom);
467   write_bigint(NANGLES,atom->nangles);
468   write_int(NANGLETYPES,atom->nangletypes);
469   write_int(ANGLE_PER_ATOM,atom->angle_per_atom);
470   write_bigint(NDIHEDRALS,atom->ndihedrals);
471   write_int(NDIHEDRALTYPES,atom->ndihedraltypes);
472   write_int(DIHEDRAL_PER_ATOM,atom->dihedral_per_atom);
473   write_bigint(NIMPROPERS,atom->nimpropers);
474   write_int(NIMPROPERTYPES,atom->nimpropertypes);
475   write_int(IMPROPER_PER_ATOM,atom->improper_per_atom);
476 
477   write_int(TRICLINIC,domain->triclinic);
478   write_double_vec(BOXLO,3,domain->boxlo);
479   write_double_vec(BOXHI,3,domain->boxhi);
480   write_double(XY,domain->xy);
481   write_double(XZ,domain->xz);
482   write_double(YZ,domain->yz);
483 
484   write_double_vec(SPECIAL_LJ,3,&force->special_lj[1]);
485   write_double_vec(SPECIAL_COUL,3,&force->special_coul[1]);
486 
487   write_double(TIMESTEP,update->dt);
488 
489   write_int(ATOM_ID,atom->tag_enable);
490   write_int(ATOM_MAP_STYLE,atom->map_style);
491   write_int(ATOM_MAP_USER,atom->map_user);
492   write_int(ATOM_SORTFREQ,atom->sortfreq);
493   write_double(ATOM_SORTBIN,atom->userbinsize);
494 
495   write_int(COMM_MODE,comm->mode);
496   write_double(COMM_CUTOFF,comm->cutghostuser);
497   write_int(COMM_VEL,comm->ghost_velocity);
498 
499   write_int(EXTRA_BOND_PER_ATOM,atom->extra_bond_per_atom);
500   write_int(EXTRA_ANGLE_PER_ATOM,atom->extra_angle_per_atom);
501   write_int(EXTRA_DIHEDRAL_PER_ATOM,atom->extra_dihedral_per_atom);
502   write_int(EXTRA_IMPROPER_PER_ATOM,atom->extra_improper_per_atom);
503   write_int(ATOM_MAXSPECIAL,atom->maxspecial);
504 
505   write_bigint(NELLIPSOIDS,atom->nellipsoids);
506   write_bigint(NLINES,atom->nlines);
507   write_bigint(NTRIS,atom->ntris);
508   write_bigint(NBODIES,atom->nbodies);
509 
510   // -1 flag signals end of header
511 
512   int flag = -1;
513   fwrite(&flag,sizeof(int),1,fp);
514 }
515 
516 /* ----------------------------------------------------------------------
517    proc 0 writes out any type-based arrays that are defined
518 ------------------------------------------------------------------------- */
519 
type_arrays()520 void WriteRestart::type_arrays()
521 {
522   if (atom->mass) write_double_vec(MASS,atom->ntypes,&atom->mass[1]);
523 
524   // -1 flag signals end of type arrays
525 
526   int flag = -1;
527   fwrite(&flag,sizeof(int),1,fp);
528 }
529 
530 /* ----------------------------------------------------------------------
531    proc 0 writes out and force field styles and data that are defined
532 ------------------------------------------------------------------------- */
533 
force_fields()534 void WriteRestart::force_fields()
535 {
536   if (force->pair) {
537     if (force->pair->restartinfo) {
538       write_string(PAIR,force->pair_style);
539       force->pair->write_restart(fp);
540     } else {
541       write_string(NO_PAIR,force->pair_style);
542     }
543   }
544   if (atom->avec->bonds_allow && force->bond) {
545     write_string(BOND,force->bond_style);
546     force->bond->write_restart(fp);
547   }
548   if (atom->avec->angles_allow && force->angle) {
549     write_string(ANGLE,force->angle_style);
550     force->angle->write_restart(fp);
551   }
552   if (atom->avec->dihedrals_allow && force->dihedral) {
553     write_string(DIHEDRAL,force->dihedral_style);
554     force->dihedral->write_restart(fp);
555   }
556   if (atom->avec->impropers_allow && force->improper) {
557     write_string(IMPROPER,force->improper_style);
558     force->improper->write_restart(fp);
559   }
560 
561   // -1 flag signals end of force field info
562 
563   int flag = -1;
564   fwrite(&flag,sizeof(int),1,fp);
565 }
566 
567 /* ----------------------------------------------------------------------
568    proc 0 writes out file layout info
569    all procs call this method, only proc 0 writes to file
570 ------------------------------------------------------------------------- */
571 
file_layout(int send_size)572 void WriteRestart::file_layout(int send_size)
573 {
574   if (me == 0) {
575     write_int(MULTIPROC,multiproc);
576     write_int(MPIIO,mpiioflag);
577   }
578 
579   if (mpiioflag) {
580     int *all_send_sizes;
581     memory->create(all_send_sizes,nprocs,"write_restart:all_send_sizes");
582     MPI_Gather(&send_size, 1, MPI_INT, all_send_sizes, 1, MPI_INT, 0,world);
583     if (me == 0) fwrite(all_send_sizes,sizeof(int),nprocs,fp);
584     memory->destroy(all_send_sizes);
585   }
586 
587   // -1 flag signals end of file layout info
588 
589   if (me == 0) {
590     int flag = -1;
591     fwrite(&flag,sizeof(int),1,fp);
592   }
593 
594   // if MPI-IO file, broadcast the end of the header offste
595   // this allows all ranks to compute offset to their data
596 
597   if (mpiioflag) {
598     if (me == 0) headerOffset = ftell(fp);
599     MPI_Bcast(&headerOffset,1,MPI_LMP_BIGINT,0,world);
600   }
601 }
602 
603 // ----------------------------------------------------------------------
604 // ----------------------------------------------------------------------
605 // low-level fwrite methods
606 // ----------------------------------------------------------------------
607 // ----------------------------------------------------------------------
608 
609 /* ---------------------------------------------------------------------- */
610 
magic_string()611 void WriteRestart::magic_string()
612 {
613   const char magic[] = MAGIC_STRING;
614   fwrite(magic,sizeof(char),strlen(magic)+1,fp);
615 }
616 
617 /* ---------------------------------------------------------------------- */
618 
endian()619 void WriteRestart::endian()
620 {
621   int endian = ENDIAN;
622   fwrite(&endian,sizeof(int),1,fp);
623 }
624 
625 /* ---------------------------------------------------------------------- */
626 
version_numeric()627 void WriteRestart::version_numeric()
628 {
629   int vn = FORMAT_REVISION;
630   fwrite(&vn,sizeof(int),1,fp);
631 }
632 
633 /* ----------------------------------------------------------------------
634    write a flag and an int into the restart file
635 ------------------------------------------------------------------------- */
636 
write_int(int flag,int value)637 void WriteRestart::write_int(int flag, int value)
638 {
639   fwrite(&flag,sizeof(int),1,fp);
640   fwrite(&value,sizeof(int),1,fp);
641 }
642 
643 /* ----------------------------------------------------------------------
644    write a flag and a bigint into the restart file
645 ------------------------------------------------------------------------- */
646 
write_bigint(int flag,bigint value)647 void WriteRestart::write_bigint(int flag, bigint value)
648 {
649   fwrite(&flag,sizeof(int),1,fp);
650   fwrite(&value,sizeof(bigint),1,fp);
651 }
652 
653 /* ----------------------------------------------------------------------
654    write a flag and a double into the restart file
655 ------------------------------------------------------------------------- */
656 
write_double(int flag,double value)657 void WriteRestart::write_double(int flag, double value)
658 {
659   fwrite(&flag,sizeof(int),1,fp);
660   fwrite(&value,sizeof(double),1,fp);
661 }
662 
663 /* ----------------------------------------------------------------------
664    write a flag and a C-style char string (including the terminating null
665    byte) into the restart file
666 ------------------------------------------------------------------------- */
667 
write_string(int flag,const char * value)668 void WriteRestart::write_string(int flag, const char *value)
669 {
670   int n = strlen(value) + 1;
671   fwrite(&flag,sizeof(int),1,fp);
672   fwrite(&n,sizeof(int),1,fp);
673   fwrite(value,sizeof(char),n,fp);
674 }
675 
676 /* ----------------------------------------------------------------------
677    write a flag and vector of N ints into the restart file
678 ------------------------------------------------------------------------- */
679 
write_int_vec(int flag,int n,int * vec)680 void WriteRestart::write_int_vec(int flag, int n, int *vec)
681 {
682   fwrite(&flag,sizeof(int),1,fp);
683   fwrite(&n,sizeof(int),1,fp);
684   fwrite(vec,sizeof(int),n,fp);
685 }
686 
687 /* ----------------------------------------------------------------------
688    write a flag and vector of N doubles into the restart file
689 ------------------------------------------------------------------------- */
690 
write_double_vec(int flag,int n,double * vec)691 void WriteRestart::write_double_vec(int flag, int n, double *vec)
692 {
693   fwrite(&flag,sizeof(int),1,fp);
694   fwrite(&n,sizeof(int),1,fp);
695   fwrite(vec,sizeof(double),n,fp);
696 }
697