import warnings
from configparser import ConfigParser, MissingSectionHeaderError
from numpy import linspace
from pathlib import Path
from math import log
import json
[docs]class Config:
"""
Class holding all simulation-relevant information. Read in from configuration file.
After creation, the generated instance is available as Config.config,
the parsed file as Config.parser.
The attributes defining paths i.e. for loading/storing scenarios and results are only
available, if `project_path` parameter is given e.g. using the match_market.py script.
Config attributes, grouped by section, with default in brackets below:
[default]
- start - initial timestep [8]\n
- nb_ts - number of timesteps to simulate [3]\n
- nb_actors - number of actors in network [5]\n
- nb_nodes - number of nodes in network [4]\n
- ts_per_hour - number of timesteps within one hour [4]\n
- list_ts - list of timesteps in simulation [generated, can't be overridden]\n
- show_plots - show various plots [False]\n
- show_prints - show debug info in terminal [False]\n
- save_csv - save orders and matched results to csv files [True]\n
- scenario_path - path of scenario directory to load and/or store (optional)\n
- project_path - path of project directory for all in-/output files (optional)\n
- data_format - how to save actor data. Supported values\n
csv: save data in separate csv file and all actors in one config file,\n
[json]: save config and data per actor in a single file\n
- reset_market: if set, discard unmatched orders after each interval [True]\n
- update_scenario: if set, always save scenario in given path (even if loaded) [False]\n
- market_type: selects matching strategy. Supported values\n
[pab]/basic (pay-as-bid)\n
pac/2pac (two-sided pay-as-clear)\n
fair (custom BEST market)\n
- energy_unit: size of energy units to be traded individually [0.01]\n
- weight_factor: conversion factor from grid fees to power network node weight [0.03]\n
- horizon: number of time steps to look ahead for prediction [24]\n
- actor_strategy: (currently unused) default actor strategy [None]\n
- schedule_update_step: every x-th time step the actor market schedule should be updated [1]
:param cfg_file: configuration file path with the attributes listed above.
:type cfg_file: str
:param project_dir: project folder path that should contain all in-/output files. [None]
:type project_dir: str
:keyword cfg_file: start
"""
def __init__(self, cfg_file, project_dir=None):
global config
config = self
global parser
parser = ConfigParser()
if not cfg_file:
# An empty string cannot be a valid filename and is evaluated as no file
print("No Configuration file path was provided. Default values will be used.")
elif not Path(cfg_file).is_file():
warnings.warn(
f"{cfg_file} was provided as Configuration file, but this file does not "
"exist. Default values will be used.")
try:
parser.read(cfg_file)
except MissingSectionHeaderError:
# headless config: insert missing section header
with open(cfg_file, 'r') as f:
config_string = "[default]\n" + f.read()
parser.read_string(config_string)
# default section: basic simulation properties
# --------------------------
# scenario
# --------------------------
if project_dir:
self.project_path = Path(project_dir)
self.scenario_path = parser.get("default", "scenario_path",
fallback=str(self.project_path / "scenario"))
self.scenario_path = Path(self.scenario_path)
self.data_format = parser.get("default", "data_format", fallback="json")
self.buy_sell_lin_param = json.loads(
parser.get("default", "buy_sell_lin_param", fallback="[0, 1]"))
# load existing scenario
self.load_scenario = parser.getboolean("default", "load_scenario", fallback=False)
# For generating random scenario
# number actors in simulation
self.nb_actors = parser.getint('default', 'nb_actors', fallback=5)
# number of nodes in simulation
self.nb_nodes = parser.getint('default', 'nb_nodes', fallback=4)
# Tolerance value for assertions, comparison and so on
self.EPS = parser.getfloat("default", "EPS", fallback=1e-6)
self.round_decimal = round(log(1 / self.EPS, 10))
# --------------------------
# market
# --------------------------
# market type to be use
self.market_type = parser.get("default", "market_type", fallback="pab").lower()
self.disputed_matching = parser.get("default", "disputed_matching",
fallback="grid_fee").lower()
# reset market after each interval (discard unmatched orders)
self.reset_market = parser.getboolean("default", "reset_market", fallback=True)
# size of energy units to be traded individually
self.energy_unit = parser.getfloat("default", "energy_unit", fallback=0.01)
# factor describing the relation of grid fee to cumulative power network edge weights
self.weight_factor = parser.getfloat("default", "weight_factor", fallback=0.03)
# default grid_fee to be used by market maker
self.default_grid_fee = parser.getfloat("default", "default_grid_fee", fallback=0)
# local grid fee to be used
self.local_grid_fee = parser.getfloat("default", "local_grid_fee", fallback=0)
# time related
# start date of time series data
self.start_date = parser.get("default", "start_date", fallback="2016-01-01")
# start time step
self.start = parser.getint("default", "start", fallback=0)
# number of timesteps in simulation
self.nb_ts = parser.getint("default", "nb_ts", fallback=5)
# number of timesteps within one hour
self.ts_per_hour = parser.getint("default", "ts_per_hour", fallback=1)
# list of timesteps in simulation
# not read from file but created from above information
self.list_ts = linspace(self.start, self.start + self.nb_ts - 1, self.nb_ts)
# --------------------------
# actor
# --------------------------
# Horizon up to which energy management is considered and predictions are made
self.horizon = parser.getint("default", "horizon", fallback=24)
# [unused] strategy that every actor uses if not specified
self.actor_strategy = parser.getint("default", "actor_strategy", fallback=None)
# every x-th time step the actor market schedule should be updated
self.schedule_update_step = parser.getint("default", "schedule_update_step", fallback=1)
# --------------------------
# output
# --------------------------
# show various plots
self.show_plots = parser.getboolean("default", "show_plots", fallback=False)
# print warning info to console
self.verbose = parser.getboolean("default", "verbose", fallback=False)
# print debug info to console
self.debug = parser.getboolean("default", "debug", fallback=False)
# print intermediate results info to console
self.show_prints = parser.getboolean("default", "show_prints", fallback=False)
# save orders and matching results to csv files
self.save_csv = parser.getboolean("default", "save_csv", fallback=False)
if project_dir:
self.results_path = parser.get("default", "results_path",
fallback=str(self.project_path / "market_results"))
self.results_path = Path(self.results_path)
else:
assert not self.save_csv, "Config marks save_csv as true, " \
"but not project_dir is given. "