Source code for edisgo.tools.config

"""This file is part of eDisGo, a python package for distribution network
analysis and optimization.

It is developed in the project open_eGo: https://openegoproject.wordpress.com

eDisGo lives on github: https://github.com/openego/edisgo/
The documentation is available on RTD: http://edisgo.readthedocs.io

Based on code by oemof developing group

This module provides a highlevel layer for reading and writing config files.

"""

__copyright__ = "Reiner Lemoine Institut gGmbH"
__license__ = "GNU Affero General Public License Version 3 (AGPL-3.0)"
__url__ = "https://github.com/openego/edisgo/blob/master/LICENSE"
__author__ = "nesnoj, gplssm"


import os
from glob import glob
import shutil
import edisgo
import logging
import datetime

logger = logging.getLogger("edisgo")

try:
    import configparser as cp
except:
    # to be compatible with Python2.7
    import ConfigParser as cp

cfg = cp.RawConfigParser()
_loaded = False

# load config dirs
package_path = edisgo.__path__[0]
internal_config_file = os.path.join(
    package_path, "config", "config_system.cfg"
)
try:
    cfg.read(internal_config_file)
except:
    logger.exception(
        "Internal config {} file not found.".format(internal_config_file)
    )


[docs]class Config: """ Container for all configurations. Parameters ----------- config_path : None or :obj:`str` or :obj:`dict` Path to the config directory. Options are: * None If `config_path` is None configs are loaded from the edisgo default config directory ($HOME$/.edisgo). If the directory does not exist it is created. If config files don't exist the default config files are copied into the directory. * :obj:`str` If `config_path` is a string configs will be loaded from the directory specified by `config_path`. If the directory does not exist it is created. If config files don't exist the default config files are copied into the directory. * :obj:`dict` A dictionary can be used to specify different paths to the different config files. The dictionary must have the following keys: * 'config_db_tables' * 'config_grid' * 'config_grid_expansion' * 'config_timeseries' Values of the dictionary are paths to the corresponding config file. In contrast to the other two options the directories and config files must exist and are not automatically created. Default: None. Notes ----- The Config object can be used like a dictionary. See example on how to use it. Examples -------- Create Config object from default config files >>> from edisgo.tools.config import Config >>> config = Config() Get reactive power factor for generators in the MV network >>> config['reactive_power_factor']['mv_gen'] """ def __init__(self, **kwargs): self._data = self._load_config(kwargs.get("config_path", None)) @staticmethod def _load_config(config_path=None): """ Load config files. Parameters ----------- config_path : None or :obj:`str` or dict See class definition for more information. Returns ------- :obj:`collections.OrderedDict` eDisGo configuration data from config files. """ config_files = [ "config_db_tables", "config_grid", "config_grid_expansion", "config_timeseries", "config_opf_julia", ] # load configs if isinstance(config_path, dict): for conf in config_files: load_config( filename="{}.cfg".format(conf), config_dir=config_path[conf], copy_default_config=False, ) else: for conf in config_files: load_config( filename="{}.cfg".format(conf), config_dir=config_path ) config_dict = cfg._sections # convert numeric values to float for sec, subsecs in config_dict.items(): for subsec, val in subsecs.items(): # try str -> float conversion try: config_dict[sec][subsec] = float(val) except: pass # convert to time object config_dict["demandlib"]["day_start"] = datetime.datetime.strptime( config_dict["demandlib"]["day_start"], "%H:%M" ) config_dict["demandlib"]["day_start"] = datetime.time( config_dict["demandlib"]["day_start"].hour, config_dict["demandlib"]["day_start"].minute, ) config_dict["demandlib"]["day_end"] = datetime.datetime.strptime( config_dict["demandlib"]["day_end"], "%H:%M" ) config_dict["demandlib"]["day_end"] = datetime.time( config_dict["demandlib"]["day_end"].hour, config_dict["demandlib"]["day_end"].minute, ) return config_dict def __getitem__(self, key1, key2=None): if key2 is None: try: return self._data[key1] except: raise KeyError( "Config does not contain section {}.".format(key1) ) else: try: return self._data[key1][key2] except: raise KeyError( "Config does not contain value for {} or " "section {}.".format(key2, key1) ) def __setitem__(self, key, value): self._data[key] = value def __delitem__(self, key): del self._data[key] def __iter__(self): return iter(self._data) def __len__(self): return len(self._data)
[docs]def load_config(filename, config_dir=None, copy_default_config=True): """ Loads the specified config file. Parameters ----------- filename : :obj:`str` Config file name, e.g. 'config_grid.cfg'. config_dir : :obj:`str`, optional Path to config file. If None uses default edisgo config directory specified in config file 'config_system.cfg' in section 'user_dirs' by subsections 'root_dir' and 'config_dir'. Default: None. copy_default_config : Boolean If True copies a default config file into `config_dir` if the specified config file does not exist. Default: True. """ if not config_dir: config_file = os.path.join(get_default_config_path(), filename) else: config_file = os.path.join(config_dir, filename) # config file does not exist -> copy default if not os.path.isfile(config_file): if copy_default_config: logger.info( "Config file {} not found, I will create a " "default version".format(config_file) ) make_directory(config_dir) shutil.copy( os.path.join( package_path, "config", filename.replace(".cfg", "_default.cfg"), ), config_file, ) else: message = "Config file {} not found.".format(config_file) logger.error(message) raise FileNotFoundError(message) if len(cfg.read(config_file)) == 0: message = "Config file {} not found or empty.".format(config_file) logger.error(message) raise FileNotFoundError(message) global _loaded _loaded = True
[docs]def get(section, key): """ Returns the value of a given key of a given section of the main config file. Parameters ----------- section : :obj:`str` key : :obj:`str` Returns -------- float or int or Boolean or str The value which will be casted to float, int or boolean. If no cast is successful, the raw string is returned. """ if not _loaded: pass try: return cfg.getfloat(section, key) except: try: return cfg.getint(section, key) except: try: return cfg.getboolean(section, key) except: return cfg.get(section, key)
[docs]def get_default_config_path(): """ Returns the basic edisgo config path. If it does not yet exist it creates it and copies all default config files into it. Returns -------- :obj:`str` Path to default edisgo config directory specified in config file 'config_system.cfg' in section 'user_dirs' by subsections 'root_dir' and 'config_dir'. """ config_dir = get("user_dirs", "config_dir") root_dir = get("user_dirs", "root_dir") root_path = os.path.join(os.path.expanduser("~"), root_dir) config_path = os.path.join(root_path, config_dir) # root directory does not exist if not os.path.isdir(root_path): # create it logger.info( "eDisGo root path {} not found, I will create it.".format( root_path ) ) make_directory(root_path) # config directory does not exist if not os.path.isdir(config_path): # create it config_path = os.path.join(root_path, config_dir) make_directory(config_path) # copy default config files logger.info( "eDisGo config path {} not found, I will create it.".format( config_path ) ) # copy default config files if they don't exist internal_config_dir = os.path.join(package_path, "config") for file in glob(os.path.join(internal_config_dir, "*.cfg")): filename = os.path.join( config_path, os.path.basename(file).replace("_default", "") ) if not os.path.isfile(filename): logger.info( "I will create a default config file {} in {}".format( file, config_path ) ) shutil.copy(file, filename) return config_path
[docs]def make_directory(directory): """ Makes directory if it does not exist. Parameters ----------- directory : :obj:`str` Directory path """ if not os.path.isdir(directory): os.makedirs(directory) logger.info("Path {} not found, I will create it.".format(directory))