example_lake_model.py

  1"""
  2An example of the lake problem using the ema workbench.
  3
  4The model itself is adapted from the Rhodium example by Dave Hadka,
  5see https://gist.github.com/dhadka/a8d7095c98130d8f73bc
  6
  7"""
  8
  9import math
 10
 11import numpy as np
 12from scipy.optimize import brentq
 13
 14from ema_workbench import (
 15    Model,
 16    RealParameter,
 17    ScalarOutcome,
 18    Constant,
 19    ema_logging,
 20    MultiprocessingEvaluator,
 21)
 22from ema_workbench.em_framework.evaluators import Samplers
 23
 24
 25def lake_problem(
 26    b=0.42,  # decay rate for P in lake (0.42 = irreversible)
 27    q=2.0,  # recycling exponent
 28    mean=0.02,  # mean of natural inflows
 29    stdev=0.001,  # future utility discount rate
 30    delta=0.98,  # standard deviation of natural inflows
 31    alpha=0.4,  # utility from pollution
 32    nsamples=100,  # Monte Carlo sampling of natural inflows
 33    **kwargs,
 34):
 35    try:
 36        decisions = [kwargs[str(i)] for i in range(100)]
 37    except KeyError:
 38        decisions = [0] * 100
 39        print("No valid decisions found, using 0 water release every year as default")
 40
 41    nvars = len(decisions)
 42    decisions = np.array(decisions)
 43
 44    # Calculate the critical pollution level (Pcrit)
 45    Pcrit = brentq(lambda x: x**q / (1 + x**q) - b * x, 0.01, 1.5)
 46
 47    # Generate natural inflows using lognormal distribution
 48    natural_inflows = np.random.lognormal(
 49        mean=math.log(mean**2 / math.sqrt(stdev**2 + mean**2)),
 50        sigma=math.sqrt(math.log(1.0 + stdev**2 / mean**2)),
 51        size=(nsamples, nvars),
 52    )
 53
 54    # Initialize the pollution level matrix X
 55    X = np.zeros((nsamples, nvars))
 56
 57    # Loop through time to compute the pollution levels
 58    for t in range(1, nvars):
 59        X[:, t] = (
 60            (1 - b) * X[:, t - 1]
 61            + (X[:, t - 1] ** q / (1 + X[:, t - 1] ** q))
 62            + decisions[t - 1]
 63            + natural_inflows[:, t - 1]
 64        )
 65
 66    # Calculate the average daily pollution for each time step
 67    average_daily_P = np.mean(X, axis=0)
 68
 69    # Calculate the reliability (probability of the pollution level being below Pcrit)
 70    reliability = np.sum(X < Pcrit) / float(nsamples * nvars)
 71
 72    # Calculate the maximum pollution level (max_P)
 73    max_P = np.max(average_daily_P)
 74
 75    # Calculate the utility by discounting the decisions using the discount factor (delta)
 76    utility = np.sum(alpha * decisions * np.power(delta, np.arange(nvars)))
 77
 78    # Calculate the inertia (the fraction of time steps with changes larger than 0.02)
 79    inertia = np.sum(np.abs(np.diff(decisions)) > 0.02) / float(nvars - 1)
 80
 81    return max_P, utility, inertia, reliability
 82
 83
 84if __name__ == "__main__":
 85    ema_logging.log_to_stderr(ema_logging.INFO)
 86
 87    # instantiate the model
 88    lake_model = Model("lakeproblem", function=lake_problem)
 89    lake_model.time_horizon = 100
 90
 91    # specify uncertainties
 92    lake_model.uncertainties = [
 93        RealParameter("b", 0.1, 0.45),
 94        RealParameter("q", 2.0, 4.5),
 95        RealParameter("mean", 0.01, 0.05),
 96        RealParameter("stdev", 0.001, 0.005),
 97        RealParameter("delta", 0.93, 0.99),
 98    ]
 99
100    # set levers, one for each time step
101    lake_model.levers = [RealParameter(str(i), 0, 0.1) for i in range(lake_model.time_horizon)]
102
103    # specify outcomes
104    lake_model.outcomes = [
105        ScalarOutcome("max_P"),
106        ScalarOutcome("utility"),
107        ScalarOutcome("inertia"),
108        ScalarOutcome("reliability"),
109    ]
110
111    # override some of the defaults of the model
112    lake_model.constants = [Constant("alpha", 0.41), Constant("nsamples", 150)]
113
114    # generate some random policies by sampling over levers
115    n_scenarios = 1000
116    n_policies = 4
117
118    with MultiprocessingEvaluator(lake_model) as evaluator:
119        res = evaluator.perform_experiments(n_scenarios, n_policies, lever_sampling=Samplers.MC)