1 /* ----------------------------------------------------------------------
2    SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
3    http://sparta.sandia.gov
4    Steve Plimpton, sjplimp@sandia.gov, Michael Gallis, magalli@sandia.gov
5    Sandia National Laboratories
6 
7    Copyright (2014) 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 SPARTA directory.
13 ------------------------------------------------------------------------- */
14 
15 #include "stdio.h"
16 #include "stdlib.h"
17 #include "string.h"
18 #include "output.h"
19 #include "style_dump.h"
20 #include "comm.h"
21 #include "input.h"
22 #include "variable.h"
23 #include "update.h"
24 #include "particle.h"
25 #include "grid.h"
26 #include "surf.h"
27 #include "domain.h"
28 #include "modify.h"
29 #include "stats.h"
30 #include "dump.h"
31 #include "write_restart.h"
32 #include "memory.h"
33 #include "error.h"
34 
35 using namespace SPARTA_NS;
36 
37 #define DELTA 1
38 
39 /* ----------------------------------------------------------------------
40    initialize all output
41 ------------------------------------------------------------------------- */
42 
Output(SPARTA * sparta)43 Output::Output(SPARTA *sparta) : Pointers(sparta)
44 {
45   // create default Stats class
46 
47   stats = new Stats(sparta);
48 
49   stats_every = 0;
50   var_stats = NULL;
51 
52   ndump = 0;
53   max_dump = 0;
54   every_dump = NULL;
55   next_dump = NULL;
56   last_dump = NULL;
57   var_dump = NULL;
58   ivar_dump = NULL;
59   dump = NULL;
60 
61   restart_flag = restart_flag_single = restart_flag_double = 0;
62   restart_every_single = restart_every_double = 0;
63   last_restart = -1;
64   restart1 = restart2a = restart2b = NULL;
65   var_restart_single = var_restart_double = NULL;
66   restart = NULL;
67 }
68 
69 /* ----------------------------------------------------------------------
70    free all memory
71 ------------------------------------------------------------------------- */
72 
~Output()73 Output::~Output()
74 {
75   if (stats) delete stats;
76   delete [] var_stats;
77 
78   memory->destroy(every_dump);
79   memory->destroy(next_dump);
80   memory->destroy(last_dump);
81   for (int i = 0; i < ndump; i++) delete [] var_dump[i];
82   memory->sfree(var_dump);
83   memory->destroy(ivar_dump);
84   for (int i = 0; i < ndump; i++) delete dump[i];
85   memory->sfree(dump);
86 
87   delete [] restart1;
88   delete [] restart2a;
89   delete [] restart2b;
90   delete [] var_restart_single;
91   delete [] var_restart_double;
92   delete restart;
93 }
94 
95 /* ---------------------------------------------------------------------- */
96 
init()97 void Output::init()
98 {
99   stats->init();
100   if (var_stats) {
101     ivar_stats = input->variable->find(var_stats);
102     if (ivar_stats < 0)
103       error->all(FLERR,"Variable name for stats every does not exist");
104     if (!input->variable->equal_style(ivar_stats))
105       error->all(FLERR,"Variable for stats every is invalid style");
106   }
107 
108   for (int i = 0; i < ndump; i++) dump[i]->init();
109   for (int i = 0; i < ndump; i++)
110     if (every_dump[i] == 0) {
111       ivar_dump[i] = input->variable->find(var_dump[i]);
112       if (ivar_dump[i] < 0)
113 	error->all(FLERR,"Variable name for dump every does not exist");
114       if (!input->variable->equal_style(ivar_dump[i]))
115 	error->all(FLERR,"Variable for dump every is invalid style");
116     }
117 
118   if (restart_flag_single && restart_every_single == 0) {
119     ivar_restart_single = input->variable->find(var_restart_single);
120     if (ivar_restart_single < 0)
121       error->all(FLERR,"Variable name for restart does not exist");
122     if (!input->variable->equal_style(ivar_restart_single))
123       error->all(FLERR,"Variable for restart is invalid style");
124   }
125   if (restart_flag_double && restart_every_double == 0) {
126     ivar_restart_double = input->variable->find(var_restart_double);
127     if (ivar_restart_double < 0)
128       error->all(FLERR,"Variable name for restart does not exist");
129     if (!input->variable->equal_style(ivar_restart_double))
130       error->all(FLERR,"Variable for restart is invalid style");
131   }
132 }
133 
134 /* ----------------------------------------------------------------------
135    perform output for setup of run/min
136    do dump first, so memory_usage will include dump allocation
137    do stats last, so will print after memory_usage
138    memflag = 0/1 for printing out memory usage
139 ------------------------------------------------------------------------- */
140 
setup(int memflag)141 void Output::setup(int memflag)
142 {
143   bigint ntimestep = update->ntimestep;
144 
145   // perform dump at start of run only if:
146   //   current timestep is multiple of every and last dump not >= this step
147   //   this is first run after dump created and firstflag is set
148   //   note that variable freq will not write unless triggered by firstflag
149   // set next_dump to multiple of every or variable value
150   // set next_dump_any to smallest next_dump
151   // wrap dumps that invoke computes and variable eval with clear/add
152   // if dump not written now, use addstep_compute_all() since don't know
153   //   what computes the dump write would invoke
154   // if no dumps, set next_dump_any to last+1 so will not influence next
155 
156   int writeflag;
157 
158   if (ndump) {
159     for (int idump = 0; idump < ndump; idump++) {
160       if (dump[idump]->clearstep || every_dump[idump] == 0)
161         modify->clearstep_compute();
162       writeflag = 0;
163       if (every_dump[idump] && ntimestep % every_dump[idump] == 0 &&
164           last_dump[idump] != ntimestep) writeflag = 1;
165       if (last_dump[idump] < 0 && dump[idump]->first_flag == 1) writeflag = 1;
166 
167       if (writeflag) {
168         dump[idump]->write();
169         last_dump[idump] = ntimestep;
170       }
171       if (every_dump[idump])
172         next_dump[idump] =
173           (ntimestep/every_dump[idump])*every_dump[idump] + every_dump[idump];
174       else {
175         bigint nextdump = static_cast<bigint>
176           (input->variable->compute_equal(ivar_dump[idump]));
177         if (nextdump <= ntimestep)
178           error->all(FLERR,"Dump every variable returned a bad timestep");
179         next_dump[idump] = nextdump;
180       }
181       if (dump[idump]->clearstep || every_dump[idump] == 0) {
182         if (writeflag) modify->addstep_compute(next_dump[idump]);
183         else modify->addstep_compute_all(next_dump[idump]);
184       }
185       if (idump) next_dump_any = MIN(next_dump_any,next_dump[idump]);
186       else next_dump_any = next_dump[0];
187     }
188   } else next_dump_any = update->laststep + 1;
189 
190   // do not write restart files at start of run
191   // set next_restart values to multiple of every or variable value
192   // wrap variable eval with clear/add
193   // if no restarts, set next_restart to last+1 so will not influence next
194 
195   if (restart_flag) {
196     if (restart_flag_single) {
197       if (restart_every_single)
198         next_restart_single =
199           (ntimestep/restart_every_single)*restart_every_single +
200           restart_every_single;
201       else {
202         bigint nextrestart = static_cast<bigint>
203           (input->variable->compute_equal(ivar_restart_single));
204         if (nextrestart <= ntimestep)
205           error->all(FLERR,"Restart variable returned a bad timestep");
206         next_restart_single = nextrestart;
207       }
208     } else next_restart_single = update->laststep + 1;
209     if (restart_flag_double) {
210       if (restart_every_double)
211         next_restart_double =
212           (ntimestep/restart_every_double)*restart_every_double +
213           restart_every_double;
214       else {
215         bigint nextrestart = static_cast<bigint>
216           (input->variable->compute_equal(ivar_restart_double));
217         if (nextrestart <= ntimestep)
218           error->all(FLERR,"Restart variable returned a bad timestep");
219         next_restart_double = nextrestart;
220       }
221     } else next_restart_double = update->laststep + 1;
222     next_restart = MIN(next_restart_single,next_restart_double);
223   } else next_restart = update->laststep + 1;
224 
225   // print memory usage unless being called between multiple runs
226 
227   if (memflag) memory_usage();
228 
229   // set next_stats to multiple of every or variable eval if var defined
230   // insure stats output on last step of run
231   // stats may invoke computes so wrap with clear/add
232 
233   modify->clearstep_compute();
234 
235   stats->header();
236   stats->compute(0);
237   last_stats = ntimestep;
238 
239   if (var_stats) {
240     next_stats = static_cast<bigint>
241       (input->variable->compute_equal(ivar_stats));
242     if (next_stats <= ntimestep)
243       error->all(FLERR,"Stats every variable returned a bad timestep");
244   } else if (stats_every) {
245     next_stats = (ntimestep/stats_every)*stats_every + stats_every;
246     next_stats = MIN(next_stats,update->laststep);
247   } else next_stats = update->laststep;
248 
249   modify->addstep_compute(next_stats);
250 
251   // next = next timestep any output will be done
252 
253   next = MIN(next_dump_any,next_restart);
254   next = MIN(next,next_stats);
255 }
256 
257 /* ----------------------------------------------------------------------
258    perform all output for this timestep
259    only perform output if next matches current step and last output doesn't
260    do dump/restart before stats so stats CPU time will include them
261 ------------------------------------------------------------------------- */
262 
write(bigint ntimestep)263 void Output::write(bigint ntimestep)
264 {
265   // next_dump does not force output on last step of run
266   // wrap dumps that invoke computes or eval of variable with clear/add
267   // download data from GPU if necessary
268 
269   if (next_dump_any == ntimestep) {
270     for (int idump = 0; idump < ndump; idump++) {
271       if (next_dump[idump] == ntimestep) {
272         if (dump[idump]->clearstep || every_dump[idump] == 0)
273           modify->clearstep_compute();
274         if (last_dump[idump] != ntimestep) {
275           dump[idump]->write();
276           last_dump[idump] = ntimestep;
277         }
278         if (every_dump[idump]) next_dump[idump] += every_dump[idump];
279         else {
280           bigint nextdump = static_cast<bigint>
281             (input->variable->compute_equal(ivar_dump[idump]));
282           if (nextdump <= ntimestep)
283             error->all(FLERR,"Dump every variable returned a bad timestep");
284           next_dump[idump] = nextdump;
285         }
286         if (dump[idump]->clearstep || every_dump[idump] == 0)
287           modify->addstep_compute(next_dump[idump]);
288       }
289       if (idump) next_dump_any = MIN(next_dump_any,next_dump[idump]);
290       else next_dump_any = next_dump[0];
291     }
292   }
293 
294   // next_restart does not force output on last step of run
295   // for toggle = 0, replace "*" with current timestep in restart filename
296   // eval of variable may invoke computes so wrap with clear/add
297 
298   if (next_restart == ntimestep) {
299     if (next_restart_single == ntimestep) {
300       char *file = new char[strlen(restart1) + 16];
301       char *ptr = strchr(restart1,'*');
302       *ptr = '\0';
303       sprintf(file,"%s" BIGINT_FORMAT "%s",restart1,ntimestep,ptr+1);
304       *ptr = '*';
305       if (last_restart != ntimestep) restart->write(file);
306       delete [] file;
307       if (restart_every_single) next_restart_single += restart_every_single;
308       else {
309         modify->clearstep_compute();
310         bigint nextrestart = static_cast<bigint>
311           (input->variable->compute_equal(ivar_restart_single));
312         if (nextrestart <= ntimestep)
313           error->all(FLERR,"Restart variable returned a bad timestep");
314         next_restart_single = nextrestart;
315         modify->addstep_compute(next_restart_single);
316       }
317     }
318     if (next_restart_double == ntimestep) {
319       if (last_restart != ntimestep) {
320         if (restart_toggle == 0) {
321           restart->write(restart2a);
322           restart_toggle = 1;
323         } else {
324           restart->write(restart2b);
325           restart_toggle = 0;
326         }
327       }
328       if (restart_every_double) next_restart_double += restart_every_double;
329       else {
330         modify->clearstep_compute();
331         bigint nextrestart = static_cast<bigint>
332           (input->variable->compute_equal(ivar_restart_double));
333         if (nextrestart <= ntimestep)
334           error->all(FLERR,"Restart variable returned a bad timestep");
335         next_restart_double = nextrestart;
336         modify->addstep_compute(next_restart_double);
337       }
338     }
339     last_restart = ntimestep;
340     next_restart = MIN(next_restart_single,next_restart_double);
341   }
342 
343   // insure next_thermo forces output on last step of run
344   // thermo may invoke computes so wrap with clear/add
345 
346   if (next_stats == ntimestep) {
347     modify->clearstep_compute();
348     if (last_stats != ntimestep) stats->compute(1);
349     last_stats = ntimestep;
350     if (var_stats) {
351       next_stats = static_cast<bigint>
352         (input->variable->compute_equal(ivar_stats));
353       if (next_stats <= ntimestep)
354         error->all(FLERR,"Stats every variable returned a bad timestep");
355     } else if (stats_every) next_stats += stats_every;
356     else next_stats = update->laststep;
357     next_stats = MIN(next_stats,update->laststep);
358     modify->addstep_compute(next_stats);
359   }
360 
361   // next = next timestep any output will be done
362 
363   next = MIN(next_dump_any,next_restart);
364   next = MIN(next,next_stats);
365 }
366 
367 /* ----------------------------------------------------------------------
368    force a snapshot to be written for all dumps
369 ------------------------------------------------------------------------- */
370 
write_dump(bigint ntimestep)371 void Output::write_dump(bigint ntimestep)
372 {
373   for (int idump = 0; idump < ndump; idump++) {
374     dump[idump]->write();
375     last_dump[idump] = ntimestep;
376   }
377 }
378 
379 /* ----------------------------------------------------------------------
380    force restart file(s) to be written
381 ------------------------------------------------------------------------- */
382 
write_restart(bigint ntimestep)383 void Output::write_restart(bigint ntimestep)
384 {
385   if (restart_flag_single) {
386     char *file = new char[strlen(restart1) + 16];
387     char *ptr = strchr(restart1,'*');
388     *ptr = '\0';
389     sprintf(file,"%s" BIGINT_FORMAT "%s",restart1,ntimestep,ptr+1);
390     *ptr = '*';
391     restart->write(file);
392     delete [] file;
393   }
394 
395   if (restart_flag_double) {
396     if (restart_toggle == 0) {
397       restart->write(restart2a);
398       restart_toggle = 1;
399     } else {
400       restart->write(restart2b);
401       restart_toggle = 0;
402     }
403   }
404 
405   last_restart = ntimestep;
406 }
407 
408 /* ----------------------------------------------------------------------
409    timestep is being changed, called by update->reset_timestep()
410    reset next timestep values for dumps, restart, thermo output
411    reset to smallest value >= new timestep
412    if next timestep set by variable evaluation,
413      eval for ntimestep-1, so current ntimestep can be returned if needed
414      no guarantee that variable can be evaluated for ntimestep-1
415        if it depends on computes, but live with that rare case for now
416 ------------------------------------------------------------------------- */
417 
reset_timestep(bigint ntimestep)418 void Output::reset_timestep(bigint ntimestep)
419 {
420   next_dump_any = MAXBIGINT;
421   for (int idump = 0; idump < ndump; idump++) {
422     if (every_dump[idump]) {
423       next_dump[idump] = (ntimestep/every_dump[idump])*every_dump[idump];
424       if (next_dump[idump] < ntimestep) next_dump[idump] += every_dump[idump];
425     } else {
426       modify->clearstep_compute();
427       update->ntimestep--;
428       bigint nextdump = static_cast<bigint>
429         (input->variable->compute_equal(ivar_dump[idump]));
430       if (nextdump < ntimestep)
431         error->all(FLERR,"Dump every variable returned a bad timestep");
432       update->ntimestep++;
433       next_dump[idump] = nextdump;
434       modify->addstep_compute(next_dump[idump]);
435     }
436     next_dump_any = MIN(next_dump_any,next_dump[idump]);
437   }
438 
439   if (restart_flag_single) {
440     if (restart_every_single) {
441       next_restart_single =
442         (ntimestep/restart_every_single)*restart_every_single;
443       if (next_restart_single < ntimestep)
444         next_restart_single += restart_every_single;
445     } else {
446       modify->clearstep_compute();
447       update->ntimestep--;
448       bigint nextrestart = static_cast<bigint>
449         (input->variable->compute_equal(ivar_restart_single));
450       if (nextrestart < ntimestep)
451         error->all(FLERR,"Restart variable returned a bad timestep");
452       update->ntimestep++;
453       next_restart_single = nextrestart;
454       modify->addstep_compute(next_restart_single);
455     }
456   } else next_restart_single = update->laststep + 1;
457 
458   if (restart_flag_double) {
459     if (restart_every_double) {
460       next_restart_double =
461         (ntimestep/restart_every_double)*restart_every_double;
462       if (next_restart_double < ntimestep)
463         next_restart_double += restart_every_double;
464     } else {
465       modify->clearstep_compute();
466       update->ntimestep--;
467       bigint nextrestart = static_cast<bigint>
468         (input->variable->compute_equal(ivar_restart_double));
469       if (nextrestart < ntimestep)
470         error->all(FLERR,"Restart variable returned a bad timestep");
471       update->ntimestep++;
472       next_restart_double = nextrestart;
473       modify->addstep_compute(next_restart_double);
474     }
475   } else next_restart_double = update->laststep + 1;
476 
477   next_restart = MIN(next_restart_single,next_restart_double);
478 
479   if (var_stats) {
480     modify->clearstep_compute();
481     update->ntimestep--;
482     next_stats = static_cast<bigint>
483       (input->variable->compute_equal(ivar_stats));
484     if (next_stats < ntimestep)
485       error->all(FLERR,"Stats_modify every variable returned a bad timestep");
486     update->ntimestep++;
487     next_stats = MIN(next_stats,update->laststep);
488     modify->addstep_compute(next_stats);
489   } else if (stats_every) {
490     next_stats = (ntimestep/stats_every)*stats_every;
491     if (next_stats < ntimestep) next_stats += stats_every;
492     next_stats = MIN(next_stats,update->laststep);
493   } else next_stats = update->laststep;
494 
495   next = MIN(next_dump_any,next_restart);
496   next = MIN(next,next_stats);
497 }
498 
499 /* ----------------------------------------------------------------------
500    add a Dump to list of Dumps
501 ------------------------------------------------------------------------- */
502 
add_dump(int narg,char ** arg)503 void Output::add_dump(int narg, char **arg)
504 {
505   if (narg < 5) error->all(FLERR,"Illegal dump command");
506 
507   // error checks
508 
509   for (int idump = 0; idump < ndump; idump++)
510     if (strcmp(arg[0],dump[idump]->id) == 0)
511       error->all(FLERR,"Reuse of dump ID");
512   if (atoi(arg[3]) <= 0) error->all(FLERR,"Invalid dump frequency");
513 
514   // extend Dump list if necessary
515 
516   if (ndump == max_dump) {
517     max_dump += DELTA;
518     dump = (Dump **)
519       memory->srealloc(dump,max_dump*sizeof(Dump *),"output:dump");
520     memory->grow(every_dump,max_dump,"output:every_dump");
521     memory->grow(next_dump,max_dump,"output:next_dump");
522     memory->grow(last_dump,max_dump,"output:last_dump");
523     var_dump = (char **)
524       memory->srealloc(var_dump,max_dump*sizeof(char *),"output:var_dump");
525     memory->grow(ivar_dump,max_dump,"output:ivar_dump");
526   }
527 
528   // create the Dump
529 
530   if (0) return;         // dummy line to enable else-if macro expansion
531 
532 #define DUMP_CLASS
533 #define DumpStyle(key,Class) \
534   else if (strcmp(arg[1],#key) == 0) dump[ndump] = new Class(sparta,narg,arg);
535 #include "style_dump.h"
536 #undef DUMP_CLASS
537 
538   else error->all(FLERR,"Unrecognized dump style");
539 
540   every_dump[ndump] = atoi(arg[3]);
541   if (every_dump[ndump] <= 0) error->all(FLERR,"Illegal dump command");
542   last_dump[ndump] = -1;
543   var_dump[ndump] = NULL;
544   ndump++;
545 }
546 
547 /* ----------------------------------------------------------------------
548    modify parameters of a Dump
549 ------------------------------------------------------------------------- */
550 
modify_dump(int narg,char ** arg)551 void Output::modify_dump(int narg, char **arg)
552 {
553   if (narg < 1) error->all(FLERR,"Illegal dump_modify command");
554 
555   // find which dump it is
556 
557   int idump;
558   for (idump = 0; idump < ndump; idump++)
559     if (strcmp(arg[0],dump[idump]->id) == 0) break;
560   if (idump == ndump) error->all(FLERR,"Cound not find dump_modify ID");
561 
562   dump[idump]->modify_params(narg-1,&arg[1]);
563 }
564 
565 /* ----------------------------------------------------------------------
566    delete a Dump from list of Dumps
567 ------------------------------------------------------------------------- */
568 
delete_dump(char * id)569 void Output::delete_dump(char *id)
570 {
571   // find which dump it is and delete it
572 
573   int idump;
574   for (idump = 0; idump < ndump; idump++)
575     if (strcmp(id,dump[idump]->id) == 0) break;
576   if (idump == ndump) error->all(FLERR,"Could not find undump ID");
577 
578   delete dump[idump];
579   delete [] var_dump[idump];
580 
581   // move other dumps down in list one slot
582 
583   for (int i = idump+1; i < ndump; i++) {
584     dump[i-1] = dump[i];
585     every_dump[i-1] = every_dump[i];
586     next_dump[i-1] = next_dump[i];
587     last_dump[i-1] = last_dump[i];
588     var_dump[i-1] = var_dump[i];
589     ivar_dump[i-1] = ivar_dump[i];
590   }
591   ndump--;
592 }
593 
594 /* ----------------------------------------------------------------------
595    set stats output frequency from input script
596 ------------------------------------------------------------------------- */
597 
set_stats(int narg,char ** arg)598 void Output::set_stats(int narg, char **arg)
599 {
600   if (narg != 1) error->all(FLERR,"Illegal stats command");
601 
602   if (strstr(arg[0],"v_") == arg[0]) {
603     delete [] var_stats;
604     int n = strlen(&arg[0][2]) + 1;
605     var_stats = new char[n];
606     strcpy(var_stats,&arg[0][2]);
607   } else {
608     stats_every = atoi(arg[0]);
609     if (stats_every < 0) error->all(FLERR,"Illegal stats command");
610   }
611 }
612 
613 /* ----------------------------------------------------------------------
614    new Stats style
615 ------------------------------------------------------------------------- */
616 
create_stats(int narg,char ** arg)617 void Output::create_stats(int narg, char **arg)
618 {
619   if (narg < 1) error->all(FLERR,"Illegal stats_style command");
620   stats->set_fields(narg,arg);
621 }
622 
623 /* ----------------------------------------------------------------------
624    setup restart capability
625    if only one filename and it contains no "*", then append ".*"
626 ------------------------------------------------------------------------- */
627 
create_restart(int narg,char ** arg)628 void Output::create_restart(int narg, char **arg)
629 {
630   if (narg < 1) error->all(FLERR,"Illegal restart command");
631 
632   int every = 0;
633   int varflag = 0;
634 
635   if (strstr(arg[0],"v_") == arg[0]) varflag = 1;
636   else every = atoi(arg[0]);
637 
638   if (!varflag && every == 0) {
639     if (narg != 1) error->all(FLERR,"Illegal restart command");
640 
641     restart_flag = restart_flag_single = restart_flag_double = 0;
642     last_restart = -1;
643 
644     delete restart;
645     restart = NULL;
646     delete [] restart1;
647     delete [] restart2a;
648     delete [] restart2b;
649     restart1 = restart2a = restart2b = NULL;
650     delete [] var_restart_single;
651     delete [] var_restart_double;
652     var_restart_single = var_restart_double = NULL;
653 
654     return;
655   }
656 
657   if (narg < 2) error->all(FLERR,"Illegal restart command");
658 
659   int nfile = 0;
660   if (narg % 2 == 0) nfile = 1;
661   else nfile = 2;
662 
663   if (nfile == 1) {
664     restart_flag = restart_flag_single = 1;
665 
666     if (varflag) {
667       delete [] var_restart_single;
668       int n = strlen(&arg[0][2]) + 1;
669       var_restart_single = new char[n];
670       strcpy(var_restart_single,&arg[0][2]);
671       restart_every_single = 0;
672     } else restart_every_single = every;
673 
674     int n = strlen(arg[1]) + 3;
675     restart1 = new char[n];
676     strcpy(restart1,arg[1]);
677     if (strchr(restart1,'*') == NULL) strcat(restart1,".*");
678   }
679 
680   if (nfile == 2) {
681     restart_flag = restart_flag_double = 1;
682 
683     if (varflag) {
684       delete [] var_restart_double;
685       int n = strlen(&arg[0][2]) + 1;
686       var_restart_double = new char[n];
687       strcpy(var_restart_double,&arg[0][2]);
688       restart_every_double = 0;
689     } else restart_every_double = every;
690 
691     restart_toggle = 0;
692     int n = strlen(arg[1]) + 3;
693     restart2a = new char[n];
694     strcpy(restart2a,arg[1]);
695     n = strlen(arg[2]) + 1;
696     restart2b = new char[n];
697     strcpy(restart2b,arg[2]);
698   }
699 
700   // check for multiproc output and an MPI-IO filename
701   // if 2 filenames, must be consistent
702 
703   int multiproc;
704   if (strchr(arg[1],'%')) multiproc = comm->nprocs;
705   else multiproc = 0;
706   if (nfile == 2) {
707     if (multiproc && !strchr(arg[2],'%'))
708       error->all(FLERR,"Both restart files must use % or neither");
709     if (!multiproc && strchr(arg[2],'%'))
710       error->all(FLERR,"Both restart files must use % or neither");
711   }
712 
713   // setup output style and process optional args
714 
715   delete restart;
716   restart = new WriteRestart(sparta);
717   int iarg = nfile+1;
718   restart->multiproc_options(multiproc,narg-iarg,&arg[iarg]);
719 }
720 
721 /* ----------------------------------------------------------------------
722    sum and print memory usage
723 ------------------------------------------------------------------------- */
724 
memory_usage()725 void Output::memory_usage()
726 {
727   bigint pbytes,gbytes,sbytes,bytes;
728   pbytes = particle->memory_usage();
729   gbytes = grid->memory_usage();
730   sbytes = surf->memory_usage();
731   bytes = pbytes + gbytes + sbytes;
732   bytes += modify->memory_usage();
733 
734   double scale = 1.0/1024.0/1024.0;
735 
736   bigint ave,min,max;
737 
738   MPI_Allreduce(&pbytes,&ave,1,MPI_SPARTA_BIGINT,MPI_SUM,world);
739   double pave = scale * ave/comm->nprocs;
740   MPI_Allreduce(&pbytes,&min,1,MPI_SPARTA_BIGINT,MPI_MIN,world);
741   double pmin = scale * min;
742   MPI_Allreduce(&pbytes,&max,1,MPI_SPARTA_BIGINT,MPI_MAX,world);
743   double pmax = scale * max;
744 
745   MPI_Allreduce(&gbytes,&ave,1,MPI_SPARTA_BIGINT,MPI_SUM,world);
746   double gave = scale * ave/comm->nprocs;
747   MPI_Allreduce(&gbytes,&min,1,MPI_SPARTA_BIGINT,MPI_MIN,world);
748   double gmin = scale * min;
749   MPI_Allreduce(&gbytes,&max,1,MPI_SPARTA_BIGINT,MPI_MAX,world);
750   double gmax = scale * max;
751 
752   MPI_Allreduce(&sbytes,&ave,1,MPI_SPARTA_BIGINT,MPI_SUM,world);
753   double save = scale * ave/comm->nprocs;
754   MPI_Allreduce(&sbytes,&min,1,MPI_SPARTA_BIGINT,MPI_MIN,world);
755   double smin = scale * min;
756   MPI_Allreduce(&sbytes,&max,1,MPI_SPARTA_BIGINT,MPI_MAX,world);
757   double smax = scale * max;
758 
759   MPI_Allreduce(&bytes,&ave,1,MPI_SPARTA_BIGINT,MPI_SUM,world);
760   double tave = scale * ave/comm->nprocs;
761   MPI_Allreduce(&bytes,&min,1,MPI_SPARTA_BIGINT,MPI_MIN,world);
762   double tmin = scale * min;
763   MPI_Allreduce(&bytes,&max,1,MPI_SPARTA_BIGINT,MPI_MAX,world);
764   double tmax = scale * max;
765 
766   if (comm->me == 0) {
767     if (screen) {
768       fprintf(screen,"Memory usage per proc in Mbytes:\n");
769       fprintf(screen,"  particles (ave,min,max) = %g %g %g\n",
770 	      pave,pmin,pmax);
771       fprintf(screen,"  grid      (ave,min,max) = %g %g %g\n",
772 	      gave,gmin,gmax);
773       fprintf(screen,"  surf      (ave,min,max) = %g %g %g\n",
774 	      save,smin,smax);
775       fprintf(screen,"  total     (ave,min,max) = %g %g %g\n",
776 	      tave,tmin,tmax);
777     }
778     if (logfile) {
779       fprintf(logfile,"Memory usage per proc in Mbytes:\n");
780       fprintf(logfile,"  particles (ave,min,max) = %g %g %g\n",
781 	      pave,pmin,pmax);
782       fprintf(logfile,"  grid      (ave,min,max) = %g %g %g\n",
783 	      gave,gmin,gmax);
784       fprintf(logfile,"  surf      (ave,min,max) = %g %g %g\n",
785 	      save,smin,smax);
786       fprintf(logfile,"  total     (ave,min,max) = %g %g %g\n",
787 	      tave,tmin,tmax);
788     }
789   }
790 }
791