One-Component Reflectance Model¶
This tutorial introduces the use of the Hapke model to compute the reflectance spectrum of a planetary surface composed of a single material — water ice. This serves as a foundational example for building more complex models later.
We will:
Load optical constants for H₂O ice at 120 K
Set up a regolith with physical properties like grain size and porosity
Simulate its reflectance under a given geometry
By the end, you’ll have a working understanding of how a regolith() object is constructed and how reflectance spectra are generated for simple surface compositions.
[ ]:
# --- Notebook Setup ---
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams['figure.dpi'] = 200
import os
# --- Imports from FROSTIE ---
import frostie.hapke as hapke
import frostie.utils as utils
First, let us set up our planetary regolith using the class regolith
[14]:
example_one_regolith = hapke.regolith()
The example_one_regolith object is currently empty, but we can add more information about what the regolith is made of and how we are observing it.
First, let us define what our regolith is made of. In this example, we only have one component: water. We will need to load the optical constants of water as they are the key input for reflectance modelling. Optical constants of water measured at 120 K, in the near infrared, are available in the Github repository of FROSTIE. We will focus on the 1-2.5 \(\mu\)m wavelength region.
To model water ice reflectance, we will use the load_default_op_cons function, which loads optical constants for H₂O ice at 120 K from Mastrapa et al. (2009), included in the FROSTIE package. We will focus on the 1–2.5 μm region where H₂O has strong absorption bands.
[15]:
wav_water, n_water, k_water = utils.load_water_op_cons()
Let’s now visualize the real (n) and imaginary (k) parts of the refractive index to understand how water ice interacts with light across the near-infrared.
[16]:
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(8,4))
ax[0].plot(wav_water,k_water)
ax[0].set_ylabel('k')
ax[0].set_xlabel('wavelength in $\mu$m')
ax[0].set_yscale('log')
ax[1].plot(wav_water,n_water)
ax[1].set_ylabel('n')
ax[1].set_xlabel('wavelength in $\mu$m')
plt.tight_layout()
In the \(k\) spectrum, there are two significant bumps near 1.5 and 2.0 \(\mu\)m. Since the absorption coefficient \(\alpha\) is directly dependent on \(k\) (\(\alpha = 4\pi k/\lambda\)), we expect to see strong water absorption features in the reflectance spectrum around 1.5 and 2.0 \(\mu\)m as well
We define a dictionary to store the optical constants and physical properties of the H₂O component, including grain size (\(D\)), phase function type (HG2), and internal scatterer density (\(s\)). For phase function, hapke provides four options, isotropic, Euler, HG1 (one parameter Henyey-Greenstein) and HG2. We will use HG2 in this example, since its the most versatile. We will set grain size \(D = 100\) microns.
[17]:
water = {'name':'h2o', 'n':n_water, 'k':k_water, 'wav':wav_water, 'D':100, 'p_type':'HG2'}
example_one_regolith.add_components([water])
Next, let us set-up the observation by providing the observation geometry angles, \(i\), \(e\) and \(g\), in degrees.
[18]:
example_one_regolith.set_obs_geometry(i=45,e=45,g=90)
Next, let us define the porosity of the regolith. Porosity is defined as the fraction of volume in a regolith that is empty, which in case of airless bodies is vacuum. Hapke model permits a range of [0.48, 1) for porosity of the regolith. If porosity gets smaller than \(\sim 0.48\), the grains in the regolith get close enough that diffraction effects start to matter, which the Hapke model currently does not incorporate.
[19]:
example_one_regolith.set_porosity(p=0.9)
We will assume no backscattering enhancement for now, so let’s set the \(B\) parameter to 0
[20]:
example_one_regolith.set_backscattering(B=0)
We will assume the density of internal scatterers \(s\) to be 0.
[21]:
example_one_regolith.set_s(s=0)
We are ready to model the reflectance of our regolith example_one_regolith
[22]:
example_one_regolith.calculate_reflectance()
Let us plot this reflectance spectrum
[23]:
plt.figure()
plt.plot(example_one_regolith.wav_model, example_one_regolith.model)
plt.ylabel('reflectance')
plt.xlabel('wavelength in $\mu$m')
[23]:
Text(0.5, 0, 'wavelength in $\\mu$m')
Great! The resulting reflectance spectrum shows broad absorptions near 1.5 and 2.0 μm, as expected from the \(k\) spectrum. This simple example helps build intuition for how optical constants shape spectral features.
Summary¶
In this tutorial, we built a simple Hapke reflectance model for a regolith composed entirely of water ice. We loaded laboratory-measured optical constants, configured physical parameters such as grain size and porosity, and generated a synthetic reflectance spectrum under a specified viewing geometry.
This example served to:
Introduce the basic structure of a
regolith()object in FROSTIEShow how physical properties (like grain size or internal scattering) impact the shape and depth of absorption bands
Provide intuition for how spectral features originate from the underlying optical constants
This single-component model provides a useful baseline for testing assumptions, validating implementation details, or comparing with more complex multi-component mixtures, as explored in the next tutorial.