AgentStack
SKILL verified MIT Self-run

Pastas

skill-steadfastasart-geoscience-skills-pastas · by SteadfastAsArt

|

No reviews yet
0 installs
6 views
0.0% view→install

Install

$ agentstack add skill-steadfastasart-geoscience-skills-pastas

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Pastas? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Pastas - Groundwater Time Series Analysis

Quick Reference

import pastas as ps
import pandas as pd

# Load data
head = pd.read_csv('well.csv', index_col=0, parse_dates=True).squeeze()
precip = pd.read_csv('precip.csv', index_col=0, parse_dates=True).squeeze()
evap = pd.read_csv('evap.csv', index_col=0, parse_dates=True).squeeze()

# Create model
ml = ps.Model(head, name='Well_001')

# Add recharge stress
sm = ps.RechargeModel(precip, evap, rfunc=ps.Gamma(), name='recharge')
ml.add_stressmodel(sm)

# Solve and plot
ml.solve()
ml.plot()

Key Classes

| Class | Purpose | |-------|---------| | ps.Model | Main model container | | ps.StressModel | Response to external stress (pumping, river) | | ps.RechargeModel | Recharge from precipitation minus evaporation | | ps.Gamma | Gamma distribution response function | | ps.Exponential | Simple exponential response function |

Essential Operations

Create and Solve Model

ml = ps.Model(head, name='well')
ml.add_stressmodel(ps.RechargeModel(precip, evap, rfunc=ps.Gamma(), name='recharge'))
ml.solve()

Add Pumping Well

pumping = pd.read_csv('pumping.csv', index_col=0, parse_dates=True).squeeze()
ml.add_stressmodel(ps.StressModel(pumping, rfunc=ps.Hantush(),
                                   name='pumping', up=False))  # up=False for drawdown

Model Diagnostics

print(f"EVP: {ml.stats.evp():.1f}%")      # Explained variance
print(f"RMSE: {ml.stats.rmse():.3f} m")   # Root mean square error
print(f"AIC: {ml.stats.aic():.1f}")       # Model selection criterion

ml.plots.diagnostics()                     # Diagnostic plots
ml.plots.acf()                            # Autocorrelation

Get Contributions

contributions = ml.get_contributions()
for name, contrib in contributions.items():
    print(f"{name}: mean={contrib.mean():.2f}")

Step and Impulse Response

step = ml.get_step_response('recharge')    # Step response
block = ml.get_block_response('recharge')  # Impulse response

Export and Load

ml.to_json('model.pas')                    # Save model
ml_loaded = ps.io.load('model.pas')        # Load model

sim = ml.simulate()
sim.to_csv('simulation.csv')               # Export results

Model Statistics

| Statistic | Description | Good Value | |-----------|-------------|------------| | EVP | Explained variance percentage | >70% | | RMSE | Root mean square error | Low (context-dependent) | | AIC | Akaike Information Criterion | Lower = better | | BIC | Bayesian Information Criterion | Lower = better |

Common Patterns

Compare Response Functions

for rfunc in [ps.Gamma(), ps.Exponential(), ps.Hantush()]:
    ml = ps.Model(head)
    ml.add_stressmodel(ps.RechargeModel(precip, evap, rfunc=rfunc, name='r'))
    ml.solve(report=False)
    print(f"{rfunc.name}: EVP={ml.stats.evp():.1f}%, AIC={ml.stats.aic():.1f}")

Forecast Future Levels

ml.solve()
forecast = ml.simulate(tmin='2024-01-01', tmax='2025-12-31')
ml.plot(tmax='2025-12-31')

River or Custom Stress

river = pd.read_csv('river_stage.csv', index_col=0, parse_dates=True).squeeze()
sm = ps.StressModel(river, rfunc=ps.Exponential(), name='river',
                    settings='waterlevel')
ml.add_stressmodel(sm)

When to Use vs Alternatives

| Use Case | Tool | Why | |----------|------|-----| | Groundwater time series analysis | Pastas | Purpose-built transfer function models | | Well response to recharge/pumping | Pastas | Built-in stress models and response functions | | Numerical groundwater flow (MODFLOW) | FloPy | Full 3D finite-difference groundwater model | | Simple exponential decay fitting | Custom scipy | scipy.optimize.curve_fit is sufficient | | Regional groundwater flow modelling | FloPy | Spatially distributed parameters and boundaries | | Aquifer test analysis (pumping tests) | Aqtesolv / custom | Dedicated well test interpretation | | Multi-well network analysis | Pastas | Model each well independently, compare responses | | Signal decomposition | Pastas | Separate recharge, pumping, and trend contributions |

Choose Pastas when: You have groundwater level time series and want to model responses to precipitation, evaporation, or pumping using transfer function noise models. Excellent for rapid model building with diagnostics.

Choose FloPy when: You need spatially distributed groundwater flow modelling with MODFLOW, including multiple layers, boundary conditions, and transport.

Choose custom scipy when: You only need to fit a simple analytical model (e.g., Theis equation) to pumping test data without time series decomposition.

Common Workflows

Groundwater Response Model with Diagnostics

  • [ ] Load head time series and stress data (precipitation, evaporation, pumping)
  • [ ] Inspect data: check for gaps, outliers, and time coverage
  • [ ] Create ps.Model(head) with observation data
  • [ ] Add recharge stress with ps.RechargeModel(precip, evap, rfunc=ps.Gamma())
  • [ ] Add pumping or river stresses if applicable
  • [ ] Solve model with ml.solve()
  • [ ] Check EVP (>70%), RMSE, and AIC
  • [ ] Run ml.plots.diagnostics() to inspect residuals
  • [ ] Check residual autocorrelation; enable noise model if needed: ml.solve(noise=True)
  • [ ] Compare response functions (Gamma vs Exponential vs Hantush) using AIC
  • [ ] Extract step/block responses to interpret aquifer behavior
  • [ ] Decompose signal into individual stress contributions
  • [ ] Export model to JSON and simulation results to CSV

Tips

  1. Start simple - Add stresses incrementally
  2. Check residuals - Should be white noise (use ml.plots.diagnostics())
  3. Compare response functions - Use AIC/BIC to select best model
  4. Use daily data - Pastas works best with daily time series
  5. Normalize units - Precipitation in mm/day, head in meters

Common Issues

| Issue | Solution | |-------|----------| | Poor fit (low EVP) | Try different response functions | | Residual autocorrelation | Add noise model: ml.solve(noise=True) | | Unstable parameters | Set parameter bounds or fix values | | Missing stress data | Interpolate or use fillna() before modeling |

References

  • [Stress Models](references/stress_models.md) - Available stress model types
  • [Response Functions](references/response_functions.md) - Response function selection

Scripts

  • [scripts/groundwatermodel.py](scripts/groundwatermodel.py) - Complete groundwater modeling workflow

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.