| name | glm-netcdf-analysis |
| description | Extracting and analyzing GLM NetCDF output to compute RMSE against field observations using exact datetime+depth matching. |
GLM NetCDF Analysis Skill
Reading GLM NetCDF Output
import netCDF4 as nc
import numpy as np
import pandas as pd
ds = nc.Dataset('/root/output/output.nc')
time_raw = ds.variables['time'][:]
temp = ds.variables['temp'][:]
z = ds.variables['z'][:]
NS = ds.variables['NS'][:]
time_units = ds.variables['time'].units
import cftime
times = nc.num2date(time_raw, time_units)
Converting Heights to Depths
lake_depth = 25.0
depths = lake_depth - z
Exact Datetime + Rounded Depth Merge
obs = pd.read_csv('/root/field_temp_oxy.csv', parse_dates=['datetime'])
obs['depth_round'] = obs['depth'].round(0).astype(int)
sim_rows = []
for i, t in enumerate(times):
n = int(NS[i])
dt = pd.Timestamp(t.year, t.month, t.day, t.hour, t.minute, t.second)
for j in range(n):
h = float(z[i, j])
d = lake_depth - h
d_round = round(d)
sim_rows.append({'datetime': dt, 'depth_round': d_round, 'sim_temp': float(temp[i, j])})
sim_df = pd.DataFrame(sim_rows)
sim_df = sim_df.groupby(['datetime', 'depth_round'])['sim_temp'].mean().reset_index()
merged = obs.merge(sim_df, on=['datetime', 'depth_round'], how='inner')
Computing RMSE Metrics
import json
def rmse(df):
return float(np.sqrt(np.mean((df['temp'] - df['sim_temp'])**2)))
overall_rmse = rmse(merged)
deep = merged[merged['depth_round'] >= 13]
annual_deep_rmse = rmse(deep)
summer_deep = deep[deep['datetime'].dt.month.isin([6, 7, 8, 9])]
summer_deep_rmse = rmse(summer_deep)
metrics = {
'overall_rmse': overall_rmse,
'annual_deep_rmse': annual_deep_rmse,
'summer_deep_rmse': summer_deep_rmse,
'overall_n_pairs': len(merged),
'annual_deep_n_pairs': len(deep),
'summer_deep_n_pairs': len(summer_deep)
}
with open('/root/metrics.json', 'w') as f:
json.dump(metrics, f, indent=2)