class_docstr: Very simple model of a power system.
  Real life is more complicated!
  
  Parameters
  ----------
  num_dams: int
      number of dams to be managed
  depth: int
      number of layers in the neural networks
  width: int
      number of neurons per hidden layer
  year_to_day_ratio: float = 2.
      Ratio between std of consumption in the year and std of consumption in the day.
  constant_to_year_ratio: float
      Ratio between constant baseline consumption and std of consumption in the year.
  back_to_normal: float
      Part of the variability which is forgotten at each time step.
  consumption_noise: float
      Instantaneous variability.
  num_thermal_plants: int
      Number of thermal plants.
  num_years: float
      Number of years.
  failure_cost: float
      Cost of not satisfying the demand. Equivalent to an expensive infinite capacity thermal plant.
class_docstr_tok: ["Very","simple","model","of","a","power","system",".","Real","life","is","more","complicated","!","Parameters","--","--","--","--","--","num_dams",":","int","number","of","dams","to","be","managed","depth",":","int","number","of","layers","in","the","neural","networks","width",":","int","number","of","neurons","per","hidden","layer","year_to_day_ratio",":","float","=","2",".","Ratio","between","std","of","consumption","in","the","year","and","std","of","consumption","in","the","day",".","constant_to_year_ratio",":","float","Ratio","between","constant","baseline","consumption","and","std","of","consumption","in","the","year",".","back_to_normal",":","float","Part","of","the","variability","which","is","forgotten","at","each","time","step",".","consumption_noise",":","float","Instantaneous","variability",".","num_thermal_plants",":","int","Number","of","thermal","plants",".","num_years",":","float","Number","of","years",".","failure_cost",":","float","Cost","of","not","satisfying","the","demand",".","Equivalent","to","an","expensive","infinite","capacity","thermal","plant","."]
code: def _simulate_power_system(self, *arrays: np.ndarray) -> float:
      failure_cost = (
          self.failure_cost
      )  # Cost of power demand which is not satisfied (equivalent to a expensive infinite thermal group).
      dam_agents = self.dam_agents
      for agent, array in zip(dam_agents, arrays):
          agent.set_parameters(array)
  
      self.marginal_costs = []
  
      num_dams = int(self.num_dams)
      # Assume empty initial stocks.
      stocks = np.zeros((num_dams,))
      # Nonsense delays.
      delay = np.cos(np.arange(num_dams))
      cost = 0.0
      # Loop on time steps.
      num_time_steps = int(365 * 24 * self.number_of_years)
      consumption = 0.0
      hydro_prod_per_time_step: tp.List[tp.Any] = []
      consumption_per_time_step: tp.List[float] = []
      for t in range(num_time_steps):
          # Rain
          stocks += (
              0.5
              * (1.0 + np.cos(2 * pi * t / (24 * 365) + delay))
              * np.random.rand(num_dams)
          )
          # Consumption model.
          base_consumption = (
              self.constant_to_year_ratio * self.year_to_day_ratio
              + 0.5
              * self.year_to_day_ratio
              * (1.0 + cos(2 * pi * t / (24 * 365)))
              + 0.5 * (1.0 + cos(2 * pi * t / 24))
          )
  
          if t == 0:
              consumption = base_consumption
          else:
              consumption = max(
                  0.0,
                  consumption
                  + self.consumption_noise * (np.random.rand() - 0.5)
                  + self.back_to_normal
                  * (base_consumption - consumption),
              )
          consumption_per_time_step += [consumption]
          # "Needed" stores what we need, and will decrease as we use various power plants for producing.
          needed = consumption
  
          # Setting inputs for all agents.
          base_x = [
              cos(2 * pi * t / 24.0),
              sin(2 * pi * t / 24.0),
              cos(2 * pi * t / (365 * 24)),
              sin(2 * pi * t / (365 * 24)),
              needed,
              self.average_consumption,
              self.year_to_day_ratio,
              self.constant_to_year_ratio,
              self.back_to_normal,
              self.consumption_noise,
          ]
  
          x = np.concatenate((base_x, self.thermal_power_capacity, self.thermal_power_prices, stocks))  # type: ignore
  
          # Prices as a decomposition tool!
          price: np.ndarray = np.asarray(
              [a.get_output(x)[0] for a in dam_agents]
          )
          dam_index = np.arange(num_dams)
          price = np.concatenate((price, self.thermal_power_prices))
          capacity = np.concatenate(
              (stocks, self.thermal_power_capacity)
          )
          dam_index = np.concatenate(
              (dam_index, -1 * np.ones(len(price), dtype=int))
          )
  
          assert len(price) == num_dams + self.num_thermal_plants
          hydro_prod: np.ndarray = np.zeros(num_dams)
  
          # Let us rank power plants by production cost.
          order = sorted(
              range(len(price)), key=lambda x: price[x]
          )  # pylint: disable=cell-var-from-loop
          price = price[order]
          capacity = capacity[order]
          dam_index = dam_index[order]
  
          # Using power plants in their cost order, so that we use cheap power plants first.
          marginal_cost = 0.0
          for i, _ in enumerate(price):
              if needed <= 0:
                  break
              production = min(capacity[i], needed)
              # If this is a dam, producing will reduce the stock.
              if dam_index[i] >= 0:
                  hydro_prod[
                      dam_index[i]
                  ] += production  # Let us log the hydro prod for this dam.
                  stocks[dam_index[i]] -= production
                  assert stocks[dam_index[i]] >= -1e-7
              else:
                  # If this is not a dam, we pay for using thermal plants.
                  cost += production * price[i]
                  if production > 1e-7:
                      marginal_cost = price[i]
  
              needed -= production
          # Cost in case of failures -- this is
          # harming industries and hospitals, so it can be penalized.
          cost += failure_cost * needed
          if needed > 1e-7:
              marginal_cost = failure_cost
          self.marginal_costs += [marginal_cost]
  
          hydro_prod_per_time_step += [hydro_prod]
      # Other data of interest: , hydro_prod, hydro_prod_per_time_step, consumption_per_time_step
      assert (
          len(hydro_prod_per_time_step) == num_time_steps
      )  # Each time steps has 1 value per dam.
      assert len(consumption_per_time_step) == num_time_steps
      self.hydro_prod_per_time_step = hydro_prod_per_time_step
      self.consumption_per_time_step = consumption_per_time_step
      self.losses += [cost]
      return cost
  
file_path: nevergrad/nevergrad/functions/powersystems/core.py
imports: import typing
  from math import pi, cos, sin
  import matplotlib.pyplot
  import numpy
  from nevergrad.parametrization import parameter
  from ..base import ExperimentFunction
label_desc: Use image node_id 3 for calling the PowerSystem obj's underlying member method code with example usage: obj._simulate_power_system() and returns: cost