Source code for andes.system.registry

"""
Registry loading helpers for System.
"""

#  [ANDES] (C)2015-2024 Hantao Cui
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 3 of the License, or
#  (at your option) any later version.

import importlib
import inspect
import logging

import andes.models
from andes.models import file_classes
from andes.models.group import GroupBase
from andes.routines import all_routines

logger = logging.getLogger(__name__)


[docs]class RegistryLoader: """ Manage imports and registration for groups, models and routines. """
[docs] def __init__(self, system): self.system = system
# Deprecated group aliases: old_name -> new_name _GROUP_ALIASES = { 'ACTopology': 'ACNode', } def load_all(self): """ Load groups, models and routines in dependency order. """ self.import_groups() self._add_group_aliases() self.import_models() self.import_routines() # routine imports come after models def check_group_common(self): """ Check if all group common variables and parameters are met. This function is called at the end of code generation by `prepare`. Raises ------ KeyError if any parameter or value is not provided """ system = self.system for group in system.groups.values(): for item in group.common_params: for model in group.models.values(): # the below includes all of BaseParam (NumParam, DataParam and ExtParam) if item not in model.__dict__: if item in model.group_param_exception: continue raise KeyError(f'Group <{group.class_name}> common param <{item}> does not exist ' f'in model <{model.class_name}>') for item in group.common_vars: for model in group.models.values(): if item not in model.cache.all_vars: if item in model.group_var_exception: continue raise KeyError(f'Group <{group.class_name}> common var <{item}> does not exist ' f'in model <{model.class_name}>') def import_groups(self): """ Import all groups classes defined in ``models/group.py``. Groups will be stored as instances with the name as class names. All groups will be stored to dictionary ``System.groups``. """ system = self.system module = importlib.import_module('andes.models.group') for m in inspect.getmembers(module, inspect.isclass): name, cls = m if name == 'GroupBase': continue if not issubclass(cls, GroupBase): # skip other imported classes such as `OrderedDict` continue system.__dict__[name] = cls() system.groups[name] = system.__dict__[name] def _add_group_aliases(self): """ Add deprecated group name aliases that point to renamed groups. Each alias shares the same group instance as the new name, so models registered under the new name are visible via either. """ system = self.system for old_name, new_name in self._GROUP_ALIASES.items(): if new_name in system.groups and old_name not in system.groups: system.__dict__[old_name] = system.__dict__[new_name] system.groups[old_name] = system.groups[new_name] logger.debug("Registered deprecated group alias %s -> %s", old_name, new_name) def import_models(self): """ Import and instantiate models as System member attributes. Models defined in ``models/__init__.py`` will be instantiated `sequentially` as attributes with the same name as the class name. In addition, all models will be stored in dictionary ``System.models`` with model names as keys and the corresponding instances as values. Examples -------- ``system.Bus`` stores the `Bus` object, and ``system.GENCLS`` stores the classical generator object, ``system.models['Bus']`` points the same instance as ``system.Bus``. """ system = self.system for fname, cls_list in file_classes: for model_name in cls_list: the_module = importlib.import_module('andes.models.' + fname) the_class = getattr(the_module, model_name) system.__dict__[model_name] = the_class(system=system, config=system._config_object) system.models[model_name] = system.__dict__[model_name] system.models[model_name].config.check() # link to the group group_name = system.__dict__[model_name].group system.__dict__[group_name].add_model(model_name, system.__dict__[model_name]) for key, val in andes.models.model_aliases.items(): system.model_aliases[key] = system.models[val] system.__dict__[key] = system.models[val] def import_routines(self): """ Import routines as defined in ``routines/__init__.py``. Routines will be stored as instances with the name as class names. All routines will be stored to dictionary ``System.routines``. Examples -------- ``System.PFlow`` is the power flow routine instance, and ``System.TDS`` and ``System.EIG`` are time-domain analysis and eigenvalue analysis routines, respectively. """ system = self.system for file, cls_list in all_routines.items(): for cls_name in cls_list: file = importlib.import_module('andes.routines.' + file) the_class = getattr(file, cls_name) attr_name = cls_name system.__dict__[attr_name] = the_class(system=system, config=system._config_object) system.routines[attr_name] = system.__dict__[attr_name] system.routines[attr_name].config.check()