FLAME  devel
 All Classes Functions Variables Typedefs Enumerations Pages
cli.cpp
1 #include <iostream>
2 #include <fstream>
3 #include <list>
4 #include <vector>
5 #include <typeinfo>
6 #include <climits>
7 
8 #include <time.h>
9 
10 #include <boost/numeric/ublas/io.hpp>
11 #include <boost/program_options.hpp>
12 #include <boost/foreach.hpp>
13 #include <boost/call_traits.hpp>
14 #include <boost/lexical_cast.hpp>
15 
16 #include <flame/base.h>
17 #include <flame/state/vector.h>
18 #include <flame/state/matrix.h>
19 #include <flame/moment.h>
20 
21 #ifdef USE_HDF5
22 #include <flame/h5writer.h>
23 #endif
24 
25 namespace po = boost::program_options;
26 
27 namespace {
28 
29 typedef std::vector<std::string> strvect;
30 
31 strvect tokenize(const std::string& inp)
32 {
33  strvect ret;
34  size_t pos = 0;
35  while(true) {
36  size_t sep = inp.find_first_of(',', pos);
37  if(sep==inp.npos) {
38  ret.push_back(inp.substr(pos));
39  break;
40  } else if(sep!=pos) {
41  ret.push_back(inp.substr(pos, sep-pos));
42  } else {
43  // ignore empty
44  }
45  pos = sep+1;
46  }
47  return ret;
48 }
49 
50 static
51 void getargs(int argc, char *argv[], po::variables_map& args)
52 {
53  std::ostringstream caption;
54  caption<<argv[0]<<" [options] <lattice file>";
55  po::options_description opts(caption.str());
56  opts.add_options()
57  ("help,h", "Display this message")
58  ("verbose,v", po::value<int>()->default_value(0)->value_name("NUM"),
59  "Make some noise. Values 0 through 6 are useful.")
60  ("define,D", po::value<std::vector<std::string> >()->composing()->value_name("name=type:val"),
61  "Define variable value (\"-Dname=str:value\")")
62  ("lattice", po::value<std::string>()->value_name("FILE"),
63  "Input lattice file")
64  ("max,M", po::value<std::string>()->value_name("NUM"),
65  "Maximum number of elements propagate through. (default is all)")
66 #ifdef USE_HDF5
67  ("format,F", po::value<std::string>()->value_name("FMT")->default_value("txt"),
68  "output format (txt or hdf5)")
69 #else
70  ("format,F", po::value<std::string>()->value_name("FMT")->default_value("txt"),
71  "output format (txt)")
72 #endif
73  ("select-all,A", "Select all elements for output")
74  ("select-type,T", po::value<std::vector<std::string> >()->composing()->value_name("ETYPE"),
75  "Select all elements of the given type for output")
76  ("select-name,N", po::value<std::vector<std::string> >()->composing()->value_name("ENAME"),
77  "Select all elements with the given name for output")
78  ("select-last,L", "Select last element for output")
79 #ifdef CLOCK_MONOTONIC
80  ("timeit", "Measure execution time")
81 #endif
82  ;
83 
84  po::positional_options_description pos;
85  pos.add("lattice", 1);
86 
87  po::store(po::command_line_parser(argc, argv).options(opts).positional(pos).run(), args);
88  po::notify(args);
89 
90  if(args.count("help") || !args.count("lattice")) {
91  std::cout<<opts<<"\n\n"
92  "Output formats:\n\n"
93  " txt - Print selected outputs states to screen ('--format txt' the default default)\n"
94  " or file ('--format txt[,file=out.txt][,verbose[=lvl#]]')\n"
95  "\n"
96  " utest - Print selected output states to screen as a python checkPropagate()\n"
97  "\n"
98 #ifdef USE_HDF5
99  " hdf5 - Write selected output states to an HDF5 file.\n"
100  " eg. '--format hdf5,file=out.h5'\n"
101  "\n"
102 #endif
103  "Definitions:\n\n"
104  " Variable defintions made by arguments must specify a name, type, and value\n"
105  " The type may be: 'str'' or 'double', which may be abbreviated as 'S' or 'D'.\n"
106  " Definitions are overwritten by those in the lattice file.\n"
107  ;
108  exit(1);
109  }
110 }
111 
112 struct ObserverFactory
113 {
114  virtual ~ObserverFactory() {}
115  virtual Observer *observe(Machine& M, ElementVoid* E) = 0;
116  virtual void before_sim(Machine&) {}
117  virtual void after_sim(Machine&) {}
118 };
119 
120 struct UnitTestObserver : public Observer
121 {
122  typedef boost::shared_ptr<std::list<std::string> > interested_t;
123  interested_t interested;
124  typedef std::vector<unsigned> interested_indicies_t;
125  interested_indicies_t interested_indicies;
126 
127  UnitTestObserver(const interested_t& I) :interested(I) {}
128  virtual ~UnitTestObserver() {}
129 
130  void lookup(StateBase *S) {
131  typedef std::map<std::string, unsigned> lookup_t;
132  lookup_t L;
133 
134  unsigned idx=0;
136  while(S->getArray(idx++, info)) {
137  bool skip = false;
138  switch(info.type) {
139  case StateBase::ArrayInfo::Sizet:
140  if(info.ndim!=0) skip = true;
141  case StateBase::ArrayInfo::Double:
142  break;
143  default:
144  skip = true;
145  }
146  if(info.ndim>2) skip=true;
147  if(skip) continue;
148 
149  L[info.name] = idx-1;
150  }
151 
152  interested_indicies.resize(interested->size());
153 
154  interested_t::element_type::const_iterator it = interested->begin();
155  for(size_t i=0; i<interested_indicies.size(); i++, ++it) {
156  lookup_t::const_iterator Lit = L.find(*it);
157  if(Lit!=L.end()) {
158  interested_indicies[i] = Lit->second;
159  }
160  }
161  }
162 
163  virtual void view(const ElementVoid *elem, const StateBase *state)
164  {
165  // hack since getArray() is non-const
166  // we won't actually modify the state
167  StateBase *S = const_cast<StateBase*>(state);
168 
169  if(interested_indicies.size()!=interested->size())
170  lookup(S);
171 
172  std::cout<<" def test_"<<elem->type_name()<<"(self):\n"
173  " # "<<elem->name<<"\n"
174  <<" self.checkPropagate(0, {}, {\n";
175 
176  for(size_t i=0; i<interested_indicies.size(); i++) {
177  unsigned idx = interested_indicies[i];
179  bool valid = S->getArray(idx, info);
180  assert(valid);
181  (void)valid;
182 
183  std::cout<<" '"<<info.name<<"':";
184  if(info.ndim==0) {
185  switch(info.type) {
186  case StateBase::ArrayInfo::Double:
187  std::cout<<std::scientific << std::setprecision(17)<<*(double*)info.ptr;
188  break;
189  case StateBase::ArrayInfo::Sizet:
190  std::cout<<*(size_t*)info.ptr;
191  }
192 
193  } else if(info.ndim==1) {
194  assert(info.type==StateBase::ArrayInfo::Double);
195  std::cout<<"asfarray([";
196  for(size_t i=0; i<info.dim[0]; i++) {
197  std::cout<<std::scientific << std::setprecision(16)<<*info.get<double>(&i);
198  if(i!=info.dim[0]-1)
199  std::cout<<", ";
200  }
201  std::cout<<"])";
202 
203  } else if(info.ndim==2) {
204  assert(info.type==StateBase::ArrayInfo::Double);
205  std::cout<<"asfarray([\n";
206  size_t idx[StateBase::ArrayInfo::maxdims];
207  memset(idx, 0, sizeof(idx));
208  for(idx[0]=0; idx[0]<info.dim[0]; idx[0]++) {
209  std::cout<<" [";
210  for(idx[1]=0; idx[1]<info.dim[1]; idx[1]++) {
211  std::cout<<std::scientific << std::setprecision(16)<<*info.get<double>(idx);
212  if(idx[1]!=info.dim[1]-1)
213  std::cout<<", ";
214  }
215  std::cout<<"],\n";
216  }
217  std::cout<<" ])";
218  } else {
219  std::cout<<"None";
220  }
221  std::cout<<",\n";
222  }
223  std::cout<<" }, max="<<elem->index+1<<")\n";
224  }
225 
226  struct Factory : public ObserverFactory
227  {
228  interested_t interested;
229  Factory(const strvect& fmt) :interested(new interested_t::element_type)
230  {
231  assert(!fmt.empty() && fmt[0]=="utest");
232 
233  for(strvect::const_iterator it=fmt.begin()+1, end=fmt.end(); it!=end; ++it)
234  {
235  const std::string& cmd = *it;
236  if(cmd.find_first_of('=')==cmd.npos) {
237  interested->push_back(cmd);
238  } else {
239  std::cerr<<"Warning: -F "<<fmt[0]<<" includes unknown option "<<cmd<<"\n";
240  }
241  }
242  }
243  virtual ~Factory() {}
244  virtual Observer *observe(Machine& M, ElementVoid* E)
245  {
246  return new UnitTestObserver(interested);
247  }
248  };
249 };
250 
251 struct StreamObserver : public Observer
252 {
253  std::ostream *strm;
254  int detail;
255  StreamObserver(std::ostream& strm, int detail=1) :strm(&strm), detail(detail) {}
256  virtual ~StreamObserver() {}
257  virtual void view(const ElementVoid* elem, const StateBase* state)
258  {
259  (*strm)<<"After Element ["<<elem->index<<"] "<<elem->name<<" ";
260  state->show(*strm, detail);
261  (*strm)<<"\n";
262  }
263 
264  struct Factory : public ObserverFactory
265  {
266  std::unique_ptr<std::ostream> owned_strm;
267  std::ostream *strm;
268  int detail;
269  Factory(const strvect& fmt) :strm(&std::cout), detail(1)
270  {
271  assert(!fmt.empty() && fmt[0]=="txt");
272 
273  for(strvect::const_iterator it=fmt.begin()+1, end=fmt.end(); it!=end; ++it)
274  {
275  const std::string& cmd = *it;
276  if(cmd.substr(0,5)=="file=") {
277  owned_strm.reset(new std::ofstream(cmd.substr(5).c_str()));
278  strm = owned_strm.get();
279  } else if(cmd=="verbose") {
280  detail=1;
281  } else if(cmd.substr(0,8)=="verbose=") {
282  detail=boost::lexical_cast<int>(cmd.substr(8));
283  } else {
284  std::cerr<<"Warning: -F "<<fmt[0]<<" includes unknown option "<<cmd<<"\n";
285  }
286  }
287  }
288  virtual ~Factory() {}
289  virtual Observer *observe(Machine& M, ElementVoid* E)
290  {
291  return new StreamObserver(*strm, detail);
292  }
293 
294  virtual void after_sim(Machine&)
295  {
296  strm->flush();
297  }
298  };
299 };
300 
301 #ifdef USE_HDF5
302 struct H5Observer : public Observer
303 {
304  H5StateWriter *writer;
305  H5Observer(H5StateWriter *writer) : writer(writer) {}
306  virtual ~H5Observer() {}
307 
308  struct Factory : public ObserverFactory
309  {
310  virtual ~Factory() {}
311  std::unique_ptr<H5StateWriter> writer;
312  Factory(const strvect& fmt)
313  {
314  assert(!fmt.empty() && fmt[0]=="hdf5");
315 
316  for(strvect::const_iterator it=fmt.begin()+1, end=fmt.end(); it!=end; ++it)
317  {
318  const std::string& cmd = *it;
319  if(cmd.substr(0,5)=="file=") {
320  writer.reset(new H5StateWriter(cmd.substr(5)));
321  } else {
322  std::cerr<<"Warning: -F "<<fmt[0]<<" includes unknown option "<<cmd<<"\n";
323  }
324  }
325  if(!writer.get()) {
326  std::cerr<<"Warning: hdf5 output format requires file=...\n";
327  }
328  }
329  virtual Observer *observe(Machine &M, ElementVoid *E)
330  {
331  if(!writer.get()) return NULL;
332  else return new H5Observer(writer.get());
333  }
334  virtual void before_sim(Machine & M)
335  {
336  if(writer.get()) writer->setAttr("sim_type", M.simtype());
337  }
338  virtual void after_sim(Machine&)
339  {
340  if(writer.get()) writer->close();
341  writer.reset();
342  }
343  };
344 
345  virtual void view(const ElementVoid *, const StateBase *state)
346  {
347  writer->append(state);
348  }
349 };
350 #endif
351 
352 struct Timer {
353  timespec ts;
354  Timer() {
355 #ifdef CLOCK_MONOTONIC
356  clock_gettime(CLOCK_MONOTONIC, &ts);
357 #endif
358  }
359  double delta() {
360 #ifdef CLOCK_MONOTONIC
361  timespec start = ts;
362  clock_gettime(CLOCK_MONOTONIC, &ts);
363 
364  // tv_nsec and tv_sec are signed integers
365  double D = ts.tv_nsec-start.tv_nsec;
366  D *= 1e-9;
367  D += ts.tv_sec-start.tv_sec;
368  return D;
369 #else
370  return std::numeric_limits<double>::quiet_NaN();
371 #endif
372  }
373  void showdelta(const char *msg) {
374 #ifdef CLOCK_MONOTONIC
375  double D = delta();
376  printf("%s : %.3f ms\n", msg, D*1e3);
377 #endif
378  }
379 };
380 
381 } // namespace
382 
383 int main(int argc, char *argv[])
384 {
385 try {
386  po::variables_map args;
387  getargs(argc, argv, args);
388 
389  bool showtime = args.count("timeit")>0;
390  Timer timeit;
391 
392  std::unique_ptr<Config> conf;
393 
394  int verb = args["verbose"].as<int>();
395  if(verb<=2)
396 #ifdef USE_HDF5
397  H5StateWriter::dontPrint();
398 #endif
399  if(verb>2)
400  Machine::log_detail=(FLAME_ERROR-10*(verb-2));
401  {
402  GLPSParser P;
403 
404  if(args.count("define")) {
405  const std::vector<std::string>& defs = args["define"].as<std::vector<std::string> >();
406 
407  BOOST_FOREACH(const std::string& def, defs) {
408  // expected form "<name>=<type>:<value>"
409  size_t equal = def.find_first_of('='),
410  colon = def.find_first_of(':', equal);
411  if(equal==def.npos) {
412  std::cerr<<"-D "<<def<<" missing '='\n";
413  exit(1);
414  } else if(colon==def.npos) {
415  std::cerr<<"-D "<<def<<" missing ':'\n";
416  exit(1);
417  } else if(equal==0) {
418  std::cerr<<"-D "<<def<<" missing variable name\n";
419  exit(1);
420  }
421 
422  std::string name(def.substr(0,equal)),
423  type(def.substr(equal+1, colon-equal-1)),
424  value(def.substr(colon+1));
425 
426  Config::value_t curval;
427 
428  if(type=="double" || type=="D") {
429  curval = boost::lexical_cast<double>(value);
430 
431  } else if(type=="str" || type=="S") {
432  curval = value;
433 
434  } else {
435  std::cerr<<"Unknown type "<<type<<" in -D "<<def<<"\n";
436  exit(1);
437  }
438 
439  P.setVar(name, curval);
440  }
441  }
442 
443  try {
444  conf.reset(P.parse_file(args["lattice"].as<std::string>().c_str()));
445  }catch(std::exception& e){
446  std::cerr<<"Parse error: "<<e.what()<<"\n";
447  return 1;
448  }
449  }
450 
451  if(showtime) timeit.showdelta("Parsing");
452 
453  if(verb) {
454  std::cout<<"# Reduced lattice\n";
455  GLPSPrint(std::cout, *conf);
456  std::cout<<"\n";
457  }
458 
459  //size_t maxelem = (size_t)-1;
460  //if(args.count("max")) {
461  // maxelem = boost::lexical_cast<size_t>(args["max"].as<std::string>());
462  //}
463  int maxelem = INT_MAX;
464  if(args.count("max")) {
465  maxelem = boost::lexical_cast<int>(args["max"].as<std::string>());
466  }
467 
468  // register state and element types
469  registerLinear();
470  registerMoment();
471 
472  std::unique_ptr<ObserverFactory> ofact;
473 
474  {
475  const std::string& ofactname = args["format"].as<std::string>();
476  strvect fmt(tokenize(ofactname));
477 
478  if(fmt.empty()) {
479  std::cerr<<"Empty output format\n";
480  exit(1);
481  } else if(fmt[0]=="txt") {
482  ofact.reset(new StreamObserver::Factory(fmt));
483 #ifdef USE_HDF5
484  } else if(fmt[0]=="hdf5") {
485  ofact.reset(new H5Observer::Factory(fmt));
486 #endif
487  } else if(fmt[0]=="utest") {
488  ofact.reset(new UnitTestObserver::Factory(fmt));
489  } else {
490  std::cerr<<"Unknown output format \""<<ofactname<<"\"\n";
491  exit(1);
492  }
493  }
494 
495  if(showtime) timeit.showdelta("Setup 1");
496 
497  Machine sim(*conf);
498 
499  if(showtime) timeit.showdelta("Create Machine");
500 
501  if(args.count("select-all")) {
502  BOOST_FOREACH(ElementVoid *elem, sim) {
503  assert(elem->observer()==NULL);
504  elem->set_observer(ofact->observe(sim, elem));
505  }
506  }
507  if(args.count("select-type")) {
508  BOOST_FOREACH(const std::string& etype, args["select-type"].as<std::vector<std::string> >()) {
509 
510  std::pair<Machine::lookup_iterator, Machine::lookup_iterator> S(sim.equal_range_type(etype));
511 
512  if(S.first==S.second) {
513  std::cerr<<"Warning: --select-type "<<etype<<" does not match any elements\n";
514  } else {
515  for(; S.first!=S.second; ++S.first) {
516  ElementVoid *elem = *S.first;
517 
518  if(elem->observer()==NULL) {
519  // don't replace existing Observer
520  elem->set_observer(ofact->observe(sim, elem));
521  }
522  }
523  }
524  }
525  }
526  if(args.count("select-name")) {
527  BOOST_FOREACH(const std::string& ename, args["select-name"].as<std::vector<std::string> >()) {
528 
529  std::pair<Machine::lookup_iterator, Machine::lookup_iterator> S(sim.equal_range(ename));
530 
531  if(S.first==S.second) {
532  std::cerr<<"Warning: --select-name "<<ename<<" does not match any elements\n";
533  } else {
534  for(; S.first!=S.second; ++S.first) {
535  ElementVoid *elem = *S.first;
536 
537  if(elem->observer()==NULL) {
538  // don't replace existing Observer
539  elem->set_observer(ofact->observe(sim, elem));
540  }
541  }
542  }
543  }
544  }
545  if(args.count("select-last") && sim.size()>0) {
546  ElementVoid *elem = sim[sim.size()-1];
547 
548  if(elem->observer()==NULL) {
549  // don't replace existing Observer
550  elem->set_observer(ofact->observe(sim, elem));
551  }
552  }
553 
554  ofact->before_sim(sim);
555 
556  if(verb) {
557  sim.set_trace(&std::cout);
558 
559  std::cout<<"# Machine configuration\n"<<sim<<"\n\n";
560  }
561 
562  if(showtime) timeit.showdelta("Setup 2");
563 
564  std::unique_ptr<StateBase> state(sim.allocState());
565  if(showtime) timeit.showdelta("Alloc State");
566  sim.propagate(state.get(), 0, maxelem);
567  if(showtime) {
568  timeit.showdelta("Simulate (cache cold)");
569  sim.propagate(state.get(), 0, maxelem);
570  timeit.showdelta("Simulate (cache hot)");
571  }
572 
573  ofact->after_sim(sim);
574 
575  if(verb) {
576  std::cout << "\n# Final " << *state << "\n";
577  }
578 
580  if(showtime) timeit.showdelta("Cleanup");
581 
582  return 0;
583 }catch(std::exception& e){
584  std::cerr<<"Error "<<typeid(e).name()<<" : "<<e.what()<<"\n";
585  return 1;
586 }
587 }
void setVar(const std::string &name, const Config::value_t &v)
Pre-define variable.
Definition: config.cpp:352
virtual void show(std::ostream &, int level=0) const
Definition: base.h:48
Base class for all simulated elements.
Definition: base.h:165
Config * parse_file(const char *fname, const bool have_lattice=true)
Open and parse a file.
Definition: config.cpp:364
const std::string & simtype() const
Return the sim_type string found during construction.
Definition: base.h:286
The core simulate Machine engine.
Definition: base.h:229
Interface to lattice file parser.
Definition: config.h:240
virtual const char * type_name() const =0
The abstract base class for all simulation state objects.
Definition: base.h:29
Used with StateBase::getArray() to describe a single parameter.
Definition: base.h:51
size_t index
Index of this element (unique in its Machine)
Definition: base.h:193
static void registeryCleanup()
Discard all registered State and Element type information.
Definition: base.cpp:251
const std::string name
Name of this element (unique in its Machine)
Definition: base.h:192
size_t dim[maxdims]
Array dimensions in elements.
Definition: base.h:69
boost::variant< double, std::vector< double >, std::string, std::vector< Config > > value_t
An individual value (double, double[], string, or Config[])
Definition: config.h:75
Allow inspection of intermediate State.
Definition: base.h:152
Observer * observer() const
The current observer, or NULL.
Definition: base.h:198
virtual bool getArray(unsigned index, ArrayInfo &Info)
Introspect named parameter of the derived class.
Definition: base.cpp:37
void set_observer(Observer *o)
Definition: base.h:203
const char * name
The parameter name.
Definition: base.h:55
unsigned ndim
Definition: base.h:67
E * get(size_t *d)
Helper to fetch the pointer for a given index (assumed valid)
Definition: base.h:92