agentsclimarketplace

Glm netcdf analysis

Skill cxcscmu/SkillLearnBench/skills/b1-one-shot-claude-sonnet-4-6/temperature-simulation/glm-netcdf-analysis

Extracting and analyzing GLM NetCDF output to compute RMSE against field observations using exact datetime+depth matching.From its SKILL.md

Install
npx -y skills add cxcscmu/SkillLearnBench --skill glm-netcdf-analysis

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

SKILL.md

2.6 KB, 688 tokens by cl100k_base, as published. Nobody here has run it

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')
# Key variables
time_raw = ds.variables['time'][:]       # days since some reference
temp = ds.variables['temp'][:]           # shape: (ntimes, nlayers)
z = ds.variables['z'][:]                 # layer heights (m above bottom), shape: (ntimes, nlayers)
NS = ds.variables['NS'][:]               # number of active layers per timestep

# Get time units and convert
time_units = ds.variables['time'].units  # e.g., "hours since 1900-01-01 00:00:00"
import cftime
times = nc.num2date(time_raw, time_units)

Converting Heights to Depths

# z is height above bottom; lake_depth converts to depth from surface
lake_depth = 25.0  # from glm3.nml init_profiles lake_depth
# depth from surface = lake_depth - height_above_bottom
depths = lake_depth - z  # array of depths for each layer, each timestep

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)

# Build simulation dataframe
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)
# Keep one sim value per datetime+depth (if duplicates, take mean or last)
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)

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.