Skip to content

Variables

This module contains definitions of LUME-model variables for use with lume tools. Variables are designed as pure descriptors and thus aren't intended to hold actual values, but they can be used to validate encountered values.

For now, only scalar floating-point variables are supported.

Variable

Bases: BaseModel, ABC

Abstract variable base class.

Attributes:

Name Type Description
name str

Name of the variable.

Source code in lume_model/variables.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Variable(BaseModel, ABC):
    """Abstract variable base class.

    Attributes:
        name: Name of the variable.
    """
    name: str

    @property
    @abstractmethod
    def default_validation_config(self) -> ConfigEnum:
        """Determines default behavior during validation."""
        return None

    @abstractmethod
    def validate_value(self, value: Any, config: dict[str, bool] = None):
        pass

    def model_dump(self, **kwargs) -> dict[str, Any]:
        config = super().model_dump(**kwargs)
        return {"variable_class": self.__class__.__name__} | config

default_validation_config abstractmethod property

Determines default behavior during validation.

ScalarVariable

Bases: Variable

Variable for float values.

Attributes:

Name Type Description
default_value Optional[float]

Default value for the variable. Note that the LUMEBaseModel requires this for input variables, but it is optional for output variables.

value_range Optional[tuple[float, float]]

Value range that is considered valid for the variable. If the value range is set to None, the variable is interpreted as a constant and values are validated against the default value.

is_constant Optional[bool]

Flag indicating whether the variable is constant.

unit Optional[str]

Unit associated with the variable.

Source code in lume_model/variables.py
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
class ScalarVariable(Variable):
    """Variable for float values.

    Attributes:
        default_value: Default value for the variable. Note that the LUMEBaseModel requires this
          for input variables, but it is optional for output variables.
        value_range: Value range that is considered valid for the variable. If the value range is set to None,
          the variable is interpreted as a constant and values are validated against the default value.
        is_constant: Flag indicating whether the variable is constant.
        unit: Unit associated with the variable.
    """
    model_config = ConfigDict(arbitrary_types_allowed=True)

    default_value: Optional[float] = None
    value_range: Optional[tuple[float, float]] = (-np.inf, np.inf)
    is_constant: Optional[bool] = False
    unit: Optional[str] = None

    @field_validator("value_range", mode="before")
    @classmethod
    def validate_value_range(cls, value):
        if value is not None:
            value = tuple(value)
            if not value[0] <= value[1]:
                raise ValueError(f"Minimum value ({value[0]}) must be lower or equal than maximum ({value[1]}).")
        return value

    @model_validator(mode="after")
    def validate_default_value(self):
        if self.default_value is not None and self.value_range is not None:
            if not self._value_is_within_range(self.default_value):
                raise ValueError(
                    "Default value ({}) is out of valid range "
                    "([{},{}]).".format(self.default_value, *self.value_range)
                )
        return self

    @property
    def default_validation_config(self) -> ConfigEnum:
        return "warn"

    def validate_value(self, value: float, config: ConfigEnum = None):
        """
        Validates the given value.

        Args:
            value (float): The value to be validated.
            config (ConfigEnum, optional): The configuration for validation. Defaults to None.
              Allowed values are "none", "warn", and "error".

        Raises:
            TypeError: If the value is not of type float.
            ValueError: If the value is out of the valid range or does not match the default value
              for constant variables.
        """
        _config = self.default_validation_config if config is None else config
        # mandatory validation
        self._validate_value_type(value)
        # validate defaults for constant inputs
        if self.is_constant:
            self._validate_constant_value(value)
        # optional validation
        if config != "none":
            self._validate_value_is_within_range(value, config=_config)

    @staticmethod
    def _validate_value_type(value: float):
        if not isinstance(value, float):
            raise TypeError(
                f"Expected value to be of type {float} or {np.float64}, "
                f"but received {type(value)}."
            )

    def _validate_value_is_within_range(self, value: float, config: ConfigEnum = None):
        if not self._value_is_within_range(value):
            error_message = "Value ({}) of '{}' is out of valid range.".format(value, self.name)
            if self.value_range is not None:
                error_message = error_message[:-1] + " ([{},{}]).".format(*self.value_range)
            if config == "warn":
                print("Warning: " + error_message)
            else:
                raise ValueError(error_message)

    def _value_is_within_range(self, value) -> bool:
        self.value_range = self.value_range or (-np.inf, np.inf)
        return self.value_range[0] <= value <= self.value_range[1]

    def _validate_constant_value(self, value: float):
        if not self.default_value == value:
            raise ValueError(
                f"Expected value to be {self.default_value} for constant variable '{self.name}', "
                f"but received {value}."
            )

validate_value(value, config=None)

Validates the given value.

Parameters:

Name Type Description Default
value float

The value to be validated.

required
config ConfigEnum

The configuration for validation. Defaults to None. Allowed values are "none", "warn", and "error".

None

Raises:

Type Description
TypeError

If the value is not of type float.

ValueError

If the value is out of the valid range or does not match the default value for constant variables.

Source code in lume_model/variables.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
def validate_value(self, value: float, config: ConfigEnum = None):
    """
    Validates the given value.

    Args:
        value (float): The value to be validated.
        config (ConfigEnum, optional): The configuration for validation. Defaults to None.
          Allowed values are "none", "warn", and "error".

    Raises:
        TypeError: If the value is not of type float.
        ValueError: If the value is out of the valid range or does not match the default value
          for constant variables.
    """
    _config = self.default_validation_config if config is None else config
    # mandatory validation
    self._validate_value_type(value)
    # validate defaults for constant inputs
    if self.is_constant:
        self._validate_constant_value(value)
    # optional validation
    if config != "none":
        self._validate_value_is_within_range(value, config=_config)