library(fieldClim)
library(here)
# Kursprojekt: bevorzugt lokale Datei unter data/.
caldern_file <- here::here("data", "caldern_wiese_2017-06-30.csv")
# Fallback: mit fieldClim ausgelieferter Beispieldatensatz.
if (!file.exists(caldern_file)) {
caldern_file <- system.file(
"extdata",
"caldern_wiese_2017-06-30.csv",
package = "fieldClim"
)
}
stopifnot(file.exists(caldern_file))
caldern <- read.csv(
caldern_file,
na.strings = c("NULL", "NA", "")
)
caldern$datetime <- as.POSIXct(
caldern$datetime,
format = "%Y-%m-%d %H:%M:%S",
tz = "Europe/Berlin"
)Der komplette R-Code dieser Lektion wird im gerenderten Dokument sichtbar angezeigt. Zusätzlich steht der extrahierte und ausführlich kommentierte R-Code als Datei bereit: L02_strahlung_bodenwaermestrom_datenpruefung.R.
Ziel dieser Kurseinheit
Diese Einheit übersetzt die bekannte Energiebilanzlogik in eine praktische Datenprüfung. Es geht noch nicht darum, turbulente Wärmeflüsse zu berechnen. Es geht darum, ob die Eingangsgrößen für solche Berechnungen zeitlich, rechnerisch und begrifflich konsistent sind.
Die beiden Arbeitsgrößen sind Netto-Strahlung und Bodenwärmestrom. Aus ihnen entsteht die verfügbare Energie, also der Energieanteil, der in den folgenden Einheiten für fühlbare und latente Wärmeflüsse betrachtet wird.
\[ A = Q^* - B \]
Dabei ist Q_star die Netto-Strahlung, B der Bodenwärmestrom und A die verfügbare Energie. In fieldClim entspricht rad_bal der Netto-Strahlung und soil_flux dem Bodenwärmestrom.
Der Code bleibt absichtlich einfach. Wir verwenden read.csv(), direkte Spaltenzugriffe mit $, kleine data.frame()-Tabellen und base-R-Plots. Das Ziel ist nicht ein eleganter R-Stil, sondern eine nachvollziehbare Übersetzung von Stationsdaten in fieldClim-Berechnungen. Pfade laufen über here::here(). Wenn die Kursdatei data/caldern_wiese_2017-06-30.csv nicht vorhanden ist, nutzt der Code als Fallback die mit dem Paket ausgelieferte Beispieldatei.
Kurzwellige Strahlung und Albedo
Die kurzwellige Bilanz beschreibt, wie viel einfallende Sonnenstrahlung nach Reflexion an der Oberfläche verbleibt.
\[ K^* = K_\downarrow - K_\uparrow \]
Die Albedo ist der reflektierte Anteil der einfallenden kurzwelligen Strahlung. Sie wird nur bei ausreichender Einstrahlung berechnet, weil Quotienten bei Dunkelheit oder sehr schwacher Strahlung instabil werden.
caldern$K_down <- caldern$rad_sw_in
caldern$K_up <- caldern$rad_sw_out
caldern$K_star <- caldern$K_down - caldern$K_up
caldern$albedo <- NA_real_
valid_rad <- is.finite(caldern$K_down) & caldern$K_down > 50
caldern$albedo[valid_rad] <- caldern$K_up[valid_rad] / caldern$K_down[valid_rad]
summary(caldern[, c("K_down", "K_up", "K_star", "albedo")])
#> K_down K_up K_star albedo
#> Min. : -2.0210 Min. : 0.448 Min. : -3.3030 Min. :0.1845
#> 1st Qu.: -0.2645 1st Qu.: 1.293 1st Qu.: -0.8845 1st Qu.:0.2121
#> Median : 89.5500 Median : 20.180 Median : 69.3700 Median :0.2188
#> Mean : 189.1081 Mean : 41.228 Mean :147.8806 Mean :0.2192
#> 3rd Qu.: 342.2500 3rd Qu.: 74.392 3rd Qu.:267.6525 3rd Qu.:0.2249
#> Max. :1003.0000 Max. :210.100 Max. :792.9000 Max. :0.2510
#> NAs :125op <- par(mfrow = c(3, 1), mar = c(3.5, 4, 2, 1))
plot(caldern$datetime, caldern$K_down, type = "l",
xlab = "Zeit", ylab = "W m-2", main = "Einfallende kurzwellige Strahlung")
plot(caldern$datetime, caldern$K_up, type = "l",
xlab = "Zeit", ylab = "W m-2", main = "Reflektierte kurzwellige Strahlung")
plot(caldern$datetime, caldern$albedo, type = "p", pch = 16,
xlab = "Zeit", ylab = "-", main = "Albedo bei ausreichender Einstrahlung")
par(op)Der Plot soll nicht nur schön aussehen. Er beantwortet eine Arbeitsfrage: Verhält sich die kurzwellige Strahlung wie ein Tagesgang? Wenn die einfallende Strahlung nachts hohe Werte zeigt oder die Albedo unrealistisch springt, ist die weitere Energiebilanzrechnung gefährdet.
Langwellige Bilanz und Netto-Strahlung
Die langwellige Bilanz wird hier als Differenz aus atmosphärischer Gegenstrahlung und langwelliger Ausstrahlung der Oberfläche gelesen.
\[ L^* = L_\downarrow - L_\uparrow \]
Das Symbol L meint hier langwellige Strahlung. Es ist nicht der fühlbare Wärmestrom. Dieser wird in der Referenznotation als H bezeichnet. Diese Trennung verhindert die häufige Verwechslung zwischen longwave radiation und sensible heat.
caldern$L_down <- caldern$LDnCo
caldern$L_up <- caldern$LUpCo
caldern$L_star <- caldern$L_down - caldern$L_up
caldern$Q_star_components <- caldern$K_star + caldern$L_star
caldern$Q_star_netcols <- caldern$RsNet + caldern$RlNet
caldern$Q_star_measured <- caldern$rad_net
q_check <- data.frame(
comparison = c("RsNet + RlNet - rad_net", "K* + L* - rad_net"),
mean_deviation = c(
mean(caldern$Q_star_netcols - caldern$Q_star_measured, na.rm = TRUE),
mean(caldern$Q_star_components - caldern$Q_star_measured, na.rm = TRUE)
),
max_abs_deviation = c(
max(abs(caldern$Q_star_netcols - caldern$Q_star_measured), na.rm = TRUE),
max(abs(caldern$Q_star_components - caldern$Q_star_measured), na.rm = TRUE)
)
)
q_check[, -1] <- round(q_check[, -1], 1)
q_check
#> comparison mean_deviation max_abs_deviation
#> 1 RsNet + RlNet - rad_net 0.0 0.1
#> 2 K* + L* - rad_net 74.1 187.7plot(caldern$datetime, caldern$Q_star_measured, type = "l",
xlab = "Zeit", ylab = "W m-2", main = "Netto-Strahlung: Messspalte und Kontrollrechnung")
lines(caldern$datetime, caldern$Q_star_netcols, lty = 2)
legend("bottomleft", legend = c("rad_net", "RsNet + RlNet"),
lty = c(1, 2), bty = "n")
Eine Abweichung zwischen den Varianten ist nicht automatisch ein Fehler. Sie kann auf unterschiedliche Korrekturen, Vorzeichenkonventionen, Sensorverarbeitung oder Zeitversatz hinweisen. Für die weitere Arbeit muss aber eine Arbeitsreihe festgelegt werden. Im Kurs verwenden wir rad_net als Q_star und behalten die anderen Reihen als Diagnosegrößen.
caldern$Q_star <- caldern$Q_star_measuredZeitversatz prüfen
Ein Versatz von einem 5-Minuten-Schritt kann Strahlungsprüfungen stark verschlechtern. Deshalb lohnt ein einfacher Vergleich ohne Versatz, mit einem Schritt nach vorne und mit einem Schritt nach hinten.
diff_0 <- mean(abs(caldern$Q_star_components - caldern$Q_star_measured), na.rm = TRUE)
diff_plus <- mean(abs(
caldern$Q_star_components[-1] -
caldern$Q_star_measured[-length(caldern$Q_star_measured)]
), na.rm = TRUE)
diff_minus <- mean(abs(
caldern$Q_star_components[-length(caldern$Q_star_components)] -
caldern$Q_star_measured[-1]
), na.rm = TRUE)
lag_check <- data.frame(
comparison = c("ohne Versatz", "Komponenten +1 Schritt", "Komponenten -1 Schritt"),
mean_absolute_deviation = round(c(diff_0, diff_plus, diff_minus), 1)
)
lag_check
#> comparison mean_absolute_deviation
#> 1 ohne Versatz 74.1
#> 2 Komponenten +1 Schritt 83.4
#> 3 Komponenten -1 Schritt 85.8Wenn eine verschobene Variante deutlich besser wird, ist ein Zeit- oder Aggregationsproblem wahrscheinlich. Wenn nicht, geht es eher um Spaltendefinitionen, Korrekturstufen oder Vorzeichen.
Bodenwärmestrom und verfügbare Energie
Der Bodenwärmestrom ist in fieldClim positiv, wenn Energie in den Boden geht. Für die verfügbare Energie wird er von der Netto-Strahlung abgezogen.
\[ A = Q^* - B \]
caldern$B <- caldern$heatflux_soil
caldern$available_energy <- caldern$Q_star - caldern$B
summary(caldern[, c("Q_star", "B", "available_energy")])
#> Q_star B available_energy
#> Min. :-52.36 Min. :-0.75577 Min. :-56.84
#> 1st Qu.:-17.43 1st Qu.: 0.02899 1st Qu.:-17.75
#> Median : 48.34 Median : 1.55380 Median : 47.75
#> Mean :110.84 Mean : 2.14723 Mean :108.69
#> 3rd Qu.:215.55 3rd Qu.: 4.48683 3rd Qu.:210.15
#> Max. :700.70 Max. : 5.59189 Max. :698.25op <- par(mfrow = c(2, 1), mar = c(3.5, 4, 2, 1))
plot(caldern$datetime, caldern$Q_star, type = "l",
xlab = "Zeit", ylab = "W m-2", main = "Q*: Netto-Strahlung")
lines(caldern$datetime, caldern$B, lty = 2)
legend("bottomleft", legend = c("Q*", "B"), lty = c(1, 2), bty = "n")
plot(caldern$datetime, caldern$available_energy, type = "l",
xlab = "Zeit", ylab = "W m-2", main = "A = Q* - B")
abline(h = 0, lty = 2, col = "grey50")
par(op)Diese Kurve ist die Brücke zur nächsten Einheit. Sie zeigt, welche Energie im Tagesgang überhaupt für turbulente Flüsse und Residualterme zur Verfügung steht. Wenn Q_star oder B falsch gelesen werden, wird jede spätere Methode formal sauber rechnen, aber mit einer falschen Energiegrundlage.
Paketfunktion als Kontrolle
Die manuelle Prüfung steht im Vordergrund. Trotzdem kann fieldClim auch direkt Bodenwärmestrom aus Bodenparametern und Temperaturgradienten berechnen. Das ist ein Rechenszenario, kein automatischer Ersatz für die gemessene Spalte.
ws <- build_weather_station(
datetime = caldern$datetime,
lon = 8.6832,
lat = 50.8405,
elev = 261,
temp = caldern$Ta_2m,
rh = caldern$Huma_2m,
t1 = caldern$Ta_2m,
t2 = caldern$Ta_10m,
hum1 = caldern$Huma_2m,
hum2 = caldern$Huma_10m,
v1 = caldern$Windspeed_2m,
v2 = caldern$Windspeed_10m,
z1 = 2,
z2 = 10,
rad_bal = caldern$rad_net,
soil_flux = caldern$heatflux_soil,
slope = 0,
exposition = 0,
valley = FALSE,
surface_type = "field",
surface_temp = caldern$Ts,
texture = "peat",
moisture = caldern$water_vol_soil,
soil_temp1 = caldern$Ts,
soil_temp2 = caldern$Ta_2m,
soil_depth1 = 0.25,
soil_depth2 = 0,
obs_height = 2
)caldern$B_model <- soil_heat_flux(ws)
summary(caldern$B_model - caldern$B)
#> Min. 1st Qu. Median Mean 3rd Qu. Max.
#> -7.0809 -5.4063 -2.9097 -3.1374 -0.9378 0.7452plot(caldern$datetime, caldern$B, type = "l",
xlab = "Zeit", ylab = "W m-2", main = "Bodenwärmestrom: gemessen und modelliert")
lines(caldern$datetime, caldern$B_model, lty = 2)
legend("bottomleft", legend = c("gemessen", "aus Bodenparametern"),
lty = c(1, 2), bty = "n")
Der Unterschied zwischen beiden Reihen ist kein Schönheitsfehler. Er zeigt, dass Messplatte, Temperaturgradient, Bodentextur und Feuchte unterschiedliche Zugriffspunkte auf denselben Prozess sind. Für den weiteren Standardworkflow bleibt die gemessene Spalte heatflux_soil die Arbeitsgröße.
Legen Sie eine Arbeitsreihe für Q_star und B fest. Dokumentieren Sie, welche Alternativen Sie geprüft haben und warum Sie diese nicht still überschreiben. Die spätere Methodenwahl ist nur so gut wie diese Eingangsentscheidung.