libpspm
solver.cpp
Go to the documentation of this file.
1 #include "solver.h"
2 #include "cubic_spline.h"
3 
4 #include <iostream>
5 #include <cmath>
6 #include <cassert>
7 #include <string>
8 #include <algorithm>
9 #include <functional>
10 
11 
12 using namespace std;
13 
14 
15 inline std::vector <double> seq(double from, double to, int len){
16  std::vector<double> x(len);
17  for (size_t i=0; i<len; ++i) x[i] = from + i*(to-from)/(len-1);
18  return x;
19 }
20 
21 inline std::vector <double> logseq(double from, double to, int len){
22  std::vector<double> x(len);
23  for (size_t i=0; i<len; ++i) x[i] = exp(log(from) + i*(log(to)-log(from))/(len-1));
24  return x;
25 }
26 
27 inline std::vector <double> diff(vector <double> breaks){
28  std::vector<double> mids(breaks.size()-1);
29  for (size_t i=0; i<mids.size(); ++i) mids[i] = (breaks[i]+breaks[i+1])/2;
30  return mids;
31 }
32 
33 
34 // ~~~~~~~~~~~ SOLVER ~~~~~~~~~~~~~~~~~~~~~
35 
36 
37 Solver::Solver(PSPM_SolverType _method, string ode_method) : odeStepper(ode_method, 0, 1e-6, 1e-6) {
38  method = _method;
39 }
40 
41 
42 void Solver::addSpecies(std::vector<double> xbreaks, Species_Base* s, int n_extra_vars, double input_birth_flux){
43  s->set_inputBirthFlux(input_birth_flux);
44  s->n_extra_statevars = n_extra_vars;
45 
46  if (method == SOLVER_FMU || method == SOLVER_IFMU){
47  std::sort(xbreaks.begin(), xbreaks.end(), std::less<double>()); // sort cohorts ascending for FMU
48  s->xb = *xbreaks.begin();
49  }
50  else {
51  std::sort(xbreaks.begin(), xbreaks.end(), std::greater<double>()); // sort cohorts descending for CM, EBT
52  s->xb = *xbreaks.rbegin();
53  }
54 
55  int J;
56  if (method == SOLVER_FMU) J = xbreaks.size()-1;
57  else if (method == SOLVER_IFMU) J = xbreaks.size()-1;
58  else if (method == SOLVER_MMU) J = xbreaks.size()-1;
59  else if (method == SOLVER_CM ) J = xbreaks.size();
60  else if (method == SOLVER_EBT) J = xbreaks.size();
61  else throw std::runtime_error("Unsupported method");
62 
63  s->x = xbreaks;
64  s->resize(J);
65 
66  // FMU has only 1 internal state variable (x), reset have 2 (x,u)
67  bool cond = (method == SOLVER_FMU || method == SOLVER_IFMU);
68  n_statevars_internal = (cond)? 1:2;
69 
70  int state_size = s->J*(n_statevars_internal + n_extra_vars); // x/u and extra vars
71 
72  //s->start_index = state.size(); // New species will be appended to the end of state vector
73  state.resize(state.size()+state_size); // This will resize state for all species additions, but this in only initialization so its okay.
74  rates.resize(rates.size()+state_size);
75 
76  species_vec.push_back(s);
77 
78  if (method == SOLVER_FMU){
79  s->X.resize(s->J);
80  for (size_t i=0; i<s->J; ++i) s->X[i] = (s->x[i]+s->x[i+1])/2.0;
81 
82  s->h.resize(s->J); // This will be used only by FMU
83  for (size_t i=0; i<s->J; ++i) s->h[i] = s->x[i+1] - s->x[i];
84  }
85 
86  if (method == SOLVER_IFMU){
87  s->X.resize(s->J);
88  s->h.resize(s->J); // This will be used only by FMU
89 
90  if (control.ifmu_centered_grids){ // grids are labelled by the grid center
91  for (size_t i=0; i<s->J; ++i) s->X[i] = (s->x[i]+s->x[i+1])/2.0;
92  for (size_t i=0; i<s->J; ++i) s->h[i] = s->x[i+1] - s->x[i];
93  // for centered grids, also set xb to X[0] rather than x[0].
94  s->xb = s->X[0];
95  }
96  else{ // grids are labelled by the lower edge (this ensures that recruitment happens exactly at xb)
97  for (size_t i=0; i<s->J; ++i) s->X[i] = s->x[i];
98  for (size_t i=0; i<s->J; ++i) s->h[i] = s->x[i+1] - s->x[i];
99  s->xb = s->X[0];
100  }
101  }
102 
103 }
104 
105 
106 void Solver::addSpecies(int _J, double _xb, double _xm, bool log_breaks, Species_Base* s,
107  int n_extra_vars, double input_birth_flux){
108  vector<double> xbreaks;
109  if (log_breaks) xbreaks = logseq(_xb, _xm, _J+1);
110  else xbreaks = seq(_xb, _xm, _J+1);
111 
112  addSpecies(xbreaks, s, n_extra_vars, input_birth_flux);
113 }
114 
115 
117  n_statevars_system = _s;
118  state.resize(state.size() + _s);
119  rates.resize(state.size() + _s);
120 }
121 
122 
123 void Solver::resetState(double t0){ // FIXME: This is currently redundant, and needs to be improved with reset of both state and cohorts for a true reset of state
124  current_time = t0;
125  odeStepper.reset(t0, control.ode_eps, control.ode_eps); // = RKCK45<vector<double>> (0, control.ode_eps, control.ode_initial_step_size); // this is a cheap operation, but this will empty the internal containers, which will then be (automatically) resized at next 1st ODE step. Maybe add a reset function to the ODE stepper?
126 
127  std::fill(state.begin(), state.end(), 0);
128  std::fill(rates.begin(), rates.end(), -999); // DEBUG
129 }
130 
131 
133  int state_size_new = n_statevars_system;
134  for (auto& spp : species_vec){
135  state_size_new += spp->J*(n_statevars_internal + spp->n_extra_statevars);
136  }
137 
138  state.resize(state_size_new, -999);
139  rates.resize(state_size_new, -999);
140 }
141 
142 
144  env = _env;
145 }
146 
147 
149 // return &species_vec[id];
150 //}
151 
152 
154  return species_vec.size();
155 }
156 
157 
159  double maxsize = 0;
160  for (auto spp : species_vec) maxsize = std::max(maxsize, spp->get_maxSize());
161  return maxsize;
162 }
163 
164 
165 
166 
168  std::cout << ">> SOLVER \n";
169  string types[] = {"FMU", "MMU", "CM", "EBT", "Implicit FMU"};
170  std::cout << "+ Type: " << types[method] << std::endl;
171 
172  std::cout << "+ State size = " << state.size() << "\n";
173  std::cout << "+ Rates size = " << rates.size() << "\n";
174  std::cout << "+ Species:\n";
175  std::cout.flush();
176  for (int i=0; i<species_vec.size(); ++i) {
177  std::cout << "Sp (" << i << "):\n";
178  species_vec[i]->print();
179  }
180 
181 }
182 
183 
184 // Layout of the state vector is as follows:
185 // ------------------------------------------------------------
186 // | x : u | x : u | x : u | a : b : c | a : b : c | a : b : c |
187 // ------------------------------------------------------------
188 // In above layout, the internal variables x and u are tightly
189 // packed first, followed by user variables a, b, c.
190 // This arrangement is cache friendly because:
191 // When setting rates, typically a,b,c are calculated one
192 // after the other for each x.
193 // TODO: should this take t0 as an argument, instead of setting to 0?
195 
196  vector<double>::iterator it = state.begin() + n_statevars_system; // TODO: replace with init_sState()
197 
198  for (int k=0; k<species_vec.size(); ++k){
199  Species_Base* s = species_vec[k];
200 
201  // Boundary cohort is not in state, but used as a reference.
202  s->set_xb(s->xb); // set x of boundary cohort
203  s->set_ub(0); // set initial density of boundary cohort to 0.
204 
205  // set birth time for each cohort to current_time
206  for (int i=0; i<s->J; ++i) s->set_birthTime(i, current_time);
207 
208  // set x, u for all cohorts
209  if (method == SOLVER_FMU || method == SOLVER_IFMU){
210  for (size_t i=0; i<s->J; ++i){
211  double X = s->X[i]; // for FMU, X is the midpoint of the cell edges
212  double U = s->init_density(i, X, env);
213  s->setX(i,X);
214  s->setU(i,U);
215  *it++ = U; // u in state (only)
216  }
217  }
218  if (method == SOLVER_CM){
219  for (size_t i=0; i<s->J; ++i){
220  double X = s->x[i];
221  double U = s->init_density(i, X, env);
222  s->setX(i,X);
223  s->setU(i,U);
224  *it++ = X; // x in state
225  *it++ = (use_log_densities)? log(U) : U; // u in state
226  }
227  }
228  if (method == SOLVER_EBT){
229  // x, u for internal cohorts in state and it cohorts
230  for (size_t i=0; i<s->J-1; ++i){
231  double X = (s->x[i]+s->x[i+1])/2.0;
232  double U = s->init_density(i, X, env)*(s->x[i]-s->x[i+1]);
233  s->setX(i,X);
234  s->setU(i,U);
235  *it++ = X; // x in state
236  *it++ = U; // u in state
237  }
238  // set pi0, N0 as x, u for the last cohort. This scheme allows using this last cohort with xb+pi0 in integrals etc
239  *it++ = 0; *it++ = 0;
240  s->setX(s->J-1,0); // TODO: should this be set to xb for init_state and set to 0 again later? maybe not, as init_state is not expected to be x dependent
241  s->setU(s->J-1,0);
242  }
243 
244  // initialize extra state for each cohort and copy it to state
246  //if (s->n_extra_statevars > 0){ // FIXME: maybe redundant
247  //auto it_prev = it;
248  //s->init_ExtraState(it); // this also inits the extra state of boundary cohort, but without advancing the iterator
249  //assert(distance(it_prev, it) == s->n_extra_statevars*s->J);
250  //}
251 
252  }
253 }
254 
255 
256 void Solver::copyStateToCohorts(std::vector<double>::iterator state_begin){
257  std::cout << "state ---> cohorts\n";
258  std::vector<double>::iterator it = state_begin + n_statevars_system; // no need to copy system state
259 
260  for (int k=0; k<species_vec.size(); ++k){
261  Species_Base* s = species_vec[k];
262 
263  s->set_xb(s->xb); // Important: "touch" boundary cohort to trigger a precompute. Note that setSize() triggers it.
264  if (method == SOLVER_FMU || method == SOLVER_IFMU){
265  for (size_t i=0; i<s->J; ++i){
266  s->setU(i,*it++);
267  }
268  }
269  if (method == SOLVER_CM){
270  for (size_t i=0; i<s->J; ++i){
271  double X = *it++; // get x from state
272  double U = *it++; // get u from state
273  U = (use_log_densities)? exp(U) : U; // u in cohorts
274  s->setX(i,X);
275  s->setU(i,U);
276  }
277  }
278  if (method == SOLVER_EBT){
279  // x, u for boundary and internal cohorts
280  for (size_t i=0; i<s->J; ++i){
281  double X = *it++;
282  double U = *it++;
283  s->setX(i,X);
284  s->setU(i,U);
285  }
286  }
287 
288  if (s->n_extra_statevars > 0){ // If extra state variables have been requested, initialize them
289  auto it_prev = it;
291  assert(distance(it_prev, it) == s->n_extra_statevars*s->J);
292  }
293  }
294 }
295 
296 
298  std::cout << "state <--- cohorts\n";
299  vector<double>::iterator it = state.begin() + n_statevars_system; // no need to copy system state
300 
301  for (int k=0; k<species_vec.size(); ++k){
302  Species_Base* s = species_vec[k];
303 
304  if (method == SOLVER_FMU || method == SOLVER_IFMU){
305  for (size_t i=0; i<s->J; ++i){
306  *it++ = s->getU(i);
307  }
308  }
309  if (method == SOLVER_CM){
310  for (size_t i=0; i<s->J; ++i){
311  double X = s->getX(i);
312  double U = s->getU(i);
313  U = (use_log_densities)? log(U) : U; // log(u) in state
314  *it++ = X; // set x to state
315  *it++ = U; // set u to state
316  }
317  }
318  if (method == SOLVER_EBT){
319  // x, u for boundary and internal cohorts
320  for (size_t i=0; i<s->J; ++i){
321  double X = s->getX(i);
322  double U = s->getU(i);
323  *it++ = X;
324  *it++ = U;
325  }
326  }
327 
328  if (s->n_extra_statevars > 0){ // If extra state variables have been requested, initialize them
329  auto it_prev = it;
331  assert(distance(it_prev, it) == s->n_extra_statevars*s->J);
332  }
333  }
334 }
335 
336 
337 
338 void Solver::step_to(double tstop){
339  auto func = [](double t){};
340  step_to(tstop, func);
341 }
342 
343 
344 void Solver::updateEnv(double t, std::vector<double>::iterator S, std::vector<double>::iterator dSdt){
345  std::cout << "update Env...\n";
346  for (auto spp : species_vec) spp->triggerPreCompute();
347  env->computeEnv(t, this, S, dSdt);
348 }
349 
350 
351 // k = species_id
352 double Solver::calcSpeciesBirthFlux(int k, double t){
353  std::cout << "calc birthFlux...\n";
354  auto spp = species_vec[k];
355  auto newborns_production = [this, spp](int i, double _t){
356  double b1 = spp->birthRate(i, spp->getX(i), _t, env);
357  return b1;
358  };
359  double birthFlux = integrate_x(newborns_production, t, k);
360  return birthFlux;
361 }
362 
363 
364 vector<double> Solver::newborns_out(double t){ // TODO: make recompute env optional
365  // update Environment from latest state
366  //copyStateToCohorts(state.begin()); // not needed here because this is done in afterStep or upon cohorts update
367  //env->computeEnv(t, this, state.begin(), rates.begin());
368  updateEnv(t, state.begin(), rates.begin()); // this will trigger a precompute
369 // for (int k = 0; k<species_vec.size(); ++k) preComputeSpecies(k,t);
370 
371  vector<double> b_out;
372  for (int k=0; k<species_vec.size(); ++k){
373  // calculate birthflux
374  // [solved] wudx doesnt work here. Why?? - works now, no idea why it was not working earlier!
375  //auto newborns_production = [this, k](int i, double t){
376  //double z = species_vec[k]->getX(i);
378  //double b = species_vec[k]->birthRate(i,z,t,env);
380  //return b;
381  //};
382  //double birthFlux = integrate_x(newborns_production, t, k);
383  //double birthFlux = integrate_wudx_above(newborns_production, t, 0, k);
384  double birthFlux = calcSpeciesBirthFlux(k, t);
385  b_out.push_back(birthFlux);
386  }
387  return b_out;
388 }
389 
390 // FOR DEBUG ONLY, using TESTMODEL
391 vector<double> Solver::u0_out(double t){
392  vector <double> u0out;
393  vector <double> newbornsout = newborns_out(t);
394  for (int k=0; k < species_vec.size(); ++k){
395  //species_vec[k]->preCompute(-1, t, env); // not req because precomputeAllCohorts called in newborns_out() precomputes BC too.
396  u0out.push_back(newbornsout[k]/species_vec[k]->growthRate(-1, species_vec[k]->xb, t, env));
397  }
398  return u0out;
399 }
400 
401 
402 
403 // xb
404 // |---*--|--*--|----*----|---*---|
405 // 0 1 2 3 <--- points
406 // 0 1 2 3 4 <--- breaks
407 
408 // Test this function with this R script:
424 struct point{
425  double xmean = 0;
426  double abund = 0;
427  int count = 0;
428 };
429 
430 std::vector<double> Solver::getDensitySpecies_EBT(int k, vector<double> breaks){
431  auto spp = species_vec[k];
432 
433  if (method == SOLVER_EBT){
434  double xm = spp->getX(0)+1e-6;
435 
436  vector<point> points(breaks.size()-1);
437 
438  // assuming breaks are sorted ascending
439  // cohorts are sorted descending
440  int current_interval = breaks.size()-2;
441  for (int i=0; i<spp->J; ++i){ // loop over all cohorts except boundary cohort
442  double x = spp->getX(i);
443  double N = spp->getU(i);
444  if (i == spp->J-1) x = spp->xb + x/(N+1e-12); // real-ize x if it's boundary cohort
445 
446  while(breaks[current_interval]>x) --current_interval; // decrement interval until x fits
447  //cout << current_interval << ", x = " << x << "(" << N << ") in [" << breaks[current_interval] << ", " << breaks[current_interval+1] << "]\n"; cout.flush();
448  if (N>0){
449  points[current_interval].abund += N;
450  points[current_interval].count += 1;
451  points[current_interval].xmean += N*x;
452  }
453  }
454 
455 
456  for (int i=0; i<points.size(); ++i) if (points[i].count>0) points[i].xmean /= points[i].abund;
457 
458  // remove 0-count points
459  auto pred = [this](const point& p) -> bool {
460  return p.count == 0;
461  };
462 
463  auto pend = std::remove_if(points.begin(), points.end(), pred);
464  points.erase(pend, points.end());
465 
466  //cout << "mean x and abund (removed):\n";
467  //for (int i=0; i<points.size(); ++i) cout << i << "\t" << points[i].count << "\t" << points[i].xmean << "\t" << points[i].abund << "\n";
468  //cout << "--\n";
469 
470  if (points.size() > 2){
471  vector<double> h(points.size());
472  h[0] = (points[1].xmean+points[0].xmean)/2 - spp->xb;
473  for (int i=1; i<h.size()-1; ++i) h[i] = (points[i+1].xmean - points[i-1].xmean)/2;
474  h[h.size()-1] = xm - (points[h.size()-1].xmean+points[h.size()-2].xmean)/2;
475 
476  vector <double> xx, uu;
477  xx.reserve(points.size());
478  uu.reserve(points.size());
479  for (int i=0; i<points.size(); ++i){
480  xx.push_back(points[i].xmean);
481  uu.push_back(points[i].abund / h[i]);
482  }
483 
484  Spline spl;
485  spl.splineType = Spline::LINEAR; //Spline::CONSTRAINED_CUBIC;
487  spl.set_points(xx, uu);
488 
489  vector <double> dens;
490  dens.reserve(points.size());
491  for (int i=0; i<breaks.size(); ++i){
492  dens.push_back(spl.eval(breaks[i]));
493  }
494 
495  return dens;
496  }
497  else return vector<double>(breaks.size(), 0);
498  }
499  else {
500  throw std::runtime_error("This function can only be called for the EBT solver");
501  }
502 
503 }
504 
505 
506 
507 
508 
PSPM_SolverType
Definition: solver.h:13
std::vector< double > logseq(double from, double to, int len)
Definition: solver.cpp:21
virtual double init_density(int i, double x, void *env)=0
virtual void resize(int _J)=0
std::vector< double > state
Definition: solver.h:28
int n_species()
Definition: solver.cpp:153
std::vector< double > h
Definition: species.h:30
int n_statevars_internal
Definition: solver.h:19
void print()
Definition: solver.cpp:167
virtual double getU(int i)=0
STL namespace.
double maxSize()
Definition: solver.cpp:158
void reset(double t_start, double rtol, double atol)
Definition: ode_solver.h:42
virtual void set_ub(double _ub)=0
int n_extra_statevars
Definition: species.h:21
virtual double getX(int i)=0
virtual void set_xb(double _xb)=0
void set_inputBirthFlux(double b)
Definition: species.cpp:30
virtual void computeEnv(double t, Solver *sol, std::vector< double >::iterator S, std::vector< double >::iterator dSdt)=0
double current_time
Definition: solver.h:27
int n_statevars_system
Definition: solver.h:20
virtual void initAndCopyExtraState(double t, void *env, std::vector< double >::iterator &it)=0
void copyStateToCohorts(std::vector< double >::iterator state_begin)
Definition: solver.cpp:256
void set_points(const Container &x, const Container &y)
Definition: cubic_spline.h:139
Extr extrapolate
Definition: cubic_spline.h:128
std::vector< double > diff(vector< double > breaks)
Definition: solver.cpp:27
EnvironmentBase * env
Definition: solver.h:24
std::vector< double > newborns_out(double t)
Definition: solver.cpp:364
double integrate_x(wFunc w, double t, int species_id)
Computes the integral for the specified species. For details, see integrate_wudx_above.
std::vector< double > x
Definition: species.h:29
std::vector< double > rates
Definition: solver.h:29
void addSpecies(int _J, double _xb, double _xm, bool log_breaks, Species_Base *_mod, int n_extra_vars, double input_birth_flux=-1)
Definition: solver.cpp:106
void setEnvironment(EnvironmentBase *_env)
Definition: solver.cpp:143
Float eval(Float x) const
Definition: cubic_spline.h:268
virtual void copyCohortsExtraToState(std::vector< double >::iterator &it)=0
void initialize()
Definition: solver.cpp:194
OdeSolver odeStepper
Definition: solver.h:23
virtual void set_birthTime(int i, double t0)=0
PSPM_SolverType method
Definition: solver.h:17
bool use_log_densities
Definition: solver.h:49
void copyCohortsToState()
Definition: solver.cpp:297
void updateEnv(double t, std::vector< double >::iterator S, std::vector< double >::iterator dSdt)
Definition: solver.cpp:344
Type splineType
Definition: cubic_spline.h:126
void resizeStateFromSpecies()
Definition: solver.cpp:132
virtual void setU(int i, double _u)=0
double xb
Definition: species.h:41
void resetState(double t0=0)
Definition: solver.cpp:123
std::vector< double > seq(double from, double to, int len)
Definition: solver.cpp:15
std::vector< Species_Base * > species_vec
Definition: solver.h:31
Solver(PSPM_SolverType _method, std::string ode_method="rk45ck")
Definition: solver.cpp:37
std::vector< double > getDensitySpecies_EBT(int k, std::vector< double > breaks)
Definition: solver.cpp:430
virtual void copyExtraStateToCohorts(std::vector< double >::iterator &it)=0
virtual void setX(int i, double _x)=0
void step_to(double tstop, AfterStepFunc &afterStep_user)
Definition: solver.tpp:15
void addSystemVariables(int _s)
Definition: solver.cpp:116
struct Solver::@1 control
std::vector< double > X
Definition: species.h:28
double calcSpeciesBirthFlux(int k, double t)
Definition: solver.cpp:352
std::vector< double > u0_out(double t)
Definition: solver.cpp:391