Hi all, I noticed a pretty significant discrepancy between the XML and normal EOP loaders in Orekit while using the EopXmlLoader and thought I’d share. I tried posting the issue on the gitlab but couldn’t get access.
The Orekit XML EOP loader currently creates EOPEntry instances with dX/dY (and ddPsi/ddEps) set to NaN when those fields are absent from predicted EOP records.
This is in contrast to the normal EOP loader, which set’s uninitialized values to 0.
This occurs in the most recent finals2000A.all.xml file on November 3rd.
This bug causes all frame conversions that need to go via CIRF → GCRF (or vice-versa) to fail with a “divide by 0” arithmetic error, including any calls to observation.estimate().
The minimum reproducible example with the latest XML EOP is:
void testXmlEopLoader()
{
// Create the XML loader
DataProvidersManager dataProvidersManager = new DataProvidersManager();
dataProvidersManager.addDefaultProviders();
LazyLoadedEop lazyLoadedEop = new LazyLoadedEop(dataProvidersManager);
TimeScales timeScales = new LazyLoadedTimeScales(lazyLoadedEop);
lazyLoadedEop.addDefaultEOP2000HistoryLoaders(
"",
"^(:?finals2000A|eopc04_\\d\\d_IAU2000)\\..*\\.xml$",
"",
"",
"",
"",
timeScales::getUTC
);
// Create some basic data context we can load the eop data and frames from
GeoMagneticFields geoMagneticFields = new LazyLoadedGeoMagneticFields(dataProvidersManager);
GravityFields gravityFields = new LazyLoadedGravityFields(dataProvidersManager, timeScales.getTT());
CelestialBodies celestialBodies = new LazyLoadedCelestialBodies(dataProvidersManager, timeScales, Frame.getRoot());
DataContext dataContext = new CompositeDataContext(
timeScales,
new LazyLoadedFrames(lazyLoadedEop, timeScales, celestialBodies),
celestialBodies, gravityFields, geoMagneticFields
);
// Create the problematic conversion frames
Frame cirf = dataContext.getFrames().getCIRF(IERSConventions.IERS_2010, false);
Frame gcrf = dataContext.getFrames().getGCRF();
// Go through the history, try the transform, if it's null print it out
EOPHistory eopHistory = dataContext.getFrames().getEOPHistory(IERSConventions.IERS_2010, false);
assert !eopHistory.getEntries().isEmpty();
for (EOPEntry eopEntry : eopHistory.getEntries())
{
Transform transform = cirf.getTransformTo(gcrf, eopEntry.getDate());
if (Double.isNaN(transform.getCartesian().getPosition().getNorm()))
{
System.out.println("%s (dx = %s, dy = %s):%s"
.formatted(eopEntry.getDate(),
eopEntry.getDx(),
eopEntry.getDy(),
transform.getCartesian())
);
}
}
}
If you run this code you’ll notice that the getTransformTo fails 2 days before the dx/dy runs out, I presume this is because of the use of interpolation over 3 values. I came up with a hotfix that overrides the LazyLoadedEop.getEOPHistory() function and basically just rebuilds the history replacing the NaNs as it goes, but I thought I would bring this to the attention of the maintainers.
Looking a bit in EopXmlLoader it appears resetEOPData() resets the values to NaN. There is a check in the else if (qName.equals(EOP_SET_ELT)) code block that checks if dpsi is NaN and if it is it uses dx,dy. I think maybe the simplest solution is to add another check here, that either checks the nro array directly after creation for NaNs and setting it to [0,0] if it is, or that checks both dpsi and dx/dy before deciding which to use to construct nro.
Hopefully this is enough information to reproduce and eliminate it. I am also very happy to create the MR myself if allowed if that means getting the fix in sooner!