Skip to content

Commit

Permalink
deleted: data/targets.csv
Browse files Browse the repository at this point in the history
	modified:   src/forcedphot/horizons_interface.py
  • Loading branch information
szilac committed Jul 9, 2024
1 parent f26505b commit b3af98c
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 28 deletions.
3 changes: 0 additions & 3 deletions data/targets.csv

This file was deleted.

117 changes: 92 additions & 25 deletions src/forcedphot/horizons_interface.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,37 @@
import numpy as np
import logging
import time
import pandas as pd

import astropy.units as u
import numpy as np
import pandas as pd
from astropy.time import Time
from astroquery.jplhorizons import Horizons
from local_dataclasses import EphemerisData, QueryInput, QueryResult

from local_dataclasses import QueryInput, EphemerisData, QueryResult

class HorizonsInterface:
"""
A class for querying ephemeris data from JPL Horizons for celestial objects.
This class provides methods to query ephemeris data for single time ranges
or multiple objects from a CSV file. It uses the astropy Horizons module
to interact with the JPL Horizons system.
Attributes:
DEFAULT_OBSERVER_LOCATION (str): The default observer location code ('X05' for Rubin).
logger (logging.Logger): Logger for the class.
observer_location (str): The observer location code used for queries.
Methods:
query_single_range(query: QueryInput) -> QueryResult:
Query ephemeris for a single time range.
query_ephemeris_from_csv(csv_filename: str, observer_location=DEFAULT_OBSERVER_LOCATION):
Query ephemeris for multiple celestial objects from a CSV file and save results to ECSV files.
The class handles large queries by splitting them into smaller time ranges
when necessary to avoid exceeding JPL Horizons' limit of 10,000 instances per query."""

# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
Expand All @@ -21,7 +44,11 @@ def __init__(self, observer_location=DEFAULT_OBSERVER_LOCATION):
Initialize the HorizonsInterface with an optional observer location.
Parameters:
observer_location (str): The observer location code. Default is 'X05' (Rubin location).
----------
self : HorizonsInterface
The instance of the class being initialized.
observer_location : str, optional
The observer location code. Default is 'X05' (Rubin location).
"""
self.observer_location = observer_location

Expand All @@ -30,20 +57,33 @@ def query_single_range(self, query: QueryInput) -> QueryResult:
"""
Query ephemeris for a single time range.
Parameters:
query (QueryInput): The query parameters.
Returns:
QueryResult: The queried ephemeris data.
Parameters
----------
self : HorizonsInterface
The instance of the class calling this method.
query : QueryInput
he query parameters containing target, start time, end time, and step.
Returns
-------
QueryResult or None
The queried ephemeris data wrapped in a QueryResult object if successful,
or None if an error occurs.
Raises
------
Exception
If an error occurs during the query process. The error is logged,
but not re-raised.
"""
try:
start_time = time.time()
obj = Horizons(id_type='smallbody', id=query.target, location=self.observer_location,
obj = Horizons(id_type='smallbody', id=query.target, location=self.observer_location,
epochs={'start': query.start.iso, 'stop': query.end.iso, 'step': query.step})
ephemeris = obj.ephemerides()
end_time = time.time()
self.logger.info(f"Query for range {query.start} to {query.end} successful for target \
{query.target}. Time taken: {end_time - start_time:.2f} seconds.")
self.logger.info(f"Query for range {query.start} to {query.end} successful for target"
f"{query.target}. Time taken: {end_time - start_time:.2f} seconds.")

ephemeris_data = EphemerisData(
datetime_jd=Time(ephemeris['datetime_jd'], format='jd'),
Expand All @@ -63,17 +103,42 @@ def query_single_range(self, query: QueryInput) -> QueryResult:

return QueryResult(query.target, query.start, query.end, ephemeris_data)
except Exception as e:
self.logger.error(f"An error occurred during query for range {query.start} to {query.end} for target {query.target}: {e}")
self.logger.error(f"An error occurred during query for range {query.start} to {query.end}"
f"for target {query.target}: {e}")
return None

@classmethod
def query_ephemeris_from_csv(cls, csv_filename: str, observer_location=DEFAULT_OBSERVER_LOCATION):
"""
Query ephemeris for multiple celestial objects from JPL Horizons based on a CSV file and save the data to ECSV files.
Parameters:
csv_filename (str): The filename of the input CSV file containing target, start time, end time, and step.
observer_location (str): The observer location code. Default is 'X05' (Rubin location).
Query ephemeris for multiple celestial objects from JPL Horizons based on a CSV file and save
the data to CSV files.
Parameters
----------
cls : type
The class itself, as this is a class method.
csv_filename : str
The filename of the input CSV file containing target, start time, end time, and step.
observer_location : str, optional
The observer location code. Default is 'X05' (Rubin location).
Returns
-------
None
This method doesn't return any value, but saves the queried data to CSV files.
Raises
------
Exception
If an error occurs during the CSV processing or querying. The error is logged,
but not re-raised.
Notes
-----
- The input CSV file should have columns for target, start time, end time, and step.
- The method creates a separate CSV file for each target in the input file.
- If a query would exceed 10,000 instances, it is automatically split into multiple queries.
- The method logs information about the query process and any errors that occur.
"""
try:
total_start_time = time.time()
Expand All @@ -94,14 +159,15 @@ def query_ephemeris_from_csv(cls, csv_filename: str, observer_location=DEFAULT_O

# Calculate the total number of instances
total_days = (query.end - query.start).jd
step_hours = float(query.step[:-1])
step_days = step_hours / 24.0
step_hours = float(query.step[:-1])
step_days = step_hours / 24.0
max_instances = 10000
step_instances = total_days / step_days

# Check if multiple queries are needed
if step_instances > max_instances:
cls.logger.info(f"Total instances exceed 10,000 for target {query.target}. Splitting the queries.")
cls.logger.info(f"Total instances exceed 10,000 for target {query.target}. Splitting"
f"the queries.")

time_splits = int(step_instances // max_instances) + 1
time_ranges = [(query.start + i * (total_days / time_splits) * u.day,
Expand Down Expand Up @@ -155,14 +221,15 @@ def query_ephemeris_from_csv(cls, csv_filename: str, observer_location=DEFAULT_O
cls.logger.info(f"Ephemeris data successfully saved to {output_filename}")

total_end_time = time.time()
cls.logger.info(f"Total time taken for processing the CSV file: {total_end_time - total_start_time:.2f} seconds.")
cls.logger.info(f"Total time taken for processing the CSV file:"
f"{total_end_time - total_start_time:.2f} seconds.")

except Exception as e:
cls.logger.error(f"An error occurred while processing the CSV file: {e}")

# Example usage
if __name__ == "__main__":
HorizonsInterface.query_ephemeris_from_csv('targets.csv')
HorizonsInterface.query_ephemeris_from_csv('./data/targets.csv')

# Different observer location
# HorizonsInterface.query_ephemeris_from_csv('targets.csv', observer_location='500') # Example: Geocentric
# HorizonsInterface.query_ephemeris_from_csv('targets.csv', observer_location='500')

0 comments on commit b3af98c

Please sign in to comment.