This is a very simple model of soil temperatures on the Moon’s Equator, intended to be used for teaching and learning about weather and climate. It’s just one-dimensional heat transfer through the soil forced by incoming solar radiation that heats the soil and outgoing thermal radiation that cools it at the surface.
The Moon is “tidally locked” to the Earth so that the time required to rotate once on its axis is exactly the same as the period of its orbit around the Earth (that is, the length of the Moon’s “day” is the same as the length of a Lunar “month:” 29 Earth days, 12 hours, 44 minutes).
The Moon has no atmosphere, and the “tilt” of its axis relative to the orbital plane of the Earth-Moon system around the Sun is negligible. Therefore the calculation of insolation at the Lunar surface is much simpler than the one you did for the Earth in problem 1 above. Assuming that the tilt of the Moon’s axis is zero, declination = 0. So at the Moon’s equator the cosine of the solar zenith angle is just equal to the cosine of the hour angle. Much simpler than the Earth!
On Earth the surface energy budget is given by Bonan’s equation 13.1:
\[ \begin{aligned} \rho c \frac{\Delta T}{\Delta t}\Delta z = (S\downarrow - S\uparrow + L\downarrow - L\uparrow) - H - \lambda E = G \end{aligned} \]
On the left-hand side, \(\rho\) is the density of the soil (kg m-3), \(c\) is the heat capacity of the soil (J m-3 K-1), \(\Delta T\) is the change in soil temperature (Kelvin) over a time step (\(\Delta t\), seconds) for each soil layer whose thickness is \(\Delta z\) (meters). On the right-hand side the four terms inside the parentheses are the fluxes of shortwave radiation down and up, and longwave radiation down and up. \(H\) is the turbulent flux of sensible heat, \(\lambda\) is the latent heat of evaporation of water, \(E\) is the rate of evaporation of water from the surface, and \(G\) is the change in ground heat storage. All terms in this equation have the units of W m-2.
The Moon’s surface energy budget is much simpler! There’s no water to evaporate and no turbulent air to carry sensible heat. There’s a little bit of longwave radiation emitted by the Earth that heats the Moon’s surface, but on the far side (which we never see) this is zero so we can neglect this. All of the upward flux of shortwave radiation is just the incoming solar radiation times the Moon’s albedo. To calculate upward longwave emission we can assume the surface emits as a blackbody. So the Moon’s surface energy budget is
\[ \begin{aligned} \rho c \frac{\Delta T}{\Delta t}\Delta z = S\downarrow (1-albedo) - \sigma T_S^4 \end{aligned} \]
In visible wavelengths, the Moon is surprisingly dark, with an albedo of just 7% (like asphalt!). Across the whole Solar spectrum it’s a little brighter, so please use the broadband albedo of 11%.
To calculate the temperature, divide the Lunar “soil” into 15 layers. Make the top layer thinnest (0.02 m = 2 cm), and then make each layer underneath 1.5 times as thick as the one above. This will get you 15 lunar soil temperatures down to about 17.5 meters depth.
The downward flux of heat at the top of each layer is proportional to the difference in temperature between adjacent layers according to Bonan’s equation 9.1:
\[ \begin{aligned} \rho c \frac{\Delta T}{\Delta t}\Delta z = -k \frac{\Delta T}{\Delta z} \end{aligned} \]
The proportionality constant \(k\) is called the “thermal conductivity.” The negative sign is there because the layers are counted down from the surface but the heat flux is defined as positive upward, away from the surface.
Changes in soil temperature are determined by the difference in the heat flux into and out of each layer. This is called the “heat flux divergence,” given by Bonan’s equation 9.2:
\[ \begin{aligned} F \uparrow = -k \frac{\Delta T}{\Delta z} = -(F_Z - F_{z+\Delta z}) \end{aligned} \]
There’s a nice worked example of this on Bonan page 133. The book also gives typical values of heat capacity and thermal conductivity for different kinds of soil.
The Lunar “soil” is actually made up of mineral grains blasted to smithereens by billions of years of impact bombardment. Unlike Earth soils, the pore space is not filled with water or air. It’s just vacuum, so the heat flow through the lunar soil is much slower than on Earth, depending on conductance and thermal radiation between adjacent grains!
According to Colozza (1991), the volumetric heat capacity of lunar regolith is about 4.4 million J m-3 K-1. Use this value for \(ρc\) in the equation above. The thermal conductivity of lunar “soil” is only about 0.025 W m-1 K-1. This is about 100 times less conductive than soils on Earth!
To calculate the changes in temperature with depth and time you’ll need to add solar radiation to the surface, subtract upward longwave, and step the heat flux equations forward in time. You’ll find that if you use a time step that’s too long, the numerical solution will become unstable and you’ll get crazy values (for example negative temperatures Kelvin). I find I can get away with a time step of a few hours.
The program that does the calculation is very simple, and the code that controls this website is surprisingly simple too! It is all written in the programming language R. You can read all about it on the “Website Code” tab to the right.
This website is controlled using the R package “shiny.” There are four important components:
Scroll down or click links in the list above to read all about it!
moon.R:
moon <- function(time.step=3600, albedo=0.08){
# Calculate temperatures in lunar soil ("regolith") for 10 lunar days
# resulting from diurnal cycles of solar heating and longwave cooling
# Assume no tilt of lunar axis, ignore seasonal changes in Moon-Sun distance
# A lunar day (or month!) is 29 Earth days plus 12.75 hours
hours.per.lunar.day <- 24 * 29 + 12.75
# Simulate 10 lunar days
n.lunar.days <- 10
# Figure out how many time steps to take to do n lunar days
n.steps <- n.lunar.days * hours.per.lunar.day * 3600 / time.step
# Parameters
heat.capacity <- 4.4e6 # (J/m3 per Kelvin, from Colozza 1991, pages 2-3)
thermal.conductivity <- 0.025 # (W/m per Kelvin, also from Colozza 1991)
sigma <- 5.67e-8 # (W/m2/K4, Stefan-Boltzmann coefficient)
solar.constant <- 1367. # Watts/m2
# Set depths and thicknesses of each soil layer
n.layers <- 15
dz <- rep(NA, n.layers) # layer thickness (meters)
depth <- dz
dz[1] <- 0.02 # Top layer is thinnest (2 cm)
depth[1] <- dz[1] / 2
layer.mult <- 1.5
for (L in 2:n.layers) {
dz[L] <- dz[L-1] * layer.mult
depth[L] <- sum(dz[1:(L-1)]) + dz[L] / 2
}
# Set up arrays for later plotting
empty <- rep(NA, n.steps-1)
R.net <- empty
SW.down <- empty
SW.up <- empty
LW.down <- empty
LW.up <- empty
lunar.date <- empty
# Initial soil temperature
soilT <- matrix(NA, nrow=n.layers, ncol=n.steps) # array to hold soil temps (Kelvin)
soilT[ , 1] <- 273. # Initial soil temps = 298 K = 25 C
# Array to hold heat fluxes
flux <- matrix(NA, nrow=n.layers+1, ncol=n.steps-1) # array to hold heat fluxes (W/m2)
flux[ ,1] <- NA # upward flux at top of layer L
# Set elapsed time to zero at beginning of simulation
elapsed <- 0.
# Time step the model
for (i in 1:(n.steps-1)) {
# Calculate date and time (lunar solar time = LST)
elapsed <- elapsed + time.step
hours <- elapsed / 3600.
lunar.date[i] <- hours/hours.per.lunar.day
day <- floor(lunar.date[i])
LST <- hours - (hours.per.lunar.day * day)
# Calculate position of Sun in Lunar sky
hour.angle <- -pi + (LST / hours.per.lunar.day) * 2 * pi
cos.z <- max(cos(hour.angle), 0.)
# Calculate SW gain using Bonan (Chapter 4)
SW.down[i] <- solar.constant * cos.z
SW.up[i] <- SW.down[i] * albedo
# Calculate LW using sky temp = 5 Kelvin
LW.down[i] <- sigma * 5^4
# Upward longwave from surface assumes blackbody (emissivity = 1)
LW.up[i] <- sigma * soilT[1,i] ^ 4
# Net radiation
R.net[i] <- SW.down[i] - SW.up[i] + LW.down[i] - LW.up[i]
# Soil temperature
# Compute upward flux at top of each layer
flux[1,i] <- -R.net[i] # top flux boundary condition
for (L in 2:n.layers){
flux[L,i] <- -thermal.conductivity * (soilT[L-1,i] - soilT[L,i]) / ((dz[L-1]+dz[L])/2)
}
flux[n.layers+1,i] <- 0. # bottom flux boundary condition
# Compute new temperatures for each layer
for (L in 1:n.layers) {
soilT[L,i+1] <- soilT[L,i] - (flux[L,i] - flux[L+1,i]) * time.step /
(heat.capacity * dz[L])
}
}
return(list(SW.down=SW.down, SW.up=SW.up, LW.down=LW.down, LW.up=LW.up, R.net=R.net,
flux=flux, soilT=soilT[,1:(n.steps-1)], lunar.date=lunar.date,
depth=depth))
}
plot.moon.R:
plot.moon <- function(output){
# ladder plot of radiation, turbulent fluxes, and temperatures
# Format is a "grid" of three timeseries panels stacked vertically on one page
# Set plot margins
bottomMargin <- 4
leftMargin <- 5
topMargin <- 1
rightMargin <- .5
orig.par <- par(no.readonly=TRUE) # Remember changable parameters to reset later
par(mfcol=c(3,1), mar=c(bottomMargin,leftMargin,topMargin,rightMargin),oma=c(0,2.,1.,0))
attach(output)
#=======================================
# Top panel shows radiation components
plot.min<- min(c(SW.down-SW.up, LW.down-LW.up, R.net))
plot.max <- max(c(SW.down-SW.up, LW.down-LW.up, R.net))
plot(lunar.date, SW.down-SW.up, typ='l', col='yellow', lwd=2, ylim=c(plot.min, plot.max),
main='Radiation', ylab=expression(W ~~ m^-2), xlab='',
cex.main=1.3, cex.lab=1.3, cex.axis=1.3)
lines(lunar.date, LW.down-LW.up, col='red',lwd=2)
lines(lunar.date, R.net, col='black',lwd=2)
legend('topright', c('SW','LW','Net'),
col=c('darkgoldenrod2','red','black'), lwd=c(2,2,2))
#=======================================
# Middle panel shows fluxes
plot.min<- min(flux)
plot.max <- max(flux)
colors <- c('darkred','darkorange2','darkgoldenrod2','green','blue')
plot(lunar.date, flux[1,], typ='l', col='red', lwd=2, ylim=c(-100,100),
main='Heat Fluxes', ylab=expression(W ~~ m^-2), xlab='',
cex.main=1.3, cex.lab=1.3, cex.axis=1.3)
for (L in 2:5) {
lines(lunar.date, flux[L,], col=colors[L],lwd=1)
}
#=======================================
# Bottom panel shows temperatures
plot.min <- min(soilT)
plot.max <- max(soilT)
plot(lunar.date, soilT[1,], typ='l', col='red', lwd=2, ylim=c(plot.min, plot.max),
main='Temperatures', ylab='Kelvin', xlab='Lunar Month',
cex.main=1.3, cex.lab=1.3, cex.axis=1.3)
for (L in 2:5) {
lines(lunar.date, soilT[L,], col=colors[L],lwd=1)
}
detach(output)
par(orig.par)
}
plot.soilT.image.R:
plot.soilT.image <- function(output){
# Make a color-filled contour plot of lunar soil temperature by depth and time
library(fields) # Need this to make the image.plot
# Draw an image using colors to represent the insolation
image.plot(output$lunar.date,100*output$depth,t(output$soilT), ylim=c(50,0),
xlab='Lunar Days', ylab='Depth (cm)',
main='Lunar Soil Temperature (Kelvin)',
cex.main=1.5, cex.axis=1.2, cex.lab=1.3)
}
ui.R:
library(shiny)
library(markdown)
# Define UI for calculation of lunar temperatures
shinyUI(pageWithSidebar(
# Application title
headerPanel("Soil Temperatures on the Moon"),
# Sidebar with sliders that control various available options
sidebarPanel(
img(src='normal_A11Aldrin.jpg'),
h4('Model Parameters'),
sliderInput("albedo", "Albedo (percent)",
min = 5, max = 30, value = 11, step=1),
sliderInput("time.step", "Time Step (hours):",
min = 0.5, max = 12, value = 1, step=0.5)
),
# Main panel consists of a tabset displays model output or model description
mainPanel(
tabsetPanel(
tabPanel("Fluxes",
h3("Component Fluxes and Temperatures"),
plotOutput("fluxes"),
tags$style(type="text/css", ".tab-content { overflow: visible; }")),
tabPanel("Depths",
h3("Temperature vs Depth"),
plotOutput("soil"),
tags$style(type="text/css", ".tab-content { overflow: visible; }")),
tabPanel("Description",
includeHTML('doc/model.description.html')),
tabPanel("Website Code",
includeMarkdown('doc/website.code.md'))
)
)
))
server.R:
library(shiny)
# Source required R scripts
source('model/moon.R')
source('model/plot.moon.R')
source('model/plot.soilT.image.R')
shinyServer(function(input, output) {
# time-depth plot of soil temperatures
output$soil <- renderPlot(
plot.soilT.image(moon(albedo=input$albedo/100, time.step=input$time.step*3600))
)
# Stacked timeseries of component fluxes and temperatures
output$fluxes <- renderPlot(
plot.moon(moon(albedo=input$albedo/100, time.step=input$time.step*3600))
)
}
)