Air Mass Coefficient
The air mass coefficient defines the path length (or column depth) of sunlight through the atmosphere. The air mass for dry air, wet air and dust are key inputs for estimating solar extinction and the irradiance intensity on the Earth’s surface. The air mass coefficient is a ratio between the path length for a specific zenith angle and the column depth when the zenith angle equals zero.
(1)
The challenge in modeling air mass and solar extinction is an atmosphere that is highly variable, exhibiting unique behavior at different altitude levels. Atmospheric models seek to overcome some of the errors in the interpolative models of air mass. Specifically, atmospheric models:
- Define a non-homogenous atmosphere with temperature, pressure and air density profiling by elevation;
- Estimate the column depth of dry air using real-time or forecast data;
- Estimate the column depth of water vapor and droplets; and
- Estimate the column depth of dust and other aerosol concentrations.
The practical goal is to use the latest measurement data and the best atmospheric models to understand the solar resource and the performance of solar PV technology.
Variable Density Atmosphere
In the real atmosphere, air density varies with temperature and pressure as a function of height. The column depth or path length through the dry atmosphere is:
(2)
where R is the distance from the center of the Earth for the observer at altitude and for the top of the atmosphere (or mesosphere as explained below) and
is the air density at altitude
. The fluid atmosphere refracts sunlight according to Snell’s law. The column depth or path length of sunlight then becomes:
(3)
The equation is the column depth of the atmosphere with a correction factor in the denominator for the the refraction of light, where is the air’s index of refraction at altitude R and
is the apparent zenith angle. Again, to get the air mass,
must be normalized by the column depth when
.
The Standard Atmosphere
The atmospheric air mass equation relies on air density as a function of altitude. This is estimated using a temperature profile combined with the ideal gas law (which expresses density in terms of pressure and temperature), along with the assumption of hydrostatic stability (which defines density in terms of pressure).
The problem of the temperature profile can only be determined by solving equations for the fluid dynamics of the atmosphere, coupled with knowledge of the radiative energy in the atmosphere and given appropriate boundary conditions. This is the fundamental challenge of numerical weather prediction for which there are standard models (WRF, MM5, etc.) and gridded data sets for both historic or forecast time frames.
Applying the Standard Atmosphere
For the temperature profile, the atmosphere is assumed to have a discrete number layers within which the temperature changes linearly with altitude. In any given layer n, the temperature is
(4)
where is the slope of the temperature rise with altitude within the layer n,
is the temperature at the bottom of that layer,
is the height (defined below), and
is the height of the bottom of the layer. This is the methodology used in the U.S. Standard Atmosphere, 1972, wherein a “standard atmosphere” appropriate for the continental United States is defined. A calculator for the standard atmosphere coded in R appears at the end of this post.
In practice, it is necessary to adjust the standard atmosphere to obtain specific temperatures and air mass profiles for the location of interest. The bottom eight temperature layers correspond roughly to the troposphere, stratosphere, and mesosphere. The top or eighth layer is at 100 km above mean sea level. In order to generalize the work to make it more appropriate for any latitude and any season, then the values defining the lowest two layers (the troposphere and lower stratosphere) are most important.
Lower Atmosphere Adjustments
Specifically, let the boundary between those two layers (the tropopause; vary as a function of time of year and latitude. The same thing goes for
, the temperature fall-off in the first layer. The bottom of the first layer (
) is set to the height
of the observer’s location. The boundary condition temperature at the bottom of first layer (
) is set to the actual local temperature at the observer. The condition
equals the temperature at the top of the first layer, i.e., as determined from the above expression for
with
and
. Finally,
is set by forcing the temperature at the top of the second layer to match
(fixed at some value as reported by the Qatar Meteorology Department, as opposed to 216.65 K, the value in the U.S. Standard Atmosphere).
Although the temperatures in the second layer and higher (altitudes > 20 km) do vary with season and latitude, the variations are less severe than in the troposphere and lower stratosphere. Fortunately, the atmosphere has already greatly thinned out by that point so these layers contribute little to the solar airmass and solar extinction results.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# Standard Atmosphere Calculator # 1.Standard sea level conditions (h = 0) ---- temp0 <- 288.16 pres0 <- 1.01325 * 10^5 dens0 <- 1.2250 grav0 <- 9.80665 radius <- 6378136.3 gasc <- 287.0368 # 2.Standard Atmosphere Results ----- # 0:11k - first gradient layer h1 <- seq(0, 11000, by = 100) h1.calc <- h1 - 0 a1 <- -6.5 * 10^-3 t1 <- temp0 + a1 * h1.calc p1 <- pres0 * (t1/temp0)^(-grav0/(a1*gasc)) d1 <- dens0 * (t1/temp0)^(-((grav0/(a1*gasc))+1)) # 11.1k:25k - first isothermal layer h2 <- seq(11100, 25000, by = 100) h2.calc <- h2 - 11000 t2 <- rep(216.66, length(h2)) p2 <- 22631.5 * exp(-(grav0/(gasc*t2))*h2.calc) d2 <- .3639048 * exp(-(grav0/(gasc*t2))*h2.calc) # 25.1k:47k - second gradient layer h3 <- seq(25100, 47000, by = 100) h3.calc <- h3 - 25000 a3 <- 3 * 10^-3 t3 <- 216.66 + a3 * h3.calc p3 <- 2488.504 * (t3/216.66)^(-grav0/(a3*gasc)) d3 <- .04001407 * (t3/216.66)^(-((grav0/(a3*gasc))+1)) # 47.1k:53k - second isothermal layer h4 <- seq(47100, 53000, by = 100) h4.calc <- h4 - 47000 t4 <- rep(282.66, length(h4)) p4 <- 120.4327 * exp(-(grav0/(gasc*t4))*h4.calc) d4 <- 0.001484340 * exp(-(grav0/(gasc*t4))*h4.calc) # 53.1k:79k - third gradient layer h5 <- seq(53100, 79000, by = 100) h5.calc <- h5 - 53000 a5 <- -4.5 * 10^-3 t5 <- 282.66 + a5 * h5.calc p5 <- 58.31566 * (t5/282.66)^(-grav0/(a5*gasc)) d5 <- 0.0007187439 * (t5/282.66)^(-((grav0/(a5*gasc))+1)) # 79.1K:90k - third isothermal layer h6 <- seq(79100, 90000, by = 100) h6.calc <- h6 - 79000 t6 <- rep(165.66, length(h6)) p6 <- 1.009319 * exp(-(grav0/(gasc*t6))*h6.calc) d6 <- 2.122580e-05 * exp(-(grav0/(gasc*t6))*h6.calc) # 90.1k:105K - fourth gradient level h7 <- seq(90100, 105000, by = 100) h7.calc <- h7 - 90000 a7 <- 4 * 10^-3 t7 <- 165.66 + a7 * h7.calc p7 <- .1044209 * (t7/165.66)^(-grav0/(a7*gasc)) d7 <- 2.195954e-06 * (t7/165.66)^(-((grav0/(a7*gasc))+1)) # 5. Plot Data ---- # consolidate data std.atmos <- data.frame(hg = c(h1, h2, h3, h4, h5, h6, h7), temp = c(t1, t2, t3, t4, t5, t6, t7) - 273.15, pres = c(p1, p2, p3, p4, p5, p6, p7), dens = c(d1, d2, d3, d4, d5, d6, d7)) std.atmos.thin <- std.atmos[seq(0, 1e3, by = 20),] # multiplot p1 <- ggplot(std.atmos, aes(hg, temp)) + annotate("rect", xmin = 0, xmax = 1.1e4, ymin = -120, ymax = 30, fill = "gold2", alpha = .7) + annotate("rect", xmin = 1.1e4, xmax = 5.1e4, ymin = -120, ymax = 30, fill = "gold2", alpha = .5) + annotate("rect", xmin = 5.1e4, xmax = 8.4852e4, ymin = -120, ymax = 30, fill = "gold2", alpha = .3) + annotate("rect", xmin = 8.4852e4, xmax = 1e5, ymin = -120, ymax = 30, fill = "gold2", alpha = .1) + geom_segment(aes(x=11e3, xend=11e3, y=-120, yend=30), size = 0.01, color = "red", linetype = "solid") + geom_segment(aes(x=20e3, xend=20e3, y=-120, yend=30), size = 0.01, color = "navy", linetype = "dashed") + geom_segment(aes(x=32e3, xend=32e3, y=-120, yend=30), size = 0.01, color = "navy", linetype = "dashed") + geom_segment(aes(x=47e3, xend=47e3, y=-120, yend=30), size = 0.01, color = "navy", linetype = "dashed") + geom_segment(aes(x=51e3, xend=51e3, y=-120, yend=30), size = 0.01, color = "red", linetype = "solid") + geom_segment(aes(x=71e3, xend=71e3, y=-120, yend=30), size = 0.01, color = "navy", linetype = "dashed") + geom_segment(aes(x=8.4851e4, xend=8.4851e4, y=-120, yend=30), size = 0.01, color = "red", linetype = "solid") + geom_segment(aes(x=1e5, xend=1e5, y=-120, yend=30), size = 0.01, color = "red", linetype = "solid") + geom_line(size = 0.5, color = "navy") + geom_point(data=std.atmos.thin, aes(hg, temp), size = 3, color="navy") + geom_point(data=std.atmos.thin, aes(hg, temp), size = 2.75, color="green") + geom_point(data=std.atmos.thin, aes(hg, temp), size = 1.5, color="navy") + coord_flip() + scale_y_continuous(limits = c(-120, 30), breaks = seq(-120, 30, 30)) + scale_x_continuous(limits = c(0, 1e5), breaks = seq(0, 1e5, 1e4)) + labs(title = expression(atop(bold("Temperature Profile"), atop(italic("Standard Atmosphere")),"")), x = "Geometric Height (m)", y = "Temperature (C)") + annotate("text", x = 9600, y = 0, label = "Tropopause Boundary", size = 3.5, color = "red", fontface = "italic") + annotate("text", x = 49600, y = -90, label = "Stratopause Boundary", size = 3.5, color = "red", fontface = "italic") + annotate("text", x = 83300, y = 0, label = "Mesopause Boundary", size = 3.55, color = "red", fontface = "italic") + annotate("text", x = 6000, y = 25, label = "Q1", size = 2.75, color = "navy", fontface = "bold") + annotate("text", x = 15000, y = 25, label = "Q2", size = 2.75, color = "navy", fontface = "bold") + annotate("text", x = 26000, y = 25, label = "Q3", size = 2.75, color = "navy", fontface = "bold") + annotate("text", x = 39500, y = 25, label = "Q4", size = 2.75, color = "navy", fontface = "bold") + annotate("text", x = 49000, y = 25, label = "Q5", size = 2.75, color = "navy", fontface = "bold") + annotate("text", x = 61000, y = 25, label = "Q6", size = 2.75, color = "navy", fontface = "bold") + annotate("text", x = 77500, y = 25, label = "Q7", size = 2.75, color = "navy", fontface = "bold") + annotate("text", x = 92000, y = 25, label = "Q8", size = 2.75, color = "navy", fontface = "bold") p2 <- ggplot(std.atmos, aes(hg, pres)) + annotate("rect", xmin = 0, xmax = 1.1e4, ymin = 0, ymax = 1e5, fill = "gold2", alpha = .7) + annotate("rect", xmin = 1.1e4, xmax = 5.1e4, ymin = 0, ymax = 1e5, fill = "gold2", alpha = .5) + annotate("rect", xmin = 5.1e4, xmax = 8.4852e4, ymin = 0, ymax = 1e5, fill = "gold2", alpha = .3) + annotate("rect", xmin = 8.4852e4, xmax = 1e5, ymin = 0, ymax = 1e5, fill = "gold2", alpha = .1) + geom_segment(aes(x=11e3, xend=11e3, y=0, yend=1e5), size = 0.01, color = "red", linetype = "solid") + geom_segment(aes(x=20e3, xend=20e3, y=0, yend=1e5), size = 0.01, color = "navy", linetype = "dashed") + geom_segment(aes(x=32e3, xend=32e3, y=0, yend=1e5), size = 0.01, color = "navy", linetype = "dashed") + geom_segment(aes(x=47e3, xend=47e3, y=0, yend=1e5), size = 0.01, color = "navy", linetype = "dashed") + geom_segment(aes(x=51e3, xend=51e3, y=0, yend=1e5), size = 0.01, color = "red", linetype = "solid") + geom_segment(aes(x=71e3, xend=71e3, y=0, yend=1e5), size = 0.01, color = "navy", linetype = "dashed") + geom_segment(aes(x=8.4851e4, xend=8.4851e4, y=0, yend=1e5), size = 0.01, color = "red", linetype = "solid") + geom_segment(aes(x=1e5, xend=1e5, y=0, yend=1e5), size = 0.01, color = "red", linetype = "solid") + geom_line(size = 0.5, color = "navy") + geom_point(data=std.atmos.thin, aes(hg, pres), size = 3, color="navy") + geom_point(data=std.atmos.thin, aes(hg, pres), size = 2.75, color="green") + geom_point(data=std.atmos.thin, aes(hg, pres), size = 1.5, color="navy") + coord_flip() + scale_y_continuous(limits = c(0, 1e5), breaks = seq(0, 1e5, 2e4)) + scale_x_continuous(limits = c(0, 1e5), breaks = seq(0, 1e5, 1e4)) + labs(title = expression(atop(bold("Pressure Profile"), atop(italic("Standard Atmosphere")),"")), x = "", y = "Pressure (Nm^2)") + annotate("text", x = 6000, y = 80000, label = "Troposphere", size = 4, color = "red", fontface = "bold") + annotate("text", x = 30000, y = 80000, label = "Stratosphere", size = 4, color = "red", fontface = "bold") + annotate("text", x = 67000, y = 80000, label = "Mesosphere", size = 4, color = "red", fontface = "bold") + annotate("text", x = 92000, y = 80000, label = "Thermosphere", size = 4, color = "red", fontface = "bold") + annotate("text", x = 6000, y = 95000, label = "Q1", size = 2.75, color = "navy", fontface = "bold") + annotate("text", x = 15000, y = 95000, label = "Q2", size = 2.75, color = "navy", fontface = "bold") + annotate("text", x = 26000, y = 95000, label = "Q3", size = 2.75, color = "navy", fontface = "bold") + annotate("text", x = 39500, y = 95000, label = "Q4", size = 2.75, color = "navy", fontface = "bold") + annotate("text", x = 49000, y = 95000, label = "Q5", size = 2.75, color = "navy", fontface = "bold") + annotate("text", x = 61000, y = 95000, label = "Q6", size = 2.75, color = "navy", fontface = "bold") + annotate("text", x = 77500, y = 95000, label = "Q7", size = 2.75, color = "navy", fontface = "bold") + annotate("text", x = 92000, y = 95000, label = "Q8", size = 2.75, color = "navy", fontface = "bold") multiplot(p1, p2, layout = matrix(c(1,2), nrow=1, byrow=TRUE)) |