Source code for idr_iisim.models.model
"""Module to parse industry's processes"""
import os
from abc import ABC, abstractmethod
from functools import partial
from typing import Any, Callable, TypedDict, Union
from sympy import parse_expr
from idr_iisim.templates import load_template
from idr_iisim.utils.structs import (
ConstantStruct,
InputStruct,
ItemStruct,
MetaStruct,
ModelStruct,
OutputStruct,
)
[docs]
class FunctionsMapType(TypedDict):
"""Type definition for FunctionsMap.
Attributes:
function (Callable[..., Any]): The function to be called.
args (list[dict[str, Any]]): List of arguments for the function.
expression (str): The expression associated with this function.
description (str): Description of the function.
"""
function: Callable[..., Any]
args: list[dict[str, Any]]
expression: str
description: str
[docs]
class Model(ABC):
"""Abstract base class representing a model in the system.
It contains the model configuration, functions map, results, and external inputs.
Attributes:
directory (str): Directory of the model file.
inputs (dict[str, InputStruct]): Dictionary holding input structures.
outputs (dict[str, OutputStruct]): Dictionary holding output structures.
constants (dict[str, ConstantStruct]): Dictionary holding constant structures.
functions_map (dict[str, FunctionsMapType]): Map of functions.
config (Union[ModelStruct, MetaStruct]): Configuration of the model.
"""
def __init__(self, path: str):
"""Initialize the Model with the provided path.
Args:
path (str): Path to the model configuration file.
"""
self.directory = os.path.dirname(path)
self.inputs: dict[str, InputStruct] = {}
self.outputs: dict[str, OutputStruct] = {}
self.constants: dict[str, ConstantStruct] = {}
self.functions_map: dict[str, FunctionsMapType] = {}
self.config: Union[ModelStruct, MetaStruct]
[docs]
def process_config(
self, items: list[ItemStruct], config: ModelStruct
) -> None:
"""Process configuration elements and initialize model attributes.
Args:
items (list[ItemStruct]): Items to process for configuration.
config (ModelStruct): The model configuration structure.
Raises:
ValueError: If any constant or input value is out of its valid range.
"""
for item in items:
operation = parse_expr(item.operation)
f = partial(lambda op, **kwargs: op.subs(kwargs), op=operation)
key = item.name
self.functions_map[key] = {
"function": f,
"args": item.args,
"expression": operation,
"description": item.description,
}
# Parse constants
for constant in config.constants:
self.constants[constant.name] = constant
if constant.range and not (
constant.range[0] <= constant.value <= constant.range[-1]
):
raise ValueError(
f"Constant '{constant.name}' in process "
+ f"'{config.name}' is not inside the valid range"
+ f" ({constant.value} not inside {constant.range})"
)
# Parse outputs
for output in config.outputs:
self.outputs[output.name] = output
# Parse inputs
for input_field in config.inputs:
self.inputs[input_field.name] = input_field
if input_field.range:
for value in input_field.value:
if (
value < input_field.range[0]
or value > input_field.range[-1]
):
raise ValueError(
f"Input '{input_field.name}' in process "
+ f"'{config.name}' is not inside the valid range"
+ f" ({input_field.value} not inside {input_field.range})"
)
[docs]
def constants_generator(self) -> str:
"""Generate code for constants defined in the model configuration.
Returns:
str: The generated code for constants, or an empty string if none are defined.
"""
constants_code = ""
# Generate constants dynamically from model configuration
constants_code = "\n".join(
f"{constant.name} = {constant.value} # {constant.description}"
for constant in self.config.constants
)
if constants_code:
constants_code = (
f"# {self.config.name}'s constants\n" + constants_code
)
return constants_code
[docs]
@abstractmethod
def get_getter_items(self) -> list[tuple[str, str]]:
"""Generate items' descriptions to configure as getters.
Returns:
list[tuple[str, str]]: A list of tuples containing variable names
and their descriptions.
"""
[docs]
def getters_generator(self) -> str:
"""Generate getter methods for the model.
Returns:
str: The generated getter methods as string.
"""
getters = []
# Load the template content
template_path = "templates/template_generated_getter.txt"
getter_template = load_template(template_path)
# outputs
for variable_name, description in self.get_getter_items():
getter_script = getter_template.substitute(
name=variable_name, description=description
)
getters.append(getter_script)
return "\n".join(getters)