glm-output
Read and process GLM output files. Use when you need to extract temperature data from NetCDF output, convert depth coordinates, or calculate RMSE against observations.
npxskills add benchflow-ai/skillsbench--skill glm-outputLoading…
Read and process GLM output files. Use when you need to extract temperature data from NetCDF output, convert depth coordinates, or calculate RMSE against observations.
npxskills add benchflow-ai/skillsbench--skill glm-outputLoading…
GLM produces NetCDF output containing simulated water temperature profiles. Processing this output requires understanding the coordinate system and matching with observations.
After running GLM, results are in output/output.nc:
| Variable | Description | Shape |
|---|---|---|
time | Hours since simulation start | (n_times,) |
z | Height from lake bottom (not depth!) | (n_times, n_layers, 1, 1) |
temp | Water temperature (°C) | (n_times, n_layers, 1, 1) |
from netCDF4 import Dataset
import numpy as np
import pandas as pd
from datetime import datetime
nc = Dataset('output/output.nc', 'r')
time = nc.variables['time'][:]
z = nc.variables['z'][:]
temp = nc.variables['temp'][:]
nc.close()
Important: GLM z is height from lake bottom, not depth from surface.
# Convert to depth from surface
# Set LAKE_DEPTH based on lake_depth in &init_profiles section of glm3.nml
LAKE_DEPTH = <lake_depth_from_nml>
depth_from_surface = LAKE_DEPTH - z
from netCDF4 import Dataset
import numpy as np
import pandas as pd
from datetime import datetime
def read_glm_output(nc_path, lake_depth):
nc = Dataset(nc_path, 'r')
time = nc.variables['time'][:]
z = nc.variables['z'][:]
temp = nc.variables['temp'][:]
start_date = datetime(2009, 1, 1, 12, 0, 0)
records = []
for t_idx in range(len(time)):
hours = float(time[t_idx])
date = pd.Timestamp(start_date) + pd.Timedelta(hours=hours)
heights = z[t_idx, :, 0, 0]
temps = temp[t_idx, :, 0, 0]
for d_idx in range(len(heights)):
h_val = heights[d_idx]
t_val = temps[d_idx]
if not np.ma.is_masked(h_val) and not np.ma.is_masked(t_val):
depth = lake_depth - float(h_val)
if 0 <= depth <= lake_depth:
records.append({
'datetime': date,
'depth': round(depth),
'temp_sim': float(t_val)
})
nc.close()
df = pd.DataFrame(records)
df = df.groupby(['datetime', 'depth']).agg({'temp_sim': 'mean'}).reset_index()
return df
def read_observations(obs_path):
df = pd.read_csv(obs_path)
df['datetime'] = pd.to_datetime(df['datetime'])
df['depth'] = df['depth'].round().astype(int)
df = df.rename(columns={'temp': 'temp_obs'})
return df[['datetime', 'depth', 'temp_obs']]
def calculate_rmse(sim_df, obs_df):
merged = pd.merge(obs_df, sim_df, on=['datetime', 'depth'], how='inner')
if len(merged) == 0:
return 999.0
rmse = np.sqrt(np.mean((merged['temp_sim'] - merged['temp_obs'])**2))
return rmse
# Usage: get lake_depth from glm3.nml &init_profiles section
sim_df = read_glm_output('output/output.nc', lake_depth=25)
obs_df = read_observations('field_temp_oxy.csv')
rmse = calculate_rmse(sim_df, obs_df)
print(f"RMSE: {rmse:.2f}C")
| Issue | Cause | Solution |
|---|---|---|
| RMSE very high | Wrong depth conversion | Use lake_depth - z, not z directly |
| No matched observations | Datetime mismatch | Check datetime format consistency |
| Empty merged dataframe | Depth rounding issues | Round depths to integers |
lake_depth in &init_profiles section of glm3.nmlCreate or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Set up and use 1Password CLI (op). Use when installing the CLI, enabling desktop app integration, signing in (single or multi-account), or reading/injecting/running secrets via op.
CLI to manage emails via IMAP/SMTP. Use `himalaya` to list, read, write, reply, forward, search, and organize emails from the terminal. Supports multiple accounts and message composition with MML (MIME Meta Language).
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Set up and use 1Password CLI (op). Use when installing the CLI, enabling desktop app integration, signing in (single or multi-account), or reading/injecting/running secrets via op.
CLI to manage emails via IMAP/SMTP. Use `himalaya` to list, read, write, reply, forward, search, and organize emails from the terminal. Supports multiple accounts and message composition with MML (MIME Meta Language).