#time #coordinates #astronomy #degree #random #ephemerides

flare

A lightweight library to perform basic astronomical calculations, inspired by Python's Astropy syntax

2 unstable releases

new 0.2.0 Apr 24, 2025
0.1.0 Sep 27, 2024

#258 in Machine learning

Download history 2/week @ 2025-01-31 6/week @ 2025-02-07 1/week @ 2025-03-07 7/week @ 2025-03-14

109 downloads per month

MIT license

73KB
624 lines

Flare

flare is a lightweight library designed to perform basic astronomical calculations, inspired by Python's Astropy syntax.

Table of Contents

Installation

To include flare in your project, add the following to your Cargo.toml:

[dependencies]
flare = "0.1.0"

Features & Usage

You can do a couple of different things with flare. We recommend reading the documentation that you can find here.

Here are some examples of what you can do with flare:

  • Handle dates in multiple standard & astronomical formats:

    use chrono::{Utc, TimeZone};
    use flare::Time;
    
    fn main() {
        let time = Time::new(2021, 6, 21, 12, 0, 0);
    
        // You can convert a time (which is timezone-agnostic, in UTC)
        // to a variety of formats:
        let time_jd = time.to_jd();
        println!("The Julian Date of the time is: {}", time_jd);
    
        // and, you can do the exact opposite, create a `Time` object
        // from a JD, MJD, GST, UTC, or ISO string:
        let time_from_jd = Time::from_jd(2459376.0);
        println!("The time from the Julian Date is: {}", time_from_jd);
    
        // you can also get the current time:
        let current_time = Time::now();
        println!("{}", current_time);
    
        // we rely on the fantastic `chrono` library for date-time handling
        // so you can convert a chrono::DateTime<Utc> object to a `Time` object
        let chrono_utc = Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap();
        let time = Time::from_utc(chrono_utc);
    
        // you can print a date in any of the supported formats:
        println!("{}", time.to_string(Some("mjd")));
    }
    
  • Calculate the angular separation between two targets/objects in the sky:

    use flare::Target;
    
    fn main() {
        let target1 = Target::new(6.374817, 20.242942, Some("A"));
        let target2 = Target::new(6.374817, 21.242942, Some("B"));
    
        let separation = target1.separation(&target2);
    
        println!("The angular separation between the two targets is: {}", separation);
    }
    
  • Given an observer on earth, find the airmass of a target (at a given time):

    use flare::{Target, Observer, Time};
    
    fn main() {
        let observer = Observer::new(30.0, 45.0, 1800.0, Some("A"));
        println!("{}", observer);
    
        let target = Target::new(6.374817, 20.242942, Some("B"));
        println!("{}", target);
    
        let time = Time::new(2020, 12, 21, 12, 0, 0);
        println!("{}", time);
    
        let airmass = target.airmass(&observer, &time);
    
        println!("Airmass at {} is: {}", time, airmass);
    }
    
  • For an observer, find the next sunrise & sunset times (after a given time):

    use flare::{Observer, Time};
    
    fn main() {
        let observer = Observer::new(33.3633675, -116.8361345, 1870.0, None);
        println!("{}", observer);
    
        let time = Time::new(2024, 9, 10, 3, 0, 0);
        println!("{}", time);
    
        let (sunrise, sunset) = observer.sun_set_time(Some(&time), None);
    
        println!("Next sunrise: {}", sunrise);
        println!("Next sunset: {}", sunset);
    
        // the time is optional, in which case the current time is used
        let (sunrise, sunset) = observer.sun_set_time(None, None);
        println!("Sunrise: {}, Sunset: {}", sunrise, sunset);
    
        // You can also specify at what altitude the sun should be considered to have risen/set, as an angle in degrees
        let (sunrise, sunset) = observer.sun_set_time(Some(&time), Some(0.0));
    
        println!("Sunrise: {}, Sunset: {} (at 0.0 deg)", sunrise, sunset);
    
        // Otherwise, you can get astronomical, nautical, and civil twilight times:
        let (sunrise, sunset) = observer.twilight_astronomical(Some(&time));
        println!("Sunrise: {}, Sunset: {} (astronomical)", sunrise, sunset);
    
        let (sunrise, sunset) = observer.twilight_nautical(Some(&time));
        println!("Sunrise: {}, Sunset: {} (nautical)", sunrise, sunset);
    
        let (sunrise, sunset) = observer.twilight_civil(Some(&time));
        println!("Sunrise: {}, Sunset: {} (civil)", sunrise, sunset);
    }
    
  • Work with photometry, in mag and flux space:

    use flare::phot::{mag_to_flux, flux_to_mag, limmag_to_fluxerr, fluxerr_to_limmag, ZP};
    
    fn main() {
        let mag = 20.0;
        let magerr = 0.1;
        let limmag = 21.0;
    
        // mag to flux
        let (flux, fluxerr) = mag_to_flux(mag, magerr, ZP);
        println!("flux: {}, fluxerr: {}", flux, fluxerr);
    
        // flux to mag
        let (mag, magerr) = flux_to_mag(flux, fluxerr, ZP);
        println!("mag: {}, mag: {}", mag, magerr);
    
        // limiting mag to fluxerr, at 5-sigma
        let fluxerr = limmag_to_fluxerr(limmag, ZP, 5.0);
        println!("fluxerr (from limmag): {}", fluxerr);
    
        // fluxerr to limiting mag at 5-sigma
        let limmag = fluxerr_to_limmag(fluxerr, ZP, 5.0);
        println!("limmag (from fluxerr, at 5-sigma): {}", limmag);
    
        // ZP = 23.9, but you can specify your own zero point for any of the above,
        // just like you can specify a different sigma value
        let limmag = limmag_to_fluxerr(limmag, 25.0, 3.0);
        println!("fluxerr (from limmag, 3-sigma & ZP=25.0): {}", limmag);
    }
    
  • Use a cosmology (custom, or one of the built-in ones) to compute distances:

    use flare::Cosmo;
    
    fn main() {
        let z = 0.1;
    
        // you can use one of the built-in cosmologies
        let cosmo = Cosmo::planck18();
    
        let luminosity_distance = cosmo.luminosity_distance(z);
        println!("Luminosity distance: {}", luminosity_distance);
    
        let dm = cosmo.dm(z);
        println!("Distance modulus: {}", dm);
    
        let angular_diameter_distance = cosmo.angular_diameter_distance(z);
        println!("Angular diameter distance: {}", angular_diameter_distance);
    
        // for example, you could this to get the absolute magnitude of a target
        let apparent_mag = 20.0;
        let abs_mag = apparent_mag - dm;
        println!("{} mag at z={} is {}", apparent_mag, z, abs_mag);
    
        // You can also create your own cosmology
        let cosmology = Cosmo::new(67.66, 0.3103, 0.6897, Some("mycosmo"));
    
        let z = 0.0246;
        let dm = cosmology.dm(z);
        println!("Distance modulus at z={} (using custom cosmology {}) is {}", z, cosmology.name.unwrap(), dm);
    }
    
  • Look for a periodic in a lightcurve:

    use flare::period::{fpw, freq_grid, get_best_freq};
    use rand::Rng;
    
    fn main() {
        // let's simulate a sinusoidal with a period of 6 hours, with some noise
        // measured at N random times over a 6 years period.
        let n_points = 1000;
        let period = 0.25; // in days
    
        // we don't want evenly spaced data, so let's randomly sample times over 6 years
        // so we grab random floats between 0 and 6*365 days (6 years)
        let mut rng = rand::rng();
        let mut t: Vec<f64> = Vec::with_capacity(n_points);
        for _ in 0..n_points {
            // generate a random time between 0 and 6*365 days
            let random_time = rng.random_range(0.0..(6.0 * 365.0));
            t.push(random_time);
        }
        // Sort the time array to ensure it's in ascending order
        t.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
    
        // Now generate the sinusoidal light curve with some noise
        let noise_level = 0.1; // noise in magnitude
        let y: Vec<f64> = t
            .iter()
            .map(|&time| {
                // Simulate a sinusoidal light curve with some noise
                let true_value = (2.0 * std::f64::consts::PI * time / period).sin();
                let noise = rng.random_range(-noise_level..noise_level);
                true_value + noise
            })
            .collect();
    
        // Define frequency grid, with a min freq of 10 days
        // and a maximum frequency of 5 minutes
        let f_min = 1.0 / (10.0 * 24.0 * 60.0);
        let f_max = 1.0 / (5.0 / 60.0);
        let freqs = freq_grid(&t, Some(f_min), Some(f_max), 3);
        println!("Using {} frequencies", freqs.len());
    
        // the FPW algorithm requires a number of bins, typically between 5 and 20
        // in this crate, we've set a maximum of 20 bins. This is enough for most
        // applications, and using a set number of bins let's us allocated memory to the stack
        // which is much faster than heap allocation.
        let n_bins = 10; // choose a number of bins between 5 and 20
        let fpw_stats = fpw(&t, &y, &vec![0.1; n_points], &freqs, n_bins);
    
        // Find the best period using the FPW statistic
        let (best_freq, best_stat) = get_best_freq(&freqs, &fpw_stats);
    
        let period = 1.0 / best_freq * 24.0; // convert from frequency in days to a period in hours
    
        println!("Period: {:.2} hours, statistic: {:.2}", period, best_stat);
    }
    

Contributing

We welcome contributions! No specific guidelines yet, but feel free to open an issue or a PR. Keep in mind that the goal is to keep this library lightweight and focused on basic astronomical calculations. We are not trying to replicate the functionality of Astropy in Rust.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Dependencies

~1.5MB
~25K SLoC