1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
|
// -*- mode: C++; c-indent-level: 4; c-basic-offset: 4; tab-width: 8; -*-
//
// Example of a planetary motion solver with interactive console
//
// Copyright (C) 2009 Dirk Eddelbuettel
// Copyright (C) 2010 - 2017 Dirk Eddelbuettel and Romain Francois
// Copyright (C) 2017 Dirk Eddelbuettel, Romain Francois and Łukasz Łaniewski-Wołłk
//
// GPL'ed
#include <RInside.h> // for the embedded R via RInside
class Wrapper;
// A planet.
struct Planet {
double x,y;
double m;
double vx, vy;
};
// A gravity simulator
class Solver {
typedef std::vector<Planet> Planets;
Planets tab;
double dt;
double G;
void Iteration() {
for (Planets::iterator a=tab.begin(); a != tab.end(); a++) {
for (Planets::iterator b=tab.begin(); b != a; b++) {
double x = a->x - b->x;
double y = a->y - b->y;
double r = sqrt(x*x + y*y);
double f = a->m * b->m * G / (r*r+1);
double fx = f * x/r;
double fy = f * y/r;
a->vx -= dt * fx / a->m;
a->vy -= dt * fy / a->m;
b->vx += dt * fx / b->m;
b->vy += dt * fy / b->m;
}
}
for (Planets::iterator a=tab.begin(); a != tab.end(); a++) {
a->x += dt * a->vx;
a->y += dt * a->vy;
}
}
public:
Solver(int n): tab(n), dt(1.0e-4), G(1.0) {
double v=0;
for (Planets::iterator a=tab.begin(); a != tab.end(); a++) {
a->x = sin(v);
a->y = cos(v);
a->m = 1;
v += 3.0/n;
}
}
void Iterate(int n) {
for (int i=0;i<n;i++) Iteration();
}
friend class Wrapper;
};
// A nice wrapper for the solver
class Wrapper {
Solver * s;
public:
Wrapper(Solver * s_) : s(s_) {}
Rcpp::DataFrame getData() {
Rcpp::NumericVector x,y,m,vx,vy;
for (Solver::Planets::iterator a=s->tab.begin(); a != s->tab.end(); a++) {
x.push_back( a->x);
y.push_back( a->y);
m.push_back( a->m);
vx.push_back(a->vx);
vy.push_back(a->vy);
}
return Rcpp::DataFrame::create(Rcpp::Named("x") = x,
Rcpp::Named("y") = y,
Rcpp::Named("mass") = m,
Rcpp::Named("Vx") = vx,
Rcpp::Named("Vy") = vy);
}
void setData(Rcpp::DataFrame tab) {
if ((size_t)tab.nrows() != s->tab.size()) {
return;
}
Rcpp::NumericVector x = tab["x"];
Rcpp::NumericVector y = tab["y"];
Rcpp::NumericVector m = tab["mass"];
Rcpp::NumericVector vx = tab["Vy"];
Rcpp::NumericVector vy = tab["Vy"];
for (int i=0;i<tab.nrows();i++) {
s->tab[i].x = x[i];
s->tab[i].y = y[i];
s->tab[i].m = m[i];
s->tab[i].vx = vx[i];
s->tab[i].vy = vy[i];
}
}
double& G() {
return s->G;
}
double& dt() {
return s->dt;
}
};
// The function which is called when running Solver$...
SEXP Dollar(Rcpp::XPtr<Wrapper> obj, std::string name) {
if (name == "data") {
return obj->getData();
} else if (name == "G") {
return Rcpp::NumericVector(1,obj->G());
} else if (name == "dt") {
return Rcpp::NumericVector(1,obj->dt());
} else {
return NULL;
}
}
// The function which is called when assigning to Solver$...
Rcpp::XPtr<Wrapper> DollarAssign(Rcpp::XPtr<Wrapper> obj, std::string name, SEXP v) {
if (name == "data") {
obj->setData(v);
} else if (name == "G") {
obj->G() = Rcpp::NumericVector(v)[0];
} else if (name == "dt") {
obj->dt() = Rcpp::NumericVector(v)[0];
}
return obj;
}
// The function listing the elements of Solver
Rcpp::CharacterVector Names(Rcpp::XPtr<Wrapper> obj) {
Rcpp::CharacterVector ret;
ret.push_back("data");
ret.push_back("G");
ret.push_back("dt");
return ret;
}
int main(int argc, char *argv[]) {
Solver S(70); // Creating the gravity simulator
RInside R(argc, argv, false, false, true); // Create an embedded R instance
Rcpp::XPtr<Wrapper> wr(new Wrapper(&S)); // Wrapping the solver
wr.attr("class") = "Solver";
R["Solver"] = wr; // Adding the wrapped solver
R["$.Solver"] = Rcpp::InternalFunction(& Dollar); // Adding the functions
R["$<-.Solver"] = Rcpp::InternalFunction(& DollarAssign);
R["names.Solver"] = Rcpp::InternalFunction(& Names);
char type;
do {
std::cout << "Want to go interactive? [y/n]";
std::cin >> type;
} while ( !std::cin.fail() && type!='y' && type!='n' );
if (type == 'y') { // Running an interactive R session
std::cout << "Running an interactive R session. You can explore (and modify) the data in the 'Solver' object." << std::endl;
std::cout << "[ You can finish the with Ctrl+D ]" << std::endl;
R.parseEval("options(prompt = 'R console > ')");
R.parseEval("X11()");
R.repl() ;
std::cout << "Finishing the interactive mode" << std::endl;
R.parseEval("dev.off()");
}
R.parseEval("X11()");
for (int i=0;i<2000;i++) { // Running the some 200'000 iterations in non-interactive mode
S.Iterate(100);
R.parseEval("P = Solver$data;");
R.parseEval("plot(P$x,P$y,cex=P$mass,asp=1,xlim=c(-5,5),ylim=c(-5,5),xlab='X',ylab='Y')");
}
exit(0);
}
|