diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3a539c0c..8f974905 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,7 +18,7 @@ repos: rev: 24.4.2 hooks: - id: black - language_version: python3.8 + language_version: python3.10 - repo: https://github.com/PyCQA/flake8 rev: 4.0.1 hooks: diff --git a/gsy_framework/constants_limits.py b/gsy_framework/constants_limits.py index 98b174a9..951dde09 100644 --- a/gsy_framework/constants_limits.py +++ b/gsy_framework/constants_limits.py @@ -214,6 +214,15 @@ class HeatPumpSettings: CALIBRATION_COEFFICIENT = 0.6 CALIBRATION_COEFFICIENT_RANGE = RangeLimit(0, 1) + class EVChargerSettings: + """Default settings for EV charger assets.""" + + # Range limits + MAX_POWER_RATING_KW_LIMIT = RangeLimit(0, sys.maxsize) + + # default values + MAX_POWER_RATING_KW = 10 + class MASettings: """Default settings for Market Agents.""" diff --git a/gsy_framework/enums.py b/gsy_framework/enums.py index 7e2ad5a1..1eb80598 100644 --- a/gsy_framework/enums.py +++ b/gsy_framework/enums.py @@ -92,6 +92,26 @@ class HeatPumpSourceType(Enum): GROUND = 1 +class GridIntegrationType(Enum): + """Selection of grid integration types for EV chargers. + + - Unidirectional: The charger can only charge EVs, even if the connected EV + supports bidirectional charging. + - Bidirectional: The charger can both charge and discharge, provided the + connected EV supports bidirectional charging. + """ + + UNIDIRECTIONAL = 0 + BIDIRECTIONAL = 1 + + +class EVChargerStatus(Enum): + """Status of the EV charger.""" + + IDLE = 0 + ACTIVE = 1 + + class SCMPropertyType(Enum): """Enum for SCM fee types""" diff --git a/gsy_framework/validators/__init__.py b/gsy_framework/validators/__init__.py index 71ed1dbe..b8b20c19 100644 --- a/gsy_framework/validators/__init__.py +++ b/gsy_framework/validators/__init__.py @@ -13,6 +13,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ + # Explicitly declare the names of the module's public API __all__ = [ "CommercialProducerValidator", @@ -24,12 +25,14 @@ "PVValidator", "StorageValidator", "WindTurbineValidator", - "HeatPumpValidator" + "HeatPumpValidator", + "EVChargerValidator", ] from gsy_framework.validators.cep_validator import CommercialProducerValidator from gsy_framework.validators.finite_diesel_generator_validator import ( - FiniteDieselGeneratorValidator) + FiniteDieselGeneratorValidator, +) from gsy_framework.validators.smart_meter_validator import SmartMeterValidator from gsy_framework.validators.infinite_bus_validator import InfiniteBusValidator from gsy_framework.validators.load_validator import LoadValidator @@ -38,3 +41,4 @@ from gsy_framework.validators.storage_validator import StorageValidator from gsy_framework.validators.wind_turbine_validator import WindTurbineValidator from gsy_framework.validators.heat_pump_validator import HeatPumpValidator +from gsy_framework.validators.ev_charger_validator import EVChargerValidator diff --git a/gsy_framework/validators/ev_charger_validator.py b/gsy_framework/validators/ev_charger_validator.py new file mode 100644 index 00000000..37506bbf --- /dev/null +++ b/gsy_framework/validators/ev_charger_validator.py @@ -0,0 +1,51 @@ +from gsy_framework.constants_limits import ConstSettings +from gsy_framework.exceptions import GSyDeviceException +from gsy_framework.validators.base_validator import BaseValidator +from gsy_framework.enums import GridIntegrationType +from gsy_framework.validators.utils import validate_range_limit + +EVChargerSettings = ConstSettings.EVChargerSettings + + +class EVChargerValidator(BaseValidator): + """Validator class for EV Charger assets.""" + + @classmethod + def validate(cls, **kwargs): + cls._validate_grid_integration(**kwargs) + cls._validate_max_power(**kwargs) + + @staticmethod + def _validate_grid_integration(**kwargs): + if kwargs.get("grid_integration"): + try: + if kwargs["grid_integration"] is not None: + GridIntegrationType(kwargs["grid_integration"]) + except ValueError as ex: + raise GSyDeviceException( + { + "misconfiguration": [ + "EV Charger grid integration not one of " + f"{[st.value for st in GridIntegrationType]}" + ] + } + ) from ex + + @classmethod + def _validate_max_power(cls, **kwargs): + """Validate energy related arguments.""" + name = "maximum_power_rating_kW" + value = kwargs.get("maximum_power_rating_kW") + min_limit = EVChargerSettings.MAX_POWER_RATING_KW_LIMIT.min + max_limit = EVChargerSettings.MAX_POWER_RATING_KW_LIMIT.max + if value is None: + raise GSyDeviceException( + {"misconfiguration": [f"Value of {name} should not be None."]} + ) + + validate_range_limit( + min_limit, + value, + max_limit, + {"misconfiguration": [f"{name} should be between {min_limit} & {max_limit}."]}, + )