27 #include <cairo_base.h>    33 #include <boost/thread.hpp>    37 #include "fontDisplay.rcd"    40 #include "signature.h"    41 #include "dimension.rcd"    42 #include "callableFunction.rcd"    43 #include "tensorInterface.xcd"    44 #include "tensorVal.xcd"    45 #include "pannableTab.rcd"    46 #include "pannableTab.xcd"    47 #include "polyRESTProcessBase.h"    51 #include <boost/filesystem.hpp>    60 #if defined(__linux__)    61 #include <sys/sysinfo.h>    65 #include <sys/types.h>    66 #include <sys/sysctl.h>    83       for (
size_t i=0; i < sizeof(enum_keysData<E>::keysData) / 
sizeof(EnumKey); ++i)
    84         r.push_back(enum_keysData<E>::keysData[i].name);
    89   bool Minsky::multipleEquities(
const bool& m) {
    91     canvas.requestRedraw();
    92     redrawAllGodleyTables();
    93     return m_multipleEquities;
    96   void Minsky::openLogFile(
const string& name)
    98     outputDataFile.reset(
new ofstream(name));
    99     *outputDataFile<< 
"#time";
   100     for (
auto& v: variableValues)
   101       if (logVarList.contains(v.first))
   102         *outputDataFile<<
" "<<
CSVQuote(v.second->name,
' ');
   103     *outputDataFile<<endl;
   107   void Minsky::logVariables()
 const   112         for (
auto& v: variableValues)
   113           if (logVarList.contains(v.first))
   114             *outputDataFile<<
" "<<v.second->value();
   115         *outputDataFile<<endl;
   121     if (edited() && autoSaver)
   126   void Minsky::clearAllMaps(
bool doClearHistory)
   129     canvas.openGroupInCanvas(model);
   132     variableValues.clear();
   133     variablePane.update();
   135     PhillipsFlow::maxFlow.clear();
   136     PhillipsStock::maxStock.clear();
   137     phillipsDiagram.clear();
   138     publicationTabs.clear();
   139     publicationTabs.emplace_back(
"Publication");
   140     userFunctions.clear();
   141     UserFunction::nextId=0;
   148     flags=reset_needed|fullEqnDisplay_needed;
   149     fileVersion=minskyVersion;
   150     if (doClearHistory) clearHistory();
   159     for (
auto& i: canvas.selection.items)
   161         if (
auto v=i->variableCast())
   162           if (v->controller.lock())
   164         model->deleteItem(*i);
   166     for (
auto& i: canvas.selection.groups)
   167       model->removeGroup(*i);
   168     for (
auto& i: canvas.selection.wires)
   169       model->removeWire(*i);
   171     canvas.itemFocus.reset();
   174     for (
auto& i: canvas.selection.items)
   176         if (
auto v=i->variableCast())
   177           if (v->controller.lock())
   179         assert(i.use_count()==1);
   181     for (
auto& i: canvas.selection.groups)
   182       assert(i.use_count()==1);
   183     for (
auto& i: canvas.selection.wires)
   184       assert(i.use_count()==1);
   186     canvas.selection.clear();
   187     canvas.requestRedraw();
   190   void Minsky::copy()
 const   192     if (canvas.selection.empty())
   193       clipboard.putClipboard(
""); 
   200         clipboard.putClipboard(os.str());
   207       (model->findAny(&Group::items, [&](
const ItemPtr& x) {
   208         auto v=x->variableCast();
   209         return v && v->valueId()==
valueId && v->defined();
   213   void Minsky::saveGroupAsFile(
const Group& g, 
const string& fileName)
   223         if (canvas.itemFocus) 
return; 
   224         map<string,string> existingParms; 
   226         for (
auto& [
valueId,vv]: variableValues)
   227           existingParms.emplace(
valueId,vv->init());
   229         istringstream is(clipboard.getClipboard());
   230         xml_unpack_t unpacker(is); 
   237         for (
auto& i: g->items) 
   238           if (
auto v=i->variableCast(); v && v->type()==VariableType::parameter)
   239             existingParms.emplace(v->valueId(),v->init());
   242         canvas.selection.clear();
   244         if (!history.empty() || !canvas.model.get()->empty()) {     
   245           bool alreadyDefinedMessageDisplayed=
false;
   248           g->recursiveDo(&GroupItems::items,
   249                          [&](
Items&, Items::iterator i) {
   250                            if (
auto v=(*i)->variableCast())
   251                              if (v->defined() || v->isStock())
   254                                  auto alreadyDefined = canvas.model->findAny
   257                                     {
return j.get()!=v && j->variableCast() &&  j->variableCast()->defined();});
   260                                      if (v->defined() && alreadyDefined && !alreadyDefinedMessageDisplayed)
   262                                          message(
"Integral/Stock variable "+v->name()+
" already defined.");
   263                                          alreadyDefinedMessageDisplayed=
true;
   265                                      else if (!v->defined() && !alreadyDefined)
   270                                              vp.retype(VariableType::flow);
   272                                              convertVarType(vp->valueId(), VariableType::flow);
   276                                  else if (alreadyDefined)
   279                                      assert(v->portsSize()>1 && !v->ports(1).lock()->wires().empty());
   280                                      g->removeWire(*v->ports(1).lock()->wires()[0]);
   287         canvas.model->addGroup(g); 
   288         auto copyOfItems=g->items;
   289         auto copyOfGroups=g->groups;
   292         canvas.model->moveContents(*g); 
   295         for (
auto& i: copyOfItems) 
   296           canvas.selection.ensureItemInserted(i);
   299         for (
auto& p: existingParms)
   300           if (
auto vv=variableValues.find(p.first); vv!=variableValues.end())
   301             vv->second->init(p.second);
   302         existingParms.clear();
   305         for (
auto& i: canvas.selection.items)
   308               canvas.setItemFocus(i);
   312         if (!copyOfGroups.empty()) canvas.setItemFocus(copyOfGroups[0]);   
   314         canvas.model->removeGroup(*g);  
   315         canvas.requestRedraw();
   319         throw runtime_error(
"clipboard data invalid");
   322   void Minsky::insertGroupFromFile(
const string& file)
   326       throw runtime_error(
string(
"failed to open ")+file);
   328     xml_unpack_t saveFile(inf);
   333     g->resizeOnContents();
   337   void Minsky::makeVariablesConsistent()
   340     set<string> existingNames;
   341     existingNames.insert(
"constant:zero");
   342     existingNames.insert(
"constant:one");
   343     vector<GodleyIcon*> godleysToUpdate;
   344     model->recursiveDo(&Group::items, 
   345                        [&](
Items&,Items::iterator i) {
   346                          if (
auto v=(*i)->variableCast())
   347                            existingNames.insert(v->valueId());
   349                          if (
auto g=dynamic_cast<GodleyIcon*>(i->get()))
   350                            godleysToUpdate.push_back(g);
   355     for (
auto g: godleysToUpdate) g->update();
   356     for (
auto i=variableValues.begin(); i!=variableValues.end(); )
   357       if (existingNames.contains(i->first))
   360         variableValues.erase(i++);
   364   void Minsky::imposeDimensions()
   366     for (
auto& v: variableValues)
   368         v.second->imposeDimensions(dimensions);
   369         v.second->tensorInit.imposeDimensions(dimensions);
   374   void Minsky::garbageCollect()
   376     makeVariablesConsistent();
   383     for (
auto v=variableValues.begin(); v!=variableValues.end();)
   384       if (v->second->temp())
   385         variableValues.erase(v++);
   389     variableValues.reset();
   392   void Minsky::constructEquations()
   394     if (cycleCheck()) 
throw error(
"cyclic network detected");
   403     userFunctions.clear();
   406        [
this](
const Items&, Items::const_iterator it){
   407          if (
auto f=dynamic_pointer_cast<CallableFunction>(*it))
   414        [
this](
const Groups&, Groups::const_iterator it){
   424     assert(variableValues.validEntries());
   427     assert(variableValues.validEntries());
   431   void Minsky::dimensionalAnalysis()
 const   433     const_cast<Minsky*
>(
this)->variableValues.resetUnitsCache();
   438        [&](
Items& m, Items::iterator i)
   440          if (
auto v=(*i)->variableCast())
   443              if (v->isStock() && (v->inputWired() || v->controller.lock().get()))
   446          else if ((*i)->portsSize()>0 && !(*i)->ports(0).lock()->input() &&
   447                   (*i)->ports(0).lock()->wires().empty())
   449          else if (
auto p=(*i)->plotWidgetCast())
   450            for (
size_t j=0; j<p->portsSize(); ++j)
   451              p->ports(j).lock()->checkUnits();
   452          else if (
auto p=dynamic_cast<Sheet*>(i->get()))
   453            for (
size_t j=0; j<p->portsSize(); ++j)
   454              p->ports(j).lock()->checkUnits();
   459   void Minsky::deleteAllUnits()
   461     for (
auto& i: variableValues)
   462       i.second->units.clear();
   466   void Minsky::requestReset()
   468     if (resetDuration<resetNowThreshold)
   480     if (resetDuration<resetPostponedThreshold)
   481       resetAt=std::chrono::system_clock::now()+resetPostponedThreshold;
   483       resetAt=std::chrono::time_point<std::chrono::system_clock>::max();
   486   void Minsky::requestRedraw()
   489     canvas.requestRedraw();
   490     equationDisplay.requestRedraw();
   491     phillipsDiagram.requestRedraw();
   492     for (
auto& pub: publicationTabs)
   496   void Minsky::populateMissingDimensions() {
   498     bool incompatibleMessageDisplayed=
false;
   499     for (
auto& v: variableValues)
   500       populateMissingDimensionsFromVariable(*v.second, incompatibleMessageDisplayed);
   502       (&Group::items,[&](
Items& m, Items::iterator it)
   504         if (
auto ri=dynamic_cast<Ravel*>(it->get()))
   506             auto state=ri->getState();
   507             for (
auto& j: state.handleStates)
   508               dimensions.emplace(j.description,Dimension());
   515   void Minsky::populateMissingDimensionsFromVariable(
const VariableValue& v, 
bool& incompatibleMessageDisplayed)
   517     set<string> varDimensions;
   520         auto d=dimensions.find(xv.name);
   521         if (d==dimensions.end())
   523             dimensions.emplace(xv.name, xv.dimension);
   524             varDimensions.insert(xv.name);
   526         else if (d->second.type!=xv.dimension.type && !incompatibleMessageDisplayed)
   528             message(
"Incompatible dimension type for dimension "+d->first+
". Please adjust the global dimension in the dimensions dialog, which can be found under the Edit menu.");
   529             incompatibleMessageDisplayed=
true;
   534       (&Group::items,[&](
Items& m, Items::iterator it)
   536         if (
auto ri=dynamic_cast<Ravel*>(it->get()))
   537           for (
size_t i=0; i<ri->numHandles(); ++i)
   538             if (varDimensions.contains(ri->handleDescription(i)))
   539               ri->setHandleSortOrder(ravel::HandleSort::forward, i);
   544   void Minsky::renameDimension(
const std::string& oldName, 
const std::string& newName)
   546     auto i=dimensions.find(oldName);
   547     if (i!=dimensions.end())
   549         dimensions[newName]=i->second;
   553     for (
auto& v: variableValues)
   555         auto hc=v.second->tensorInit.hypercube();
   556         for (
auto& x: hc.xvectors)
   561         v.second->tensorInit.hypercube(hc);
   573       case GodleyAssetClass::liability:
   574       case GodleyAssetClass::equity:
   575         target_ac=GodleyAssetClass::asset;
   577       case GodleyAssetClass::asset:
   578         target_ac=GodleyAssetClass::liability;
   584     std::set<string> duplicatedColumns;
   587        [&](
Items& m, Items::iterator it)
   589          if (
auto gi=dynamic_cast<GodleyIcon*>(it->get()))
   591              for (
size_t i=1; i<gi->table.cols(); ++i)
   592                if (!gi->table.cell(0,i).empty())
   594                    auto v=gi->table.cell(0,i);
   600                    if (r.contains(v) || gi->table.assetClass(i)!=target_ac) 
   603                        duplicatedColumns.insert(v);
   605                    else if (!duplicatedColumns.contains(v) && gi->table.assetClass(i)==target_ac &&
   608                             ((ac!=GodleyAssetClass::equity && gi!=&
godley) || (ac==GodleyAssetClass::equity && gi==&
godley) ))
   617   void Minsky::importDuplicateColumn(
GodleyTable& srcTable, 
int srcCol)
   619     if (srcCol<0 || 
size_t(srcCol)>=srcTable.
cols()) 
return;
   621     const string& colName=
trimWS(srcTable.
cell(0,srcCol));
   622     if (colName.empty()) 
return; 
   628            [&](
Items& m, Items::iterator i)
   630              if (
auto gi=dynamic_cast<GodleyIcon*>(i->get()))
   631                for (
size_t col=1; col<gi->table.cols(); col++)
   632                  if ((&gi->table!=&srcTable || 
int(col)!=srcCol) && 
trimWS(gi->table.cell(0,col))==colName) 
   633                    balanceDuplicateColumns(*gi, col);
   639         srcTable.
cell(0,srcCol).clear();
   644   void Minsky::balanceColumns(
const GodleyIcon& srcGodley, 
int srcCol, 
GodleyIcon& destGodley, 
int destCol)
 const   646     auto& srcTable=srcGodley.
table;
   647     auto& destTable=destGodley.
table;
   649     map<string,string> srcRowLabels;
   650     map<string, int> destRowLabels;
   651     set<string> uniqueSrcRowLabels; 
   652     for (
size_t row=1; row!=srcTable.rows(); ++row)
   653       if (!srcTable.initialConditionRow(row) && !srcTable.cell(row,0).empty() &&
   654           !srcTable.cell(row,srcCol).empty())
   656           if (!uniqueSrcRowLabels.insert(srcTable.cell(row,0)).second)
   657             throw runtime_error(
"Duplicate source row label: "+srcTable.cell(row,0));
   658           const FlowCoef fc(srcTable.cell(row,srcCol));
   659           if (!fc.name.empty())
   660             srcRowLabels[srcGodley.
valueId(fc.name)]=
   661               trimWS(srcTable.cell(row,0));
   663       else if (srcTable.initialConditionRow(row))
   665         for (
size_t r=1; r<destTable.rows(); ++r)
   666           if (destTable.initialConditionRow(r))
   667             destTable.cell(r,destCol)=srcTable.cell(row,srcCol);
   668     for (
size_t row=1; row!=destTable.rows(); ++row)
   669       if (!destTable.initialConditionRow(row) && !destTable.cell(row,0).empty())
   670         destRowLabels[
trimWS(destTable.cell(row,0))]=row;
   677     for (map<string,double>::iterator i=srcFlows.begin(); i!=srcFlows.end(); ++i)
   678       if (i->second != destFlows[i->first])
   681           if (i->first.find(
':')!=string::npos)
   684           if (
scope==-1 || !variableValues.count(i->first))
   687             df.
name=variableValues[i->first]->name;
   688           df.
coef=i->second-destFlows[i->first];
   689           if (df.
coef==0) 
continue;
   690           const string flowEntry=df.
str();
   691           const string rowLabel=srcRowLabels[srcGodley.
valueId(i->first)];
   692           const map<string,int>::iterator dr=destRowLabels.find(rowLabel);
   693           if (dr!=destRowLabels.end())
   694             if (
FlowCoef(destTable.cell(dr->second, destCol)).coef==0)
   695               destTable.cell(dr->second, destCol) = flowEntry;
   699                 destTable.resize(destTable.rows()+1,destTable.cols());
   700                 destTable.cell(destTable.rows()-1, destCol) = flowEntry;
   705               destTable.resize(destTable.rows()+1,destTable.cols());
   706               destTable.cell(destTable.rows()-1, 0) = rowLabel;
   707               destRowLabels[rowLabel] = destTable.rows()-1;
   708               destTable.cell(destTable.rows()-1, destCol) = flowEntry;
   712     set<size_t> rowsToDelete;
   713     for (map<string,double>::iterator i=destFlows.begin(); i!=destFlows.end(); ++i)
   714       if (i->second!=0 && srcFlows[i->first]==0)
   715         for (
size_t row=1; row<destTable.rows(); ++row)
   717             FlowCoef fc(destTable.cell(row, destCol));
   718             if (!fc.name.empty())
   720             if (fc.name==destGodley.
valueId(i->first))
   722                 destTable.cell(row, destCol).clear();
   724                 for (
size_t c=0; c<destTable.cols(); ++c)
   725                   if (!destTable.cell(row, c).empty())
   727                 rowsToDelete.insert(row);
   732     map<string,double> unlabelledSigs;
   733     for (
size_t row=1; row<destTable.rows(); ++row)
   735         if (!destTable.singularRow(row, destCol)) 
continue;
   736         const FlowCoef fc(destTable.cell(row, destCol));
   737         unlabelledSigs[fc.name]+=fc.coef;
   738         rowsToDelete.insert(row);
   741     for (
auto& i: unlabelledSigs)
   744           destTable.insertRow(destTable.rows());
   745           destTable.cell(destTable.rows()-1,destCol)=
FlowCoef(i.second,i.first).
str();
   748     for (
auto row=rowsToDelete.rbegin(); row!=rowsToDelete.rend(); ++row)
   749       destTable.deleteRow(*row);
   752   void Minsky::balanceDuplicateColumns(
const GodleyIcon& srcGodley, 
int srcCol)
   755     if (srcCol<0 || 
size_t(srcCol)>=srcTable.
cols()) 
return;
   758     if (colName.empty() || colName==
":_") 
return; 
   760     bool matchFound=
false;
   763        [&](
Items& m, Items::iterator i)
   765          if (
auto gi=dynamic_cast<GodleyIcon*>(i->get()))
   767              if (&gi->table==&srcTable) 
   769                  for (
size_t col=1; col<gi->table.cols(); col++)
   770                    if (col==
size_t(srcCol)) 
continue; 
   775                          case GodleyAssetClass::asset:
   776                            if (srcTable.
assetClass(col)!=GodleyAssetClass::equity)
   777                              throw error(
"asset column %s matches a non-liability column",colName.c_str());
   779                          case GodleyAssetClass::equity:
   780                            if (srcTable.
assetClass(col)!=GodleyAssetClass::asset)
   781                              throw error(
"equity column %s matches a non-asset column",colName.c_str());
   784                            throw error(
"invalid asset class for duplicate column %s",colName.c_str());
   786                        balanceColumns(srcGodley, srcCol, *gi, col);
   790                for (
size_t col=1; col<gi->table.cols(); col++)
   791                  if (gi->valueId(
trimWS(gi->table.cell(0,col)))==colName) 
   796                        case GodleyAssetClass::asset:
   797                          if (gi->table.assetClass(col)!=GodleyAssetClass::liability)
   798                            throw error(
"asset column %s matches a non-liability column",colName.c_str());
   800                        case GodleyAssetClass::liability:
   801                          if (gi->table.assetClass(col)!=GodleyAssetClass::asset)
   802                            throw error(
"liability column %s matches a non-asset column",colName.c_str());
   805                          throw error(
"invalid asset class for duplicate column %s",colName.c_str());
   810                        throw error(
"more than one duplicated column detected for %s",colName.c_str());
   812                      balanceColumns(srcGodley, srcCol, *gi, col);
   819   vector<string> Minsky::allGodleyFlowVars()
 const   822     model->recursiveDo(&GroupItems::items, [&](
const Items&, Items::const_iterator i) {
   823       if (
auto g=dynamic_cast<GodleyIcon*>(i->get()))
   825           auto flowVars=g->table.getVariables();
   826           r.insert(flowVars.begin(),flowVars.end());
   830     return {r.begin(),r.end()};
   835     struct GodleyIt: 
public vector<GodleyIcon*>::iterator
   837       typedef vector<GodleyIcon*>::iterator 
Super;
   841       const std::vector<std::vector<std::string> >& 
data()
 const {
   856   void Minsky::initGodleys()
   858     auto toGodleyIcon=[](
const ItemPtr& i) {
return dynamic_cast<GodleyIcon*
>(i.get());};
   860       (toGodleyIcon, &GroupItems::items, toGodleyIcon);
   861     evalGodley.initialiseGodleys(GodleyIt(godleyItems.begin()), 
   862                                  GodleyIt(godleyItems.end()), variableValues);
   871             flags |= reset_needed;
   872             if (RKThreadRunning) 
return;
   875         auto start=chrono::high_resolution_clock::now();
   876         auto updateResetDuration=
onStackExit([&]{resetDuration=chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now()-start);});
   877         canvas.itemIndicator.reset();
   882         constructEquations();
   883         constructEquations();
   887         if (stockVars.empty()) stockVars.resize(1,0);
   892         if (!stockVars.empty())
   902            [&](
Items& m, Items::iterator i)
   904              if (
auto r=dynamic_cast<Ravel*>(i->get()))
   906                  if (r->ports(1).lock()->numWires()>0)
   907                    if (
auto vv=r->ports(1).lock()->getVariableValue())
   908                      r->populateHypercube(vv->hypercube());
   910              else if (
auto v=(*i)->variableCast())
   918            [&](
Items& m, Items::iterator it)
   920              if (
auto p=(*it)->plotWidgetCast())
   922                  p->disconnectAllVars();
   923                  p->clearPenAttributes();
   925                  for (
size_t i=0; i<p->portsSize(); ++i)
   927                      auto pp=p->ports(i).lock();
   928                      for (
auto wire: pp->wires())
   929                        if (
auto fromPort=
wire->from())
   930                          if (
auto vv=
wire->from()->getVariableValue())
   932                              p->connectVar(vv, i);
   938                    p->addConstantCurves();
   946         flags &= ~reset_needed; 
   947         resetAt=std::chrono::time_point<std::chrono::system_clock>::max();
   955         PhillipsFlow::maxFlow.clear();
   956         PhillipsStock::maxStock.clear();
   957         for (
auto& v: variableValues)
   959             if (v.second->type()==VariableType::stock)
   961                 PhillipsStock::maxStock[v.second->units]+=v.second->value();
   964         for (
auto& i: PhillipsStock::maxStock) i.second=abs(i.second);
   969         flags &= ~reset_needed; 
   970         resetAt=std::chrono::time_point<std::chrono::system_clock>::max();
   971         resetDuration=chrono::milliseconds::max();
   975   vector<double> Minsky::step()
   984        [&](
Items&, Items::iterator i) 
   985        {(*i)->updateIcon(t); 
return false;});
   988     const time_duration maxWait=milliseconds(maxWaitMS);
   989     if ((microsec_clock::local_time()-(ptime&)lastRedraw) > maxWait)
   992         lastRedraw=microsec_clock::local_time();
   995     return {t, deltaT()};
   998   string Minsky::diagnoseNonFinite()
 const  1001     for (VariableValues::const_iterator v=variableValues.begin();
  1002          v!=variableValues.end(); ++v)
  1007     for (EvalOpVector::const_iterator e=equations.begin(); e!=equations.end(); ++e)
  1008       if (!
isfinite(flowVars[(*e)->out]))
  1009         return OperationType::typeName((*e)->type());
  1013   void Minsky::save(
const std::string& filename)
  1016     rename(filename.c_str(), (filename+
"~").c_str());
  1019     Saver saver(filename);
  1020     saver.
packer.prettyPrint=
true;
  1027       rename((filename+
"~").c_str(), filename.c_str());
  1030           throw runtime_error(
"cannot save to "+filename);
  1034     flags &= ~is_edited;
  1035     fileVersion=minskyVersion;
  1039     for (
int i=numBackups; i>1; --i)
  1040       rename((filename+
";"+to_string(i-1)).c_str(), (filename+
";"+to_string(i)).c_str());
  1042       rename((filename+
"~").c_str(), (filename+
";1").c_str());
  1047   void Minsky::load(
const std::string& filename) 
  1052     ifstream inf(filename);
  1054       throw runtime_error(
"failed to open "+filename);
  1059       xml_unpack_t saveFile(inf);
  1063         message(
"You are converting the model from an older version of Minsky. "  1064                 "Once you save this file, you may not be able to open this file"  1065                 " in older versions of Minsky.");
  1069     flags=fullEqnDisplay_needed;
  1074         model->recursiveDo(&Group::items, 
  1075                            [
this](
Items&,Items::iterator i) {
  1078                                  if (
auto g=dynamic_cast<GodleyIcon*>(i->get()))
  1080                                      for (
unsigned j=1; j<g->table.cols(); ++j)
  1081                                        balanceDuplicateColumns(*g,j);
  1083                                  (*i)->adjustBookmark();
  1091         populateMissingDimensions();
  1093     catch (...) {flags|=reset_needed;}
  1094     canvas.requestRedraw();
  1097     model->recursiveDo(&Group::items,
  1098                        [](
Items&,Items::iterator i) {
  1099                          (*i)->updateBoundingBox();
  1105   void Minsky::exportSchema(
const string& filename, 
int schemaLevel)
  1108     switch (schemaLevel)
  1123     ofstream 
f(filename);
  1129     struct Network: 
public multimap<const Port*,const Port*>
  1134       {multimap<const Port*,const Port*>::emplace(x,y);}
  1138         if (!portsVisited.insert(p).second)
  1140             if (std::find(stack.begin(), stack.end(), p) != stack.end())
  1148         const pair<iterator,iterator> range=equal_range(p);
  1149         for (iterator i=range.first; i!=range.second; ++i)
  1150           if (followWire(i->second))
  1158   bool Minsky::cycleCheck()
 const  1162     for (
auto& w: model->findWires([](
const WirePtr&){return true;}))
  1163       net.emplace(w->from().get(), w->to().get());
  1164     for (
auto& i: model->findItems([](
const ItemPtr&){return true;}))
  1165       if (!dynamic_cast<IntOp*>(i.get()) && !dynamic_cast<GodleyIcon*>(i.get()))
  1166         for (
unsigned j=1; j<i->portsSize(); ++j)
  1167           net.emplace(i->ports(j).lock().get(), i->ports(0).lock().get());
  1170       if (!i.first->input() && !net.portsVisited.contains(i.first))
  1171         if (net.followWire(i.first))
  1176   bool Minsky::checkEquationOrder()
 const  1178     ecolab::array<bool> fvInit(flowVars.size(), 
false);
  1180     for (
auto& v: variableValues)
  1181       if (!inputWired(v.first) && v.second->idx()>=0)
  1182         fvInit[v.second->idx()]=
true;
  1184     for (
auto& e: equations)
  1185       if (
auto eo=dynamic_cast<const ScalarEvalOp*>(e.get()))
  1187           if (eo->out < 0|| (eo->numArgs()>0 && eo->in1.empty()) ||
  1188               (eo->numArgs() > 1 && eo->in2.empty()))
  1192           switch  (eo->numArgs())
  1195               fvInit[eo->out]=
true;
  1198               fvInit[eo->out]=!eo->flow1 || fvInit[eo->in1[0]];
  1205               if (
auto op=eo->state)
  1208                   case OperationType::add: 
case OperationType::subtract:
  1209                   case OperationType::multiply: 
case OperationType::divide:
  1210                     fvInit[eo->in1[0]] |= 
op->ports(1).lock()->wires().empty();
  1211                     fvInit[eo->in2[0][0].idx] |= 
op->ports(3).lock()->wires().empty();
  1217                 (!eo->flow1 ||  fvInit[eo->in1[0]]) && (!eo->flow2 ||  fvInit[eo->in2[0][0].idx]);
  1229   void Minsky::displayErrorItem(
const Item& 
op)
 const  1233     auto& canvas=
const_cast<Canvas&
>(this->canvas);
  1236     else if (
auto v=
op.variableCast())
  1237       if (
auto c=v->controller.lock())
  1238         displayErrorItem(*c);
  1240     if (!canvas.itemIndicator)
  1241       if (
auto g=
op.group.lock())
  1243           while (g && !g->visible()) g=g->group.lock();
  1244           if (g && g->visible())
  1245             canvas.itemIndicator=g;
  1248     if (canvas.itemIndicator)
  1250         auto physX=canvas.itemIndicator->x();
  1251         auto physY=canvas.itemIndicator->y();
  1252         if (physX<100 || physX>canvas.frameArgs().childWidth-100 ||
  1253             physY<100 || physY>canvas.frameArgs().childHeight-100)
  1255             canvas.model->moveTo(0.5*canvas.frameArgs().childWidth-physX+canvas.model->x(),
  1256                                  0.5*canvas.frameArgs().childHeight-physY+canvas.model->y());
  1261     if (!RKThreadRunning) canvas.requestRedraw();
  1264   bool Minsky::pushHistory()
  1268       return undone=
false;
  1275     if (history.empty())
  1277         history.emplace_back();
  1278         buf.swap(history.back());
  1279         historyPtr=history.size();
  1282     while (history.size()>maxHistory)
  1283       history.pop_front();
  1284     if (history.empty() || history.back().size()!=buf.size() || memcmp(buf.data(), history.back().data(), buf.size())!=0)
  1287         ostringstream prev, curr;
  1288         xml_pack_t prevXbuf(prev), currXbuf(curr);
  1291         history.back().reseto()>>previousMinsky;
  1292         xml_pack(prevXbuf,
"Minsky",previousMinsky);
  1294         if (curr.str()!=prev.str())
  1304             history.emplace_back();
  1305             buf.swap(history.back());
  1306             historyPtr=history.size();
  1307             if (autoSaver && doPushHistory)
  1321     historyPtr=history.size();
  1325   bool Minsky::commandHook(
const std::string& command, 
unsigned nargs)
  1327     static const set<string> constableCommands={ 
  1328       "minsky.availableOperations",
  1329       "minsky.canvas.displayDelayedTooltip",
  1330       "minsky.canvas.findVariableDefinition",
  1331       "minsky.canvas.focusFollowsMouse",
  1332       "minsky.canvas.moveTo",
  1333       "minsky.canvas.model.moveTo",
  1334       "minsky.canvas.model.zoom",
  1336       "minsky.canvas.mouseDown",
  1337       "minsky.canvas.mouseMove",
  1338       "minsky.canvas.position",
  1339       "minsky.canvas.recentre",
  1340       "minsky.canvas.select",
  1341       "minsky.canvas.selectVar",
  1342       "minsky.canvas.scaleFactor",
  1343       "minsky.canvas.zoom",
  1344       "minsky.canvas.zoomToFit",
  1345       "minsky.histogramResource.setResource",
  1346       "minsky.model.moveTo",
  1347       "minsky.clearAllMaps",
  1348       "minsky.doPushHistory",
  1351       "minsky.model.zoom",
  1352       "minsky.multipleEquities",
  1353       "minsky.openGroupInCanvas",
  1354       "minsky.openModelInCanvas",
  1357       "minsky.pushHistory",
  1358       "minsky.redrawAllGodleyTables",
  1364       "minsky.setGodleyDisplayValue",
  1365       "minsky.setGodleyIconResource",
  1366       "minsky.setGroupIconResource",
  1367       "minsky.setLockIconResource",
  1368       "minsky.setRavelIconResource",
  1369       "minsky.setAutoSaveFile",
  1373     if (doPushHistory && !constableCommands.contains(command) &&
  1374         command.find(
"minsky.phillipsDiagram")==string::npos &&
  1375         command.find(
"minsky.equationDisplay")==string::npos && 
  1376         command.find(
"minsky.publicationTabs")==string::npos && 
  1377         command.find(
".renderFrame")==string::npos && 
  1378         command.find(
".requestRedraw")==string::npos && 
  1379         command.find(
".backgroundColour")==string::npos && 
  1380         command.find(
".get")==string::npos && 
  1381         command.find(
".@elem")==string::npos && 
  1382         command.find(
".mouseFocus")==string::npos
  1385         auto t=getCommandData(command);
  1386         if (t==
generic || (t==is_setterGetter && nargs>0))
  1388             const bool modelChanged=pushHistory();
  1389             if (modelChanged && command.find(
".keyPress")==string::npos)
  1393             return modelChanged;
  1396     return command==
"minsky.canvas.requestRedraw" || command==
"minsky.canvas.mouseDown" || command==
"minsky.canvas.mouseMove" || command.find(
".get")!=string::npos;
  1400   long Minsky::undo(
int changes)
  1403     if (historyPtr==history.size())
  1405     historyPtr-=changes;
  1406     if (historyPtr > 0 && historyPtr <= history.size())
  1409         history[historyPtr-1].reseto()>>m;
  1411         auto stashedValues=std::move(variableValues);
  1414         unsigned numBookmarks=0;
  1415         model->recursiveDo(&GroupItems::groups, [&](
const Groups&,
const Groups::const_iterator i) {
  1416           numBookmarks+=(*i)->bookmarks.size();
  1419         auto stashedGlobalBookmarks=model->bookmarks;
  1420         auto stashedCanvasBookmarks=canvas.model->bookmarks;
  1421         clearAllMaps(
false);
  1430         for (
auto& v: variableValues)
  1432             auto stashedValue=stashedValues.find(v.first);
  1433             if (stashedValue!=stashedValues.end())
  1434               v.second->tensorInit=std::move(stashedValue->second->tensorInit);
  1437         model->bookmarks=std::move(stashedGlobalBookmarks);
  1438         canvas.model->bookmarks=std::move(stashedCanvasBookmarks);
  1439         unsigned numBookmarksAfterwards=0;
  1440         model->recursiveDo(&GroupItems::groups, [&](
const Groups&,
const Groups::const_iterator i) {
  1441           numBookmarksAfterwards+=(*i)->bookmarks.size();
  1444         if (numBookmarksAfterwards!=numBookmarks)
  1445           message(
"This undo/redo operation potentially deletes some bookmarks");
  1446         try {requestReset();}
  1451       historyPtr+=changes; 
  1459     const VariableValues::iterator i=variableValues.find(name);
  1460     if (i==variableValues.end())
  1461       throw error(
"variable %s doesn't exist",name.c_str());
  1462     if (i->second->type()==type) 
return; 
  1464     string newName=name; 
  1466       (&GroupItems::items,
  1467        [&](
const Items&,Items::const_iterator i)
  1469          if (
auto g=dynamic_cast<GodleyIcon*>(i->get()))
  1471              if (type!=VariableType::flow)
  1472                for (
auto& v: g->flowVars())
  1473                  if (v->valueId()==name)
  1475                      newName=v->name()+
"^{Flow}";
  1476                      const VariableValues::iterator iv=variableValues.find(newName);
  1477                      if (iv==variableValues.end()) {g->table.renameFlows(v->name(),newName); v->retype(VariableType::flow);}
  1478                      else throw error(
"flow variables in Godley tables cannot be converted to a different type");
  1480              if (type!=VariableType::stock)
  1481                for (
auto& v: g->stockVars())
  1482                  if (v->valueId()==name)
  1484                      newName=v->name()+
"^{Stock}";
  1485                      const VariableValues::iterator iv=variableValues.find(newName);
  1486                      if (iv==variableValues.end()) {g->table.renameStock(v->name(),newName); v->retype(VariableType::stock);}
  1487                      else throw error(
"stock variables in Godley tables cannot be converted to a different type");
  1493     if (
auto var=definingVar(name))
  1495       if (name==newName && 
var->type() != type && (!
var->isStock() || 
var->controller.lock()))
  1496         throw error(
"cannot convert a variable to a type other than its defined type");
  1501       case VariableType::undefined: 
case VariableType::numVarTypes: 
  1502       case VariableType::tempFlow:
  1503         throw error(
"convertVarType not supported for type=%s",
  1504                     VariableType::typeName(type).c_str());
  1509     model->recursiveDo(&Group::items,
  1510                        [&](
Items&, Items::iterator i) {
  1512                            if (v->valueId()==name)
  1515                                if (*i==canvas.item)
  1521     auto init=i->second->init();
  1523     i->second->init(init);
  1526   void Minsky::addIntegral()
  1528     if (
auto v=canvas.item->variableCast())
  1529       if (
auto g=v->group.lock())
  1532           convertVarType(v->valueId(),VariableType::integral);
  1533           auto integ=
new IntOp;
  1535           integ->
moveTo(canvas.item->x(), canvas.item->y());
  1537           if (
auto g=integ->intVar->group.lock())
  1538             g->removeItem(*integ->intVar);
  1539           integ->intVar=dynamic_pointer_cast<
VariableBase>(canvas.item);
  1540           integ->toggleCoupled();
  1542           canvas.requestRedraw();
  1547   void Minsky::renderAllPlotsAsSVG(
const string& prefix)
 const  1550     model->recursiveDo(&Group::items,
  1551                        [&](
Items&, Items::iterator i) {
  1552                          if (
auto p=(*i)->plotWidgetCast())
  1554                              if (!p->title.empty())
  1555                                p->RenderNativeWindow::renderToSVG(prefix+
"-"+p->title+
".svg");
  1557                                p->RenderNativeWindow::renderToSVG(prefix+
"-"+
str(plotNum++)+
".svg");
  1562   void Minsky::exportAllPlotsAsCSV(
const string& prefix)
 const  1565     model->recursiveDo(&Group::items,
  1566                        [&](
Items&, Items::iterator i) {
  1567                          if (
auto p=(*i)->plotWidgetCast())
  1569                              if (!p->title.empty())
  1570                                p->exportAsCSV((prefix+
"-"+p->title+
".csv"));
  1572                                p->exportAsCSV((prefix+
"-"+
str(plotNum++)+
".csv"));
  1578   void Minsky::setAllDEmode(
bool mode) {
  1579     model->recursiveDo(&GroupItems::items, [mode](
Items&,Items::iterator i) {
  1580       if (
auto g=dynamic_cast<GodleyIcon*>(i->get()))
  1581         g->table.setDEmode(mode);
  1588     this->displayValues=displayValues;
  1589     this->displayStyle=displayStyle;
  1590     canvas.requestRedraw();
  1591     model->recursiveDo(&GroupItems::items, [](
Items&,Items::iterator i) {
  1592       if (
auto g=dynamic_cast<GodleyIcon*>(i->get()))
  1593         g->popup.requestRedraw();
  1598   void Minsky::importVensim(
const string& filename)
  1600     ifstream 
f(filename);
  1602     canvas.requestRedraw();
  1605   vector<string> Minsky::availableOperations() 
  1606   {
return enumVals<OperationType::Type>();}
  1607   vector<string> Minsky::variableTypes()
  1608   {
return enumVals<VariableType::Type>();}
  1609   vector<string> Minsky::assetClasses()
  1610   {
return enumVals<GodleyTable::AssetClass>();}
  1617         if (classifyOp(
op)==OperationType::general) 
continue;
  1618         if (
op==OperationType::copy) 
continue;
  1619         r[classdesc::to_string(classifyOp(
op))].push_back(
op);
  1624   void Minsky::autoLayout()
  1626     canvas.model->autoLayout();
  1630   void Minsky::randomLayout()
  1632     canvas.model->randomLayout();
  1636   void Minsky::listAllInstances()
  1639       if (
auto v=canvas.item->variableCast())
  1641           variableInstanceList=std::make_shared<VariableInstanceList>(*canvas.model, v->valueId());
  1644     variableInstanceList.reset();
  1649     if (
wire.from()->wires().size()==1)
  1651         auto& item=
wire.from()->item();
  1652         if (!item.variableCast())
  1654             for (
size_t i=1; i<item.portsSize(); ++i)
  1655               if (
auto p=item.ports(i).lock())
  1656                 for (
auto w: p->wires())
  1658             model->removeItem(item);
  1660         else if (
auto p=item.ports(1).lock())
  1661           if (p->wires().empty())
  1662             model->removeItem(item); 
  1664     model->removeWire(
wire);
  1667   void Minsky::setDefinition(
const std::string& 
valueId, 
const std::string& definition)
  1672         if (
auto v=it->variableCast())
  1677       if (
auto p=
var->ports(1).lock())
  1685               for (
auto w: p->wires())
  1692               group->addItem(udf); 
  1693               const bool notFlipped=!
flipped(
var->rotation());
  1701   void Minsky::reloadAllCSVParameters()
  1703     model->recursiveDo(&GroupItems::items,
  1704                          [&](
Items&, Items::iterator i) {
  1705                            if (
auto v=(*i)->variableCast())
  1715   void Minsky::redrawAllGodleyTables()
  1717     model->recursiveDo(&Group::items, 
  1718                        [&](
Items&,Items::iterator i) {
  1719                          if (
auto g=dynamic_cast<GodleyIcon*>(i->get()))
  1720                            g->popup.requestRedraw();
  1725   size_t Minsky::physicalMem()
  1727 #if defined(__linux__)  1731 #elif defined(WIN32)  1732     MEMORYSTATUSEX s{
sizeof(MEMORYSTATUSEX)};
  1733     GlobalMemoryStatusEx(&s);
  1734     return s.ullTotalPhys;
  1735 #elif defined(__APPLE__)  1736     int mib[]={CTL_HW,HW_MEMSIZE};
  1737     uint64_t physical_memory;
  1738     size_t length = 
sizeof(uint64_t);
  1739     if (sysctl(mib, 
sizeof(mib)/
sizeof(
int), &physical_memory, &length, NULL, 0))
  1740       perror(
"physicalMem:");
  1741     return physical_memory;
  1750   string Minsky::defaultFont()
  1753   string Minsky::defaultFont(
const std::string& x)
  1761   double Minsky::fontScale()
  1762   {
return ecolab::Pango::scaleFactor;}
  1764   double Minsky::fontScale(
double s)
  1765   {
return ecolab::Pango::scaleFactor=s;}
  1769     if (cycleCheck()) 
throw error(
"cyclic network detected");
  1770     ofstream 
f(filename);
  1772     f<<
"\\documentclass{article}\n";
  1775         f<<
"\\usepackage{breqn}\n\\begin{document}\n";
  1780         f<<
"\\begin{document}\n";
  1783     f<<
"\\end{document}\n";
  1789     return op->numPorts()-1;
  1792   void Minsky::setAutoSaveFile(
const std::string& file) {
  1800   {
if (!
minsky.busyCursorStack++) 
minsky.setBusyCursor();}
  1803   {
if (!--
minsky.busyCursorStack) 
minsky.clearBusyCursor();}
  1811         if (!inDestruct) 
throw std::runtime_error(
"Cancelled");
  1824   {t.add(d, 
new RESTProcessAssociativeContainer<minsky::VariableValues>(a));}
 
void emplace(Port *x, Port *y)
 
void populatePublicationTabs(std::vector< minsky::PubTab > &) const
 
const Hypercube & hypercube() const override
 
const vector< AssetClass > & assetClass() const
class of each column (used in DE compliant mode) 
 
void displayErrorItem(const Item &op) const
indicate operation item has error, if visible, otherwise contining group 
 
ostream & latexWrapped(ostream &) const
 
void populatePhillipsDiagram(minsky::PhillipsDiagram &) const
populate a Phillips Diagram from this 
 
void populateGroup(minsky::Group &g) const
populate a group object from this. This mutates the ids in a consistent way into the free id space of...
 
shared_ptr class for polymorphic operation objects. Note, you may assume that this pointer is always ...
 
exception-safe increment/decrement of a counter in a block 
 
std::shared_ptr< std::atomic< bool > > cancel
set to true to cancel process in progreess displayProgress will throw if cancel is set...
 
PhillipsDiagram phillipsDiagram
 
CLASSDESC_ACCESS_EXPLICIT_INSTANTIATION(minsky::Minsky)
 
size_t scope(const string &name)
extract scope from a qualified variable name 
 
void populateEvalOpVector(EvalOpVector &equations, std::vector< Integral > &integrals)
 
void xsd_generate(xsd_generate_t &g, const string &d, const minsky::Optional< T > &a)
 
std::shared_ptr< Item > ItemPtr
 
std::string uqName(const std::string &name)
extract unqualified portion of name 
 
std::shared_ptr< Wire > WirePtr
 
string & cell(unsigned row, unsigned col)
 
void RESTProcess(RESTProcess_t &t, const string &d, minsky::VariableValues &a)
 
string valueId(const string &name)
construct a valueId from fully qualified name @ name should not be canonicalised 
 
void populateMinsky(minsky::Minsky &) const
create a Minsky model from this 
 
a shared_ptr that default constructs a default target, and is always valid 
 
bool flipped(double rotation)
returns if the angle (in degrees) is in the second or third quadrant 
 
GodleyTable table
table data. Must be declared before editor 
 
bool signConventionReversed(int col) const
 
ItemPtr itemIndicator
for drawing error indicator on the canvas 
 
void displayProgress(bool inDestruct=false)
 
classdesc::StringKeyMap< std::vector< OperationType::Type > > AvailableOperationsMapping
 
std::string valueId(const std::string &x) const
returns valueid for variable reference in table 
 
void remove(std::vector< T > &x, const V &v)
remove an element from a vector. V must be comparable to a T 
 
static std::unique_ptr< char[]> _defaultFont
 
set< const Port * > portsVisited
 
void save(const schema3::Minsky &)
 
string valueId(const std::string &x) const
 
void readMdl(Group &group, Simulation &simParms, istream &mdlFile)
import a Vensim mdl file into group, also populating simParms from the control block ...
 
std::vector< ItemPtr > Items
 
std::string trimWS(const std::string &s)
 
double isfinite(double x)
 
void updatePortVariableValue(EvalOpVector &equations)
 
std::vector< GroupPtr > Groups
 
OnStackExit< F > onStackExit(F f)
generator function 
 
std::string str(T x)
utility function to create a string representation of a numeric type 
 
const Minsky & cminsky()
const version to help in const correctness 
 
std::map< string, double > flowSignature(unsigned col) const
flows, along with multipliers, appearing in col 
 
string canonicalName(const string &name)
convert a raw name into a canonical name - this is not idempotent. 
 
GodleyAssetClass::AssetClass assetClass(size_t col) const
 
represents a numerical coefficient times a variable (a "flow") 
 
bool initialConditionRow(int row) const
 
bool isValueId(const string &name)
check that name is a valid valueId (useful for assertions) 
 
void xml_pack(xml_pack_t &t, const string &d, minsky::Optional< T > &a)
 
Expr operator*(const NodePtr &x, const Expr &y)
 
GodleyIcon * operator->()
 
virtual void resetScroll()
reset main window scroll bars after model has been panned 
 
vector< GodleyIcon * >::iterator Super
 
virtual void progress(const std::string &title, int)
set progress bar, out of 100, labelling the progress bar with title 
 
string valueIdFromScope(const GroupPtr &scope, const std::string &name)
value Id from scope and canonical name name 
 
std::shared_ptr< Group > GroupPtr
 
std::string CSVQuote(const std::string &x, char sep)
quotes a string if it contains a separator character, and double quotes quotes 
 
void moveTo(float x, float y)
 
RAII set the minsky object to a different one for the current scope. 
 
vector< string > enumVals()
list the possible string values of an enum (for TCL) 
 
UnitsExpressionWalker timeUnit
 
string latex(double)
convert double to a LaTeX string representing that value 
 
void stripByteOrderingMarker(std::istream &s)
checks if the input stream has the UTF-8 byte ordering marker, and removes it if present ...
 
virtual std::weak_ptr< Port > ports(std::size_t i) const
callback to be run when item deleted from group 
 
Minsky & minsky()
global minsky object 
 
Item & item()
owner of this port 
 
bool followWire(const Port *p)
 
vector< const Port * > stack
 
const std::vector< std::vector< std::string > > & data() const
 
classdesc::xml_pack_t packer
 
ostream & latex(ostream &) const
render as a LaTeX eqnarray Use LaTeX brqn environment to wrap long lines