diff --git a/pyproject.toml b/pyproject.toml index 9e4816c..a59aaf0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ classifiers = [ "Operating System :: OS Independent", ] dependencies = [ - "infrasys~=0.2.1", + "infrasys~=0.2.3", "jsonschema~=4.23", "loguru~=0.7.2", "pandas~=2.2", diff --git a/src/r2x/exporter/handler.py b/src/r2x/exporter/handler.py index 5a8b614..0258b69 100644 --- a/src/r2x/exporter/handler.py +++ b/src/r2x/exporter/handler.py @@ -11,6 +11,7 @@ import numpy as np import infrasys from loguru import logger +from pint import Quantity from r2x.api import System from r2x.config_scenario import Scenario @@ -161,8 +162,9 @@ def export_data_files(self, year: int, time_series_folder: str = "Data") -> None string_template = string.Template(csv_fname) for component_type, (datetime_array, time_series) in datetime_arrays.items(): - time_series_arrays = list(map(lambda x: x.data.to_numpy(), time_series)) - + time_series_arrays = list( + map(lambda x: x.data.magnitude if isinstance(x.data, Quantity) else x.data, time_series) + ) config_dict["component_type"] = component_type csv_fname = string_template.safe_substitute(config_dict) csv_table = np.column_stack([datetime_array, *time_series_arrays]) diff --git a/src/r2x/exporter/plexos.py b/src/r2x/exporter/plexos.py index 5579fc3..7641dcf 100644 --- a/src/r2x/exporter/plexos.py +++ b/src/r2x/exporter/plexos.py @@ -49,7 +49,7 @@ from r2x.units import get_magnitude from r2x.utils import custom_attrgetter, get_enum_from_string, read_json -NESTED_ATTRIBUTES = ["ext", "bus", "services"] +NESTED_ATTRIBUTES = {"ext", "bus", "services"} TIME_SERIES_PROPERTIES = ["Min Provision", "Static Risk"] DEFAULT_XML_TEMPLATE = "master_9.2R6_btu.xml" EXT_PROPERTIES = {"UoS Charge", "Fixed Load"} @@ -226,7 +226,7 @@ def insert_component_properties( filter_func: Callable | None = None, scenario: str | None = None, records: list[dict] | None = None, - exclude_fields: list[str] | None = NESTED_ATTRIBUTES, + exclude_fields: set[str] | None = NESTED_ATTRIBUTES, ) -> None: """Bulk insert properties from selected component type.""" logger.debug("Adding {} table properties...", component_type.__name__) @@ -437,6 +437,7 @@ def add_topology(self) -> None: # Add node memberships to zone and regions. # On our default Plexos translation, both Zones and Regions are child of the Node class. for bus in self.system.get_components(ACBus): + bus_load_zone = bus.load_zone self._db_mgr.add_membership( bus.name, bus.name, # Zone has the same name @@ -444,9 +445,11 @@ def add_topology(self) -> None: child_class=ClassEnum.Region, collection=CollectionEnum.Region, ) + if bus_load_zone is None: + continue self._db_mgr.add_membership( bus.name, - bus.load_zone.name, + bus_load_zone.name, parent_class=ClassEnum.Node, child_class=ClassEnum.Zone, collection=CollectionEnum.Zone, @@ -663,7 +666,7 @@ def add_reserves(self) -> None: Reserve, parent_class=ClassEnum.System, collection=CollectionEnum.Reserves, - exclude_fields=[*NESTED_ATTRIBUTES, "max_requirement"], + exclude_fields=NESTED_ATTRIBUTES | {"max_requirement"}, ) for reserve in self.system.get_components(Reserve): properties: dict[str, Any] = {} @@ -700,13 +703,15 @@ def add_reserves(self) -> None: # Add Regions properties. Currently, we only add the load_risk component_dict = reserve.model_dump( - exclude_none=True, exclude=[*NESTED_ATTRIBUTES, "max_requirement"] + exclude_none=True, exclude=NESTED_ATTRIBUTES | {"max_requirement"} ) if not reserve.region: return + reserve_region = reserve.region + assert reserve_region is not None regions = self.system.get_components( - ACBus, filter_func=lambda x: x.load_zone.name == reserve.region.name + ACBus, filter_func=lambda x: x.load_zone.name == reserve_region.name ) collection_properties = self._db_mgr.get_valid_properties( diff --git a/src/r2x/parser/reeds.py b/src/r2x/parser/reeds.py index 19af33d..6d799b8 100644 --- a/src/r2x/parser/reeds.py +++ b/src/r2x/parser/reeds.py @@ -440,13 +440,15 @@ def _construct_generators(self) -> None: # noqa: C901 ) bus = self.system.get_component(ACBus, name=row["region"]) row["bus"] = bus + bus_load_zone = bus.load_zone + assert bus_load_zone is not None # Add reserves/services to generator if they are not excluded if row["tech"] not in self.reeds_config.defaults["excluded_reserve_techs"]: row["services"] = list( self.system.get_components( Reserve, - filter_func=lambda x: x.region.name == bus.load_zone.name, + filter_func=lambda x: x.region.name == bus_load_zone.name, ) ) reserve_map = self.system.get_component(ReserveMap, name="reserve_map") @@ -643,7 +645,9 @@ def _construct_reserve_provision(self): ) solar_capacity = list( map( - lambda component: self.system.get_component_by_label(component.label).active_power, + lambda component: self.system.get_component_by_label( + component.label + ).active_power.magnitude, provision_objects["solar"], ) ) @@ -735,7 +739,9 @@ def _construct_hydro_budgets(self) -> None: if generator.category == "can-imports": continue tech = generator.ext["reeds_tech"] - region = generator.bus.name + generator_bus = generator.bus + assert generator_bus + region = generator_bus.name hydro_ratings = hydro_data.filter((pl.col("tech") == tech) & (pl.col("region") == region)) hourly_time_series = np.zeros(len(month_of_day), dtype=float) @@ -781,7 +787,9 @@ def _construct_hydro_rating_profiles(self) -> None: initial_time = datetime(self.weather_year, 1, 1) for generator in self.system.get_components(HydroEnergyReservoir): tech = generator.ext["reeds_tech"] - region = generator.bus.name + generator_bus = generator.bus + assert generator_bus is not None + region = generator_bus.name hourly_time_series = np.zeros(len(month_of_hour), dtype=float) hydro_ratings = hydro_data.filter((pl.col("tech") == tech) & (pl.col("region") == region))