Skip to content

Commit 631f367

Browse files
committed
Refactor spice module for improved code clarity and functionality
- Update dataset retrieval in Subsetter class to use `at` for better performance. - Enhance `show_loaded_kernels` function to return a list of loaded kernel paths, improving usability. - Clean up formatting and structure in spicer.py for better readability and maintainability. - Introduce pytest configuration in conftest.py to manage slow tests effectively.
1 parent 8234deb commit 631f367

File tree

4 files changed

+92
-25
lines changed

4 files changed

+92
-25
lines changed

src/planetarypy/spice/archived_kernels.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ def payload(self):
197197
"One of start/stop is outside the supported date-range. See `datasets`."
198198
)
199199
p = {
200-
"dataset": datasets.loc[self.mission, "path"],
200+
"dataset": datasets.at[self.mission, "path"],
201201
"start": self.start.iso,
202202
"stop": self.stop.iso,
203203
"action": "Subset",

src/planetarypy/spice/generic_kernels.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,15 @@ def load_generic_kernels():
6969

7070

7171
def show_loaded_kernels():
72-
"""Print overview of all currently loaded SPICE kernels."""
72+
"""Show overview of all currently loaded SPICE kernels.
73+
74+
Returns
75+
-------
76+
list
77+
List of loaded kernel paths relative to KERNEL_STORAGE
78+
"""
7379
count = spice.ktotal("all")
80+
loaded_kernels = []
7481
if count == 0:
7582
print("No kernels loaded at this time.")
7683
else:
@@ -79,8 +86,10 @@ def show_loaded_kernels():
7986
out = spice.kdata(which, "all", 100, 100, 100)
8087
print("Position:", which)
8188
p = Path(out[0])
82-
print("Path", p.relative_to(KERNEL_STORAGE))
89+
rel_path = p.relative_to(KERNEL_STORAGE)
90+
print("Path", rel_path)
8391
print("Type:", out[1])
8492
print("Source:", out[2])
8593
print("Handle:", out[3])
86-
# print("Found:", out[4])
94+
loaded_kernels.append(str(rel_path))
95+
return loaded_kernels

src/planetarypy/spice/spicer.py

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
11
"""SPICE manager to make simple SPICE calculations simple."""
22

3-
__all__ = ['Radii', 'make_axis_rotation_matrix', 'IllumAngles', 'SurfaceCoords', 'Spicer', 'MarsSpicer', 'TritonSpicer',
4-
'EnceladusSpicer', 'PlutoSpicer', 'EarthSpicer', 'MoonSpicer', 'Mars_Ls_now']
3+
__all__ = [
4+
"Radii",
5+
"make_axis_rotation_matrix",
6+
"IllumAngles",
7+
"SurfaceCoords",
8+
"Spicer",
9+
"MarsSpicer",
10+
"TritonSpicer",
11+
"EnceladusSpicer",
12+
"PlutoSpicer",
13+
"EarthSpicer",
14+
"MoonSpicer",
15+
"Mars_Ls_now",
16+
]
517

618

719
import datetime as dt
@@ -10,17 +22,16 @@
1022

1123
import dateutil.parser as tparser
1224
import numpy as np
25+
import planets
1326
import spiceypy as spice
1427
from astropy import units as u
1528
from astropy.constants import L_sun
1629
from astropy.visualization import quantity_support
1730
from matplotlib import pyplot as plt
1831
from traitlets import Enum, HasTraits, Unicode
1932

20-
import planets
2133
from ..exceptions import MissingParameterError, SPointNotSetError
22-
from .kernels import load_generic_kernels
23-
34+
from .generic_kernels import load_generic_kernels
2435

2536
load_generic_kernels()
2637

@@ -49,7 +60,9 @@ def make_axis_rotation_matrix(direction, angle):
4960

5061
eye = np.eye(3, dtype=np.float64)
5162
ddt = np.outer(d, d)
52-
skew = np.array([[0, d[2], -d[1]], [-d[2], 0, d[0]], [d[1], -d[0], 0]], dtype=np.float64)
63+
skew = np.array(
64+
[[0, d[2], -d[1]], [-d[2], 0, d[0]], [d[1], -d[0], 0]], dtype=np.float64
65+
)
5366

5467
mtx = ddt + np.cos(angle) * (eye - ddt) + np.sin(angle) * skew
5568
return mtx
@@ -99,8 +112,9 @@ def demission(self):
99112

100113
def __str__(self):
101114
s = "Print-out precision is '.2f'\n"
102-
s += "Phase: {0:.2f}\nSolar Incidence: {1:.2f}\nEmission: {2:.2f}".format(self.dphase, self.dsolar,
103-
self.demission)
115+
s += "Phase: {0:.2f}\nSolar Incidence: {1:.2f}\nEmission: {2:.2f}".format(
116+
self.dphase, self.dsolar, self.demission
117+
)
104118
return s
105119

106120
def __repr__(self):
@@ -155,7 +169,9 @@ def dlat(self):
155169

156170
def __str__(self):
157171
"Return string with useful summary."
158-
return "Longitude: {0}\nLatitude: {1}\nRadius: {2}".format(self.dlon, self.dlat, self.radius)
172+
return "Longitude: {0}\nLatitude: {1}\nRadius: {2}".format(
173+
self.dlon, self.dlat, self.radius
174+
)
159175

160176
def __repr__(self):
161177
return self.__str__()
@@ -295,7 +311,7 @@ def center_to_sun(self):
295311
def solar_constant(self):
296312
"float : With global value L_s, solar constant at coordinates of body center."
297313
dist = spice.vnorm(self.center_to_sun.value) * u.km
298-
return (L_sun / (2 * tau * (dist)**2)).to(u.W / u.m / u.m)
314+
return (L_sun / (2 * tau * (dist) ** 2)).to(u.W / u.m / u.m)
299315

300316
@property
301317
def north_pole(self):
@@ -344,7 +360,9 @@ def set_spoint_by(self, func_str=None, lon=None, lat=None):
344360
345361
"""
346362
if func_str is not None and func_str not in ["subpnt", "sincpt"]:
347-
raise NotImplementedError('Only "sincpt" and "subpnt" are supported at this time.')
363+
raise NotImplementedError(
364+
'Only "sincpt" and "subpnt" are supported at this time.'
365+
)
348366
elif func_str is not None:
349367
raise NotImplementedError("not yet implemented.")
350368
# if not self.instrument or not self.obs:
@@ -376,8 +394,7 @@ def sun_direction(self):
376394

377395
@property
378396
def illum_angles(self):
379-
"""Ilumin returns (trgepoch, srfvec, phase, solar, emission)
380-
"""
397+
"""Ilumin returns (trgepoch, srfvec, phase, solar, emission)"""
381398
if self.obs is not None:
382399
output = spice.ilumin(
383400
"Ellipsoid",
@@ -408,14 +425,20 @@ def coords(self):
408425

409426
@property
410427
def local_soltime(self):
411-
return spice.et2lst(self.et, self.target_id, self.coords.lon.value, "PLANETOGRAPHIC")[3]
428+
return spice.et2lst(
429+
self.et, self.target_id, self.coords.lon.value, "PLANETOGRAPHIC"
430+
)[3]
412431

413432
def _get_flux(self, vector):
414433
diff_angle = spice.vsep(vector, self.sun_direction)
415434
if (self.illum_angles.dsolar > 90 * u.deg) or (np.degrees(diff_angle) > 90):
416435
return 0 * u.W / (u.m * u.m)
417436
else:
418-
return (self.solar_constant * np.cos(diff_angle) * np.exp(-self.tau / np.cos(self.illum_angles.solar)))
437+
return (
438+
self.solar_constant
439+
* np.cos(diff_angle)
440+
* np.exp(-self.tau / np.cos(self.illum_angles.solar))
441+
)
419442

420443
@property
421444
def F_flat(self):
@@ -505,7 +528,9 @@ def time_series(self, flux_name, dt, no_of_steps=None, provide_times=None):
505528
criteria = i < no_of_steps
506529

507530
self.time = saved_time
508-
energies = (np.array([e.value for e in energies]) * energies[0].unit).to("J/m**2")
531+
energies = (np.array([e.value for e in energies]) * energies[0].unit).to(
532+
"J/m**2"
533+
)
509534
fluxes = np.array([f.value for f in fluxes]) * fluxes[0].unit
510535
if provide_times:
511536
return np.array(times), energies
@@ -602,7 +627,9 @@ def projected_tilted_rotated_normal(self, pixel_res=0.5):
602627
coords = SurfaceCoords.fromtuple(spice.reclat(nB))
603628
return coords.dlon, coords.dlat
604629

605-
def fluxes_around_equator(self, deltalon=10): # delta between points at equator where flux is calculated
630+
def fluxes_around_equator(
631+
self, deltalon=10
632+
): # delta between points at equator where flux is calculated
606633
quantity_support()
607634
longitudes = range(0, 360, deltalon)
608635
fluxes = []
@@ -621,10 +648,12 @@ class MarsSpicer(Spicer):
621648
obs = Enum([None, "MRO", "MGS", "MEX"])
622649
instrument = Enum([None, "MRO_HIRISE", "MRO_CRISM", "MRO_CTX"])
623650
# Coords dictionary to store often used coords
624-
location_coords = dict(inca=(220.09830399469547, -440.60853011059214, -3340.5081261541495))
651+
location_coords = dict(
652+
inca=(220.09830399469547, -440.60853011059214, -3340.5081261541495)
653+
)
625654

626655
def __init__(self, time=None, obs=None, inst=None, **kwargs):
627-
""" Initialising MarsSpicer class.
656+
"""Initialising MarsSpicer class.
628657
629658
Demo:
630659
>>> mspicer = MarsSpicer(time='2007-02-16T17:45:48.642')
@@ -707,7 +736,7 @@ def albedo_var(self):
707736
a, b = self.constants.albedoCoef
708737
A0 = self.constants.albedo
709738
i = self.illum_angles.solar.value
710-
return A0 + a * (i / (np.pi / 4))**3 + b * (i / (np.pi / 2))**8
739+
return A0 + a * (i / (np.pi / 4)) ** 3 + b * (i / (np.pi / 2)) ** 8
711740

712741
@property
713742
def Qs(self):
@@ -758,7 +787,9 @@ def time_series(self, flux_name, dt, no_of_steps=None, provide_times=None):
758787
criteria = i < no_of_steps
759788

760789
self.time = saved_time
761-
energies = (np.array([e.value for e in energies]) * energies[0].unit).to("J/m**2")
790+
energies = (np.array([e.value for e in energies]) * energies[0].unit).to(
791+
"J/m**2"
792+
)
762793
fluxes = np.array([f.value for f in fluxes]) * fluxes[0].unit
763794
if provide_times:
764795
return np.array(times), energies

tests/conftest.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import pytest
2+
3+
4+
def pytest_addoption(parser):
5+
"""Add command line options."""
6+
parser.addoption(
7+
"--runslow",
8+
action="store_true",
9+
default=False,
10+
help="run slow tests",
11+
)
12+
13+
14+
def pytest_configure(config):
15+
"""Register custom markers."""
16+
config.addinivalue_line(
17+
"markers", "slow: marks tests as slow (deselect with '-m \"not slow\"')"
18+
)
19+
20+
21+
def pytest_collection_modifyitems(config, items):
22+
"""Skip slow tests by default unless --runslow is given."""
23+
if not config.getoption("--runslow"):
24+
skip_slow = pytest.mark.skip(reason="need --runslow option to run")
25+
for item in items:
26+
if "slow" in item.keywords:
27+
item.add_marker(skip_slow)

0 commit comments

Comments
 (0)