use std::path::Path;
use crate::error::{Error, Result};
use crate::od::{DetermineEntry, DetermineResults};
use super::path_to_cstring;
#[derive(Debug, Clone, PartialEq)]
pub struct FitSummaryRow {
pub object_id: String,
pub status: String,
pub converged: bool,
pub iterations: u32,
pub n_obs: usize,
pub n_selected: usize,
pub rms_ra_arcsec: f64,
pub rms_dec_arcsec: f64,
pub reduced_chi2: f64,
pub fit_acceptable: bool,
pub extrapolation_acceptable: bool,
pub selection_fraction_ok: bool,
pub selection_fraction: f64,
pub selection_fraction_threshold: f64,
pub selected_arc_coverage_ok: bool,
pub selected_arc_days: f64,
pub selected_arc_fraction: f64,
pub selected_arc_fraction_threshold: f64,
pub trailing_gap_ok: bool,
pub trailing_gap_days: f64,
pub trailing_gap_threshold_days: f64,
pub fractional_sigma_a_ok: bool,
pub fractional_sigma_a: f64,
pub fractional_sigma_a_threshold: f64,
pub solve_for_width: u32,
pub error: Option<String>,
}
impl FitSummaryRow {
pub fn from_entry(entry: &DetermineEntry) -> Self {
match &entry.outcome {
Ok(fit) => {
let a = &fit.acceptability;
Self {
object_id: entry.object_id.clone(),
status: "delivered".to_string(),
converged: fit.converged,
iterations: fit.iterations,
n_obs: fit.summary.num_obs,
n_selected: fit.summary.num_selected,
rms_ra_arcsec: fit.summary.rms_ra_arcsec,
rms_dec_arcsec: fit.summary.rms_dec_arcsec,
reduced_chi2: fit.summary.reduced_chi2,
fit_acceptable: a.fit_acceptable,
extrapolation_acceptable: a.extrapolation_acceptable,
selection_fraction_ok: a.selection_fraction_ok,
selection_fraction: a.selection_fraction_value,
selection_fraction_threshold: a.selection_fraction_threshold,
selected_arc_coverage_ok: a.selected_arc_coverage_ok,
selected_arc_days: a.selected_arc_days_value,
selected_arc_fraction: a.selected_arc_fraction_value,
selected_arc_fraction_threshold: a.selected_arc_fraction_threshold,
trailing_gap_ok: a.trailing_gap_ok,
trailing_gap_days: a.trailing_gap_days_value,
trailing_gap_threshold_days: a.trailing_gap_threshold,
fractional_sigma_a_ok: a.fractional_sigma_a_ok,
fractional_sigma_a: a.fractional_sigma_a_value,
fractional_sigma_a_threshold: a.fractional_sigma_a_threshold,
solve_for_width: fit
.solved_covariance
.as_ref()
.map(|sc| sc.width as u32)
.unwrap_or(6),
error: None,
}
}
Err(failure) => Self {
object_id: entry.object_id.clone(),
status: "failed".to_string(),
converged: false,
iterations: 0,
n_obs: 0,
n_selected: 0,
rms_ra_arcsec: f64::NAN,
rms_dec_arcsec: f64::NAN,
reduced_chi2: f64::NAN,
fit_acceptable: false,
extrapolation_acceptable: false,
selection_fraction_ok: false,
selection_fraction: f64::NAN,
selection_fraction_threshold: f64::NAN,
selected_arc_coverage_ok: false,
selected_arc_days: f64::NAN,
selected_arc_fraction: f64::NAN,
selected_arc_fraction_threshold: f64::NAN,
trailing_gap_ok: false,
trailing_gap_days: f64::NAN,
trailing_gap_threshold_days: f64::NAN,
fractional_sigma_a_ok: false,
fractional_sigma_a: f64::NAN,
fractional_sigma_a_threshold: f64::NAN,
solve_for_width: 0,
error: Some(failure.message.clone()),
},
}
}
pub fn from_results(results: &DetermineResults) -> Vec<Self> {
results.iter().map(Self::from_entry).collect()
}
}
fn rows_to_ffi_array(
rows: &[FitSummaryRow],
keep: &mut Vec<std::ffi::CString>,
) -> Result<Vec<empyrean_sys::EmpyreanFitSummary>> {
fn str_ptr(s: &str, keep: &mut Vec<std::ffi::CString>) -> *const std::ffi::c_char {
match std::ffi::CString::new(s) {
Ok(c) => {
let p = c.as_ptr();
keep.push(c);
p
}
Err(_) => std::ptr::null(),
}
}
Ok(rows
.iter()
.map(|r| empyrean_sys::EmpyreanFitSummary {
object_id: str_ptr(&r.object_id, keep),
status: str_ptr(&r.status, keep),
converged: u8::from(r.converged),
iterations: r.iterations,
n_obs: r.n_obs,
n_selected: r.n_selected,
rms_ra_arcsec: r.rms_ra_arcsec,
rms_dec_arcsec: r.rms_dec_arcsec,
reduced_chi2: r.reduced_chi2,
fit_acceptable: u8::from(r.fit_acceptable),
extrapolation_acceptable: u8::from(r.extrapolation_acceptable),
selection_fraction_ok: u8::from(r.selection_fraction_ok),
selection_fraction: r.selection_fraction,
selection_fraction_threshold: r.selection_fraction_threshold,
selected_arc_coverage_ok: u8::from(r.selected_arc_coverage_ok),
selected_arc_days: r.selected_arc_days,
selected_arc_fraction: r.selected_arc_fraction,
selected_arc_fraction_threshold: r.selected_arc_fraction_threshold,
trailing_gap_ok: u8::from(r.trailing_gap_ok),
trailing_gap_days: r.trailing_gap_days,
trailing_gap_threshold_days: r.trailing_gap_threshold_days,
fractional_sigma_a_ok: u8::from(r.fractional_sigma_a_ok),
fractional_sigma_a: r.fractional_sigma_a,
fractional_sigma_a_threshold: r.fractional_sigma_a_threshold,
solve_for_width: r.solve_for_width,
error: match &r.error {
Some(m) => str_ptr(m, keep),
None => std::ptr::null(),
},
})
.collect())
}
fn write_via<F>(path: &Path, rows: &[FitSummaryRow], c_call: F) -> Result<()>
where
F: FnOnce(*const std::ffi::c_char, *const empyrean_sys::EmpyreanFitSummary, usize) -> i32,
{
let path_c = path_to_cstring(path)?;
let mut keep: Vec<std::ffi::CString> = Vec::new();
let ffi = rows_to_ffi_array(rows, &mut keep)?;
let code = c_call(path_c.as_ptr(), ffi.as_ptr(), ffi.len());
drop(keep); if code != 0 {
return Err(Error::capture(code));
}
Ok(())
}
pub fn write_fit_summary_parquet(path: impl AsRef<Path>, rows: &[FitSummaryRow]) -> Result<()> {
write_via(path.as_ref(), rows, |p, ptr, n| unsafe {
empyrean_sys::empyrean_fit_summary_write_parquet(p, ptr, n)
})
}
pub fn write_fit_summary_json(path: impl AsRef<Path>, rows: &[FitSummaryRow]) -> Result<()> {
write_via(path.as_ref(), rows, |p, ptr, n| unsafe {
empyrean_sys::empyrean_fit_summary_write_json(p, ptr, n)
})
}
pub fn write_fit_summary_csv(path: impl AsRef<Path>, rows: &[FitSummaryRow]) -> Result<()> {
write_via(path.as_ref(), rows, |p, ptr, n| unsafe {
empyrean_sys::empyrean_fit_summary_write_csv(p, ptr, n)
})
}