LRO Albedo and Thermal IR (Complex)
Overview
This example demonstrates advanced computation of albedo and thermal infrared accelerations using the object-oriented interface of the pyRTX library.
Key Features
Spatially-varying albedo: Uses a grid of albedo values across the planet surface
Spatially-varying temperature: Incorporates temperature variations with location
Digital elevation model: Represents planetary topography for accurate shadowing
OBJ file format: Loads detailed shape models from standard mesh files
This advanced approach is essential for high-precision orbit determination and propagation where planetary radiation pressure effects need to be accurately modeled.
When to Use This Approach
Use this complex method when:
High-fidelity force modeling is required
Surface property data is available
Topographic effects are significant
Comparing with the simple uniform model shows non-negligible differences
Code
1### ------------------------------------------------------------------------------------------------------- ###
2
3# Example purpose:
4# Show the object-oriented interface of the pyRTX library
5#
6# Example case:
7# Show how to compute albedo and thermal-ir accelerations
8# Here we use an albedo and temperature grid for the planet. We also represent the shape of the planet
9# with a digital elevation model stored in a .obj file.
10
11### ------------------------------------------------------------------------------------------------------- ###
12### IMPORTS
13
14import sys, os
15import numpy as np
16import spiceypy as sp
17import xarray as xr
18import matplotlib.pyplot as plt
19import timeit
20
21from pyRTX.classes.Spacecraft import Spacecraft
22from pyRTX.classes.Planet import Planet, TemperatureGrid, AlbedoGrid
23from pyRTX.classes.Radiation import Albedo, Emissivity
24from pyRTX.classes.Precompute import Precompute
25from pyRTX.classes.LookUpTable import LookUpTable
26from pyRTX.core.analysis_utils import epochRange2
27
28from numpy import floor, mod
29
30### ------------------------------------------------------------------------------------------------------- ###
31### INPUTS
32
33# NOTE: before running this script you must generate the input file 'luts/lro_accel_lut.nc' by running the
34# example 'compute_lut.py' using type = 'accel'.
35
36ref_epc = "2010 may 10 09:25:00"
37duration = 10000 # seconds
38timestep = 100
39METAKR = '../example_data/LRO/metakernel_lro.tm' # metakernel
40obj_path = '../example_data/LRO/' # folder with .obj files
41lutfile = 'luts/lro_accel_lut.nc' # lookup table file
42base_flux = 1361.5
43ref_radius = 1737.4
44n_cores = 10
45
46if not os.path.exists(lutfile):
47 print(f"Before running this script you must generate the file '{lutfile}' by running the example 'compute_lut.py' setting type = 'accel'.")
48 sys.exit()
49
50# The spacecraft mass can be a float, int or a xarray with times and values [kg]
51# You can generate the xarray by running the script 'lro_mass.py'.
52sc_mass = xr.open_dataset('mass/lro_mass.nc')
53sc_mass.load()
54sc_mass.close()
55
56# Planet representation (set None for representing the planet as a simple sphere.
57# Load an obj for representing it with digital elevation models)
58fromFile = 'moon_obj/fib_1e4_v2.obj'
59
60# Albedo grids
61# Reference: https://ode.rsl.wustl.edu/moon/pagehelp/Content/Missions_Instruments/LRO/LOLA/GDR/GDRDAM.htm
62alb_grid = 'grids/bond_albedo.npy'
63alb_lon = 'grids/ldam_4_lon.npy'
64alb_lat = 'grids/ldam_4_lat.npy'
65
66# Temperature grids
67# Reference: https://doi.org/10.1016/j.icarus.2016.08.012
68temp_grid = 'grids/temp.npy'
69temp_lon = 'grids/temp_lon.npy'
70temp_lat = 'grids/temp_lat.npy'
71
72### ------------------------------------------------------------------------------------------------------- ###
73### OBJECTS DEFINITION
74
75# Time initialization
76tic = timeit.default_timer()
77
78# Load the metakernel containing references to the necessary SPICE frames
79sp.furnsh(METAKR)
80
81# Define epochs
82epc_et0 = sp.str2et( ref_epc )
83epc_et1 = epc_et0 + duration
84epochs = epochRange2(startEpoch = epc_et0, endEpoch = epc_et1, step = timestep)
85
86# Define the Spacecraft Object (Refer to the class documentation for further details)
87lro = Spacecraft( name = 'LRO',
88
89 base_frame = 'LRO_SC_BUS', # Name of the spacecraft body-fixed frame
90
91 mass = sc_mass,
92
93 spacecraft_model = { # Define a spacecraft model
94
95 'LRO_BUS': {
96 'file' : obj_path + 'bus_rotated.obj', # .obj file of the spacecraft component
97 'frame_type': 'Spice', # type of frame (can be 'Spice' or 'UD'
98 'frame_name': 'LRO_SC_BUS', # Name of the frame
99 'center': [0.0,0.0,0.0], # Origin of the component
100 'diffuse': 0.1, # Diffuse reflect. coefficient
101 'specular': 0.3, # Specular reflect. coefficient
102 },
103
104 'LRO_SA': {
105 'file': obj_path + 'SA_recentred.obj',
106 'frame_type': 'Spice',
107 'frame_name': 'LRO_SA',
108 'center': [-1,-1.1, -0.1],
109 'diffuse': 0,
110 'specular': 0.3,
111 },
112
113
114 'LRO_HGA': {
115 'file': obj_path + 'HGA_recentred.obj',
116 'frame_type': 'Spice',
117 'frame_name': 'LRO_HGA',
118 'center':[-0.99, -0.3, -3.1],
119 'diffuse': 0.2,
120 'specular': 0.1,
121 },
122 }
123 )
124
125# Define the Moon object
126moon = Planet( fromFile = fromFile,
127 radius = ref_radius,
128 name = 'Moon',
129 bodyFrame = 'MOON_PA',
130 sunFixedFrame = 'GSE_MOON',
131 units = 'km',
132 subdivs = 5,
133 )
134
135# Define the Albedo grid object
136Lon = np.load(alb_lon)
137Lat = np.load(alb_lat)
138
139ALB = AlbedoGrid(
140 radius = ref_radius,
141 frame = 'MOON_PA',
142 planet_name = 'Moon',
143 from_array = alb_grid,
144 axes = (Lon, Lat),
145 )
146
147# Define the temperature grid object
148Lon = np.load(temp_lon)
149Lat = np.load(temp_lat)
150
151TEMP = TemperatureGrid(
152 radius = ref_radius,
153 frame = 'GSE_MOON',
154 planet_name = 'Moon',
155 from_array = temp_grid,
156 axes = (Lon, Lat),
157 )
158
159# Set thermal properties
160moon.emissivity = 0.9
161moon.albedo = ALB
162moon.gridded_temperature = TEMP
163
164# Load the Look up table
165LUT = LookUpTable(lutfile)
166
167# Precomputation object
168prec = Precompute(epochs = epochs,)
169prec.precomputePlanetaryRadiation(lro, moon, LUT.moving_frames, correction='CN')
170prec.dump()
171
172# Create the albedo object
173albedo = Albedo(lro, LUT, moon, precomputation = prec, baseflux = base_flux,)
174
175# Create the thermal infrared object
176thermal_ir = Emissivity(lro, LUT, moon, precomputation = prec, baseflux = base_flux,)
177
178### ------------------------------------------------------------------------------------------------------- ###
179### COMPUTATIONS
180
181# Both the albedo and emissivity objects have a .compute() method.
182# This method returns the normalized fluxes, direction and albedo values
183# for each of the planet faces contributing to the computation
184# The general formula for computing the acceleration of an elementary face is:
185#
186# acc_i = L * albedo_value/mass * norm_flux
187#
188# where L is the normalized optical response of the spacecraft which can be computed
189# with raytracing setting a unitary radiance of the impacting rays
190# Due to the very high number of rays involved in these computations
191# it is mandatory to precompute L in the form of a lookup table.
192# Here we import the lookup table for LRO which can be computed using the example script
193# 'compute_lut.py'
194
195# Parallel computations
196alb_accel = albedo.compute(epochs, n_cores = n_cores)[0] * 1e3
197ir_accel = thermal_ir.compute(epochs, n_cores = n_cores)[0] * 1e3
198
199# Always unload the SPICE kernels
200sp.unload(METAKR)
201
202### ... Elapsed time
203toc = timeit.default_timer()
204time_min = int(floor((toc-tic)/60))
205time_sec = int(mod((toc-tic), 60))
206print("")
207print("\t Elapsed time: %d min, %d sec" %(time_min, time_sec))
208print("")
209
210### ------------------------------------------------------------------------------------------------------- ###
211### PLOT
212
213epochs = [float( epc - epc_et0 )/3600 for epc in epochs]
214
215# ALBEDO
216fig, ax = plt.subplots(3, 1, figsize=(14,8), sharex = True)
217
218ax[0].plot(epochs, alb_accel[:,0], linewidth = 2, color = "tab:blue")
219ax[0].set_ylabel('X [m/s^2]')
220ax[1].plot(epochs, alb_accel[:,1], linewidth = 2, color = "tab:blue")
221ax[1].set_ylabel('Y [m/s^2]')
222ax[2].plot(epochs, alb_accel[:,2], linewidth = 2, color = "tab:blue")
223ax[2].set_ylabel('Z [m/s^2]')
224ax[2].set_xlabel('Hours from CA')
225fig.suptitle('Albedo in S/C body frame')
226plt.tight_layout()
227
228# IR
229fig, ax = plt.subplots(3, 1, figsize=(14,8), sharex = True)
230
231ax[0].plot(epochs, ir_accel[:,0], linewidth = 2, color = "tab:blue")
232ax[0].set_ylabel('X [m/s^2]')
233ax[1].plot(epochs, ir_accel[:,1], linewidth = 2, color = "tab:blue")
234ax[1].set_ylabel('Y [m/s^2]')
235ax[2].plot(epochs, ir_accel[:,2], linewidth = 2, color = "tab:blue")
236ax[2].set_ylabel('Z [m/s^2]')
237ax[2].set_xlabel('Hours from CA')
238fig.suptitle('IR in S/C body frame')
239plt.tight_layout()
240
241plt.show()
242
243### ------------------------------------------------------------------------------------------------------- ###