-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: pump v2 support for single speed #348
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from dataclasses import dataclass | ||
from datetime import datetime | ||
from typing import Any, Dict, Generic, List, Optional, Protocol, TypeVar | ||
|
||
from libecalc.common.temporal_model import TemporalModel | ||
|
||
# from libecalc.core.result import EcalcModelResult # TODO: Cannot include due to circular import ...yet | ||
from libecalc.domain.stream_conditions import StreamConditions | ||
from libecalc.dto.base import ComponentType, ConsumerUserDefinedCategoryType | ||
|
||
|
||
class Evaluationable(Protocol): | ||
def evaluate(self, streams: List[StreamConditions]) -> Any: | ||
... | ||
|
||
|
||
EquipmentType = TypeVar("EquipmentType", bound=Evaluationable) | ||
|
||
|
||
@dataclass | ||
class TemporalEquipment(Generic[EquipmentType]): | ||
""" | ||
V2 only. | ||
|
||
The temporal layer of equipment, includes metadata and the equipment domain model itself for each timestep, basically | ||
a parsed and flattened version of the yaml | ||
|
||
Except from data, the properties are just needed for metadata, aggregation etc for LTP and similar | ||
|
||
This class should be the same for all equipment represented in e.g. yaml, basically a wrapper around the core domain layer | ||
""" | ||
|
||
id: Optional[str] | ||
component_type: Optional[ComponentType] | ||
name: Optional[str] | ||
user_defined_category: Optional[Dict[datetime, ConsumerUserDefinedCategoryType]] | ||
fuel: Optional[Dict[datetime, Any]] # cannot specify FuelType due to circular import .. | ||
data: TemporalModel[EquipmentType] | ||
|
||
def evaluate(self, stream_conditions: Dict[datetime, List[StreamConditions]]) -> Any: | ||
""" | ||
Evaluate the temporal domain models. | ||
|
||
TODO: Here we might want to use cache somehow, either for this single run wrt same results for same timesteps, | ||
but also across runs etc | ||
|
||
Args: | ||
stream_conditions: | ||
|
||
Returns: | ||
|
||
""" | ||
if not stream_conditions: | ||
raise ValueError(f"Missing stream conditions for {self.name}") | ||
|
||
result = None | ||
for timestep, model in self.data.items(): | ||
if result is None: # TODO: Use map reduce | ||
result = model.evaluate(stream_conditions.get(timestep.start)) | ||
else: | ||
result.extend(model.evaluate(stream_conditions.get(timestep.start))) | ||
|
||
return result |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,7 +33,16 @@ def extend(self, other: Self) -> Self: | |
# In case of nested models such as compressor with turbine | ||
values.extend(other_values) | ||
elif isinstance(values, list): | ||
if isinstance(other_values, list): | ||
# Temporary v2 only | ||
# We are not able to currently import PumpModelResult due to circular import, therefore we use duck typing... | ||
if ( | ||
len(values) == 1 | ||
and len(other_values) == 1 | ||
and hasattr(values[0], "extend") | ||
and hasattr(other_values[0], "extend") | ||
): | ||
values[0].extend(other_values[0]) | ||
Comment on lines
+36
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why was this added? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Due to the comment above...circular import. Next step with v2 will have to be to sort out a few ofthe ciruclar import issues.. |
||
elif isinstance(other_values, list): | ||
self.__setattr__(attribute, values + other_values) | ||
else: | ||
self.__setattr__(attribute, values + [other_values]) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
from abc import ABC | ||
from collections import defaultdict | ||
from datetime import datetime | ||
from typing import Dict, List, Literal, Optional, TypeVar, Union | ||
from typing import Any, Dict, List, Literal, Optional, TypeVar, Union | ||
|
||
try: | ||
from pydantic.v1 import Field, root_validator | ||
|
@@ -25,6 +25,8 @@ | |
TimeSeriesFloat, | ||
TimeSeriesStreamDayRate, | ||
) | ||
|
||
# from libecalc.core.consumers.pump import Pump # TODO: Cannot import due to circular deps .. | ||
from libecalc.dto.base import ( | ||
Component, | ||
ComponentType, | ||
|
@@ -293,7 +295,10 @@ class GeneratorSet(BaseEquipment): | |
component_type: Literal[ComponentType.GENERATOR_SET] = ComponentType.GENERATOR_SET | ||
fuel: Dict[datetime, FuelType] | ||
generator_set_model: Dict[datetime, GeneratorSetSampled] | ||
consumers: List[Union[ElectricityConsumer, ConsumerSystem]] = Field(default_factory=list) | ||
consumers: List[Union[ElectricityConsumer, ConsumerSystem, Any]] = Field( | ||
default_factory=list | ||
) # Any here is Pump, that cannot be explicitly specified due to circular import ...temporalequipment? | ||
# Any must be set in order for pydantic to accept/validate the consumer | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How do we fix this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not a quick fix, but solve all the "hacks" where we have incorrect directions of dependencies. |
||
_validate_genset_temporal_models = validator("generator_set_model", "fuel", allow_reuse=True)( | ||
validate_temporal_model | ||
) | ||
|
@@ -382,6 +387,30 @@ def id(self): | |
def installation_ids(self) -> List[str]: | ||
return [installation.id for installation in self.installations] | ||
|
||
def get_component_by_id(self, id: str) -> Optional[Component]: | ||
""" | ||
Get a component by id, if it exists, otherwise None | ||
Args: | ||
id: | ||
|
||
Returns: | ||
|
||
""" | ||
for installation in self.installations: | ||
if installation.id == id: | ||
return installation | ||
for fuel_consumer in installation.fuel_consumers: | ||
if fuel_consumer.id == id: | ||
return fuel_consumer | ||
if isinstance(fuel_consumer, dto.GeneratorSet): | ||
for electricity_consumer in fuel_consumer.consumers: | ||
if electricity_consumer.id == id: | ||
return electricity_consumer | ||
for venting_emitter in installation.venting_emitters: | ||
if venting_emitter.id == id: | ||
return venting_emitter | ||
return None | ||
|
||
def get_component_ids_for_installation_id(self, installation_id: str) -> List[str]: | ||
installation = self.get_installation(installation_id) | ||
component_ids = [] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When is it None?