#include #include #include #include #include #include #include #include #include #include using namespace GeographicLib; namespace ode = boost::numeric::odeint; typedef Math::real real; typedef std::array point; // Initialize with the end points of a line class LineDistance { point _a, _b; real _l, _nx, _ny; public: LineDistance(const point& a, const point& b); real Distance(const point& p) const; // negative on left of line, pos on right real Displacement(const point& p) const; }; // Use Ramer-Douglas-Peucker to simplify a polyline class LineSimplifier { // Simplify range [a,b] static void InternalSimplify(std::vector& p, unsigned a, unsigned b, real thresh); public: static void Simplify(std::vector& p, real thresh); }; inline LineDistance::LineDistance(const point& a, const point& b) : _a(a), _b(b) { using std::hypot; real dx = _b[0] - _a[0], dy = _b[1] - _a[1]; _l = hypot(dx, dy); if (_l > std::numeric_limits::epsilon()) { _nx = dx / _l; _ny = dy / _l; } else { _l = 0; _nx = 1; _ny = 0; } } inline real LineDistance::Distance(const point& p) const { using std::abs; using std::hypot; real x = p[0] - _a[0], y = p[1] - _a[1]; if (_l != 0) { real X = x * _nx + y * _ny, Y = abs(x * _ny - y * _nx); X = X < 0 ? -X : (X > _l ? X - _l : 0); return X != 0 ? hypot(X, Y) : Y; } else return hypot(x, y); } inline real LineDistance::Displacement(const point& p) const { real x = p[0] - _a[0], y = p[1] - _a[1]; return x * _ny - y * _nx; } inline void LineSimplifier::Simplify(std::vector& p, real thresh) { using std::isnan; unsigned n = p.size(); InternalSimplify(p, 0, n-1, thresh); unsigned i = 0, j = 0; while (i < n) { // Squeeze out nans if (!isnan(p[i][0])) { p[j] = p[i]; ++j; } ++i; } p.resize(j); } void LineSimplifier::InternalSimplify(std::vector& p, unsigned a, unsigned b, real thresh) { if (b - a + 1 <= 2) return; // Assume b > a+1, thresh > 0 real maxdist = -1; unsigned maxind = (a + b) / 2; // initial value isn't used LineDistance dist(p[a], p[b]); for (unsigned i = a + 1; i <= b - 1; ++i) { real d = dist.Distance(p[i]); if (d > maxdist) { maxdist = d; maxind = i; } } if (maxdist > thresh) { InternalSimplify(p, a, maxind, thresh); InternalSimplify(p, maxind, b, thresh); } else { for (unsigned i = a+1; i <= b-1; ++i) p[i][0] = Math::NaN(); } } class PointTest { real _xmin, _xmax, _ymin, _ymax; public: PointTest(real xmin, real xmax, real ymin, real ymax) : _xmin(xmin) , _xmax(xmax) , _ymin(ymin) , _ymax(ymax) {} bool operator() (const point &p) const { return p[0] >= _xmin && p[0] <= _xmax && p[1] >= _ymin && p[1] <= _ymax; } }; class GravityInt { const NormalGravity& _grav; unsigned _dir; public: GravityInt(const NormalGravity& grav, unsigned dir) : _grav(grav) , _dir(dir & 3u) {} void operator() (const point& p, point &d, const real /*t*/) const { real Y = 0, gX, gY, gZ; _grav.U(p[0], Y, p[1], gX, gY, gZ); Math::norm(gX, gZ); switch (_dir) { case 0: d[0] = gX; d[1] = gZ; break; // parallel to g case 1: d[0] = -gZ; d[1] = gX; break; // counterclockwise 90d from g case 2: d[0] = -gX; d[1] = -gZ; break; // antiparallel to g case 3: default: d[0] = gZ; d[1] = -gX; break; // clockwise 90d from g } } }; class GravityFollow { public: static void follow(const GravityInt& sys, const point& p0, real t0, real ds, std::vector& points, const PointTest& box) { ode::result_of::make_dense_output < ode::runge_kutta_dopri5< point, real > >::type integrator = ode::make_dense_output(real(1.0e-8), real(0*1.0e-8), ode::runge_kutta_dopri5< point, real >() ); integrator.initialize(p0, t0, real(1e-2)); integrator.do_step(sys); int n = 1, i = 1; point out; while (true) { real tout = i * ds; while (integrator.current_time() < tout) { integrator.do_step(sys); ++n; } integrator.calc_state(tout, out); points.push_back(out); if (!box(out)) break; ++i; } } }; // Evaluate gravity potential or gradient along 1 axis class GravityEval { const NormalGravity& _grav; bool _zaxis, _grad; real _disp; public: GravityEval(const NormalGravity& grav, bool zaxis, bool grad, real disp) : _grav(grav) , _zaxis(zaxis) , _grad(grad) , _disp(disp) {}; real operator() (real v) const { real X = _zaxis ? _disp : v, Y = 0, Z = _zaxis ? v : _disp, gX, gY, gZ, U = _grav.U(X, Y, Z, gX, gY, gZ); return _grad ? gX : U; } }; class Interpolator { private: int _maxit; real _eps; public: Interpolator() : _maxit(20), _eps(sqrt(std::numeric_limits::epsilon())) {} real operator() (const GravityEval& gravfun, real x0, real x1, real val) { using std::abs; real y0 = gravfun(x0) - val, y1; for (int i = 0, trip = 0; i < _maxit; ++i) { y1 = gravfun(x1) - val; if (y1 == y0) break; real x2 = x1 - y1 * (x1 - x0) / (y1 - y0); x0 = x1; x1 = x2; y0 = y1; if (abs(x0 - x1) <= _eps) ++trip; if (trip > 2) break; } return x1; } }; void dump(const std::vector& points) { for (auto p = points.begin(); p != points.end(); ++p) { std::cout << (*p)[0] << "," << (*p)[1] << ";\n"; } } int main() { Utility::set_digits(); using std::sqrt; real a = 1, GM = 1, omega = 3/Math::real(10), f = 2/Math::real(10); real gX, gY, gZ, b = (1 - f) * a, E = a * sqrt(f * (2 - f)); real thresh = real(0.5e-3); NormalGravity grav(a, GM, omega, f, true); real xmax = 3, ymax = 2; PointTest box(0, xmax, 0, ymax); Interpolator intpol; real X0 = a, U0 = grav.U(X0, 0, 0, gX, gY, gZ), Uc = grav.U(0, 0, 0, gX, gY, gZ); real Xnull, Unull; { GravityEval eval(grav, false, true, 0); Xnull = intpol(eval, 1, 2, 0); Unull = grav.U(Xnull, 0, 0, gX, gY, gZ); } int ndiv = 20; real del = (U0 - Unull) / 20; std::cout << std::setprecision(6); std::cout << "a=" << a << "; b=" << b << "; Rnull=" << Xnull << "; xmax=" << xmax << "; ymax=" << ymax << ";\n"; std::cout << "q={}; p={}; qa={}; pa={};\n"; std::vector points; point p0; int k = 0; for (int i = 0; i <= 90; i += 10) { points.clear(); real s, c; Math::sincosd(real(i), s, c); p0[0] = a * c; p0[1] = b * s; points.push_back(p0); if (i == 0) { p0[0] = xmax; p0[1] = 0; points.push_back(p0); } else if (i == 90) { p0[0] = 0; p0[1] = ymax; points.push_back(p0); } else { GravityInt sys(grav, 2u); GravityFollow::follow(sys, p0, real(0), 1/real(100), points, box); } LineSimplifier::Simplify(points, thresh); std::cout << "q{" << ++k << "}=[\n"; dump(points); std::cout << "];\n"; } { points.clear(); p0[0] = Xnull; p0[1] = 0; points.push_back(p0); p0[0] = Xnull; p0[1] = real(0.001); points.push_back(p0); GravityInt sys(grav, 2u); GravityFollow::follow(sys, p0, real(0), 1/real(100), points, box); LineSimplifier::Simplify(points, thresh); std::cout << "q{" << ++k << "}=[\n"; dump(points); std::cout << "];\n"; } for (int i = 1; i <= 5; ++i) { points.clear(); p0[0] = xmax; p0[1] = real(i == 1 ? 0.45 : (i == 2 ? 0.9 : (i == 3 ? 1.25 : (i == 4 ? 1.6 : 1.9)))); points.push_back(p0); if (!box(p0)) break; GravityInt sys(grav, 2u); GravityFollow::follow(sys, p0, real(0), 1/real(100), points, box); LineSimplifier::Simplify(points, thresh); std::cout << "q{" << ++k << "}=[\n"; dump(points); std::cout << "];\n"; } k = 0; for (int i = 0; i <= 90; i += 10) { points.clear(); real s, c; Math::sincosd(real(i), s, c); p0[0] = a * c; p0[1] = b * s; points.push_back(p0); if (i == 0) { p0[0] = E; p0[1] = 0; points.push_back(p0); } else if (i == 90) { p0[0] = 0; p0[1] = 0; points.push_back(p0); } else { GravityInt sys(grav, 0u); GravityFollow::follow(sys, p0, real(0), 1/real(100), points, box); } LineSimplifier::Simplify(points, thresh); std::cout << "qa{" << ++k << "}=[\n"; dump(points); std::cout << "];\n"; } k = 0; { GravityEval eval(grav, false, false, 0); for (int i = 0; i < ndiv; ++i) { points.clear(); real U = U0 - del * i; p0[0] = intpol(eval, X0, Xnull, U); p0[1] = 0; points.push_back(p0); GravityInt sysa(grav, 3u); GravityFollow::follow(sysa, p0, real(0), 1/real(100), points, box); LineSimplifier::Simplify(points, thresh); std::cout << "p{" << ++k << "}=[\n"; dump(points); std::cout << "];\n"; } for (int i = ndiv - 1;; --i) { points.clear(); real U = U0 - del * i; p0[0] = intpol(eval, Xnull + 3, Xnull, U); p0[1] = 0; if (!box(p0)) break; points.push_back(p0); GravityInt sysa(grav, 1u); GravityFollow::follow(sysa, p0, real(0), 1/real(100), points, box); LineSimplifier::Simplify(points, thresh); std::cout << "p{" << ++k << "}=[\n"; dump(points); std::cout << "];\n"; } } { GravityEval eval(grav, true, false, 0); for (int i = ndiv + 1;; ++i) { points.clear(); real U = U0 - del * i; p0[1] = intpol(eval, X0, Xnull, U); p0[0] = 0; if (!box(p0)) break; points.push_back(p0); GravityInt sysa(grav, 1u); GravityFollow::follow(sysa, p0, real(0), 1/real(100), points, box); LineSimplifier::Simplify(points, thresh); std::cout << "p{" << ++k << "}=[\n"; dump(points); std::cout << "];\n"; } } { real dy = real(0.02); GravityEval eval(grav, false, false, dy); points.clear(); p0[0] = Xnull; p0[1] = 0; points.push_back(p0); p0[0] = intpol(eval, Xnull - real(0.1), Xnull, Unull); p0[1] = dy; points.push_back(p0); GravityInt sysa(grav, 3u); GravityFollow::follow(sysa, p0, real(0), 1/real(100), points, box); LineSimplifier::Simplify(points, thresh); std::cout << "p{" << ++k << "}=[\n"; dump(points); std::cout << "];\n"; } { real dy = real(0.02); GravityEval eval(grav, false, false, dy); points.clear(); p0[0] = Xnull; p0[1] = 0; points.push_back(p0); p0[0] = intpol(eval, Xnull + real(0.1), Xnull, Unull); p0[1] = dy; points.push_back(p0); GravityInt sysa(grav, 1u); GravityFollow::follow(sysa, p0, real(0), 1/real(100), points, box); LineSimplifier::Simplify(points, thresh); std::cout << "p{" << ++k << "}=[\n"; dump(points); std::cout << "];\n"; } k = 0; { GravityEval eval(grav, false, false, 0); for (int i = 1;; ++i) { points.clear(); real U = U0 + 5 * del * i; if (U > Uc) break; p0[0] = intpol(eval, 0, X0, U); p0[1] = 0; points.push_back(p0); GravityInt sysa(grav, 3u); GravityFollow::follow(sysa, p0, real(0), 1/real(100), points, box); LineSimplifier::Simplify(points, thresh); std::cout << "pa{" << ++k << "}=[\n"; dump(points); std::cout << "];\n"; } } }