Commit 790ad13f authored by Turner, Sean's avatar Turner, Sean
Browse files

initial multi res

parent e2fc7264
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
Wolf_Creek:
  capacity: 100.0
  initial_storage: 50.0
  max_release: 15.0
  min_release: 3.0
Cordell_Hull:
  capacity: 80.0
  initial_storage: 40.0
  max_release: 12.0
  min_release: 2.0
 No newline at end of file
+56 −8
Original line number Diff line number Diff line
@@ -10,29 +10,77 @@ library(dplyr)
library(lubridate)
library(readr)
library(ggplot2)
library(patchwork)
setwd("examples/dev/")
use_condaenv("powersheds")
```

```{python}
import pandas as pd
import powersheds
import yaml
from dataclasses import dataclass
```


```{python}

@dataclass
class ReservoirSpecs:
    capacity: float
    initial_storage: float
    max_release: float
    min_release: float

# Read the reservoir configuration
with open('reservoirs/reservoir_config.yaml', 'r') as file:
    config_dict  = yaml.safe_load(file)

df = pd.read_csv("examples/dev/inflow_release_ex1.csv")
inflow = df['inflow']
release = df['release']
# Convert dictionary to class instances
wolf_creek = ReservoirSpecs(**config_dict['Wolf_Creek'])
cordell_hull = ReservoirSpecs(**config_dict['Cordell_Hull'])

storage, actual_release, spill = powersheds.simulate_cascade(inflow, release)
df = pd.read_csv("inflow_release_ex1.csv")
inflow = df['inflow'].tolist()
release = df['release'].tolist()

storage, actual_release, spill = powersheds.simulate_cascade(inflow, release, cordell_hull)
```

```{r}

```{r}
tibble(
    inflow = py$inflow,
    release_target = py$release,
    storage = py$storage,
    release = py$actual_release,
    release_implemented = py$actual_release,
    spill = py$spill
    ) |>
    bind_cols(read_csv("examples/dev/inflow_release_ex1.csv")) |>
    ggplot(aes(datetime, storage)) + geom_line()
    mutate(hr_index = 1:n()) ->
    ts_data_for_plot

powersheds_plot_theme <- function(){
    theme_classic() +
        theme(
            axis.text = element_text(size = 18),
            axis.title = element_text(size = 18),
            legend.text = element_text(size = 18),
            legend.position = "top")
}

ts_data_for_plot |>
    select(hr_index, inflow, release_target, release_implemented, spill) |>
    tidyr::pivot_longer(-hr_index, names_to = "variable") |>
    ggplot(aes(hr_index, value, col = variable)) +
    geom_line() +
    powersheds_plot_theme() -> flows_plot

ts_data_for_plot |>
    select(hr_index, storage) |>
    ggplot(aes(hr_index, storage)) +
    geom_line() +
    powersheds_plot_theme() -> storage_plot

flows_plot + storage_plot + plot_layout(ncol = 1)

```
 No newline at end of file
+18 −13
Original line number Diff line number Diff line
use pyo3::prelude::*;
use pyo3::exceptions::PyValueError;

#[derive(FromPyObject)]
struct ReservoirSpecs {
    capacity: f64,
    initial_storage: f64,
    max_release: f64,
    min_release: f64,
}

/// Simulates a cascade of reservoirs
#[pyfunction]
fn simulate_cascade(inflow: Vec<f64>, release: Vec<f64>) -> PyResult<(Vec<f64>, Vec<f64>, Vec<f64>)> {
fn simulate_cascade(inflow: Vec<f64>,
                    release: Vec<f64>,
                    specs: ReservoirSpecs) -> PyResult<(Vec<f64>, Vec<f64>, Vec<f64>)> {

    // Reservoir specifications
    const CAPACITY: f64 = 100.0;  // Maximum storage in MCM (Million cubic meters)
    const INITIAL_STORAGE: f64 = 50.0;  // Initial storage in MCM
    const MAX_RELEASE: f64 = 15.0;  // Maximum release per timestep in MCM
    const MIN_RELEASE: f64 = 3.0;  // Minimum release per timestep in MCM

    if inflow.len() != release.len() {
        return Err(PyValueError::new_err("Input vectors must have the same length"));
@@ -21,28 +26,28 @@ fn simulate_cascade(inflow: Vec<f64>, release: Vec<f64>) -> PyResult<(Vec<f64>,
    let mut spill = vec![0.0; n];  // Spill for timestep

    // Initialize first timestep storage
    storage[0] = INITIAL_STORAGE;
    storage[0] = specs.initial_storage;

    for t in 0..n {
        let available_water = if t == 0 {
            INITIAL_STORAGE + inflow[t]
            specs.initial_storage + inflow[t]
        } else {
            storage[t-1] + inflow[t]
        };

        // Calculate actual release (constrained by available water and max/min release)
        actual_release[t] = release[t]
            .max(MIN_RELEASE)  // Ensure we meet minimum release
            .min(MAX_RELEASE)  // Cannot exceed maximum release
            .max(specs.min_release)  // Ensure we meet minimum release
            .min(specs.max_release)  // Cannot exceed maximum release
            .min(available_water);  // Cannot release more than available

        // Calculate new storage
        let potential_storage = available_water - actual_release[t];

        // Calculate spill if storage would exceed capacity
        if potential_storage > CAPACITY {
            storage[t] = CAPACITY;
            spill[t] = potential_storage - CAPACITY;
        if potential_storage > specs.capacity {
            storage[t] = specs.capacity;
            spill[t] = potential_storage - specs.capacity;
        } else {
            storage[t] = potential_storage;
            spill[t] = 0.0;