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