Program Listing for File body.cc¶
↰ Return to documentation for file (environment/body.cc)
#include "lupnt/environment/body.h"
#include <filesystem>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include "lupnt/conversions/frame_converter.h"
#include "lupnt/core/error.h"
#include "lupnt/core/file.h"
namespace lupnt {
namespace {
void PopulateBodyFromData(Body& body, const BodyData& data) {
body.id = data.id;
body.name = data.name;
body.GM = data.GM;
body.R = data.R;
body.omega = data.omega;
body.fixed_frame = data.fixed_frame;
body.inertial_frame = data.inertial_frame;
body.units = data.units;
}
BodyData ScaleBodyData(BodyData data, const UnitSystem& units) {
data.GM = units.GravitationalParameter(data.GM.val());
data.R = units.Length(data.R.val());
data.omega = units.AngularVelocity(data.omega.val());
data.units = units;
return data;
}
} // namespace
Body Body::Moon(int n_max, int m_max, std::string gravity_file) {
return Body::Moon(SI_UNITS, n_max, m_max, gravity_file);
}
Body Body::Moon(const UnitSystem& units, int n_max, int m_max, std::string gravity_file) {
Body moon;
PopulateBodyFromData(moon, GetBodyData(BodyId::MOON, units));
moon.use_gravity_field = (n_max > 1 && m_max > 1);
if (moon.use_gravity_field)
moon.gravity_field = ReadHarmonicGravityField<Real>(gravity_file, n_max, m_max, true, units);
return moon;
}
Body Body::Earth(int n_max, int m_max, std::string gravity_file) {
return Body::Earth(SI_UNITS, n_max, m_max, gravity_file);
}
Body Body::Earth(const UnitSystem& units, int n_max, int m_max, std::string gravity_file) {
Body earth;
PopulateBodyFromData(earth, GetBodyData(BodyId::EARTH, units));
earth.use_gravity_field = (n_max > 1 && m_max > 1);
if (earth.use_gravity_field)
earth.gravity_field = ReadHarmonicGravityField<Real>(gravity_file, n_max, m_max, true, units);
return earth;
}
Body Body::Sun() { return Body::Sun(SI_UNITS); }
Body Body::Sun(const UnitSystem& units) {
Body sun;
PopulateBodyFromData(sun, GetBodyData(BodyId::SUN, units));
sun.use_gravity_field = false;
return sun;
}
Body Body::Mars(int n_max, int m_max, std::string gravity_file) {
return Body::Mars(SI_UNITS, n_max, m_max, gravity_file);
}
Body Body::Mars(const UnitSystem& units, int n_max, int m_max, std::string gravity_file) {
Body mars;
PopulateBodyFromData(mars, GetBodyData(BodyId::MARS, units));
mars.use_gravity_field = (n_max > 1 && m_max > 1);
if (mars.use_gravity_field)
mars.gravity_field = ReadHarmonicGravityField<Real>(gravity_file, n_max, m_max, true, units);
return mars;
}
Body Body::Venus(int n_max, int m_max, std::string gravity_file) {
return Body::Venus(SI_UNITS, n_max, m_max, gravity_file);
}
Body Body::Venus(const UnitSystem& units, int n_max, int m_max, std::string gravity_file) {
Body venus;
PopulateBodyFromData(venus, GetBodyData(BodyId::VENUS, units));
venus.use_gravity_field = (n_max > 1 && m_max > 1);
if (venus.use_gravity_field)
venus.gravity_field = ReadHarmonicGravityField<Real>(gravity_file, n_max, m_max, true, units);
return venus;
}
Body Body::Jupiter() { return Body::Jupiter(SI_UNITS); }
Body Body::Jupiter(const UnitSystem& units) {
Body jupiter;
PopulateBodyFromData(jupiter, GetBodyData(BodyId::JUPITER, units));
jupiter.use_gravity_field = false;
return jupiter;
}
Body Body::Saturn() { return Body::Saturn(SI_UNITS); }
Body Body::Saturn(const UnitSystem& units) {
Body saturn;
PopulateBodyFromData(saturn, GetBodyData(BodyId::SATURN, units));
saturn.use_gravity_field = false;
return saturn;
}
Body Body::Uranus() { return Body::Uranus(SI_UNITS); }
Body Body::Uranus(const UnitSystem& units) {
Body uranus;
PopulateBodyFromData(uranus, GetBodyData(BodyId::URANUS, units));
uranus.use_gravity_field = false;
return uranus;
}
Body Body::Neptune() { return Body::Neptune(SI_UNITS); }
Body Body::Neptune(const UnitSystem& units) {
Body neptune;
PopulateBodyFromData(neptune, GetBodyData(BodyId::NEPTUNE, units));
neptune.use_gravity_field = false;
return neptune;
}
Body Body::CreateBody(BodyId body, int n, int m, std::string gravity_file) {
return Body::CreateBody(body, SI_UNITS, n, m, gravity_file);
}
Body Body::CreateBody(BodyId body, const UnitSystem& units, int n, int m,
std::string gravity_file) {
Logger::Debug(fmt::format("Creating body {}", magic_enum::enum_name(body).data()), "Body");
if (body == BodyId::SUN) return Body::Sun(units);
if (body == BodyId::EARTH) {
if (!gravity_file.empty()) return Body::Earth(units, n, m, gravity_file);
return Body::Earth(units, n, m);
}
if (body == BodyId::MOON) {
if (!gravity_file.empty()) return Body::Moon(units, n, m, gravity_file);
return Body::Moon(units, n, m);
}
if (body == BodyId::MARS) {
if (!gravity_file.empty()) return Body::Mars(units, n, m, gravity_file);
return Body::Mars(units, n, m);
}
if (body == BodyId::VENUS) {
if (!gravity_file.empty()) return Body::Venus(units, n, m, gravity_file);
return Body::Venus(units, n, m);
}
if (body == BodyId::JUPITER) return Body::Jupiter(units);
if (body == BodyId::SATURN) return Body::Saturn(units);
if (body == BodyId::URANUS) return Body::Uranus(units);
if (body == BodyId::NEPTUNE) return Body::Neptune(units);
LUPNT_CHECK(false, "Body not found", "Body");
}
BodyData GetBodyData(BodyId id) { return GetBodyData(id, SI_UNITS); }
BodyData GetBodyData(BodyId id, const UnitSystem& units) {
BodyData data;
switch (id) {
case BodyId::SUN:
data = {.id = BodyId::SUN,
.name = "SUN",
.GM = GM_SUN,
.R = R_SUN,
.fixed_frame = Frame::ICRF,
.inertial_frame = Frame::ICRF,
.flattening = SUN_F,
.omega = OMEGA_SUN};
break;
case BodyId::MERCURY:
data = {.id = BodyId::MERCURY,
.name = "MERCURY",
.GM = GM_MERCURY,
.R = R_MERCURY,
.fixed_frame = Frame::MERCURY_FIXED,
.inertial_frame = Frame::MERCURY_CI,
.flattening = MERCURY_F,
.omega = OMEGA_MERCURY};
break;
case BodyId::VENUS:
data = {.id = BodyId::VENUS,
.name = "VENUS",
.GM = GM_VENUS,
.R = R_VENUS,
.fixed_frame = Frame::VENUS_FIXED,
.inertial_frame = Frame::VENUS_CI,
.flattening = VENUS_F,
.omega = OMEGA_VENUS};
break;
case BodyId::EARTH:
data = {.id = BodyId::EARTH,
.name = "EARTH",
.GM = GM_EARTH,
.R = R_EARTH,
.fixed_frame = Frame::ITRF,
.inertial_frame = Frame::GCRF,
.flattening = WGS84_F,
.omega = OMEGA_EARTH};
break;
case BodyId::MOON:
data = {.id = BodyId::MOON,
.name = "MOON",
.GM = GM_MOON,
.R = R_MOON,
.fixed_frame = Frame::MOON_PA,
.inertial_frame = Frame::MOON_CI,
.flattening = MOON_F,
.omega = OMEGA_MOON};
break;
case BodyId::MARS:
data = {.id = BodyId::MARS,
.name = "MARS",
.GM = GM_MARS,
.R = R_MARS,
.fixed_frame = Frame::MARS_FIXED,
.inertial_frame = Frame::MARS_CI,
.flattening = MARS_F,
.omega = OMEGA_MARS};
break;
case BodyId::JUPITER:
data = {.id = BodyId::JUPITER,
.name = "JUPITER",
.GM = GM_JUPITER,
.R = R_JUPITER,
.fixed_frame = Frame::JUPITER_FIXED,
.inertial_frame = Frame::JUPITER_CI,
.flattening = JUPITER_F,
.omega = OMEGA_JUPITER};
break;
case BodyId::SATURN:
data = {.id = BodyId::SATURN,
.name = "SATURN",
.GM = GM_SATURN,
.R = R_SATURN,
.fixed_frame = Frame::SATURN_FIXED,
.inertial_frame = Frame::SATURN_CI,
.flattening = SATURN_F,
.omega = OMEGA_SATURN};
break;
case BodyId::URANUS:
data = {.id = BodyId::URANUS,
.name = "URANUS",
.GM = GM_URANUS,
.R = R_URANUS,
.fixed_frame = Frame::URANUS_FIXED,
.inertial_frame = Frame::URANUS_CI,
.flattening = URANUS_F,
.omega = OMEGA_URANUS};
break;
case BodyId::NEPTUNE:
data = {.id = BodyId::NEPTUNE,
.name = "NEPTUNE",
.GM = GM_NEPTUNE,
.R = R_NEPTUNE,
.fixed_frame = Frame::NEPTUNE_FIXED,
.inertial_frame = Frame::NEPTUNE_CI,
.flattening = NEPUTUNE_F,
.omega = OMEGA_NEPTUNE};
break;
default: throw std::runtime_error("Body not found");
}
return ScaleBodyData(data, units);
}
double GetBodyRadius(BodyId body) { return GetBodyRadius(body, SI_UNITS); }
double GetBodyRadius(BodyId body, const UnitSystem& units) {
BodyData data = GetBodyData(body, units);
return data.R.val();
}
double GetBodyGM(BodyId body) { return GetBodyGM(body, SI_UNITS); }
double GetBodyGM(BodyId body, const UnitSystem& units) {
BodyData data = GetBodyData(body, units);
return data.GM.val();
}
double GetBodyFlattening(BodyId body) {
BodyData data = GetBodyData(body);
return data.flattening.val();
}
double GetBodyOmega(BodyId body) { return GetBodyOmega(body, SI_UNITS); }
double GetBodyOmega(BodyId body, const UnitSystem& units) {
BodyData data = GetBodyData(body, units);
return data.omega.val();
}
std::string GetBodyName(BodyId body) {
BodyData data = GetBodyData(body);
return data.name;
}
Frame GetInertialFrameName(BodyId body) {
BodyData data = GetBodyData(body);
return data.inertial_frame;
}
Frame GetBodyFixedFrameName(BodyId body) {
BodyData data = GetBodyData(body);
return data.fixed_frame;
}
Body CreateBody(BodyId body, int n, int m) { return CreateBody(body, SI_UNITS, n, m); }
Body CreateBody(BodyId body, const UnitSystem& units, int n, int m) {
return Body::CreateBody(body, units, n, m);
}
double kron(int i, int j) { return (i == j) ? 1 : 0; }
double factprod(int n, int m) {
double f = 1.0;
for (int i = n - m + 1; i <= n + m; i++) {
f /= i;
}
return f;
}
template <typename T> GravityField<T> ReadHarmonicGravityField(const std::string& filename, int n,
int m, bool normalized) {
return ReadHarmonicGravityField<T>(filename, n, m, normalized, SI_UNITS);
}
template <typename T> GravityField<T> ReadHarmonicGravityField(const std::string& filename, int n,
int m, bool normalized,
const UnitSystem& units) {
GravityField<T> gravity_field;
gravity_field.units = units;
std::filesystem::path filepath = GetFilePath(filename);
std::ifstream file = OpenFile<std::ifstream>(filepath);
// Read header lines
std::string line;
while (std::getline(file, line)) {
if (line.find("POTFIELD") != std::string::npos) {
std::string potfield = line.substr(0, 8);
int n_max_in = std::stoi(line.substr(8, 3));
int m_max_in = std::stoi(line.substr(11, 3));
std::istringstream iss(line.substr(14));
double dummy1, GM, r, dummy2;
iss >> dummy1 >> GM >> r >> dummy2;
gravity_field.n_max = n_max_in;
gravity_field.m_max = m_max_in;
gravity_field.GM = units.GravitationalParameter(GM);
gravity_field.R = units.Length(r);
break;
}
}
gravity_field.n = n;
gravity_field.m = m;
// Initialize Eigen matrices with the specified maxN and maxM
if (n > gravity_field.n_max || m > gravity_field.m_max)
throw std::runtime_error("Degree and order exceed maximum values");
gravity_field.CS = Eigen::MatrixXd::Zero(n + 1, m + 1);
gravity_field.CS(0, 0) = 1.0; // C00 = 1.0
// Read coefficient lines
while (std::getline(file, line)) {
if (line.find("RECOEF") != std::string::npos) {
std::string recoef = line.substr(0, 8);
int n_in = std::stoi(line.substr(8, 3));
int m_in = std::stoi(line.substr(11, 3));
std::istringstream iss(line.substr(14));
double cnm, snm = 0.0;
iss >> cnm;
if (n_in >= n + 1) break;
if (m_in >= m + 1) continue;
if (m_in == 0) {
double N = (normalized) ? sqrt(2 * n_in + 1) : 1.0;
gravity_field.CS(n_in, m_in) = N * cnm;
} else {
double N = (normalized)
? sqrt((2 - kron(0, m_in)) * (2 * n_in + 1) * factprod(n_in, m_in))
: 1.0;
iss >> snm;
double C = N * cnm;
double S = N * snm;
gravity_field.CS(n_in, m_in) = C;
gravity_field.CS(m_in - 1, n_in) = S;
}
} else if (line.find("END") != std::string::npos) {
break;
}
}
file.close();
return gravity_field;
}
} // namespace lupnt