Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1044 get rid of all global variables and move to a config data class #1077

Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions src/sorcha/ephemeris/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@
create_ecl_to_eq_rotation_matrix,
)
from .simulation_data_files import (
DATA_FILE_LIST,
JPL_PLANETS,
JPL_SMALL_BODIES,
DE440S,
make_retriever,
)
from .simulation_geometry import (
Expand Down
77 changes: 5 additions & 72 deletions src/sorcha/ephemeris/simulation_data_files.py
Original file line number Diff line number Diff line change
@@ -1,75 +1,7 @@
import pooch

# Define variables for the file names

DE440S = "de440s.bsp"
EARTH_PREDICT = "earth_200101_990827_predict.bpc"
EARTH_HISTORICAL = "earth_620120_240827.bpc"
EARTH_HIGH_PRECISION = "earth_latest_high_prec.bpc"
JPL_PLANETS = "linux_p1550p2650.440"
JPL_SMALL_BODIES = "sb441-n16.bsp"
LEAP_SECONDS = "naif0012.tls"
META_KERNEL = "meta_kernel.txt"
OBSERVATORY_CODES = "ObsCodes.json"
OBSERVATORY_CODES_COMPRESSED = "ObsCodes.json.gz"
ORIENTATION_CONSTANTS = "pck00010.pck"

# Dictionary of filename: url
URLS = {
DE440S: "https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/de440s.bsp",
EARTH_PREDICT: "https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/earth_200101_990827_predict.bpc",
EARTH_HISTORICAL: "https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/earth_620120_240827.bpc",
EARTH_HIGH_PRECISION: "https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/earth_latest_high_prec.bpc",
JPL_PLANETS: "https://ssd.jpl.nasa.gov/ftp/eph/planets/Linux/de440/linux_p1550p2650.440",
JPL_SMALL_BODIES: "https://ssd.jpl.nasa.gov/ftp/eph/small_bodies/asteroids_de441/sb441-n16.bsp",
LEAP_SECONDS: "https://naif.jpl.nasa.gov/pub/naif/generic_kernels/lsk/naif0012.tls",
OBSERVATORY_CODES_COMPRESSED: "https://minorplanetcenter.net/Extended_Files/obscodes_extended.json.gz",
ORIENTATION_CONSTANTS: "https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/pck00010.tpc",
}

# Convenience list of all the file names
DATA_FILE_LIST = [
DE440S,
EARTH_PREDICT,
EARTH_HISTORICAL,
EARTH_HIGH_PRECISION,
JPL_PLANETS,
JPL_SMALL_BODIES,
LEAP_SECONDS,
META_KERNEL,
OBSERVATORY_CODES,
OBSERVATORY_CODES_COMPRESSED,
ORIENTATION_CONSTANTS,
]

# List of files that need to be downloaded
DATA_FILES_TO_DOWNLOAD = [
DE440S,
EARTH_PREDICT,
EARTH_HISTORICAL,
EARTH_HIGH_PRECISION,
JPL_PLANETS,
JPL_SMALL_BODIES,
LEAP_SECONDS,
OBSERVATORY_CODES_COMPRESSED,
ORIENTATION_CONSTANTS,
]

# List of kernels ordered from least to most precise - used to assemble META_KERNEL file
ORDERED_KERNEL_FILES = [
LEAP_SECONDS,
EARTH_HISTORICAL,
EARTH_PREDICT,
ORIENTATION_CONSTANTS,
DE440S,
EARTH_HIGH_PRECISION,
]

# Default Pooch registry to define which files will be tracked and retrievable
REGISTRY = {data_file: None for data_file in DATA_FILE_LIST}


def make_retriever(directory_path: str = None, registry: dict = REGISTRY) -> pooch.Pooch:
def make_retriever(sconfigs, directory_path: str = None) -> pooch.Pooch:
"""Helper function that will create a Pooch object to track and retrieve files.

Parameters
Expand All @@ -79,7 +11,8 @@ def make_retriever(directory_path: str = None, registry: dict = REGISTRY) -> poo
registry : dictionary, optional
A dictionary of file names to SHA hashes. Generally we'll not use SHA=None
because the files we're tracking change frequently. Default = REGISTRY

sconfigs: dataclass
Dataclass of configuration file arguments.
Returns
-------
: pooch
Expand All @@ -92,7 +25,7 @@ def make_retriever(directory_path: str = None, registry: dict = REGISTRY) -> poo
return pooch.create(
path=dir_path,
base_url="",
urls=URLS,
registry=registry,
urls=sconfigs.auxiliary.urls,
registry=sconfigs.auxiliary.registry,
retry_if_failed=25,
)
6 changes: 3 additions & 3 deletions src/sorcha/ephemeris/simulation_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,13 @@ def create_ephemeris(orbits_df, pointings_df, args, sconfigs):
ephemeris_csv_filename = os.path.join(args.outpath, args.output_ephemeris_file)

verboselog("Building ASSIST ephemeris object.")
ephem, gm_sun, gm_total = create_assist_ephemeris(args)
ephem, gm_sun, gm_total = create_assist_ephemeris(args, sconfigs)
verboselog("Furnishing SPICE kernels.")
furnish_spiceypy(args)
furnish_spiceypy(args, sconfigs)
verboselog("Generating ASSIST+REBOUND simulations.")
sim_dict = generate_simulations(ephem, gm_sun, gm_total, orbits_df, args)
pixel_dict = defaultdict(list)
observatories = Observatory(args)
observatories = Observatory(args, sconfigs)

output = StringIO()
in_memory_csv = writer(output)
Expand Down
27 changes: 15 additions & 12 deletions src/sorcha/ephemeris/simulation_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,10 @@
import numpy as np
import spiceypy as spice
from pooch import Decompress

from sorcha.utilities.sorchaConfigs import sorchaConfigs, auxiliaryConfigs
from sorcha.ephemeris.simulation_constants import RADIUS_EARTH_KM
from sorcha.ephemeris.simulation_geometry import ecliptic_to_equatorial
from sorcha.ephemeris.simulation_data_files import (
OBSERVATORY_CODES,
OBSERVATORY_CODES_COMPRESSED,
make_retriever,
)
from sorcha.ephemeris.simulation_data_files import make_retriever
from sorcha.ephemeris.orbit_conversion_utilities import universal_cartesian


Expand Down Expand Up @@ -129,35 +125,42 @@ def parse_orbit_row(row, epochJD_TDB, ephem, sun_dict, gm_sun, gm_total):
return tuple(np.concatenate([equatorial_coords, equatorial_velocities]))


default = sorchaConfigs()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are now global variables because the are outside of any function. Can you have oc_file=None and if it's None then you create default? within class Obsevatory

default.auxiliary = auxiliaryConfigs() # using the default observatory_codes in the auxiliaryConfigs class
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are now global variables because the are outside of any function. Can you have oc_file=None and if it's None then you create default? within class Obsevatory

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need all of default or can you just make an
auxConfig = auxiliaryConfigs() and send that around. If we're not using any part of sconfigs other than auxConfig bits i'd just send that through



class Observatory:
"""
Class containing various utility tools related to the calculation of the observatory position
"""

def __init__(self, args, oc_file=OBSERVATORY_CODES):
def __init__(self, args, sconfigs, oc_file=default.auxiliary.observatory_codes):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if sconfigs is None than you set oc_file- do you really need to set oc_file here with a global?

"""
Initialization method

Parameters
----------
args : dictionary or `sorchaArguments` object
dictionary of command-line arguments.
sconfigs: dataclass
Dataclass of configuration file arguments.
oc_file : str
Path for the file with observatory codes
"""
self.observatoryPositionCache = {} # previously calculated positions to speed up the process

if oc_file == OBSERVATORY_CODES:
retriever = make_retriever(args.ar_data_file_path)
if oc_file == sconfigs.auxiliary.observatory_codes:
retriever = make_retriever(sconfigs, args.ar_data_file_path)

# is the file available locally, if so, return the full path
if os.path.isfile(os.path.join(retriever.abspath, OBSERVATORY_CODES)):
obs_file_path = retriever.fetch(OBSERVATORY_CODES)
if os.path.isfile(os.path.join(retriever.abspath, sconfigs.auxiliary.observatory_codes)):
obs_file_path = retriever.fetch(sconfigs.auxiliary.observatory_codes)

# if the file is not local, download, and decompress it, then return the path.
else:
obs_file_path = retriever.fetch(
OBSERVATORY_CODES_COMPRESSED, processor=Decompress(name=OBSERVATORY_CODES)
sconfigs.auxiliary.observatory_codes_compressed,
processor=Decompress(name=sconfigs.auxiliary.observatory_codes),
)

else:
Expand Down
43 changes: 22 additions & 21 deletions src/sorcha/ephemeris/simulation_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,7 @@
import numpy as np

from sorcha.ephemeris.simulation_constants import *
from sorcha.ephemeris.simulation_data_files import (
make_retriever,
JPL_PLANETS,
JPL_SMALL_BODIES,
META_KERNEL,
ORDERED_KERNEL_FILES,
)
from sorcha.ephemeris.simulation_data_files import make_retriever

from sorcha.ephemeris.simulation_geometry import (
barycentricObservatoryRates,
Expand All @@ -32,9 +26,12 @@
from sorcha.utilities.generate_meta_kernel import build_meta_kernel_file


def create_assist_ephemeris(args) -> tuple:
def create_assist_ephemeris(args, sconfigs) -> tuple:
"""Build the ASSIST ephemeris object

Parameter
---------
sconfigs: dataclass
Dataclass of configuration file arguments.
Returns
---------
Ephem : ASSIST ephemeris obejct
Expand All @@ -46,9 +43,9 @@ def create_assist_ephemeris(args) -> tuple:
"""
pplogger = logging.getLogger(__name__)

retriever = make_retriever(args.ar_data_file_path)
planets_file_path = retriever.fetch(JPL_PLANETS)
small_bodies_file_path = retriever.fetch(JPL_SMALL_BODIES)
retriever = make_retriever(sconfigs, args.ar_data_file_path)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd consider only passing it the ephemeris config bits since we use this in bootstrap utility where we don't have a config file class loaded since it's doesn't read one in @bernardinelli what do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that makes sense. One way to do this would be to have an independent class or dictionary or something has only the ephemeris stuff, so that it can also be created independently in bootstrap. Or if sconfigs doesn't need to have everything set up, it'd accomplish the same thing (with no need to change the code)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently I commented and did not finish the review so no one could see me comments. Thanks @Little-Ryugu for pointing that out. - there is a smaller ephemeris auxiliary file class instance that is made within the sconfigs class so we should just be able to pass the ephemeris auxiliary file class here

planets_file_path = retriever.fetch(sconfigs.auxiliary.jpl_planets)
small_bodies_file_path = retriever.fetch(sconfigs.auxiliary.jpl_small_bodies)
ephem = Ephem(planets_path=planets_file_path, asteroids_path=small_bodies_file_path)
gm_sun = ephem.get_particle("Sun", 0).m
gm_total = sum(sorted([ephem.get_particle(i, 0).m for i in range(27)]))
Expand All @@ -59,27 +56,31 @@ def create_assist_ephemeris(args) -> tuple:
return ephem, gm_sun, gm_total


def furnish_spiceypy(args):
def furnish_spiceypy(args, sconfigs):
"""
Builds the SPICE kernel, downloading the required files if needed
Parameters
-----------
sconfigs: dataclass
Dataclass of configuration file arguments.
"""
# The goal here would be to download the spice kernel files (if needed)
# Then call spice.furnish(<filename>) on each of those files.

pplogger = logging.getLogger(__name__)

retriever = make_retriever(args.ar_data_file_path)
retriever = make_retriever(sconfigs, args.ar_data_file_path)

for kernel_file in ORDERED_KERNEL_FILES:
for kernel_file in sconfigs.auxiliary.ordered_kernel_files:
retriever.fetch(kernel_file)

# check if the META_KERNEL file exists. If it doesn't exist, create it.
if not os.path.exists(os.path.join(retriever.abspath, META_KERNEL)):
build_meta_kernel_file(retriever)
if not os.path.exists(os.path.join(retriever.abspath, sconfigs.auxiliary.meta_kernel)):
build_meta_kernel_file(sconfigs, retriever)

# try to get the META_KERNEL file. If it's not there, error out.
try:
meta_kernel = retriever.fetch(META_KERNEL)
meta_kernel = retriever.fetch(sconfigs.auxiliary.meta_kernel)
except ValueError:
pplogger.error(
"ERROR: furnish_spiceypy: Must create meta_kernel.txt by running `bootstrap_sorcha_data_files` on the command line."
Expand Down Expand Up @@ -181,11 +182,11 @@ def precompute_pointing_information(pointings_df, args, sconfigs):
pointings_df : pandas dataframe
The original dataframe with several additional columns of precomputed values.
"""
ephem, _, _ = create_assist_ephemeris(args)
ephem, _, _ = create_assist_ephemeris(args, sconfigs)

furnish_spiceypy(args)
furnish_spiceypy(args, sconfigs)
obsCode = sconfigs.simulation.ar_obs_code
observatories = Observatory(args)
observatories = Observatory(args, sconfigs)

# vectorize the calculation to get x,y,z vector from ra/dec
vectors = ra_dec2vec(
Expand Down
13 changes: 5 additions & 8 deletions src/sorcha/utilities/generate_meta_kernel.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import os
import pooch

from sorcha.ephemeris.simulation_data_files import (
META_KERNEL,
ORDERED_KERNEL_FILES,
)

"""
An example output from running `build_meta_kernel_file` might look like
Expand All @@ -29,29 +25,30 @@
"""


def build_meta_kernel_file(retriever: pooch.Pooch) -> None:
def build_meta_kernel_file(sconfigs, retriever: pooch.Pooch) -> None:
"""Builds a specific text file that will be fed into `spiceypy` that defines
the list of spice kernel to load, as well as the order to load them.

Parameters
----------
retriever : pooch
Pooch object that maintains the registry of files to download

sconfigs: dataclass
Dataclass of configuration file arguments.
Returns
---------
None
"""
# build meta_kernel file path
meta_kernel_file_path = os.path.join(retriever.abspath, META_KERNEL)
meta_kernel_file_path = os.path.join(retriever.abspath, sconfigs.auxiliary.meta_kernel)

# build a meta_kernel.txt file
with open(meta_kernel_file_path, "w") as meta_file:
meta_file.write("\\begindata\n\n")
meta_file.write(f"PATH_VALUES = ('{retriever.abspath}')\n\n")
meta_file.write("PATH_SYMBOLS = ('A')\n\n")
meta_file.write("KERNELS_TO_LOAD=(\n")
for file_name in ORDERED_KERNEL_FILES:
for file_name in sconfigs.auxiliary.ordered_kernel_files:
shortened_file_name = _build_file_name(retriever.abspath, retriever.fetch(file_name))
meta_file.write(f" '{shortened_file_name}',\n")
meta_file.write(")\n\n")
Expand Down
12 changes: 5 additions & 7 deletions src/sorcha/utilities/retrieve_ephemeris_data_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@
import pooch

from functools import partial
from sorcha.ephemeris.simulation_data_files import (
make_retriever,
DATA_FILES_TO_DOWNLOAD,
DATA_FILE_LIST,
)
from sorcha.ephemeris.simulation_data_files import make_retriever
from sorcha.utilities.generate_meta_kernel import build_meta_kernel_file


Expand All @@ -35,7 +31,7 @@ def _decompress(fname, action, pup): # pragma: no cover
pooch.Decompress(method="auto", name=os.path.splitext(fname)[0]).__call__(fname, action, pup)


def _remove_files(retriever: pooch.Pooch) -> None: # pragma: no cover
def _remove_files(sconfigs, retriever: pooch.Pooch) -> None: # pragma: no cover
"""Utility to remove all the files tracked by the pooch retriever. This includes
the decompressed ObservatoryCodes.json file as well as the META_KERNEL file
that are created after downloading the files in the DATA_FILES_TO_DOWNLOAD
Expand All @@ -45,9 +41,11 @@ def _remove_files(retriever: pooch.Pooch) -> None: # pragma: no cover
------------
retriever : pooch
Pooch object that maintains the registry of files to download.
sconfigs: dataclass
Dataclass of configuration file arguments.
"""

for file_name in DATA_FILE_LIST:
for file_name in sconfigs.auxiliary.data_file_list:
file_path = retriever.fetch(file_name)
print(f"Deleting file: {file_path}")
os.remove(file_path)
Expand Down
Loading
Loading