27 #include "TCL_obj_stl.h" 28 #include <cairo_base.h> 34 #include <boost/thread.hpp> 38 #include "fontDisplay.rcd" 41 #include "signature.h" 42 #include "dimension.rcd" 43 #include "callableFunction.rcd" 44 #include "tensorInterface.xcd" 45 #include "tensorVal.xcd" 46 #include "pannableTab.rcd" 47 #include "pannableTab.xcd" 48 #include "polyRESTProcessBase.h" 52 #include <boost/filesystem.hpp> 61 #if defined(__linux__) 62 #include <sys/sysinfo.h> 66 #include <sys/types.h> 67 #include <sys/sysctl.h> 84 for (
size_t i=0; i < sizeof(enum_keysData<E>::keysData) /
sizeof(EnumKey); ++i)
85 r.push_back(enum_keysData<E>::keysData[i].name);
90 bool Minsky::multipleEquities(
const bool& m) {
92 canvas.requestRedraw();
93 redrawAllGodleyTables();
94 return m_multipleEquities;
97 void Minsky::openLogFile(
const string& name)
99 outputDataFile.reset(
new ofstream(name));
100 *outputDataFile<<
"#time";
101 for (
auto& v: variableValues)
102 if (logVarList.contains(v.first))
103 *outputDataFile<<
" "<<
CSVQuote(v.second->name,
' ');
104 *outputDataFile<<endl;
108 void Minsky::logVariables()
const 113 for (
auto& v: variableValues)
114 if (logVarList.contains(v.first))
115 *outputDataFile<<
" "<<v.second->value();
116 *outputDataFile<<endl;
122 if (edited() && autoSaver)
127 void Minsky::clearAllMaps(
bool doClearHistory)
130 canvas.openGroupInCanvas(model);
133 variableValues.clear();
136 PhillipsFlow::maxFlow.clear();
137 PhillipsStock::maxStock.clear();
138 phillipsDiagram.clear();
139 publicationTabs.clear();
140 publicationTabs.emplace_back(
"Publication");
141 userFunctions.clear();
142 UserFunction::nextId=0;
149 flags=reset_needed|fullEqnDisplay_needed;
150 fileVersion=minskyVersion;
151 if (doClearHistory) clearHistory();
160 for (
auto& i: canvas.selection.items)
162 if (
auto v=i->variableCast())
163 if (v->controller.lock())
165 model->deleteItem(*i);
167 for (
auto& i: canvas.selection.groups)
168 model->removeGroup(*i);
169 for (
auto& i: canvas.selection.wires)
170 model->removeWire(*i);
172 canvas.itemFocus.reset();
175 for (
auto& i: canvas.selection.items)
177 if (
auto v=i->variableCast())
178 if (v->controller.lock())
180 assert(i.use_count()==1);
182 for (
auto& i: canvas.selection.groups)
183 assert(i.use_count()==1);
184 for (
auto& i: canvas.selection.wires)
185 assert(i.use_count()==1);
187 canvas.selection.clear();
188 canvas.requestRedraw();
191 void Minsky::copy()
const 193 if (canvas.selection.empty())
194 clipboard.putClipboard(
"");
201 clipboard.putClipboard(os.str());
208 (model->findAny(&Group::items, [&](
const ItemPtr& x) {
209 auto v=x->variableCast();
210 return v && v->valueId()==
valueId && v->defined();
214 void Minsky::saveGroupAsFile(
const Group& g,
const string& fileName)
224 if (canvas.itemFocus)
return;
225 map<string,string> existingParms;
227 for (
auto& [
valueId,vv]: variableValues)
228 existingParms.emplace(
valueId,vv->init());
230 istringstream is(clipboard.getClipboard());
231 xml_unpack_t unpacker(is);
238 for (
auto& i: g->items)
239 if (
auto v=i->variableCast(); v && v->type()==VariableType::parameter)
240 existingParms.emplace(v->valueId(),v->init());
243 canvas.selection.clear();
245 if (!history.empty() || !canvas.model.get()->empty()) {
246 bool alreadyDefinedMessageDisplayed=
false;
249 g->recursiveDo(&GroupItems::items,
250 [&](
Items&, Items::iterator i) {
251 if (
auto v=(*i)->variableCast())
252 if (v->defined() || v->isStock())
255 auto alreadyDefined = canvas.model->findAny
258 {
return j.get()!=v && j->variableCast() && j->variableCast()->defined();});
261 if (v->defined() && alreadyDefined && !alreadyDefinedMessageDisplayed)
263 message(
"Integral/Stock variable "+v->name()+
" already defined.");
264 alreadyDefinedMessageDisplayed=
true;
266 else if (!v->defined() && !alreadyDefined)
271 vp.retype(VariableType::flow);
273 convertVarType(vp->valueId(), VariableType::flow);
277 else if (alreadyDefined)
280 assert(v->portsSize()>1 && !v->ports(1).lock()->wires().empty());
281 g->removeWire(*v->ports(1).lock()->wires()[0]);
288 canvas.model->addGroup(g);
289 auto copyOfItems=g->items;
290 auto copyOfGroups=g->groups;
293 canvas.model->moveContents(*g);
296 for (
auto& i: copyOfItems)
297 canvas.selection.ensureItemInserted(i);
300 for (
auto& p: existingParms)
301 if (
auto vv=variableValues.find(p.first); vv!=variableValues.end())
302 vv->second->init(p.second);
303 existingParms.clear();
306 for (
auto& i: canvas.selection.items)
309 canvas.setItemFocus(i);
313 if (!copyOfGroups.empty()) canvas.setItemFocus(copyOfGroups[0]);
315 canvas.model->removeGroup(*g);
316 canvas.requestRedraw();
320 throw runtime_error(
"clipboard data invalid");
323 void Minsky::insertGroupFromFile(
const string& file)
327 throw runtime_error(
string(
"failed to open ")+file);
329 xml_unpack_t saveFile(inf);
334 g->resizeOnContents();
338 void Minsky::makeVariablesConsistent()
341 set<string> existingNames;
342 existingNames.insert(
"constant:zero");
343 existingNames.insert(
"constant:one");
344 vector<GodleyIcon*> godleysToUpdate;
345 model->recursiveDo(&Group::items,
346 [&](
Items&,Items::iterator i) {
347 if (
auto v=(*i)->variableCast())
348 existingNames.insert(v->valueId());
350 if (
auto g=dynamic_cast<GodleyIcon*>(i->get()))
351 godleysToUpdate.push_back(g);
356 for (
auto g: godleysToUpdate) g->update();
357 for (
auto i=variableValues.begin(); i!=variableValues.end(); )
358 if (existingNames.contains(i->first))
361 variableValues.erase(i++);
365 void Minsky::imposeDimensions()
367 for (
auto& v: variableValues)
369 v.second->imposeDimensions(dimensions);
370 v.second->tensorInit.imposeDimensions(dimensions);
375 void Minsky::garbageCollect()
377 makeVariablesConsistent();
384 for (
auto v=variableValues.begin(); v!=variableValues.end();)
385 if (v->second->temp())
386 variableValues.erase(v++);
390 variableValues.reset();
393 void Minsky::constructEquations()
395 if (cycleCheck())
throw error(
"cyclic network detected");
404 userFunctions.clear();
407 [
this](
const Items&, Items::const_iterator it){
408 if (
auto f=dynamic_pointer_cast<CallableFunction>(*it))
415 [
this](
const Groups&, Groups::const_iterator it){
425 assert(variableValues.validEntries());
428 assert(variableValues.validEntries());
432 void Minsky::dimensionalAnalysis()
const 434 const_cast<Minsky*
>(
this)->variableValues.resetUnitsCache();
439 [&](
Items& m, Items::iterator i)
441 if (
auto v=(*i)->variableCast())
444 if (v->isStock() && (v->inputWired() || v->controller.lock().get()))
447 else if ((*i)->portsSize()>0 && !(*i)->ports(0).lock()->input() &&
448 (*i)->ports(0).lock()->wires().empty())
450 else if (
auto p=(*i)->plotWidgetCast())
451 for (
size_t j=0; j<p->portsSize(); ++j)
452 p->ports(j).lock()->checkUnits();
453 else if (
auto p=dynamic_cast<Sheet*>(i->get()))
454 for (
size_t j=0; j<p->portsSize(); ++j)
455 p->ports(j).lock()->checkUnits();
460 void Minsky::deleteAllUnits()
462 for (
auto& i: variableValues)
463 i.second->units.clear();
467 void Minsky::requestReset()
469 if (resetDuration<resetNowThreshold)
481 if (resetDuration<resetPostponedThreshold)
482 resetAt=std::chrono::system_clock::now()+resetPostponedThreshold;
484 resetAt=std::chrono::time_point<std::chrono::system_clock>::max();
487 void Minsky::requestRedraw()
490 canvas.requestRedraw();
491 equationDisplay.requestRedraw();
492 phillipsDiagram.requestRedraw();
493 for (
auto& pub: publicationTabs)
497 void Minsky::populateMissingDimensions() {
499 bool incompatibleMessageDisplayed=
false;
500 for (
auto& v: variableValues)
501 populateMissingDimensionsFromVariable(*v.second, incompatibleMessageDisplayed);
503 (&Group::items,[&](
Items& m, Items::iterator it)
505 if (
auto ri=dynamic_cast<Ravel*>(it->get()))
507 auto state=ri->getState();
508 for (
auto& j: state.handleStates)
509 dimensions.emplace(j.description,Dimension());
516 void Minsky::populateMissingDimensionsFromVariable(
const VariableValue& v,
bool& incompatibleMessageDisplayed)
518 set<string> varDimensions;
521 auto d=dimensions.find(xv.name);
522 if (d==dimensions.end())
524 dimensions.emplace(xv.name, xv.dimension);
525 varDimensions.insert(xv.name);
527 else if (d->second.type!=xv.dimension.type && !incompatibleMessageDisplayed)
529 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.");
530 incompatibleMessageDisplayed=
true;
535 (&Group::items,[&](
Items& m, Items::iterator it)
537 if (
auto ri=dynamic_cast<Ravel*>(it->get()))
538 for (
size_t i=0; i<ri->numHandles(); ++i)
539 if (varDimensions.contains(ri->handleDescription(i)))
540 ri->setHandleSortOrder(ravel::HandleSort::forward, i);
545 void Minsky::renameDimension(
const std::string& oldName,
const std::string& newName)
547 auto i=dimensions.find(oldName);
548 if (i!=dimensions.end())
550 dimensions[newName]=i->second;
554 for (
auto& v: variableValues)
556 auto hc=v.second->tensorInit.hypercube();
557 for (
auto& x: hc.xvectors)
562 v.second->tensorInit.hypercube(hc);
574 case GodleyAssetClass::liability:
575 case GodleyAssetClass::equity:
576 target_ac=GodleyAssetClass::asset;
578 case GodleyAssetClass::asset:
579 target_ac=GodleyAssetClass::liability;
585 std::set<string> duplicatedColumns;
588 [&](
Items& m, Items::iterator it)
590 if (
auto gi=dynamic_cast<GodleyIcon*>(it->get()))
592 for (
size_t i=1; i<gi->table.cols(); ++i)
593 if (!gi->table.cell(0,i).empty())
595 auto v=gi->table.cell(0,i);
601 if (r.contains(v) || gi->table._assetClass(i)!=target_ac)
604 duplicatedColumns.insert(v);
606 else if (!duplicatedColumns.contains(v) && gi->table._assetClass(i)==target_ac &&
609 ((ac!=GodleyAssetClass::equity && gi!=&
godley) || (ac==GodleyAssetClass::equity && gi==&
godley) ))
618 void Minsky::importDuplicateColumn(
GodleyTable& srcTable,
int srcCol)
620 if (srcCol<0 ||
size_t(srcCol)>=srcTable.
cols())
return;
622 const string& colName=
trimWS(srcTable.
cell(0,srcCol));
623 if (colName.empty())
return;
629 [&](
Items& m, Items::iterator i)
631 if (
auto gi=dynamic_cast<GodleyIcon*>(i->get()))
632 for (
size_t col=1; col<gi->table.cols(); col++)
633 if ((&gi->table!=&srcTable ||
int(col)!=srcCol) &&
trimWS(gi->table.cell(0,col))==colName)
634 balanceDuplicateColumns(*gi, col);
640 srcTable.
cell(0,srcCol).clear();
645 void Minsky::balanceColumns(
const GodleyIcon& srcGodley,
int srcCol,
GodleyIcon& destGodley,
int destCol)
const 647 auto& srcTable=srcGodley.
table;
648 auto& destTable=destGodley.
table;
650 map<string,string> srcRowLabels;
651 map<string, int> destRowLabels;
652 set<string> uniqueSrcRowLabels;
653 for (
size_t row=1; row!=srcTable.rows(); ++row)
654 if (!srcTable.initialConditionRow(row) && !srcTable.cell(row,0).empty() &&
655 !srcTable.cell(row,srcCol).empty())
657 if (!uniqueSrcRowLabels.insert(srcTable.cell(row,0)).second)
658 throw runtime_error(
"Duplicate source row label: "+srcTable.cell(row,0));
659 const FlowCoef fc(srcTable.cell(row,srcCol));
660 if (!fc.name.empty())
661 srcRowLabels[srcGodley.
valueId(fc.name)]=
662 trimWS(srcTable.cell(row,0));
664 else if (srcTable.initialConditionRow(row))
666 for (
size_t r=1; r<destTable.rows(); ++r)
667 if (destTable.initialConditionRow(r))
668 destTable.cell(r,destCol)=srcTable.cell(row,srcCol);
669 for (
size_t row=1; row!=destTable.rows(); ++row)
670 if (!destTable.initialConditionRow(row) && !destTable.cell(row,0).empty())
671 destRowLabels[
trimWS(destTable.cell(row,0))]=row;
678 for (map<string,double>::iterator i=srcFlows.begin(); i!=srcFlows.end(); ++i)
679 if (i->second != destFlows[i->first])
682 if (i->first.find(
':')!=string::npos)
685 if (
scope==-1 || !variableValues.count(i->first))
688 df.
name=variableValues[i->first]->name;
689 df.
coef=i->second-destFlows[i->first];
690 if (df.
coef==0)
continue;
691 const string flowEntry=df.
str();
692 const string rowLabel=srcRowLabels[srcGodley.
valueId(i->first)];
693 const map<string,int>::iterator dr=destRowLabels.find(rowLabel);
694 if (dr!=destRowLabels.end())
695 if (
FlowCoef(destTable.cell(dr->second, destCol)).coef==0)
696 destTable.cell(dr->second, destCol) = flowEntry;
700 destTable.resize(destTable.rows()+1,destTable.cols());
701 destTable.cell(destTable.rows()-1, destCol) = flowEntry;
706 destTable.resize(destTable.rows()+1,destTable.cols());
707 destTable.cell(destTable.rows()-1, 0) = rowLabel;
708 destRowLabels[rowLabel] = destTable.rows()-1;
709 destTable.cell(destTable.rows()-1, destCol) = flowEntry;
713 set<size_t> rowsToDelete;
714 for (map<string,double>::iterator i=destFlows.begin(); i!=destFlows.end(); ++i)
715 if (i->second!=0 && srcFlows[i->first]==0)
716 for (
size_t row=1; row<destTable.rows(); ++row)
718 FlowCoef fc(destTable.cell(row, destCol));
719 if (!fc.name.empty())
721 if (fc.name==destGodley.
valueId(i->first))
723 destTable.cell(row, destCol).clear();
725 for (
size_t c=0; c<destTable.cols(); ++c)
726 if (!destTable.cell(row, c).empty())
728 rowsToDelete.insert(row);
733 map<string,double> unlabelledSigs;
734 for (
size_t row=1; row<destTable.rows(); ++row)
736 if (!destTable.singularRow(row, destCol))
continue;
737 const FlowCoef fc(destTable.cell(row, destCol));
738 unlabelledSigs[fc.name]+=fc.coef;
739 rowsToDelete.insert(row);
742 for (
auto& i: unlabelledSigs)
745 destTable.insertRow(destTable.rows());
746 destTable.cell(destTable.rows()-1,destCol)=
FlowCoef(i.second,i.first).
str();
749 for (
auto row=rowsToDelete.rbegin(); row!=rowsToDelete.rend(); ++row)
750 destTable.deleteRow(*row);
753 void Minsky::balanceDuplicateColumns(
const GodleyIcon& srcGodley,
int srcCol)
756 if (srcCol<0 ||
size_t(srcCol)>=srcTable.
cols())
return;
759 if (colName.empty() || colName==
":_")
return;
761 bool matchFound=
false;
764 [&](
Items& m, Items::iterator i)
766 if (
auto gi=dynamic_cast<GodleyIcon*>(i->get()))
768 if (&gi->table==&srcTable)
770 for (
size_t col=1; col<gi->table.cols(); col++)
771 if (col==
size_t(srcCol))
continue;
776 case GodleyAssetClass::asset:
777 if (srcTable.
_assetClass(col)!=GodleyAssetClass::equity)
778 throw error(
"asset column %s matches a non-liability column",colName.c_str());
780 case GodleyAssetClass::equity:
781 if (srcTable.
_assetClass(col)!=GodleyAssetClass::asset)
782 throw error(
"equity column %s matches a non-asset column",colName.c_str());
785 throw error(
"invalid asset class for duplicate column %s",colName.c_str());
787 balanceColumns(srcGodley, srcCol, *gi, col);
791 for (
size_t col=1; col<gi->table.cols(); col++)
792 if (gi->valueId(
trimWS(gi->table.cell(0,col)))==colName)
797 case GodleyAssetClass::asset:
798 if (gi->table._assetClass(col)!=GodleyAssetClass::liability)
799 throw error(
"asset column %s matches a non-liability column",colName.c_str());
801 case GodleyAssetClass::liability:
802 if (gi->table._assetClass(col)!=GodleyAssetClass::asset)
803 throw error(
"liability column %s matches a non-asset column",colName.c_str());
806 throw error(
"invalid asset class for duplicate column %s",colName.c_str());
811 throw error(
"more than one duplicated column detected for %s",colName.c_str());
813 balanceColumns(srcGodley, srcCol, *gi, col);
820 vector<string> Minsky::allGodleyFlowVars()
const 823 model->recursiveDo(&GroupItems::items, [&](
const Items&, Items::const_iterator i) {
824 if (
auto g=dynamic_cast<GodleyIcon*>(i->get()))
826 auto flowVars=g->table.getVariables();
827 r.insert(flowVars.begin(),flowVars.end());
831 return {r.begin(),r.end()};
836 struct GodleyIt:
public vector<GodleyIcon*>::iterator
838 typedef vector<GodleyIcon*>::iterator
Super;
842 const std::vector<std::vector<std::string> >&
data()
const {
857 void Minsky::initGodleys()
859 auto toGodleyIcon=[](
const ItemPtr& i) {
return dynamic_cast<GodleyIcon*
>(i.get());};
861 (toGodleyIcon, &GroupItems::items, toGodleyIcon);
862 evalGodley.initialiseGodleys(GodleyIt(godleyItems.begin()),
863 GodleyIt(godleyItems.end()), variableValues);
872 flags |= reset_needed;
873 if (RKThreadRunning)
return;
876 auto start=chrono::high_resolution_clock::now();
877 auto updateResetDuration=
onStackExit([&]{resetDuration=chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now()-start);});
878 canvas.itemIndicator.reset();
883 constructEquations();
884 constructEquations();
888 if (stockVars.empty()) stockVars.resize(1,0);
893 if (!stockVars.empty())
903 [&](
Items& m, Items::iterator i)
905 if (
auto r=dynamic_cast<Ravel*>(i->get()))
907 if (r->ports(1).lock()->numWires()>0)
908 if (
auto vv=r->ports(1).lock()->getVariableValue())
909 r->populateHypercube(vv->hypercube());
911 else if (
auto v=(*i)->variableCast())
919 [&](
Items& m, Items::iterator it)
921 if (
auto p=(*it)->plotWidgetCast())
923 p->disconnectAllVars();
924 p->clearPenAttributes();
926 for (
size_t i=0; i<p->portsSize(); ++i)
928 auto pp=p->ports(i).lock();
929 for (
auto wire: pp->wires())
930 if (
auto fromPort=
wire->from())
931 if (
auto vv=
wire->from()->getVariableValue())
933 p->connectVar(vv, i);
939 p->addConstantCurves();
947 flags &= ~reset_needed;
948 resetAt=std::chrono::time_point<std::chrono::system_clock>::max();
956 PhillipsFlow::maxFlow.clear();
957 PhillipsStock::maxStock.clear();
958 for (
auto& v: variableValues)
960 if (v.second->type()==VariableType::stock)
962 PhillipsStock::maxStock[v.second->units]+=v.second->value();
965 for (
auto& i: PhillipsStock::maxStock) i.second=abs(i.second);
970 flags &= ~reset_needed;
971 resetAt=std::chrono::time_point<std::chrono::system_clock>::max();
972 resetDuration=chrono::milliseconds::max();
985 [&](
Items&, Items::iterator i)
986 {(*i)->updateIcon(t);
return false;});
989 const time_duration maxWait=milliseconds(maxWaitMS);
990 if ((microsec_clock::local_time()-(ptime&)lastRedraw) > maxWait)
993 lastRedraw=microsec_clock::local_time();
996 return {t, deltaT()};
999 string Minsky::diagnoseNonFinite()
const 1002 for (VariableValues::const_iterator v=variableValues.begin();
1003 v!=variableValues.end(); ++v)
1008 for (EvalOpVector::const_iterator e=equations.begin(); e!=equations.end(); ++e)
1009 if (!
isfinite(flowVars[(*e)->out]))
1010 return OperationType::typeName((*e)->type());
1017 rename(filename.c_str(), (filename+
"~").c_str());
1020 Saver saver(filename);
1021 saver.
packer.prettyPrint=
true;
1028 rename((filename+
"~").c_str(), filename.c_str());
1031 throw runtime_error(
"cannot save to "+filename);
1035 flags &= ~is_edited;
1036 fileVersion=minskyVersion;
1040 for (
int i=numBackups; i>1; --i)
1043 rename((filename+
"~").c_str(), (filename+
";1").c_str());
1048 void Minsky::load(
const std::string& filename)
1053 ifstream inf(filename);
1055 throw runtime_error(
"failed to open "+filename);
1060 xml_unpack_t saveFile(inf);
1064 message(
"You are converting the model from an older version of Minsky. " 1065 "Once you save this file, you may not be able to open this file" 1066 " in older versions of Minsky.");
1070 flags=fullEqnDisplay_needed;
1075 model->recursiveDo(&Group::items,
1076 [
this](
Items&,Items::iterator i) {
1079 if (
auto g=dynamic_cast<GodleyIcon*>(i->get()))
1081 for (
unsigned j=1; j<g->table.cols(); ++j)
1082 balanceDuplicateColumns(*g,j);
1084 (*i)->adjustBookmark();
1092 populateMissingDimensions();
1094 catch (...) {flags|=reset_needed;}
1095 canvas.requestRedraw();
1098 model->recursiveDo(&Group::items,
1099 [](
Items&,Items::iterator i) {
1100 (*i)->updateBoundingBox();
1106 void Minsky::exportSchema(
const string& filename,
int schemaLevel)
1109 switch (schemaLevel)
1124 ofstream
f(filename);
1130 struct Network:
public multimap<const Port*,const Port*>
1135 {multimap<const Port*,const Port*>::emplace(x,y);}
1139 if (!portsVisited.insert(p).second)
1141 if (std::find(stack.begin(), stack.end(), p) != stack.end())
1149 const pair<iterator,iterator> range=equal_range(p);
1150 for (iterator i=range.first; i!=range.second; ++i)
1151 if (followWire(i->second))
1159 bool Minsky::cycleCheck()
const 1163 for (
auto& w: model->findWires([](
const WirePtr&){return true;}))
1164 net.emplace(w->from().get(), w->to().get());
1165 for (
auto& i: model->findItems([](
const ItemPtr&){return true;}))
1166 if (!dynamic_cast<IntOp*>(i.get()) && !dynamic_cast<GodleyIcon*>(i.get()))
1167 for (
unsigned j=1; j<i->portsSize(); ++j)
1168 net.emplace(i->ports(j).lock().get(), i->ports(0).lock().get());
1171 if (!i.first->input() && !net.portsVisited.contains(i.first))
1172 if (net.followWire(i.first))
1177 bool Minsky::checkEquationOrder()
const 1179 ecolab::array<bool> fvInit(flowVars.size(),
false);
1181 for (
auto& v: variableValues)
1182 if (!inputWired(v.first) && v.second->idx()>=0)
1183 fvInit[v.second->idx()]=
true;
1185 for (
auto& e: equations)
1186 if (
auto eo=dynamic_cast<const ScalarEvalOp*>(e.get()))
1188 if (eo->out < 0|| (eo->numArgs()>0 && eo->in1.empty()) ||
1189 (eo->numArgs() > 1 && eo->in2.empty()))
1193 switch (eo->numArgs())
1196 fvInit[eo->out]=
true;
1199 fvInit[eo->out]=!eo->flow1 || fvInit[eo->in1[0]];
1206 if (
auto op=eo->state)
1209 case OperationType::add:
case OperationType::subtract:
1210 case OperationType::multiply:
case OperationType::divide:
1211 fvInit[eo->in1[0]] |=
op->ports(1).lock()->wires().empty();
1212 fvInit[eo->in2[0][0].idx] |=
op->ports(3).lock()->wires().empty();
1218 (!eo->flow1 || fvInit[eo->in1[0]]) && (!eo->flow2 || fvInit[eo->in2[0][0].idx]);
1230 void Minsky::displayErrorItem(
const Item&
op)
const 1234 auto& canvas=
const_cast<Canvas&
>(this->canvas);
1237 else if (
auto v=
op.variableCast())
1238 if (
auto c=v->controller.lock())
1239 displayErrorItem(*c);
1241 if (!canvas.itemIndicator)
1242 if (
auto g=
op.group.lock())
1244 while (g && !g->visible()) g=g->group.lock();
1245 if (g && g->visible())
1246 canvas.itemIndicator=g;
1249 if (canvas.itemIndicator)
1251 auto physX=canvas.itemIndicator->x();
1252 auto physY=canvas.itemIndicator->y();
1253 if (physX<100 || physX>canvas.frameArgs().childWidth-100 ||
1254 physY<100 || physY>canvas.frameArgs().childHeight-100)
1256 canvas.model->moveTo(0.5*canvas.frameArgs().childWidth-physX+canvas.model->x(),
1257 0.5*canvas.frameArgs().childHeight-physY+canvas.model->y());
1262 if (!RKThreadRunning) canvas.requestRedraw();
1265 bool Minsky::pushHistory()
1269 return undone=
false;
1276 if (history.empty())
1278 history.emplace_back();
1279 buf.swap(history.back());
1280 historyPtr=history.size();
1283 while (history.size()>maxHistory)
1284 history.pop_front();
1285 if (history.empty() || history.back().size()!=buf.size() || memcmp(buf.data(), history.back().data(), buf.size())!=0)
1288 ostringstream prev, curr;
1289 xml_pack_t prevXbuf(prev), currXbuf(curr);
1292 history.back().reseto()>>previousMinsky;
1293 xml_pack(prevXbuf,
"Minsky",previousMinsky);
1295 if (curr.str()!=prev.str())
1305 history.emplace_back();
1306 buf.swap(history.back());
1307 historyPtr=history.size();
1308 if (autoSaver && doPushHistory)
1322 historyPtr=history.size();
1326 bool Minsky::commandHook(
const std::string& command,
unsigned nargs)
1328 static const set<string> constableCommands={
1329 "minsky.availableOperations",
1330 "minsky.canvas.displayDelayedTooltip",
1331 "minsky.canvas.findVariableDefinition",
1332 "minsky.canvas.focusFollowsMouse",
1333 "minsky.canvas.moveTo",
1334 "minsky.canvas.model.moveTo",
1335 "minsky.canvas.model.zoom",
1337 "minsky.canvas.mouseDown",
1338 "minsky.canvas.mouseMove",
1339 "minsky.canvas.position",
1340 "minsky.canvas.recentre",
1341 "minsky.canvas.select",
1342 "minsky.canvas.selectVar",
1343 "minsky.canvas.scaleFactor",
1344 "minsky.canvas.zoom",
1345 "minsky.canvas.zoomToFit",
1346 "minsky.histogramResource.setResource",
1347 "minsky.model.moveTo",
1348 "minsky.clearAllMaps",
1349 "minsky.doPushHistory",
1352 "minsky.model.zoom",
1353 "minsky.multipleEquities",
1354 "minsky.openGroupInCanvas",
1355 "minsky.openModelInCanvas",
1358 "minsky.pushHistory",
1359 "minsky.redrawAllGodleyTables",
1365 "minsky.setGodleyDisplayValue",
1366 "minsky.setGodleyIconResource",
1367 "minsky.setGroupIconResource",
1368 "minsky.setLockIconResource",
1369 "minsky.setRavelIconResource",
1370 "minsky.setAutoSaveFile",
1374 if (doPushHistory && !constableCommands.contains(command) &&
1375 command.find(
"minsky.phillipsDiagram")==string::npos &&
1376 command.find(
"minsky.equationDisplay")==string::npos &&
1377 command.find(
"minsky.publicationTabs")==string::npos &&
1378 command.find(
".renderFrame")==string::npos &&
1379 command.find(
".requestRedraw")==string::npos &&
1380 command.find(
".backgroundColour")==string::npos &&
1381 command.find(
".get")==string::npos &&
1382 command.find(
".@elem")==string::npos &&
1383 command.find(
".mouseFocus")==string::npos
1387 if (t==
generic || (t==is_setterGetter && nargs>0))
1389 const bool modelChanged=pushHistory();
1390 if (modelChanged && command.find(
".keyPress")==string::npos)
1394 return modelChanged;
1397 return command==
"minsky.canvas.requestRedraw" || command==
"minsky.canvas.mouseDown" || command==
"minsky.canvas.mouseMove" || command.find(
".get")!=string::npos;
1404 if (historyPtr==history.size())
1406 historyPtr-=changes;
1407 if (historyPtr > 0 && historyPtr <= history.size())
1410 history[historyPtr-1].reseto()>>m;
1412 auto stashedValues=std::move(variableValues);
1415 unsigned numBookmarks=0;
1416 model->recursiveDo(&GroupItems::groups, [&](
const Groups&,
const Groups::const_iterator i) {
1417 numBookmarks+=(*i)->bookmarks.size();
1420 auto stashedGlobalBookmarks=model->bookmarks;
1421 auto stashedCanvasBookmarks=canvas.model->bookmarks;
1422 clearAllMaps(
false);
1431 for (
auto& v: variableValues)
1433 auto stashedValue=stashedValues.find(v.first);
1434 if (stashedValue!=stashedValues.end())
1435 v.second->tensorInit=std::move(stashedValue->second->tensorInit);
1438 model->bookmarks=std::move(stashedGlobalBookmarks);
1439 canvas.model->bookmarks=std::move(stashedCanvasBookmarks);
1440 unsigned numBookmarksAfterwards=0;
1441 model->recursiveDo(&GroupItems::groups, [&](
const Groups&,
const Groups::const_iterator i) {
1442 numBookmarksAfterwards+=(*i)->bookmarks.size();
1445 if (numBookmarksAfterwards!=numBookmarks)
1446 message(
"This undo/redo operation potentially deletes some bookmarks");
1447 try {requestReset();}
1452 historyPtr+=changes;
1460 const VariableValues::iterator i=variableValues.find(name);
1461 if (i==variableValues.end())
1462 throw error(
"variable %s doesn't exist",name.c_str());
1463 if (i->second->type()==type)
return;
1465 string newName=name;
1467 (&GroupItems::items,
1468 [&](
const Items&,Items::const_iterator i)
1470 if (
auto g=dynamic_cast<GodleyIcon*>(i->get()))
1472 if (type!=VariableType::flow)
1473 for (
auto& v: g->flowVars())
1474 if (v->valueId()==name)
1476 newName=v->name()+
"^{Flow}";
1477 const VariableValues::iterator iv=variableValues.find(newName);
1478 if (iv==variableValues.end()) {g->table.renameFlows(v->name(),newName); v->retype(VariableType::flow);}
1479 else throw error(
"flow variables in Godley tables cannot be converted to a different type");
1481 if (type!=VariableType::stock)
1482 for (
auto& v: g->stockVars())
1483 if (v->valueId()==name)
1485 newName=v->name()+
"^{Stock}";
1486 const VariableValues::iterator iv=variableValues.find(newName);
1487 if (iv==variableValues.end()) {g->table.renameStock(v->name(),newName); v->retype(VariableType::stock);}
1488 else throw error(
"stock variables in Godley tables cannot be converted to a different type");
1494 if (
auto var=definingVar(name))
1496 if (name==newName &&
var->type() != type && (!
var->isStock() ||
var->controller.lock()))
1497 throw error(
"cannot convert a variable to a type other than its defined type");
1502 case VariableType::undefined:
case VariableType::numVarTypes:
1503 case VariableType::tempFlow:
1504 throw error(
"convertVarType not supported for type=%s",
1505 VariableType::typeName(type).c_str());
1510 model->recursiveDo(&Group::items,
1511 [&](
Items&, Items::iterator i) {
1513 if (v->valueId()==name)
1516 if (*i==canvas.item)
1522 auto init=i->second->init();
1524 i->second->init(
init);
1527 void Minsky::addIntegral()
1529 if (
auto v=canvas.item->variableCast())
1530 if (
auto g=v->group.lock())
1533 convertVarType(v->valueId(),VariableType::integral);
1534 auto integ=
new IntOp;
1536 integ->
moveTo(canvas.item->x(), canvas.item->y());
1538 if (
auto g=integ->intVar->group.lock())
1539 g->removeItem(*integ->intVar);
1540 integ->intVar=dynamic_pointer_cast<
VariableBase>(canvas.item);
1541 integ->toggleCoupled();
1543 canvas.requestRedraw();
1548 void Minsky::renderAllPlotsAsSVG(
const string& prefix)
const 1551 model->recursiveDo(&Group::items,
1552 [&](
Items&, Items::iterator i) {
1553 if (
auto p=(*i)->plotWidgetCast())
1555 if (!p->title.empty())
1556 p->renderToSVG(prefix+
"-"+p->title+
".svg");
1558 p->renderToSVG(prefix+
"-"+
str(plotNum++)+
".svg");
1563 void Minsky::exportAllPlotsAsCSV(
const string& prefix)
const 1566 model->recursiveDo(&Group::items,
1567 [&](
Items&, Items::iterator i) {
1568 if (
auto p=(*i)->plotWidgetCast())
1570 if (!p->title.empty())
1571 p->exportAsCSV((prefix+
"-"+p->title+
".csv"));
1573 p->exportAsCSV((prefix+
"-"+
str(plotNum++)+
".csv"));
1579 void Minsky::setAllDEmode(
bool mode) {
1580 model->recursiveDo(&GroupItems::items, [mode](
Items&,Items::iterator i) {
1581 if (
auto g=dynamic_cast<GodleyIcon*>(i->get()))
1582 g->table.setDEmode(mode);
1589 this->displayValues=displayValues;
1590 this->displayStyle=displayStyle;
1591 canvas.requestRedraw();
1592 model->recursiveDo(&GroupItems::items, [](
Items&,Items::iterator i) {
1593 if (
auto g=dynamic_cast<GodleyIcon*>(i->get()))
1594 g->popup.requestRedraw();
1599 void Minsky::importVensim(
const string& filename)
1601 ifstream
f(filename);
1603 canvas.requestRedraw();
1606 vector<string> Minsky::availableOperations()
1607 {
return enumVals<OperationType::Type>();}
1608 vector<string> Minsky::variableTypes()
1609 {
return enumVals<VariableType::Type>();}
1610 vector<string> Minsky::assetClasses()
1611 {
return enumVals<GodleyTable::AssetClass>();}
1618 if (classifyOp(
op)==OperationType::general)
continue;
1619 if (
op==OperationType::copy)
continue;
1625 void Minsky::autoLayout()
1627 canvas.model->autoLayout();
1631 void Minsky::randomLayout()
1633 canvas.model->randomLayout();
1637 void Minsky::listAllInstances()
1640 if (
auto v=canvas.item->variableCast())
1642 variableInstanceList=std::make_shared<VariableInstanceList>(*canvas.model, v->valueId());
1645 variableInstanceList.reset();
1650 if (
wire.from()->wires().size()==1)
1652 auto& item=
wire.from()->item();
1653 if (!item.variableCast())
1655 for (
size_t i=1; i<item.portsSize(); ++i)
1656 if (
auto p=item.ports(i).lock())
1657 for (
auto w: p->wires())
1659 model->removeItem(item);
1661 else if (
auto p=item.ports(1).lock())
1662 if (p->wires().empty())
1663 model->removeItem(item);
1665 model->removeWire(
wire);
1668 void Minsky::setDefinition(
const std::string&
valueId,
const std::string& definition)
1673 if (
auto v=it->variableCast())
1678 if (
auto p=
var->ports(1).lock())
1686 for (
auto w: p->wires())
1693 group->addItem(udf);
1694 const bool notFlipped=!
flipped(
var->rotation());
1703 void Minsky::redrawAllGodleyTables()
1705 model->recursiveDo(&Group::items,
1706 [&](
Items&,Items::iterator i) {
1707 if (
auto g=dynamic_cast<GodleyIcon*>(i->get()))
1708 g->popup.requestRedraw();
1713 size_t Minsky::physicalMem()
const 1715 #if defined(__linux__) 1719 #elif defined(WIN32) 1720 MEMORYSTATUSEX s{
sizeof(MEMORYSTATUSEX)};
1721 GlobalMemoryStatusEx(&s);
1722 return s.ullTotalPhys;
1723 #elif defined(__APPLE__) 1724 int mib[]={CTL_HW,HW_MEMSIZE};
1725 uint64_t physical_memory;
1726 size_t length =
sizeof(uint64_t);
1727 if (sysctl(mib,
sizeof(mib)/
sizeof(
int), &physical_memory, &length, NULL, 0))
1728 perror(
"physicalMem:");
1729 return physical_memory;
1738 string Minsky::defaultFont()
1741 string Minsky::defaultFont(
const std::string& x)
1749 double Minsky::fontScale()
1750 {
return ecolab::Pango::scaleFactor;}
1752 double Minsky::fontScale(
double s)
1753 {
return ecolab::Pango::scaleFactor=s;}
1757 if (cycleCheck())
throw error(
"cyclic network detected");
1758 ofstream
f(filename);
1760 f<<
"\\documentclass{article}\n";
1763 f<<
"\\usepackage{breqn}\n\\begin{document}\n";
1768 f<<
"\\begin{document}\n";
1771 f<<
"\\end{document}\n";
1777 return op->numPorts()-1;
1780 void Minsky::setAutoSaveFile(
const std::string& file) {
1788 {
if (!
minsky.busyCursorStack++)
minsky.setBusyCursor();}
1791 {
if (!--
minsky.busyCursorStack)
minsky.clearBusyCursor();}
1799 if (!inDestruct)
throw std::runtime_error(
"Cancelled");
1812 {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
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
struct TCLcmd::trap::init_t init
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 &)
Creation and access to the minskyTCL_obj object, which has code to record whenever Minsky's state cha...
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
const vector< AssetClass > & _assetClass() const
class of each column (used in DE compliant mode)
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
cmd_data * getCommandData(const string &name)
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
string to_string(CONST84 char *x)
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