Skip to content

Commit

Permalink
feat: add stream conditions to compressor v2 (#194)
Browse files Browse the repository at this point in the history
Also expanded merge to handle recursive objects and lists.
  • Loading branch information
jsolaas authored Sep 26, 2023
1 parent 018b472 commit 232f83b
Show file tree
Hide file tree
Showing 14 changed files with 3,478 additions and 30 deletions.
26 changes: 26 additions & 0 deletions src/libecalc/common/stream.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from typing import Literal

from libecalc.common.string_utils import to_camel_case
from libecalc.common.utils.rates import TimeSeriesFloat, TimeSeriesRate
from pydantic import BaseModel, Extra


class StreamCondition(BaseModel):
class Config:
extra = Extra.forbid
alias_generator = to_camel_case
allow_population_by_field_name = True

rate: TimeSeriesRate
pressure: TimeSeriesFloat
fluid_density: TimeSeriesFloat = None


class Stage(BaseModel):
class Config:
extra = Extra.forbid
alias_generator = to_camel_case
allow_population_by_field_name = True

name: Literal["inlet", "before_choke", "outlet"]
stream_condition: StreamCondition
27 changes: 27 additions & 0 deletions src/libecalc/common/tabular_time_series.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import itertools
from typing import Protocol, TypeVar

from libecalc.common.list_utils import transpose
from libecalc.common.utils.rates import TimeSeries
from pydantic import BaseModel
from typing_extensions import Self


Expand Down Expand Up @@ -52,5 +54,30 @@ def merge(cls, *objects_with_time_series: ObjectWithTimeSeries):
merged_object.__setattr__(key, merged_timesteps)
elif isinstance(value, TimeSeries):
merged_object.__setattr__(key, accumulated_value.merge(other_value))
elif isinstance(value, BaseModel):
merged_object.__setattr__(
key, cls.merge(*[obj.__getattribute__(key) for obj in objects_with_time_series])
)
elif (
isinstance(value, list)
and len(value) > 0
and (isinstance(value[0], TimeSeries) or isinstance(value[0], BaseModel))
):
list_attributes = [obj.__getattribute__(key) for obj in objects_with_time_series]
transposed_list_attributes = transpose(list_attributes)
merged_list_attributes = []
if isinstance(value[0], TimeSeries):
for time_series_to_merge in transposed_list_attributes:
first_time_series, *others_time_series = time_series_to_merge
merged_time_series = first_time_series
for other_time_series in others_time_series:
merged_time_series = merged_time_series.merge(other_time_series)
merged_list_attributes.append(merged_time_series)
elif isinstance(value[0], BaseModel):
merged_list_attributes = [
cls.merge(*objs_to_merge) for objs_to_merge in transposed_list_attributes
]

merged_object.__setattr__(key, merged_list_attributes)

return merged_object
52 changes: 44 additions & 8 deletions src/libecalc/core/consumers/compressor/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import numpy as np
from libecalc import dto
from libecalc.common.exceptions import ProgrammingError
from libecalc.common.stream import Stage, StreamCondition
from libecalc.common.temporal_model import TemporalModel
from libecalc.common.units import Unit
from libecalc.common.utils.rates import (
Expand Down Expand Up @@ -75,7 +76,7 @@ def evaluate(
evaluated_timesteps = []

# TODO: This is a false assumption and will be dealt with shortly (that the regularity is the same
# for all timesteps, and only taken for the first timestep)
# for all timesteps, and only taken for the first timestep). Not the first timestep, the first rate
evaluated_regularity = operational_settings.stream_day_rates[0].regularity
for timestep in operational_settings.timesteps:
compressor = self._temporal_model.get_model(timestep)
Expand Down Expand Up @@ -108,6 +109,24 @@ def evaluate(
if energy_usage.unit == Unit.STANDARD_CUBIC_METER_PER_DAY:
energy_usage = energy_usage.to_calendar_day() # provide fuel usage in calendar day, same as legacy consumer

# Creating a single input rate until we decide how to deal with multiple rates, multiple rates added because of
# multiple streams and pressures model, but we need to look at how streams are defined there.
total_requested_rate = TimeSeriesRate(
timesteps=operational_settings.timesteps,
values=list(np.sum([rate.values for rate in operational_settings.stream_day_rates], axis=0)),
unit=operational_settings.stream_day_rates[0].unit,
regularity=operational_settings.stream_day_rates[0].regularity,
rate_type=operational_settings.stream_day_rates[0].rate_type,
)

outlet_pressure_before_choke = TimeSeriesFloat(
values=aggregated_result.outlet_pressure_before_choking
if aggregated_result.outlet_pressure_before_choking
else [np.nan for _ in evaluated_timesteps],
timesteps=evaluated_timesteps,
unit=Unit.BARA,
)

component_result = core_results.CompressorResult(
timesteps=evaluated_timesteps,
power=TimeSeriesRate(
Expand All @@ -132,13 +151,30 @@ def evaluate(
timesteps=evaluated_timesteps,
unit=Unit.NONE,
),
outlet_pressure_before_choking=TimeSeriesFloat(
values=aggregated_result.outlet_pressure_before_choking
if aggregated_result.outlet_pressure_before_choking
else [np.nan for _ in evaluated_timesteps],
timesteps=evaluated_timesteps,
unit=Unit.BARA,
),
outlet_pressure_before_choking=outlet_pressure_before_choke,
stages=[
Stage(
name="inlet",
stream_condition=StreamCondition(
rate=total_requested_rate,
pressure=operational_settings.inlet_pressure,
),
),
Stage(
name="before_choke",
stream_condition=StreamCondition(
rate=total_requested_rate,
pressure=outlet_pressure_before_choke,
),
),
Stage(
name="outlet",
stream_condition=StreamCondition(
rate=total_requested_rate,
pressure=operational_settings.outlet_pressure,
),
),
],
)

return EcalcModelResult(
Expand Down
2 changes: 2 additions & 0 deletions src/libecalc/core/result/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from datetime import datetime
from typing import Any, Dict, List, Optional, Union

from libecalc.common.stream import Stage
from libecalc.common.tabular_time_series import TabularTimeSeriesUtils
from libecalc.common.utils.rates import (
TimeSeriesBoolean,
Expand Down Expand Up @@ -62,6 +63,7 @@ class CompressorResult(GenericComponentResult):
recirculation_loss: TimeSeriesRate
rate_exceeds_maximum: TimeSeriesBoolean
outlet_pressure_before_choking: TimeSeriesFloat
stages: List[Stage] = None # Optional because only in v2

def get_subset(self, indices: List[int]) -> Self:
return self.__class__(
Expand Down
3 changes: 3 additions & 0 deletions src/libecalc/dto/result/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from libecalc.common.component_info.component_level import ComponentLevel
from libecalc.common.logger import logger
from libecalc.common.stream import Stage
from libecalc.common.time_utils import Frequency
from libecalc.common.units import Unit
from libecalc.common.utils.rates import (
Expand Down Expand Up @@ -117,6 +118,8 @@ class CompressorResult(EquipmentResultBase):
rate_exceeds_maximum: TimeSeriesBoolean
outlet_pressure_before_choking: TimeSeriesFloat

stages: List[Stage] = None # Optional because only in v2


class DirectEmitterResult(EquipmentResultBase):
componentType: Literal[ComponentType.DIRECT_EMITTER]
Expand Down
5 changes: 2 additions & 3 deletions src/libecalc/input/mappers/component_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ def from_yaml_to_dto(

if EcalcYamlKeywords.type in data:
if data[EcalcYamlKeywords.type] == ComponentType.COMPRESSOR_SYSTEM_V2:
compressor_system_yaml = YamlCompressorSystem(**data)
try:
compressor_system_yaml = YamlCompressorSystem(**data)
return compressor_system_yaml.to_dto(
consumes=consumes,
regularity=regularity,
Expand All @@ -120,9 +120,8 @@ def from_yaml_to_dto(
except ValidationError as e:
raise DtoValidationError(data=data, validation_error=e) from e
if data[EcalcYamlKeywords.type] == ComponentType.PUMP_SYSTEM_V2:
pump_system_yaml = YamlPumpSystem(**data)

try:
pump_system_yaml = YamlPumpSystem(**data)
return pump_system_yaml.to_dto(
consumes=consumes,
regularity=regularity,
Expand Down
3 changes: 1 addition & 2 deletions src/libecalc/input/validation_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,8 @@ def __init__(
error_key: Optional[str] = None,
dump_flow_style: Optional[DumpFlowStyle] = None,
):
self.message = message
super().__init__(message)

self.message = message
extended_message = "\nError in object\n"

if data is not None:
Expand Down
Loading

0 comments on commit 232f83b

Please sign in to comment.