diff --git a/src/libecalc/common/priority_optimizer.py b/src/libecalc/common/priority_optimizer.py index 3931fb8165..6f4ab839ea 100644 --- a/src/libecalc/common/priority_optimizer.py +++ b/src/libecalc/common/priority_optimizer.py @@ -7,30 +7,29 @@ from libecalc.common.priorities import PriorityID -TResult = TypeVar("TResult") - ComponentID = str -@dataclass -class PriorityOptimizerResult(Generic[TResult]): - priority_used: PriorityID - priority_results: List[typing.Any] # TODO: typing. This is the consumer results merged based on priorities used +class EvaluatorResult(typing.Protocol): + id: ComponentID + is_valid: bool + + +TResult = TypeVar("TResult", bound=EvaluatorResult) @dataclass -class EvaluatorResult(Generic[TResult]): - id: ComponentID - result: TResult - is_valid: bool +class PriorityOptimizerResult(Generic[TResult]): + priority_used: PriorityID + priority_results: List[TResult] -class PriorityOptimizer(Generic[TResult]): +class PriorityOptimizer: def optimize( self, priorities: List[PriorityID], - evaluator: typing.Callable[[PriorityID], List[EvaluatorResult[TResult]]], - ) -> PriorityOptimizerResult: + evaluator: typing.Callable[[PriorityID], List[TResult]], + ) -> PriorityOptimizerResult[TResult]: """ Given a list of priorities, evaluate each priority using the evaluator. If the result of an evaluation is valid the priority is selected, if invalid try the next priority. @@ -52,7 +51,7 @@ def optimize( for priority in priorities: evaluator_results = evaluator(priority) for evaluator_result in evaluator_results: - priority_results[priority][evaluator_result.id] = evaluator_result.result + priority_results[priority][evaluator_result.id] = evaluator_result # Check if consumers are valid for this priority, should be valid for all consumers all_evaluator_results_valid = reduce( @@ -65,7 +64,5 @@ def optimize( break return PriorityOptimizerResult( priority_used=priority_used, - priority_results=[ - ecalc_model_result.component_result for ecalc_model_result in priority_results[priority_used].values() - ], + priority_results=list(priority_results[priority_used].values()), ) diff --git a/src/libecalc/core/consumers/base/__init__.py b/src/libecalc/core/consumers/base/__init__.py index 41e4f9a886..bd9d5f4270 100644 --- a/src/libecalc/core/consumers/base/__init__.py +++ b/src/libecalc/core/consumers/base/__init__.py @@ -1 +1 @@ -from .component import BaseConsumer, BaseConsumerWithoutOperationalSettings +from .component import Consumer, ConsumerID diff --git a/src/libecalc/core/consumers/base/component.py b/src/libecalc/core/consumers/base/component.py index c1897a5802..5ae642a336 100644 --- a/src/libecalc/core/consumers/base/component.py +++ b/src/libecalc/core/consumers/base/component.py @@ -3,27 +3,18 @@ from libecalc.common.stream_conditions import TimeSeriesStreamConditions from libecalc.common.utils.rates import TimeSeriesFloat -from libecalc.core.result import EcalcModelResult -from libecalc.dto import VariablesMap +from libecalc.core.consumers.base.result import ConsumerResult - -class BaseConsumer(ABC): - @abstractmethod - def evaluate( - self, - variables_map: VariablesMap, - temporal_operational_settings, - ) -> EcalcModelResult: - ... +ConsumerID = str -class BaseConsumerWithoutOperationalSettings(ABC): - id: str +class Consumer(ABC): + id: ConsumerID @abstractmethod def get_max_rate(self, inlet_stream: TimeSeriesStreamConditions, target_pressure: TimeSeriesFloat) -> List[float]: ... @abstractmethod - def evaluate(self, **kwargs) -> EcalcModelResult: + def evaluate(self, **kwargs) -> ConsumerResult: ... diff --git a/src/libecalc/core/consumers/base/result.py b/src/libecalc/core/consumers/base/result.py new file mode 100644 index 0000000000..f9b866aabd --- /dev/null +++ b/src/libecalc/core/consumers/base/result.py @@ -0,0 +1,77 @@ +from abc import abstractmethod +from datetime import datetime +from functools import partial +from typing import List, Optional, Protocol + +from orjson import orjson +from pydantic import BaseModel, Extra +from pydantic.json import custom_pydantic_encoder + +from libecalc.common.string.string_utils import to_camel_case +from libecalc.core.consumers.base.component import ConsumerID +from libecalc.domain.stream_conditions import Rate, StreamConditions + + +def orjson_dumps(v, *, default, indent: bool = False): + options = orjson.OPT_NON_STR_KEYS | orjson.OPT_SERIALIZE_NUMPY | orjson.OPT_PASSTHROUGH_DATETIME + + if indent: + options = options | orjson.OPT_INDENT_2 + + # orjson.dumps returns bytes, to match standard json.dumps we need to decode + # default is the pydantic json encoder + return orjson.dumps(v, default=default, option=options).decode("utf-8") + + +class EcalcBaseModel(BaseModel): + class Config: + extra = Extra.forbid + alias_generator = to_camel_case + allow_population_by_field_name = True + json_dumps = orjson_dumps + json_encoders = { + datetime: lambda v: v.strftime("%Y-%m-%dT%H:%M:%S"), + } + copy_on_model_validation = "deep" + + def json(self, date_format: Optional[str] = None, **kwargs) -> str: + if date_format is None: + return super().json(**kwargs) + + if kwargs.get("encoder") is None: + # Override datetime encoder if not already overridden, use user specified date_format_option + encoder = partial( + custom_pydantic_encoder, + { + datetime: lambda v: v.strftime(date_format), + }, + ) + else: + encoder = kwargs["encoder"] + + return super().json(**kwargs, encoder=encoder) # Encoder becomes default, i.e. should handle unhandled types + + +class ModelResult(Protocol): + @abstractmethod + @property + def streams(self) -> List[StreamConditions]: + ... + + +class ConsumerResult(EcalcBaseModel): + """Base component for all results: Model, Installation, GenSet, Consumer System, Consumer, etc.""" + + id: ConsumerID + timestep: datetime + is_valid: bool + + # We need both energy usage and power rate since we sometimes want both fuel and power usage. + energy_usage: Rate + power: Optional[Rate] + streams: List[StreamConditions] + + @abstractmethod + @property + def models(self) -> List[ModelResult]: + ... diff --git a/src/libecalc/core/consumers/compressor/component.py b/src/libecalc/core/consumers/compressor/component.py index 125128500f..fe38fead22 100644 --- a/src/libecalc/core/consumers/compressor/component.py +++ b/src/libecalc/core/consumers/compressor/component.py @@ -4,24 +4,17 @@ import numpy as np -from libecalc.common.stream_conditions import TimeSeriesStreamConditions from libecalc.common.units import Unit -from libecalc.common.utils.rates import ( - TimeSeriesBoolean, - TimeSeriesFloat, - TimeSeriesStreamDayRate, -) -from libecalc.core.consumers.base import BaseConsumerWithoutOperationalSettings +from libecalc.core.consumers.base import Consumer +from libecalc.core.consumers.compressor.result import CompressorResult from libecalc.core.models.compressor import CompressorModel -from libecalc.core.result import EcalcModelResult -from libecalc.core.result import results as core_results -from libecalc.domain.stream_conditions import Pressure, StreamConditions +from libecalc.domain.stream_conditions import Pressure, Rate, StreamConditions from libecalc.dto.core_specs.compressor.operational_settings import ( CompressorOperationalSettings, ) -class Compressor(BaseConsumerWithoutOperationalSettings): +class Compressor(Consumer): def __init__(self, id: str, compressor_model: CompressorModel): self.id = id self._compressor_model = compressor_model @@ -43,7 +36,7 @@ def get_max_rate(self, inlet_stream: StreamConditions, target_pressure: Pressure def evaluate( self, streams: List[StreamConditions], - ) -> EcalcModelResult: + ) -> CompressorResult: inlet_streams = streams[:-1] outlet_stream = streams[-1] @@ -59,79 +52,23 @@ def evaluate( outlet_stream.rate = total_requested_inlet_stream.rate - energy_usage = TimeSeriesStreamDayRate( - values=model_result.energy_usage, - timesteps=[current_timestep], - unit=model_result.energy_usage_unit, - ) - - outlet_pressure_before_choke = TimeSeriesFloat( - values=model_result.outlet_pressure_before_choking - if model_result.outlet_pressure_before_choking - else [np.nan], - timesteps=[current_timestep], - unit=Unit.BARA, - ) - - component_result = core_results.CompressorResult( - timesteps=[current_timestep], - power=TimeSeriesStreamDayRate( - values=model_result.power, - timesteps=[current_timestep], - unit=model_result.power_unit, - ).fill_nan(0.0), - energy_usage=energy_usage.fill_nan(0.0), - is_valid=TimeSeriesBoolean(values=model_result.is_valid, timesteps=[current_timestep], unit=Unit.NONE), + return CompressorResult( id=self.id, - recirculation_loss=TimeSeriesStreamDayRate( - values=model_result.recirculation_loss, - timesteps=[current_timestep], - unit=Unit.MEGA_WATT, - ), - rate_exceeds_maximum=TimeSeriesBoolean( - values=model_result.rate_exceeds_maximum, - timesteps=[current_timestep], - unit=Unit.NONE, + timestep=current_timestep, + power=Rate( + value=model_result.power[0], + unit=model_result.power_unit, ), - outlet_pressure_before_choking=outlet_pressure_before_choke, + energy_usage=Rate(value=model_result.energy_usage[0], unit=model_result.energy_usage_unit), + is_valid=model_result.is_valid[0], + recirculation_loss=Rate(value=model_result.recirculation_loss[0], unit=Unit.MEGA_WATT), + rate_exceeds_maximum=model_result.rate_exceeds_maximum[0], streams=[ - TimeSeriesStreamConditions.from_stream_condition(total_requested_inlet_stream), - *[ - TimeSeriesStreamConditions.from_stream_condition(inlet_stream_conditions) - for inlet_stream_conditions in inlet_streams - ], - TimeSeriesStreamConditions.from_stream_condition(outlet_stream), - ], - ) - - return EcalcModelResult( - component_result=component_result, - sub_components=[], - models=[ - core_results.CompressorModelResult( - name="N/A", # No context available to populate model name - timesteps=[current_timestep], - is_valid=TimeSeriesBoolean( - timesteps=[current_timestep], - values=model_result.is_valid, - unit=Unit.NONE, - ), - power=TimeSeriesStreamDayRate( - timesteps=[current_timestep], - values=model_result.power, - unit=model_result.power_unit, - ) - if model_result.power is not None - else None, - energy_usage=TimeSeriesStreamDayRate( - timesteps=[current_timestep], - values=model_result.energy_usage, - unit=model_result.energy_usage_unit, - ), - energy_usage_unit=model_result.energy_usage_unit, - rate_sm3_day=model_result.rate_sm3_day, - stage_results=model_result.stage_results, - failure_status=model_result.failure_status, - ) + total_requested_inlet_stream, + *inlet_streams, + outlet_stream.copy( + {"pressure": Pressure(value=model_result.outlet_pressure_before_choking[0], unit=Unit.BARA)} + ), + outlet_stream, ], ) diff --git a/src/libecalc/core/consumers/compressor/result.py b/src/libecalc/core/consumers/compressor/result.py new file mode 100644 index 0000000000..dd771df72f --- /dev/null +++ b/src/libecalc/core/consumers/compressor/result.py @@ -0,0 +1,11 @@ +from libecalc.core.consumers.base.result import ConsumerResult +from libecalc.domain.stream_conditions import Rate + + +class CompressorResult(ConsumerResult): + recirculation_loss: Rate + rate_exceeds_maximum: bool # Seems very specific, should we replace this and is_valid with failure flags? + + @property + def models(self): + return [] diff --git a/src/libecalc/core/consumers/consumer_system.py b/src/libecalc/core/consumers/consumer_system.py index 6b9b4f7ed6..e70031138d 100644 --- a/src/libecalc/core/consumers/consumer_system.py +++ b/src/libecalc/core/consumers/consumer_system.py @@ -5,10 +5,10 @@ import networkx as nx -from libecalc.common.priority_optimizer import EvaluatorResult from libecalc.common.utils.rates import ( TimeSeriesInt, ) +from libecalc.core.consumers.base.result import ConsumerResult from libecalc.core.consumers.compressor import Compressor from libecalc.core.consumers.pump import Pump from libecalc.core.result import ComponentResult, ConsumerSystemResult, EcalcModelResult @@ -96,7 +96,7 @@ def _get_stream_conditions_adjusted_for_crossover( return adjusted_stream_conditions - def evaluate_consumers(self, system_stream_conditions: Dict[str, List[StreamConditions]]) -> List[EvaluatorResult]: + def evaluate_consumers(self, system_stream_conditions: Dict[str, List[StreamConditions]]) -> List[ConsumerResult]: """ Function to evaluate the consumers in the system given stream conditions for a single point in time @@ -112,14 +112,7 @@ def evaluate_consumers(self, system_stream_conditions: Dict[str, List[StreamCond consumer_results_for_priority = [ consumer.evaluate(adjusted_system_stream_conditions[consumer.id]) for consumer in self._consumers ] - return [ - EvaluatorResult( - id=consumer_result_for_priority.component_result.id, - result=consumer_result_for_priority, - is_valid=consumer_result_for_priority.component_result.is_valid.values[0], - ) - for consumer_result_for_priority in consumer_results_for_priority - ] + return consumer_results_for_priority @staticmethod def get_system_result( diff --git a/src/libecalc/core/consumers/legacy_consumer/component.py b/src/libecalc/core/consumers/legacy_consumer/component.py index b1ee56f4af..e15fe4878d 100644 --- a/src/libecalc/core/consumers/legacy_consumer/component.py +++ b/src/libecalc/core/consumers/legacy_consumer/component.py @@ -1,5 +1,6 @@ import itertools import math +from abc import ABC, abstractmethod from collections import defaultdict from datetime import datetime from typing import DefaultDict, Iterable, List, Union @@ -19,7 +20,6 @@ TimeSeriesInt, TimeSeriesStreamDayRate, ) -from libecalc.core.consumers.base import BaseConsumer from libecalc.core.consumers.legacy_consumer.consumer_function import ( ConsumerFunctionResult, ) @@ -61,6 +61,16 @@ def get_operational_settings_used_from_consumer_result( ConsumerResult = Union[ConsumerSystemResult, PumpResult, CompressorResult] +class BaseConsumer(ABC): + @abstractmethod + def evaluate( + self, + variables_map: VariablesMap, + temporal_operational_settings, + ) -> EcalcModelResult: + ... + + class Consumer(BaseConsumer): def __init__( self, diff --git a/src/libecalc/core/consumers/pump/__init__.py b/src/libecalc/core/consumers/pump/__init__.py index bef37c0130..345a6ba2fd 100644 --- a/src/libecalc/core/consumers/pump/__init__.py +++ b/src/libecalc/core/consumers/pump/__init__.py @@ -1 +1,2 @@ from .component import Pump +from .result import PumpResult diff --git a/src/libecalc/core/consumers/pump/component.py b/src/libecalc/core/consumers/pump/component.py index 2cd25c67a8..48ad61cce3 100644 --- a/src/libecalc/core/consumers/pump/component.py +++ b/src/libecalc/core/consumers/pump/component.py @@ -4,22 +4,14 @@ import numpy as np -from libecalc.common.stream_conditions import TimeSeriesStreamConditions -from libecalc.common.units import Unit -from libecalc.common.utils.rates import ( - TimeSeriesBoolean, - TimeSeriesFloat, - TimeSeriesStreamDayRate, -) -from libecalc.core.consumers.base import BaseConsumerWithoutOperationalSettings +from libecalc.core.consumers.base import Consumer +from libecalc.core.consumers.pump.result import PumpResult from libecalc.core.models.pump import PumpModel -from libecalc.core.result import EcalcModelResult -from libecalc.core.result import results as core_results -from libecalc.domain.stream_conditions import Pressure, StreamConditions +from libecalc.domain.stream_conditions import Pressure, Rate, StreamConditions from libecalc.dto.core_specs.pump.operational_settings import PumpOperationalSettings -class Pump(BaseConsumerWithoutOperationalSettings): +class Pump(Consumer): def __init__(self, id: str, pump_model: PumpModel): self.id = id self._pump_model = pump_model @@ -43,7 +35,7 @@ def get_max_rate(self, inlet_stream: StreamConditions, target_pressure: Pressure def evaluate( self, streams: List[StreamConditions], - ) -> EcalcModelResult: + ) -> PumpResult: inlet_streams = streams[:-1] outlet_stream = streams[-1] @@ -59,78 +51,19 @@ def evaluate( outlet_stream.rate = total_requested_inlet_stream.rate - component_result = core_results.PumpResult( + return PumpResult( id=self.id, - timesteps=[current_timestep], - power=TimeSeriesStreamDayRate( - values=model_result.power, - timesteps=[current_timestep], + timestep=current_timestep, + power=Rate( + value=model_result.power[0], unit=model_result.power_unit, - ).fill_nan(0.0), - energy_usage=TimeSeriesStreamDayRate( - values=model_result.energy_usage, - timesteps=[current_timestep], - unit=model_result.energy_usage_unit, - ).fill_nan(0.0), - inlet_liquid_rate_m3_per_day=TimeSeriesStreamDayRate( - values=model_result.rate, - timesteps=[current_timestep], - unit=Unit.STANDARD_CUBIC_METER_PER_DAY, ), - inlet_pressure_bar=TimeSeriesFloat( - values=model_result.suction_pressure, - timesteps=[current_timestep], - unit=Unit.BARA, - ), - outlet_pressure_bar=TimeSeriesFloat( - values=model_result.discharge_pressure, - timesteps=[current_timestep], - unit=Unit.BARA, - ), - operational_head=TimeSeriesFloat( - values=model_result.operational_head, - timesteps=[current_timestep], - unit=Unit.POLYTROPIC_HEAD_KILO_JOULE_PER_KG, - ), - is_valid=TimeSeriesBoolean(values=model_result.is_valid, timesteps=[current_timestep], unit=Unit.NONE), + energy_usage=Rate(value=model_result.energy_usage[0], unit=model_result.energy_usage_unit), + is_valid=model_result.is_valid[0], + operational_head=model_result.operational_head[0], streams=[ - TimeSeriesStreamConditions.from_stream_condition(total_requested_inlet_stream), - *[ - TimeSeriesStreamConditions.from_stream_condition(inlet_stream_conditions) - for inlet_stream_conditions in inlet_streams - ], - TimeSeriesStreamConditions.from_stream_condition(outlet_stream), - ], - ) - - return EcalcModelResult( - component_result=component_result, - sub_components=[], - models=[ - core_results.PumpModelResult( - name="N/A", # No context available to populate model name - timesteps=[current_timestep], - is_valid=TimeSeriesBoolean( - timesteps=[current_timestep], - values=model_result.is_valid, - unit=Unit.NONE, - ), - power=TimeSeriesStreamDayRate( - timesteps=[current_timestep], - values=model_result.power, - unit=model_result.power_unit, - ) - if model_result.power is not None - else None, - energy_usage=TimeSeriesStreamDayRate( - timesteps=[current_timestep], - values=model_result.energy_usage, - unit=model_result.energy_usage_unit, - ), - inlet_liquid_rate_m3_per_day=model_result.rate, - inlet_pressure_bar=model_result.suction_pressure, - outlet_pressure_bar=model_result.discharge_pressure, - operational_head=model_result.operational_head, - ) + total_requested_inlet_stream, + *inlet_streams, + outlet_stream, ], ) diff --git a/src/libecalc/core/consumers/pump/result.py b/src/libecalc/core/consumers/pump/result.py new file mode 100644 index 0000000000..0c991a4752 --- /dev/null +++ b/src/libecalc/core/consumers/pump/result.py @@ -0,0 +1,9 @@ +from libecalc.core.consumers.base.result import ConsumerResult + + +class PumpResult(ConsumerResult): + operational_head: float + + @property + def models(self): + return [] diff --git a/src/libecalc/core/ecalc.py b/src/libecalc/core/ecalc.py index 61cf57509e..16900a2c5a 100644 --- a/src/libecalc/core/ecalc.py +++ b/src/libecalc/core/ecalc.py @@ -1,7 +1,6 @@ from collections import defaultdict from datetime import datetime -from functools import reduce -from typing import Dict +from typing import Dict, List import numpy as np @@ -12,22 +11,21 @@ from libecalc.common.priority_optimizer import PriorityOptimizer from libecalc.common.units import Unit from libecalc.common.utils.rates import TimeSeriesInt, TimeSeriesString +from libecalc.core.consumers.base.result import ConsumerResult +from libecalc.core.consumers.compressor.result import CompressorResult from libecalc.core.consumers.consumer_system import ConsumerSystem from libecalc.core.consumers.factory import create_consumer from libecalc.core.consumers.generator_set import Genset from libecalc.core.consumers.legacy_consumer.component import Consumer from libecalc.core.consumers.venting_emitter import VentingEmitter from libecalc.core.models.fuel import FuelModel -from libecalc.core.result import ComponentResult, EcalcModelResult +from libecalc.core.result import CompressorResult as TimeSeriesCompressorResult +from libecalc.core.result import EcalcModelResult from libecalc.core.result.emission import EmissionResult from libecalc.dto.component_graph import ComponentGraph from libecalc.dto.types import ConsumptionType -def merge_results(results_per_timestep: Dict[datetime, EcalcModelResult]) -> EcalcModelResult: - return reduce(lambda acc, x: acc.merge(x), results_per_timestep.values()) - - class EnergyCalculator: def __init__( self, @@ -70,7 +68,7 @@ def evaluate_energy_usage(self, variables_map: dto.VariablesMap) -> Dict[str, Ec ) optimizer = PriorityOptimizer() - results_per_timestep: Dict[str, Dict[datetime, ComponentResult]] = defaultdict(dict) + results_per_timestep: Dict[str, Dict[datetime, ConsumerResult]] = defaultdict(dict) priorities_used = TimeSeriesString( timesteps=[], values=[], @@ -91,7 +89,7 @@ def evaluate_energy_usage(self, variables_map: dto.VariablesMap) -> Dict[str, Ec component_conditions=component_dto.component_conditions, ) - def evaluator(priority: PriorityID): + def evaluator(priority: PriorityID) -> List[ConsumerResult]: stream_conditions_for_priority = evaluated_stream_conditions[priority] stream_conditions_for_timestep = { component_id: [ @@ -113,8 +111,10 @@ def evaluator(priority: PriorityID): consumer_ids = [consumer.id for consumer in component_dto.consumers] merged_consumer_results = [] for consumer_id in consumer_ids: - first_result, *rest_results = list(results_per_timestep[consumer_id].values()) - merged_consumer_results.append(first_result.merge(*rest_results)) + results = list(results_per_timestep[consumer_id].values()) + if isinstance(results[0], CompressorResult): + TimeSeriesCompressorResult.from_domain(*results) + # TODO from single timestep results to time-series-result, remove merge? # Convert to legacy compatible operational_settings_used priorities_to_int_map = { diff --git a/src/libecalc/core/models/compressor/base.py b/src/libecalc/core/models/compressor/base.py index fa6aa3de36..ba27ae8d18 100644 --- a/src/libecalc/core/models/compressor/base.py +++ b/src/libecalc/core/models/compressor/base.py @@ -66,7 +66,7 @@ def evaluate_streams( self, inlet_streams: List[StreamConditions], outlet_stream: StreamConditions, - ): + ) -> CompressorTrainResult: raise NotImplementedError diff --git a/src/libecalc/core/result/base.py b/src/libecalc/core/result/base.py index 74bb16e394..018dc602c4 100644 --- a/src/libecalc/core/result/base.py +++ b/src/libecalc/core/result/base.py @@ -29,6 +29,7 @@ def extend(self, other: Self) -> Self: f"Concatenating two temporal compressor model results where attribute {attribute} changes" f" over time. The result is ambiguous and leads to loss of information." ) + self.__setattr__(attribute, None) elif isinstance(values, EcalcResultBaseModel): # In case of nested models such as compressor with turbine values.extend(other_values) diff --git a/src/libecalc/core/result/results.py b/src/libecalc/core/result/results.py index 1c75289701..e29a879252 100644 --- a/src/libecalc/core/result/results.py +++ b/src/libecalc/core/result/results.py @@ -8,6 +8,7 @@ from libecalc.common.stream_conditions import TimeSeriesStreamConditions from libecalc.common.tabular_time_series import TabularTimeSeriesUtils +from libecalc.common.units import Unit from libecalc.common.utils.rates import ( TimeSeriesBoolean, TimeSeriesFloat, @@ -17,6 +18,7 @@ from libecalc.core.models.results import CompressorTrainResult from libecalc.core.result.base import EcalcResultBaseModel from libecalc.dto.base import ComponentType +from libecalc.core.consumers.compressor.result import CompressorResult as CoreCompressorResult class CommonResultBase(EcalcResultBaseModel): @@ -66,6 +68,29 @@ class CompressorResult(GenericComponentResult): outlet_pressure_before_choking: TimeSeriesFloat streams: List[TimeSeriesStreamConditions] = None # Optional because only in v2 + @staticmethod + def from_domain(self, *results: CoreCompressorResult) -> CompressorResult: + compressor_result = CompressorResult( + id=results[0].id, + timesteps=[], + is_valid=TimeSeriesBoolean( + timesteps=[], + values=[], + unit=Unit.NONE, + ), + energy_usage=TimeSeriesStreamDayRate( + values=[], + timesteps=[], + unit=results[0].energy_usage.unit, + ) + energy_usage: TimeSeriesStreamDayRate + power: Optional[TimeSeriesStreamDayRate] + + ) + return CompressorResult( + timesteps=results. + ) + def get_subset(self, indices: List[int]) -> Self: return self.__class__( id=self.id, @@ -87,18 +112,6 @@ class PumpResult(GenericComponentResult): streams: List[TimeSeriesStreamConditions] = None # Optional because only in v2 - def get_subset(self, indices: List[int]) -> Self: - return self.__class__( - id=self.id, - timesteps=[self.timesteps[index] for index in indices], - energy_usage=self.energy_usage[indices], - is_valid=self.is_valid[indices], - power=self.power[indices] if self.power is not None else None, - inlet_liquid_rate_m3_per_day=self.inlet_liquid_rate_m3_per_day[indices], - inlet_pressure_bar=self.inlet_pressure_bar[indices], - outlet_pressure_bar=self.outlet_pressure_bar[indices], - operational_head=self.operational_head[indices], - ) class ConsumerModelResultBase(ABC, CommonResultBase):