Program Listing for File kernels.cc¶
↰ Return to documentation for file (data/kernels.cc)
#include "lupnt/data/kernels.h"
#include <filesystem>
#include <mutex>
#include <string>
#include "lupnt/conversions/frame_converter.h"
#include "lupnt/conversions/time_conversions.h"
#include "lupnt/data/kernels.h"
namespace lupnt {
namespace EphemID {
int MERCURY_BARYCENTER = 0;
int VENUS_BARYCENTER = 1;
int EARTH_MOON_BARYCENTER = 2;
int EMB = EARTH_MOON_BARYCENTER;
int MARS_BARYCENTER = 3;
int JUPITER_BARYCENTER = 4;
int SATURN_BARYCENTER = 5;
int URANUS_BARYCENTER = 6;
int NEPTUNE_BARYCENTER = 7;
int PLUTO_BARYCENTER = 8;
int MOON = 9;
int SUN = 10;
int EARTH_NUTATIONS = 11;
int MOON_MANTLE_LIBRATIONS = 12;
int MOON_MANTLE_ANGULAR_VELOCITY = 13;
int TT_TDB = 14;
}; // namespace EphemID
std::map<BodyId, int> naif2ephemId = {
{BodyId::MERCURY, EphemID::MERCURY_BARYCENTER},
{BodyId::MERCURY_BARYCENTER, EphemID::MERCURY_BARYCENTER},
{BodyId::VENUS, EphemID::VENUS_BARYCENTER},
{BodyId::VENUS_BARYCENTER, EphemID::VENUS_BARYCENTER},
{BodyId::MARS, EphemID::MARS_BARYCENTER},
{BodyId::MARS_BARYCENTER, EphemID::MARS_BARYCENTER},
{BodyId::EARTH_MOON_BARYCENTER, EphemID::EARTH_MOON_BARYCENTER},
{BodyId::JUPITER, EphemID::JUPITER_BARYCENTER},
{BodyId::JUPITER_BARYCENTER, EphemID::JUPITER_BARYCENTER},
{BodyId::SATURN, EphemID::SATURN_BARYCENTER},
{BodyId::SATURN_BARYCENTER, EphemID::SATURN_BARYCENTER},
{BodyId::URANUS, EphemID::URANUS_BARYCENTER},
{BodyId::URANUS_BARYCENTER, EphemID::URANUS_BARYCENTER},
{BodyId::NEPTUNE, EphemID::NEPTUNE_BARYCENTER},
{BodyId::NEPTUNE_BARYCENTER, EphemID::NEPTUNE_BARYCENTER},
{BodyId::PLUTO_BARYCENTER, EphemID::PLUTO_BARYCENTER},
{BodyId::MOON, EphemID::MOON},
{BodyId::SUN, EphemID::SUN},
};
namespace {
Vec6 StateFromSI(const Vec6& rv, const UnitSystem& units) {
Vec6 out;
out.head(3) = rv.head(3) / units.length;
out.tail(3) = rv.tail(3) * (units.time / units.length);
return out;
}
Vec3 PositionFromSI(const Vec3& r, const UnitSystem& units) { return r / units.length; }
} // namespace
struct EphemerisHeaderData {
int KSIZE;
int NCOEFF;
std::string info;
std::string start_info;
std::string final_info;
double jd_tdb_start;
double jd_tdb_end;
double step;
std::vector<int> coeff_offset;
std::vector<int> n_coeffs;
std::vector<int> n_subintervals;
std::map<std::string, double> constants;
std::vector<int> n_properties = {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 1};
double emrat;
};
struct EphemerisBlock {
double jd_tdb_start;
double jd_tdb_end;
double coeff[MAXCOEFF];
};
struct EphemerisData {
EphemerisHeaderData header;
double jd_tdb_start;
double jd_tdb_end;
std::vector<EphemerisBlock> blocks;
};
UniquePtr<EphemerisData> ephemeris_data;
std::mutex kernels_mutex;
double ParseDouble(const std::string& str) {
std::string s = str;
for (char& c : s)
if (c == 'D') c = 'e';
return std::stod(s);
}
void ParseGroup1010(std::ifstream& infile, EphemerisHeaderData& data) {
std::string line, empty_line;
std::getline(infile, empty_line);
std::getline(infile, data.info);
std::getline(infile, data.start_info);
std::getline(infile, data.final_info);
std::getline(infile, empty_line);
}
void ParseGroup1030(std::ifstream& infile, EphemerisHeaderData& data) {
std::string line, empty_line;
std::getline(infile, empty_line);
std::getline(infile, line);
std::istringstream iss(line);
iss >> data.jd_tdb_start >> data.jd_tdb_end >> data.step;
std::getline(infile, empty_line);
}
std::vector<std::string> ParseGroup1040(std::ifstream& infile, EphemerisHeaderData& data) {
(void)data;
std::string line, empty_line;
std::getline(infile, empty_line);
int num_constants;
infile >> num_constants;
std::string constant;
std::vector<std::string> constant_names;
for (int i = 0; i < num_constants; ++i) {
infile >> constant;
constant_names.push_back(constant);
}
std::getline(infile, empty_line);
std::getline(infile, empty_line);
return constant_names;
}
std::vector<double> ParseGroup1041(std::ifstream& infile, EphemerisHeaderData& data) {
(void)data;
std::string line, empty_line;
std::getline(infile, empty_line);
int num_values;
infile >> num_values;
std::string value_str;
std::vector<double> constant_values;
for (int i = 0; i < num_values; ++i) {
infile >> value_str;
constant_values.push_back(ParseDouble(value_str));
}
std::getline(infile, empty_line);
std::getline(infile, empty_line);
return constant_values;
}
void ParseGroup1050(std::ifstream& infile, EphemerisHeaderData& data) {
std::string line, empty_line;
std::getline(infile, empty_line);
std::vector<int> start_locations, n_coeffs, n_subintervals;
// Read the next three lines and store the values in respective vectors
for (int i = 0; i < 3; ++i) {
std::getline(infile, line);
std::istringstream iss(line);
int value;
while (iss >> value) {
if (i == 0) {
start_locations.push_back(value);
} else if (i == 1) {
n_coeffs.push_back(value);
} else if (i == 2) {
n_subintervals.push_back(value);
}
}
}
std::getline(infile, empty_line);
data.coeff_offset = start_locations;
data.n_coeffs = n_coeffs;
data.n_subintervals = n_subintervals;
}
void ReadEphemerisHeaderFile(const std::filesystem::path& filepath, EphemerisHeaderData& data) {
std::ifstream infile(filepath);
if (!infile.is_open()) {
std::string msg = "Unable to open file: " + filepath.string();
throw std::runtime_error(msg);
}
std::string line, empty_line;
std::vector<std::string> constant_names;
std::vector<double> constant_values;
while (std::getline(infile, line)) {
if (line.find("KSIZE") != std::string::npos) {
std::istringstream iss(line);
std::string temp;
iss >> temp >> data.KSIZE >> temp >> data.NCOEFF;
} else if (line.find("GROUP 1010") != std::string::npos) {
ParseGroup1010(infile, data);
} else if (line.find("GROUP 1030") != std::string::npos) {
ParseGroup1030(infile, data);
} else if (line.find("GROUP 1040") != std::string::npos) {
constant_names = ParseGroup1040(infile, data);
} else if (line.find("GROUP 1041") != std::string::npos) {
constant_values = ParseGroup1041(infile, data);
} else if (line.find("GROUP 1050") != std::string::npos) {
ParseGroup1050(infile, data);
}
}
infile.close();
for (size_t i = 0; i < constant_names.size(); ++i)
data.constants[constant_names[i]] = constant_values[i];
data.emrat = data.constants["EMRAT"];
}
// void ReadEphemerisCoefficientsFile(const std::filesystem::path& filepath,
// EphemerisHeaderData& data) {
// ephemeris_data = MakeUnique<EphemerisData>();
// ephemeris_data->header = data;
// ephemeris_data->blocks.clear();
// std::ifstream infile(filepath);
// if (!infile.is_open()) {
// std::string msg = "Unable to open file: " + filepath.string();
// throw std::runtime_error(msg);
// }
// std::cout << "Loading ephemeris coefficients from " + filepath.string() + " ..." <<
// std::endl; std::string value_str, line; int block = 1; int block_in, tmp; while
// (std::getline(infile, line)) {
// std::istringstream iss(line);
// iss >> block_in >> tmp;
// std::cout << "\r Reading block " << block_in << std::flush;
// std::cout << "Block expected: " << block << ", Block read: " << block_in << std::endl;
// if (block_in != block) throw std::runtime_error("Block number mismatch");
// EphemerisBlock block_data;
// infile >> value_str;
// block_data.jd_tdb_start = ParseDouble(value_str);
// infile >> value_str;
// block_data.jd_tdb_end = ParseDouble(value_str);
// for (int i = 0; i < data.NCOEFF - 2; ++i) {
// infile >> value_str;
// block_data.coeff[i] = ParseDouble(value_str);
// }
// ephemeris_data->blocks.push_back(block_data);
// std::getline(infile, line); // Finish the line
// block++;
// }
// infile.close();
// ephemeris_data->jd_tdb_start = ephemeris_data->blocks[0].jd_tdb_start;
// ephemeris_data->jd_tdb_end = ephemeris_data->blocks.back().jd_tdb_end;
// }
void ReadEphemerisCoefficientsFile(const std::filesystem::path& filepath,
EphemerisHeaderData& data) {
ephemeris_data = MakeUnique<EphemerisData>();
ephemeris_data->header = data;
ephemeris_data->blocks.clear();
std::ifstream infile(filepath);
if (!infile.is_open()) {
std::string msg = "Unable to open file: " + filepath.string();
throw std::runtime_error(msg);
}
// std::cout << "Loading ephemeris coefficients from "
// << filepath.string() << " ..." << std::endl;
std::string value_str;
int block = 1;
int block_in, tmp;
// Read until we fail to read a new block header
while (infile >> block_in >> tmp) {
// std::cout << " Reading block " << block_in
// << " (expected " << block << ")\n";
// Basic sanity check on block numbering
if (block_in != block) {
throw std::runtime_error("Block number mismatch");
}
EphemerisBlock block_data;
// Start / end JD
infile >> value_str;
block_data.jd_tdb_start = ParseDouble(value_str);
infile >> value_str;
block_data.jd_tdb_end = ParseDouble(value_str);
// Coefficients (NCOEFF includes the 2 JD entries)
for (int i = 0; i < data.NCOEFF - 2; ++i) {
infile >> value_str;
block_data.coeff[i] = ParseDouble(value_str);
}
ephemeris_data->blocks.push_back(block_data);
++block;
}
infile.close();
if (ephemeris_data->blocks.empty()) {
throw std::runtime_error("No ephemeris blocks read from file: " + filepath.string());
}
ephemeris_data->jd_tdb_start = ephemeris_data->blocks.front().jd_tdb_start;
ephemeris_data->jd_tdb_end = ephemeris_data->blocks.back().jd_tdb_end;
}
Vec2 ComputePolynomialPosVel(Real x, const double* scale, const double* coeff, int offset,
int num) {
Real x2, w0 = 0., w1 = 0., dw0 = 0., dw1 = 0., tmp;
x = (x - scale[0]) / scale[1];
x2 = x * 2.;
while (--num) {
tmp = dw1;
dw1 = dw0;
dw0 = w0 * 2. + dw0 * x2 - tmp;
tmp = w1;
w1 = w0;
w0 = coeff[offset + num] + (x2 * w0 - tmp);
}
Real f = coeff[offset] + (x * w0 - w1);
Real df = (w0 + x * dw0 - dw1) / scale[1];
return Vec2(f, df) * M_KM;
}
Real ComputePolynomialPos(Real x, const double* scale, const double* coeff, int offset, int num) {
Real x2, w0 = 0., w1 = 0., tmp;
x = (x - scale[0]) / scale[1];
x2 = x * 2.;
while (--num) {
tmp = w1;
w1 = w0;
w0 = coeff[offset + num] + (x2 * w0 - tmp);
}
Real f = coeff[offset] + (x * w0 - w1);
return f * M_KM;
}
void LoadEphemerisData() {
std::lock_guard<std::mutex> lock(kernels_mutex);
if (ephemeris_data) return; // Data already loaded
EphemerisHeaderData data;
ReadEphemerisHeaderFile(GetAsciiKernelDir() / "de440" / "header.440", data);
ReadEphemerisCoefficientsFile(GetAsciiKernelDir() / "de440" / "ascp01950.440", data);
}
struct CacheKernelData {
Real t_tdb = NAN;
Vec6 rv;
bool compute_vel;
};
static std::array<CacheKernelData, 15> cache_kernel_data;
static std::array<std::mutex, 15> cache_mutex;
Vec6 GetKernelData(Real t_tdb, int id, bool compute_vel = true) {
if (!ephemeris_data) LoadEphemerisData();
// Check cache
// std::unique_lock<std::mutex> lock(cache_mutex[id]);
// CacheKernelData cache = cache_kernel_data[id];
// if (abs(cache.t_tdb - t_tdb) < EPS && cache.compute_vel == compute_vel) {
// lock.unlock();
// return cache.rv;
// }
// lock.unlock();
Real jd_tdb = TimeToJd(t_tdb);
// Block
if (jd_tdb < ephemeris_data->jd_tdb_start || jd_tdb > ephemeris_data->jd_tdb_end) {
std::string msg = "Kernels not loaded for the requested time\n";
msg += "- Start: " + std::to_string(ephemeris_data->jd_tdb_start) + "\n";
msg += "- End: " + std::to_string(ephemeris_data->jd_tdb_end) + "\n";
msg += "- Requested: " + std::to_string(jd_tdb.val());
throw std::runtime_error(msg);
}
double Dt = ephemeris_data->header.step;
int i = int((jd_tdb - ephemeris_data->jd_tdb_start) / Dt);
if (!(i >= 0 && i < (int)ephemeris_data->blocks.size())) {
std::string msg = "Block index out of range\n";
msg += "- Index: " + std::to_string(i) + "\n";
msg += "- Number of blocks: " + std::to_string(ephemeris_data->blocks.size());
throw std::runtime_error(msg);
}
EphemerisBlock& block = ephemeris_data->blocks[i];
EphemerisHeaderData& header = ephemeris_data->header;
// Subinterval
double Dt_subint = Dt / header.n_subintervals[id];
int j = int((jd_tdb - block.jd_tdb_start) / Dt_subint);
int n_coeff = header.n_coeffs[id] * header.n_properties[id];
int offset = header.coeff_offset[id] + j * n_coeff - 3.;
double jd_tdb_subint = block.jd_tdb_start + j * Dt_subint;
double scale[2] = {jd_tdb_subint + Dt_subint / 2., Dt_subint / 2.}; // center and half width
scale[0] = JdToTime(scale[0]).val();
scale[1] *= SECS_DAY;
Vec6 rv;
for (int i = 0; i < 3; i++) {
if (compute_vel) {
Vec2 rv_tmp
= ComputePolynomialPosVel(t_tdb, scale, block.coeff, offset, header.n_coeffs[id]);
rv[i] = rv_tmp(0);
rv[i + 3] = rv_tmp(1);
} else {
rv[i] = ComputePolynomialPos(t_tdb, scale, block.coeff, offset, header.n_coeffs[id]);
rv[i + 3] = 0.;
}
offset += header.n_coeffs[id];
}
// cache.t_tdb = t_tdb;
// cache.rv = rv;
// cache.compute_vel = compute_vel;
// lock.lock();
// if (t_tdb > cache_kernel_data[id].t_tdb + EPS) {
// cache_kernel_data[id] = cache;
// }
// lock.unlock();
return rv;
} // namespace lupnt
Vec6 GetLunarMantleData(Real t_tdb, bool compute_vel) {
return GetKernelData(t_tdb, EphemID::MOON_MANTLE_LIBRATIONS, compute_vel) / M_KM;
}
MatX6 GetLunarMantleData(VecX t_tdb, bool compute_vel) {
MatX6 rv(t_tdb.size(), 6);
for (int i = 0; i < t_tdb.size(); i++) {
rv.row(i) = GetLunarMantleData(t_tdb(i), compute_vel) / M_KM;
}
return rv;
}
Vec6 GetBodyPosVel(Real t_tdb, BodyId center, BodyId target, Frame frame) {
if (!ephemeris_data) LoadEphemerisData();
if (center == target) return Vec6::Zero();
Vec6 rv_center = Vec6::Zero();
Vec6 rv_target = Vec6::Zero();
double emr = ephemeris_data->header.emrat;
// Earth-Moon system
if (center == BodyId::EARTH && target == BodyId::MOON) {
rv_target = GetKernelData(t_tdb, EphemID::MOON);
} else if (center == BodyId::MOON && target == BodyId::EARTH) {
rv_center = GetKernelData(t_tdb, EphemID::MOON);
} else if (center == BodyId::EMB && target == BodyId::MOON) {
rv_target = GetKernelData(t_tdb, EphemID::MOON);
rv_center = rv_target / (1. + emr);
} else if (center == BodyId::MOON && target == BodyId::EMB) {
rv_center = GetKernelData(t_tdb, EphemID::MOON);
rv_target = rv_center / (1. + emr);
} else if (center == BodyId::EMB && target == BodyId::EARTH) {
rv_center = GetKernelData(t_tdb, EphemID::MOON) / (1. + emr);
} else if (center == BodyId::EARTH && target == BodyId::EMB) {
rv_target = GetKernelData(t_tdb, EphemID::MOON) / (1. + emr);
} else {
if (center == BodyId::EARTH || center == BodyId::MOON || target == BodyId::EARTH
|| target == BodyId::MOON) {
Vec6 rv_emb = GetKernelData(t_tdb, EphemID::EMB);
Vec6 rv_moon = GetKernelData(t_tdb, EphemID::MOON);
Vec6 rv_earth = rv_emb - rv_moon / (1. + emr);
if (center == BodyId::EARTH)
rv_center = rv_earth;
else if (center == BodyId::MOON)
rv_center = rv_earth + rv_moon;
if (target == BodyId::EARTH)
rv_target = rv_earth;
else if (target == BodyId::MOON)
rv_target = rv_earth + rv_moon;
}
if (center != BodyId::EARTH && center != BodyId::MOON && center != BodyId::SSB)
rv_center = GetKernelData(t_tdb, naif2ephemId.at(center));
if (target != BodyId::EARTH && target != BodyId::MOON && target != BodyId::SSB)
rv_target = GetKernelData(t_tdb, naif2ephemId.at(target));
}
if (frame == Frame::GCRF) return rv_target - rv_center;
rv_target = ConvertFrame(t_tdb, rv_target, Frame::GCRF, frame);
rv_center = ConvertFrame(t_tdb, rv_center, Frame::GCRF, frame);
return rv_target - rv_center;
}
Vec3 GetBodyPos(Real t_tdb, BodyId center, BodyId target, Frame frame) {
return GetBodyPosVel(t_tdb, center, target, frame).head(3);
}
Vec6 GetBodyPosVel(Real t_tdb, BodyId target, Frame frame) {
LUPNT_CHECK(frame != Frame::UNDEFINED, "Frame is undefined", "GetBodyPosVel");
return GetBodyPosVel(t_tdb, frame_centers.at(frame), target, frame);
}
Vec6 GetBodyPosVel(Real t_tdb, BodyId center, BodyId target, Frame frame, const UnitSystem& units,
CoordinateScale scale) {
Vec6 rv = GetBodyPosVel(t_tdb, center, target, frame);
rv = ScaleStateForCoordinateScale(rv, CoordinateScale::TDB, scale);
return StateFromSI(rv, units);
}
Vec6 GetBodyPosVel(Real t_tdb, BodyId target, Frame frame, const UnitSystem& units,
CoordinateScale scale) {
LUPNT_CHECK(frame != Frame::UNDEFINED, "Frame is undefined", "GetBodyPosVel");
return GetBodyPosVel(t_tdb, frame_centers.at(frame), target, frame, units, scale);
}
Vec3 GetBodyPos(Real t_tdb, BodyId target, Frame frame) {
LUPNT_CHECK(frame != Frame::UNDEFINED, "Frame is undefined", "GetBodyPos");
return GetBodyPos(t_tdb, frame_centers.at(frame), target, frame);
}
Vec3 GetBodyPos(Real t_tdb, BodyId center, BodyId target, Frame frame, const UnitSystem& units,
CoordinateScale scale) {
Vec3 r = GetBodyPos(t_tdb, center, target, frame);
r = ScalePositionForCoordinateScale(r, CoordinateScale::TDB, scale);
return PositionFromSI(r, units);
}
Vec3 GetBodyPos(Real t_tdb, BodyId target, Frame frame, const UnitSystem& units,
CoordinateScale scale) {
LUPNT_CHECK(frame != Frame::UNDEFINED, "Frame is undefined", "GetBodyPos");
return GetBodyPos(t_tdb, frame_centers.at(frame), target, frame, units, scale);
}
MatX6 GetBodyPosVel(const VecX& t_tdb, BodyId target, Frame frame) {
MatX6 rv(t_tdb.size(), 6);
for (int i = 0; i < t_tdb.size(); i++) {
rv.row(i) = GetBodyPosVel(t_tdb(i), target, frame);
}
return rv;
}
MatX3 GetBodyPos(const VecX& t_tdb, BodyId target, Frame frame) {
MatX3 r(t_tdb.size(), 3);
for (int i = 0; i < t_tdb.size(); i++) {
r.row(i) = GetBodyPos(t_tdb(i), target, frame);
}
return r;
}
MatX6 GetBodyPosVel(const VecX& t_tdb, BodyId center, BodyId target, Frame frame) {
MatX6 rv(t_tdb.size(), 6);
for (int i = 0; i < t_tdb.size(); i++) {
rv.row(i) = GetBodyPosVel(t_tdb(i), center, target, frame);
}
return rv;
}
MatX6 GetBodyPosVel(const VecX& t_tdb, BodyId target, Frame frame, const UnitSystem& units,
CoordinateScale scale) {
MatX6 rv(t_tdb.size(), 6);
for (int i = 0; i < t_tdb.size(); i++) {
rv.row(i) = GetBodyPosVel(t_tdb(i), target, frame, units, scale);
}
return rv;
}
MatX6 GetBodyPosVel(const VecX& t_tdb, BodyId center, BodyId target, Frame frame,
const UnitSystem& units, CoordinateScale scale) {
MatX6 rv(t_tdb.size(), 6);
for (int i = 0; i < t_tdb.size(); i++) {
rv.row(i) = GetBodyPosVel(t_tdb(i), center, target, frame, units, scale);
}
return rv;
}
MatX3 GetBodyPos(const VecX& t_tdb, BodyId center, BodyId target, Frame frame) {
MatX3 r(t_tdb.size(), 3);
for (int i = 0; i < t_tdb.size(); i++) {
r.row(i) = GetBodyPos(t_tdb(i), center, target, frame);
}
return r;
}
MatX3 GetBodyPos(const VecX& t_tdb, BodyId target, Frame frame, const UnitSystem& units,
CoordinateScale scale) {
MatX3 r(t_tdb.size(), 3);
for (int i = 0; i < t_tdb.size(); i++) {
r.row(i) = GetBodyPos(t_tdb(i), target, frame, units, scale);
}
return r;
}
MatX3 GetBodyPos(const VecX& t_tdb, BodyId center, BodyId target, Frame frame,
const UnitSystem& units, CoordinateScale scale) {
MatX3 r(t_tdb.size(), 3);
for (int i = 0; i < t_tdb.size(); i++) {
r.row(i) = GetBodyPos(t_tdb(i), center, target, frame, units, scale);
}
return r;
}
double GetTtTdbDifference(double t_tdb) {
(void)t_tdb;
throw std::runtime_error("Not implemented");
}
} // namespace lupnt