1 /***
2  *         Author:  Dilawar Singh <dilawar@subcom.tech>
3  *    Description:  Simulation class.
4  */
5 
6 #include "Command.h"
7 
8 #include "Simulation.h"
9 
10 using namespace std;
11 
Simulation(vector<double> & low,vector<double> & high,vector<string> & boundary_type)12 Simulation::Simulation(vector<double>& low,
13   vector<double>& high,
14   vector<string>& boundary_type)
15   : low_(low)
16   , high_(high)
17   , curtime_(0.0)
18   , initDisplay_(false)
19   , debug_(false)
20 {
21     assert(low.size() == high.size());
22 
23     sim_ = smolNewSim(low.size(), &low[0], &high[0]);
24     assert(sim_);
25 
26     assert(boundary_type.size() == getDim());
27 
28     for (size_t d = 0; d < getDim(); d++) {
29         string t = boundary_type[d];
30         if (t.size() == 1)
31             smolSetBoundaryType(sim_, d, -1, t[0]);
32         else
33             for (size_t ii = 0; ii < 2; ii++)
34                 smolSetBoundaryType(sim_, d, ii, t[ii]);
35     }
36 }
37 
38 // Factory function.
Simulation(const char * filepath,const char * flags)39 Simulation::Simulation(const char* filepath, const char* flags)
40   : curtime_(0.0)
41   , initDisplay_(false)
42   , debug_(false)
43 {
44     auto path = splitPath(string(filepath));
45     sim_ = smolPrepareSimFromFile(path.first.c_str(), path.second.c_str(), flags);
46 }
47 
~Simulation()48 Simulation::~Simulation()
49 {
50     smolFreeSim(sim_);
51 }
52 
53 size_t
getDim() const54 Simulation::getDim() const
55 {
56     assert(sim_->dim >= 0);
57     return (size_t)sim_->dim;
58 }
59 
60 map<string, size_t>
count() const61 Simulation::count() const
62 {
63     map<string, size_t> res{
64 #ifdef ENABLE_PYTHON_CALLBACK
65         { "functions", sim_->ncallbacks },
66 #endif
67         { "dim", sim_->dim },
68         { "variables", sim_->nvar }
69     };
70 
71     res["species"] = sim_->mols ? sim_->mols->nspecies : 0;
72     res["compartment"] = sim_->cmptss ? sim_->cmptss->ncmpt : 0;
73     res["surface"] = sim_->srfss ? sim_->srfss->nsrf : 0;
74 
75     return res;
76 }
77 
78 bool
initialize()79 Simulation::initialize()
80 {
81     if (sim_)
82         return true;
83 
84     if (getDim() == 0 || getDim() > 3) {
85         cerr << __FUNCTION__ << ": dim must be between 0 and 3. Got " << getDim() << endl;
86         return false;
87     }
88 
89     if (low_.size() != getDim()) {
90         cerr << __FUNCTION__ << ": missing lowbounds" << endl;
91         return false;
92     }
93 
94     if (high_.size() != getDim()) {
95         cerr << __FUNCTION__ << ": missing highbounds" << endl;
96         return false;
97     }
98 
99     for (size_t d = 0; d < getDim(); d++) {
100         if (low_[d] >= high_[d]) {
101             cerr << __FUNCTION__ << ": lowbounds must be < highbounds"
102                  << " which is not true at index " << d << " where lowbounds is "
103                  << low_[d] << " and highbound is " << high_[d] << endl;
104             return false;
105         }
106     }
107 
108     sim_ = smolNewSim(getDim(), &low_[0], &high_[0]);
109     assert(sim_);
110 
111     if (debug_)
112         smolSetDebugMode(1);
113     return sim_ ? true : false;
114 }
115 
116 /**
117  * @brief Set filepath and filename on sim_->getSimPtr(). When python scripts
118  * are used, these variables are not set my smolsimulate and related fucntions.
119  *
120  * @param modelpath modelpath e.g. when using command `python3
121  * ~/Work/smoldyn.py` in terminal, modelpath is set to
122  * `/home/user/Work/Smoldyn.py`.
123  *
124  * @return true.
125  */
126 bool
setModelpath(const string & modelpath)127 Simulation::setModelpath(const string& modelpath)
128 {
129     assert(sim_);
130     auto p = splitPath(modelpath);
131     strncpy(sim_->filepath, p.first.c_str(), p.first.size());
132     strncpy(sim_->filename, p.second.c_str(), p.second.size());
133     return true;
134 }
135 
136 ErrorCode
runSim(double stoptime,double dt,bool display,bool overwrite)137 Simulation::runSim(double stoptime, double dt, bool display, bool overwrite)
138 {
139     ErrorCode er;
140 
141     if (!sim_) {
142         if (!initialize()) {
143             cerr << __FUNCTION__ << ": Could not initialize sim." << endl;
144             return ECerror;
145         }
146     }
147 
148     gl2glutInit(0, NULL);
149 
150     er = smolOpenOutputFiles(sim_, overwrite);
151     if (er != ErrorCode::ECok) {
152         cerr << __FUNCTION__ << ": Could not open output files." << endl;
153         return er;
154     }
155 
156     auto er1 = smolSetTimeStep(sim_, dt);
157     auto er2 = smolSetTimeStop(sim_, stoptime);
158     if (er1 != ErrorCode::ECok || er2 != ErrorCode::ECok) {
159         cerr << __FUNCTION__ << ": Could not update simtimes." << endl;
160         return er;
161     }
162 
163     er = smolUpdateSim(sim_);
164     if (er != ErrorCode::ECok) {
165         cerr << __FUNCTION__ << ": Could not update simstruct." << endl;
166         return er;
167     }
168 
169     if (display && !initDisplay_) {
170         smolDisplaySim(sim_);
171         initDisplay_ = true;
172     }
173 
174     er = smolRunSim(sim_);
175     curtime_ = stoptime;
176 
177     return er;
178 }
179 
180 ErrorCode
runUntil(const double breaktime,const double dt,bool display,bool overwrite)181 Simulation::runUntil(const double breaktime,
182   const double dt,
183   bool display,
184   bool overwrite)
185 {
186     assert(sim_);
187 
188     if (!sim_) {
189         if (!initialize()) {
190             cerr << __FUNCTION__ << ": Could not initialize sim." << endl;
191             return ECerror;
192         }
193     }
194 
195     auto er = smolOpenOutputFiles(sim_, overwrite);
196     if (er != ErrorCode::ECok) {
197         cerr << __FUNCTION__ << ": Simulation skipped." << endl;
198     }
199 
200     // If dt>0, reset dt else use the old one.
201     if (dt > 0.0)
202         smolSetTimeStep(sim_, dt);
203     smolUpdateSim(sim_);
204 
205     if (display && (!initDisplay_)) {
206         smolDisplaySim(sim_);
207         initDisplay_ = true;
208     }
209     return smolRunSimUntil(sim_, breaktime);
210 }
211 
212 bool
connect(const py::function & func,const py::handle & target,const size_t step,const py::list & args)213 Simulation::connect(const py::function& func,
214   const py::handle& target,
215   const size_t step,
216   const py::list& args)
217 {
218     assert(sim_->ncallbacks < MAX_PY_CALLBACK);
219     if (sim_->ncallbacks >= MAX_PY_CALLBACK) {
220         py::print("Error: Maximum of ",
221           MAX_PY_CALLBACK,
222           " callbacks are allowed. Current number of callbacks: ",
223           sim_->ncallbacks);
224         return false;
225     }
226 
227     // Note: cleanup is the job of simfree.
228     auto f = new CallbackFunc();
229     assert(f);
230 
231     f->setFunc(func);
232     f->setStep(step);
233     f->setTarget(target);
234     f->setArgs(args);
235 
236     sim_->callbacks[sim_->ncallbacks] = f;
237     sim_->ncallbacks += 1;
238     return true;
239 }
240 
241 // get the pointer
242 simptr
getSimPtr() const243 Simulation::getSimPtr() const
244 {
245     assert(sim_);
246     return sim_;
247 }
248 
249 // set the pointer.
250 void
setSimPtr(simptr sim)251 Simulation::setSimPtr(simptr sim)
252 {
253     sim_ = sim;
254 }
255 
256 void
setCurtime(double curtime)257 Simulation::setCurtime(double curtime)
258 {
259     curtime_ = curtime;
260 }
261 
262 double
getCurtime() const263 Simulation::getCurtime() const
264 {
265     return curtime_;
266 }
267 
268 bool
isValid()269 Simulation::isValid()
270 {
271     return sim_ ? true : false;
272 }
273 
274 pair<vector<double>, vector<double>>
getBoundaries(void)275 Simulation::getBoundaries(void)
276 {
277     vector<double> low;
278     vector<double> high;
279     for (size_t d = 0; d < (size_t)sim_->dim; d++) {
280         low.push_back(sim_->wlist[2 * d]->pos);
281         high.push_back(sim_->wlist[2 * d + 1]->pos);
282     }
283     return make_pair(low, high);
284 }
285 
286 void
addCommand(const string & cmd,char cmd_type,const map<string,double> & kwargs)287 Simulation::addCommand(const string& cmd,
288   char cmd_type,
289   const map<string, double>& kwargs)
290 {
291     auto c = make_unique<Command>(getSimPtr(), cmd.c_str(), cmd_type, kwargs);
292     c->addCommandToSimptr();
293 
294     // Don't call any method on c after this statement. std::move(c) will
295     // invalidate c.
296     commands_.push_back(std::move(c));
297     // printf("*** Simulation.cpp Simulation::addCommand B\n"); //??
298 }
299 
300 void
addCommandStr(char * cmd)301 Simulation::addCommandStr(char* cmd)
302 {
303     auto c = make_unique<Command>(getSimPtr(), cmd);
304     c->addCommandToSimptr();
305     commands_.push_back(std::move(c));
306 }
307 
308