From 8ec888732fc7045c1a8167db7209ba3ddd2ea702 Mon Sep 17 00:00:00 2001 From: Mike Rosseel Date: Sun, 22 Mar 2026 20:09:08 +0100 Subject: [PATCH] fix: handle MPC comet data with unparseable orbital elements Skyfield's load_comets_dataframe() can return string dtypes for numeric columns when the MPC data file contains rows it can't parse (e.g. the MPEC 2026-F34 batch of 27 historical comets with malformed fields). Even a few bad rows cause pandas to fall back to dtype=object for the entire perihelion_year column, which then crashes comet_orbit() for *every* comet with a numpy UFuncNoLoopError. Fix: force numeric coercion with pd.to_numeric() after loading, drop rows with unparseable essential fields, and add a try/except safety net around individual comet processing. Co-Authored-By: Claude Opus 4.6 --- python/PiFinder/comets.py | 40 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/python/PiFinder/comets.py b/python/PiFinder/comets.py index 9430094ff..a3185c054 100644 --- a/python/PiFinder/comets.py +++ b/python/PiFinder/comets.py @@ -4,6 +4,7 @@ from skyfield.constants import GM_SUN_Pitjeva_2005_km3_s2 as GM_SUN from PiFinder.utils import Timer, comet_file from PiFinder.calc_utils import sf_utils +import pandas as pd import requests import os import logging @@ -187,6 +188,39 @@ def calc_comets( with open(comet_file, "rb") as f: comets_df = mpc.load_comets_dataframe(f) + # Ensure orbital element columns are numeric — MPC data sometimes + # contains rows that Skyfield's parser can't handle (e.g. MPEC + # 2026-F34 historical comets), which causes the entire column to + # become dtype=object (strings). Skyfield's comet_orbit() then + # crashes on every comet with a numpy UFuncNoLoopError. + numeric_cols = [ + "perihelion_year", + "perihelion_month", + "perihelion_day", + "argument_of_perihelion_degrees", + "longitude_of_ascending_node_degrees", + "inclination_degrees", + "eccentricity", + "perihelion_distance_au", + "magnitude_g", + "magnitude_k", + ] + for col in numeric_cols: + if col in comets_df.columns: + comets_df[col] = pd.to_numeric(comets_df[col], errors="coerce") + + # Drop rows where essential orbital elements couldn't be parsed + essential = [ + "perihelion_year", + "perihelion_month", + "perihelion_day", + "eccentricity", + "perihelion_distance_au", + ] + comets_df = comets_df.dropna( + subset=[c for c in essential if c in comets_df.columns] + ) + # Report progress after file loading (roughly 33% of setup time) if progress_callback: progress_callback(1) @@ -208,7 +242,11 @@ def calc_comets( for comet in comet_data: if comet_names is None or comet[0] in comet_names: - result = process_comet(comet, dt) + try: + result = process_comet(comet, dt) + except Exception as e: + logger.warning(f"Skipping comet {comet[0]}: {e}") + result = {} if result: comet_dict[result["name"]] = result