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