- Mauna Loa CO2 trend and 2030 projection
- Temperature anomalies against a reference decade
- Household carbon footprint
- Warming trend and a hidden cycle
- Zero-dimensional energy balance model after a CO2 doubling
- Acceleration of the sea level rise by QR least squares
- Planck feedback and climate sensitivity
- Hot summers: shift of the mean and return period
- Amplitude and phase of the annual temperature cycle
- Dates of the remaining 1.5 C carbon budget
Mauna Loa CO2 trend and 2030 projection
Fit the annual Mauna Loa CO2 means with a line and an exponential, project 2030, and convert the result to radiative forcing and atmospheric carbon stock.
#1. Mauna Loa annual mean CO2 in ppm, years counted from 2000; -> "[yr]" "[14, 15, 16, 17, 18, 19, 20, 21, 22, 23]"; -> "[co2]" "[397.12, 399.41, 403.08, 405.22, 407.61, 410.07, 412.44, 414.70, 418.53, 421.08]"; #2. Linear trend in ppm per year and exponential fit for the projection; -> "[lin]" (stat fit [yr] [co2] "linear"); json load "rx_co2lin" [lin]; -> "[exp]" (stat fit [yr] [co2] "exp"); json load "rx_co2exp" [exp]; -> "[c2030]" (math eval (json select "rx_co2exp" /expression) "{\"x\": 30}"); #3. Radiative forcing 5.35 ln C/C0 against 280 ppm and atmospheric carbon stock 2.124 GtC per ppm; -> "[dF]" (math calc "5.35*ln([c2030]/280)"); concat "trend = " (math round (json select "rx_co2lin" /b) 3) " ppm/yr, growth = " (math round (math calc (concat "100*" (json select "rx_co2exp" /b))) 3) " %/yr, 2030 = " (math round [c2030] 1) " ppm, forcing = " (math round [dF] 2) " W/m2, stock = " (math round (math calc "[c2030]*2.124") 0) " GtC"
trend = 2.616 ppm/yr, growth = 0.64 %/yr, 2030 = 440.1 ppm, forcing = 2.42 W/m2, stock = 935 GtC
- Remove the seasonal cycle of monthly data with
dsp detrendanddsp spectrum - Turn the forcing into warming with a climate sensitivity
Temperature anomalies against a reference decade
Compute the baseline of a reference decade, the anomalies of the recent decade, and test with a Welch t-test whether the warming is significant.
#1. Annual mean temperature of a station in degC, reference decade 1994-2003 and recent decade 2014-2023; -> "[ref]" "[11.8, 12.1, 11.5, 12.0, 12.3, 12.2, 12.4, 11.9, 12.5, 12.8]"; -> "[now]" "[12.9, 13.2, 13.1, 12.8, 13.4, 13.3, 13.0, 13.6, 13.9, 13.7]"; #2. Baseline and anomalies of the recent years; -> "[base]" (stat mean [ref]); -> "[anom]" (vector sub [now] (vector scale "[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]" [base])); #3. Is the warming significant? Welch t-test between the two decades; -> "[t]" (stat ttest_2 [now] [ref]); json load "rx_ttest" [t]; concat "baseline = " (math round [base] 2) " degC, mean anomaly = +" (math round (stat mean [anom]) 2) " degC, max anomaly = +" (math round (stat max [anom]) 2) " degC, t = " (math round (json select "rx_ttest" /t) 2) ", p = " (math round_sig (json select "rx_ttest" /p_value) 2)
baseline = 12.15 degC, mean anomaly = +1.14 degC, max anomaly = +1.75 degC, t = 6.93, p = 0.0000018
- Get the trend in degC per decade with
stat fit - Smooth the series with
dsp moving_average
Household carbon footprint
Turn a year of car fuel, flights, electricity, gas heating billed in BTU and methane into CO2 equivalent, then into tonnes of carbon.
#1. Yearly activity data of a household: car, two long-haul flights, electricity, gas heating billed in BTU; -> "[car]" (math calc "12000*6.5/100*2.31"); -> "[fly]" (math calc "2*6000*0.152"); -> "[elec]" (math calc "4200*0.056"); -> "[gas]" (math calc (concat (unit convert 30.7e6 "BTU" "kWh") "*0.227")); #2. Methane from waste, 15 kg CH4 converted with the GWP100 of 27.9; -> "[ch4]" (math calc "15*27.9"); #3. Total in tonnes of CO2 equivalent and in tonnes of carbon, share of the largest item; -> "[items]" (concat "[" [car] ", " [fly] ", " [elec] ", " [gas] ", " [ch4] "]"); -> "[tot]" (stat sum [items]); concat "total = " (math round (math calc "[tot]/1000") 2) " t CO2e = " (math round (math calc "[tot]/1000*12/44") 2) " t C, largest item = " (math round (math calc (concat "100*" (stat max [items]) "/[tot]")) 1) " %"
total = 6.32 t CO2e = 1.72 t C, largest item = 32.3 %
- Compare with the 2 t per person target
- Put the items in a JSON object and sort them with
json sort
Zero-dimensional energy balance model after a CO2 doubling
Compute the emission temperature of the Earth with the Stefan-Boltzmann law, the radiative forcing of a CO2 doubling (Myhre formula), then integrate the energy balance C dT/dt = F - lambda T of a 100 m ocean mixed layer to see the warming after 10, 30 and 60 years.
#1. Emission temperature of the Earth: solar constant 1361 W/m2, albedo 0.30; -> "[sigma]" (const get "sigma"); -> "[Te]" (math calc "(1361*(1-0.30)/(4*[sigma]))^0.25"); #2. Radiative forcing of an abrupt CO2 doubling, Myhre formula 5.35 ln C/C0; -> "[F]" (math calc "5.35*ln(560/280)"); #3. Feedback parameter and mixed layer of 100 m: heat capacity per year; -> "[lambda]" 1.2; -> "[C]" (math calc "1025*3990*100/3.156e7"); #4. Energy balance model C dT/dt = F - lambda T over 60 years; json load "rx_ebm" (num ode "{\"T\": \"([F]-[lambda]*T)/[C]\"}" "{\"T\": 0}" 0 60 "{\"points\": 7}"); #5. Warming after 10, 30 and 60 years against the equilibrium; -> "[ecs]" (/ [F] [lambda]); concat "Te = " (math round [Te] 1) " K | forcing 2xCO2 = " (math round [F] 2) " W/m2 | ECS = " (math round [ecs] 2) " K, tau = " (math round (/ [C] [lambda]) 1) " years | T10 = " (math round (json select "rx_ebm" "/T/[1]") 2) " K, T30 = " (math round (json select "rx_ebm" "/T/[3]") 2) " K, T60 = " (math round (json select "rx_ebm" "/T/[6]") 2) " K"
Te = 254.6 K | forcing 2xCO2 = 3.71 W/m2 | ECS = 3.09 K, tau = 10.8 years | T10 = 1.87 K, T30 = 2.9 K, T60 = 3.08 K
Acceleration of the sea level rise by QR least squares
The satellite record of the global mean sea level is fitted with a quadratic a + b t + c t^2. The script builds the design matrix, solves the least squares problem with a Householder QR decomposition (R b = Q^T y, stable even when t^2 is large), and gets the rate, the acceleration 2c, the residual and the level reached in 2050.
#1. Global mean sea level by satellite altimetry, mm, every 3 years since 1993; -> "[t]" "0,3,6,9,12,15,18,21,24,27,30"; -> "[y]" "[0, 8.1, 15.0, 24.2, 33.0, 40.9, 51.2, 61.3, 73.0, 86.1, 99.4]"; #2. Design matrix of the quadratic model a + b t + c t^2; -> "[X]" ""; for (-> "[i]" 1) (<= [i] (atom size [t] ",")) (++ "[i]") { -> "[ti]" (atom get [t] [i] ","); -> "[X]" (concat [X] (if (> [i] 1) {","} {""}) "[1," [ti] "," (math calc "[ti]^2") "]"); }; -> "[X]" (concat "[" [X] "]"); #3. Householder QR, then the triangular system R b = Q^T y; json load "rx_p4e_qr" (matrix qr [X]); -> "[b]" (matrix solve (json select "rx_p4e_qr" "/R") (matrix mul (matrix transpose (json select "rx_p4e_qr" "/Q")) [y])); json unload "rx_p4e_qr"; #4. Rate b + 2 c t in 2008, acceleration 2c, residual and level in 2050, t = 57; -> "[rate]" (vector dot [b] "[0, 1, 30]"); -> "[acc]" (vector dot [b] "[0, 0, 2]"); -> "[res]" (vector norm (vector sub (matrix mul [X] [b]) [y])); concat "coefficients a, b, c = " [b] [_n_] "rate in 2008 = " (math round [rate] 2) " mm/yr, acceleration = " (math round [acc] 4) " mm/yr2, residual norm = " (math round [res] 2) " mm, level in 2050 = " (math round (vector dot [b] "[1, 57, 3249]") 0) " mm above 1993"
coefficients a, b, c = [0.9055944055944333,2.138352758352755,0.037347837347837434] rate in 2008 = 3.26 mm/yr, acceleration = 0.0747 mm/yr2, residual norm = 2.37 mm, level in 2050 = 244 mm above 1993
- Compare with the normal equations X^T X b = X^T y:
matrix condshows why QR is preferred. - Add a sine of period 1 year to the design matrix to remove the seasonal cycle of monthly data.
Planck feedback and climate sensitivity
The script extracts the Stefan-Boltzmann constant from the physics library, gets the effective emission temperature of the Earth with a fourth root, the Planck feedback 4 sigma Te^3, the net feedback after water vapour, lapse rate, albedo and cloud feedbacks, then the forcing of a CO2 doubling, the equilibrium climate sensitivity and the warming committed at 420 ppm.
#1. Stefan-Boltzmann constant of the physics library, value extracted from the text; -> "[sigma]" (string regexp_extract (constant physics_stefan_boltzmann) "[0-9.]+(E-?[0-9]+)?" 0); #2. Effective emission temperature Te = fourth root of S 1 - albedo / 4 sigma; -> "[Te]" (math nroot (math calc "1361*(1-0.30)/(4*[sigma])") 4); #3. Planck feedback 4 sigma Te^3 and the other feedbacks: water vapour, lapse rate, albedo, clouds, W/m2/K; -> "[l0]" (math calc "4*[sigma]*[Te]^3"); -> "[lambda]" (math calc "[l0]-(1.8-0.5+0.35+0.42)"); #4. Forcing of a CO2 doubling and of 420 ppm against 280 ppm, 5.35 ln C/C0; -> "[F2x]" (math calc "5.35*ln(2)"); -> "[F]" (math calc (concat "5.35*" (math log (math calc "420/280")))); concat "sigma = " [sigma] ", Te = " (math round [Te] 2) " K, Planck feedback = " (math round [l0] 3) " W/m2/K, net feedback = " (math round [lambda] 3) " W/m2/K, F2x = " (math round [F2x] 2) " W/m2, ECS = " (math round (math calc "[F2x]/[lambda]") 2) " K, equilibrium warming at 420 ppm = " (math round (math calc "[F]/[lambda]") 2) " K"
sigma = 5.6703744191844314E-8, Te = 254.58 K, Planck feedback = 3.742 W/m2/K, net feedback = 1.672 W/m2/K, F2x = 3.71 W/m2, ECS = 2.22 K, equilibrium warming at 420 ppm = 1.3 K
- Draw the warming against the cloud feedback from 0 to 1 W/m2/K with
plot function: the tail of high sensitivities. - Add the ocean heat uptake to get the transient response with
num ode.
Hot summers: shift of the mean and return period
Ten recent summer anomalies of a station are ranked, tested against the 1961-1990 climate with a z-test (the climatological standard deviation is known), and the return period of a +2.1 K summer is computed in the old and in the current climate with the complementary error function: the risk ratio of an attribution study.
#1. Summer JJA temperature anomalies of a station against 1961-1990, last 10 years, known sigma 0.6 K; -> "[jja]" "[0.9, 1.4, 0.7, 2.1, 1.1, 1.6, 0.8, 2.4, 1.3, 1.9]"; -> "[sigma]" 0.6; #2. Ranking of the summers, hottest last; -> "[rank]" (math order_double [jja]); #3. z-test: has the mean shifted from 0?; -> "[z]" (stat ztest [jja] 0 [sigma]); #4. Probability of a summer above +2.1 K in the old climate and now, 0.5 erfc x / sigma sqrt 2; -> "[p_old]" (math calc (concat "0.5*" (math erfc (math calc "2.1/([sigma]*sqrt(2))")))); -> "[p_new]" (math calc (concat "0.5*" (math erfc (math calc (concat "(2.1-" (stat mean [jja]) ")/([sigma]*sqrt(2))"))))); concat "ordered = " [rank] [_n_] "z-test = " [z] [_n_] "summer above +2.1 K: return period " (math round (math calc "1/[p_old]") 0) " years in 1961-1990, " (math round (math calc "1/[p_new]") 1) " years now, risk ratio = " (math round (math calc "[p_new]/[p_old]") 0)
ordered = [0.7,0.8,0.9,1.1,1.3,1.4,1.6,1.9,2.1,2.4]
z-test = {"z": 7.484057129065165, "p_value": 7.20626055562942E-14}
summer above +2.1 K: return period 4299 years in 1961-1990, 7.8 years now, risk ratio = 553- Use
stat ttestif the standard deviation must be estimated from the sample. - Fit a GEV law to the yearly maxima instead of a normal law on the summer means.
Amplitude and phase of the annual temperature cycle
The twelve monthly normals of a station are projected on the first harmonic. The script gets the annual mean, the amplitude with hypot, the phase with atan2, the date of the warmest day of the fitted cycle and its lag after the summer solstice, a signature of the thermal inertia of the site.
#1. Monthly mean temperatures of a mid-latitude station, degC, January to December; -> "[T]" "2.1,3.4,6.9,10.2,14.1,17.6,19.8,19.3,15.9,11.4,6.3,2.9"; -> "[a]" 0; -> "[b]" 0; #2. First harmonic: a1 and b1 by projection on cos and sin at mid-month; for (-> "[m]" 1) (<= [m] 12) (++ "[m]") { -> "[x]" (math calc "2*pi*([m]-0.5)/12"); -> "[Tm]" (atom get [T] [m] ","); -> "[a]" (math calc "[a]+[Tm]*cos([x])/6"); -> "[b]" (math calc "[b]+[Tm]*sin([x])/6"); }; #3. Amplitude and phase of the annual cycle, date of the maximum; -> "[amp]" (math hypot [a] [b]); -> "[phase]" (math calc (concat "mod(" (math atan2 [b] [a]) "+2*pi, 2*pi)")); -> "[day]" (math round (math calc "[phase]/(2*pi)*365") 0); #4. Annual mean and date of the warmest day of the fitted cycle; concat "annual mean = " (math round (math avg (concat "[" [T] "]")) 2) " degC, amplitude = " (math round [amp] 2) " K, phase = " (math round (math rad_to_deg [phase]) 1) " deg, warmest day of the fit = " (date add "2025-01-01" "DAY" [day]) ", lag after the solstice = " (date nb_day "2025-06-21" (date add "2025-01-01" "DAY" [day])) " days"
annual mean = 10.83 degC, amplitude = 8.78 K, phase = 198.1 deg, warmest day of the fit = 2025-07-21, lag after the solstice = 30 days
- Compare a coastal and a continental station: the coast has a smaller amplitude and a longer lag.
- Get the same harmonic with
dsp spectrumon several years of daily data.
Dates of the remaining 1.5 C carbon budget
From the remaining carbon budget for 1.5 C and the current CO2 emissions, the script gets the date when the budget is used at constant emissions, the net zero date of a straight line path that stays within the budget, the yearly cut it needs, and the warming that the budget represents with the TCRE.
#1. Remaining carbon budget for 1.5 C, 50 percent chance, 130 GtCO2 from 1 January 2025, emissions 41.6 GtCO2/yr; -> "[B]" 130; -> "[E]" 41.6; #2. Constant emissions: budget gone after B/E years, converted to a date; -> "[years]" (math calc "[B]/[E]"); -> "[gone]" (date add "2025-01-01" "DAY" (math trunc (math calc "[years]*365.25"))); #3. Straight line to net zero within the budget: T = 2B/E; -> "[T]" (math calc "2*[B]/[E]"); -> "[nz]" (date add "2025-01-01" "DAY" (math trunc (math calc "[T]*365.25"))); #4. Warming from the TCRE, 0.45 C per 1000 GtCO2, and the yearly cut needed; concat "budget gone on " [gone] " at constant emissions, " (math trunc [years]) " years and " (math round (math calc (concat "12*" (math frac_part [years]))) 1) " months; linear path: net zero on " [nz] " in year " (date year [nz]) ", cut of " (math round (math calc "[E]/[T]") 2) " GtCO2 per year; warming of the budget " (math round (math calc "0.45*[B]/1000") 3) " C"
budget gone on 2028-02-16 at constant emissions, 3 years and 1.5 months; linear path: net zero on 2031-04-02 in year 2031, cut of 6.66 GtCO2 per year; warming of the budget 0.059 C
- Use an exponential decline of the emissions: the integral E/k gives the rate k needed.
- Repeat for the 2 C budget, about 1050 GtCO2 from 2025.
