Skip to content

Commit

Permalink
refactor: avoid using dtos in core
Browse files Browse the repository at this point in the history
Remove dto usage in Consumer and Genset
  • Loading branch information
jsolaas committed Sep 10, 2024
1 parent 11e5e4e commit 76a472e
Show file tree
Hide file tree
Showing 9 changed files with 267 additions and 96 deletions.
33 changes: 31 additions & 2 deletions src/libecalc/application/energy_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@
from libecalc.common.math.numbers import Numbers
from libecalc.common.priorities import PriorityID
from libecalc.common.priority_optimizer import PriorityOptimizer
from libecalc.common.temporal_model import TemporalModel
from libecalc.common.units import Unit
from libecalc.common.utils.rates import TimeSeriesInt, TimeSeriesString
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.legacy_consumer.consumer_function_mapper import EnergyModelMapper
from libecalc.core.models.fuel import FuelModel
from libecalc.core.models.generator import GeneratorModelSampled
from libecalc.core.result import ComponentResult, EcalcModelResult
from libecalc.core.result.emission import EmissionResult
from libecalc.dto.component_graph import ComponentGraph
Expand Down Expand Up @@ -47,10 +50,36 @@ def evaluate_energy_usage(self, variables_map: dto.VariablesMap) -> Dict[str, Ec

for component_dto in component_dtos:
if isinstance(component_dto, (dto.ElectricityConsumer, dto.FuelConsumer)):
consumer = Consumer(consumer_dto=component_dto)
consumer = Consumer(
id=component_dto.id,
name=component_dto.name,
component_type=component_dto.component_type,
regularity=TemporalModel(component_dto.regularity),
consumes=component_dto.consumes,
energy_usage_model=TemporalModel(
{
start_time: EnergyModelMapper.from_dto_to_domain(model)
for start_time, model in component_dto.energy_usage_model.items()
}
),
)
consumer_results[component_dto.id] = consumer.evaluate(variables_map=variables_map)
elif isinstance(component_dto, dto.GeneratorSet):
fuel_consumer = Genset(component_dto)
fuel_consumer = Genset(
id=component_dto.id,
name=component_dto.name,
temporal_generator_set_model=TemporalModel(
{
start_time: GeneratorModelSampled(
fuel_values=model.fuel_values,
power_values=model.power_values,
energy_usage_adjustment_constant=model.energy_usage_adjustment_constant,
energy_usage_adjustment_factor=model.energy_usage_adjustment_factor,
)
for start_time, model in component_dto.generator_set_model.items()
}
),
)

power_requirement = elementwise_sum(
*[
Expand Down
25 changes: 11 additions & 14 deletions src/libecalc/core/consumers/generator_set.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import numpy as np
from numpy.typing import NDArray

from libecalc import dto
from libecalc.common.list.list_utils import array_to_list
from libecalc.common.logger import logger
from libecalc.common.temporal_model import TemporalModel
Expand All @@ -20,16 +19,14 @@
class Genset:
def __init__(
self,
data_transfer_object: dto.GeneratorSet,
id: str,
name: str,
temporal_generator_set_model: TemporalModel[GeneratorModelSampled],
):
logger.debug(f"Creating Genset: {data_transfer_object.name}")
self.data_transfer_object = data_transfer_object
self.temporal_generator_set_model = TemporalModel(
{
start_time: GeneratorModelSampled(model)
for start_time, model in data_transfer_object.generator_set_model.items()
}
)
logger.debug(f"Creating Genset: {name}")
self.id = id
self.name = name
self.temporal_generator_set_model = temporal_generator_set_model

def evaluate(
self,
Expand All @@ -39,7 +36,7 @@ def evaluate(
"""Warning! We are converting energy usage to NaN when the energy usage models has invalid timesteps. this will
probably be changed soon.
"""
logger.debug(f"Evaluating Genset: {self.data_transfer_object.name}")
logger.debug(f"Evaluating Genset: {self.name}")

if not len(power_requirement) == len(variables_map.time_vector):
raise ValueError("length of power_requirement does not match the time vector.")
Expand All @@ -62,7 +59,7 @@ def evaluate(
fuel_rate = np.nan_to_num(fuel_rate)

return GeneratorSetResult(
id=self.data_transfer_object.id,
id=self.id,
timesteps=variables_map.time_vector,
is_valid=TimeSeriesBoolean(
timesteps=variables_map.time_vector,
Expand All @@ -87,7 +84,7 @@ def evaluate(
)

def evaluate_fuel_rate(
self, power_requirement: NDArray[np.float64], variables_map: dto.VariablesMap
self, power_requirement: NDArray[np.float64], variables_map: VariablesMap
) -> NDArray[np.float64]:
result = np.full_like(power_requirement, fill_value=np.nan).astype(float)
for period, model in self.temporal_generator_set_model.items():
Expand All @@ -97,7 +94,7 @@ def evaluate_fuel_rate(
return result

def evaluate_power_capacity_margin(
self, power_requirement: NDArray[np.float64], variables_map: dto.VariablesMap
self, power_requirement: NDArray[np.float64], variables_map: VariablesMap
) -> NDArray[np.float64]:
result = np.zeros_like(power_requirement).astype(float)
for period, model in self.temporal_generator_set_model.items():
Expand Down
69 changes: 36 additions & 33 deletions src/libecalc/core/consumers/legacy_consumer/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

import numpy as np
from numpy.typing import NDArray
from typing_extensions import assert_never

from libecalc import dto
from libecalc.common.list.list_utils import array_to_list
from libecalc.common.logger import logger
from libecalc.common.temporal_model import TemporalExpression, TemporalModel
Expand All @@ -22,11 +22,9 @@
)
from libecalc.core.consumers.base import BaseConsumer
from libecalc.core.consumers.legacy_consumer.consumer_function import (
ConsumerFunction,
ConsumerFunctionResult,
)
from libecalc.core.consumers.legacy_consumer.consumer_function_mapper import (
EnergyModelMapper,
)
from libecalc.core.consumers.legacy_consumer.result_mapper import (
get_consumer_system_models,
get_operational_settings_results_from_consumer_result,
Expand All @@ -43,9 +41,10 @@
GenericComponentResult,
PumpResult,
)
from libecalc.dto import VariablesMap
from libecalc.dto.base import ComponentType
from libecalc.dto.types import ConsumptionType
from libecalc.dto.variables import VariablesMap
from libecalc.expression import Expression


def get_operational_settings_used_from_consumer_result(
Expand All @@ -65,31 +64,35 @@ def get_operational_settings_used_from_consumer_result(
class Consumer(BaseConsumer):
def __init__(
self,
consumer_dto: dto.components.Consumer,
):
logger.debug(f"Creating Consumer: {consumer_dto.name}")
self._consumer_dto = consumer_dto
self._consumer_time_function = TemporalModel(
{
start_time: EnergyModelMapper.from_dto_to_domain(model)
for start_time, model in consumer_dto.energy_usage_model.items()
}
)
id: str,
name: str,
component_type: ComponentType,
consumes: ConsumptionType,
regularity: TemporalModel[Expression],
energy_usage_model: TemporalModel[ConsumerFunction],
) -> None:
logger.debug(f"Creating Consumer: {name}")
self._id = id
self.name = name
self.component_type = component_type
self.consumes: ConsumptionType = consumes
self.regularity = regularity
self._consumer_time_function = energy_usage_model

@property
def id(self):
return self._consumer_dto.id
return self._id

def map_model_result(self, model_result: Union[ConsumerOrSystemFunctionResult]) -> List[ConsumerModelResult]:
if self._consumer_dto.component_type in [ComponentType.PUMP_SYSTEM, ComponentType.COMPRESSOR_SYSTEM]:
if self.component_type in [ComponentType.PUMP_SYSTEM, ComponentType.COMPRESSOR_SYSTEM]:
return get_consumer_system_models(
model_result,
name=self._consumer_dto.name,
name=self.name,
)
else:
return get_single_consumer_models(
result=model_result,
name=self._consumer_dto.name,
name=self.name,
)

def get_consumer_result(
Expand All @@ -100,7 +103,7 @@ def get_consumer_result(
power_usage: TimeSeriesStreamDayRate,
aggregated_result: Union[ConsumerOrSystemFunctionResult],
) -> ConsumerResult:
if self._consumer_dto.component_type in [ComponentType.PUMP_SYSTEM, ComponentType.COMPRESSOR_SYSTEM]:
if self.component_type in [ComponentType.PUMP_SYSTEM, ComponentType.COMPRESSOR_SYSTEM]:
operational_settings_used = get_operational_settings_used_from_consumer_result(result=aggregated_result)
operational_settings_used.values = self.reindex_time_vector(
values=operational_settings_used.values,
Expand All @@ -111,15 +114,15 @@ def get_consumer_result(
operational_settings_used.timesteps = timesteps

operational_settings_result = get_operational_settings_results_from_consumer_result(
aggregated_result, parent_id=self._consumer_dto.id
aggregated_result, parent_id=self.id
)

# convert to 1-based index
operational_settings_result = {i + 1: result for i, result in operational_settings_result.items()}
operational_settings_used.values = [i + 1 for i in operational_settings_used.values]

consumer_result = ConsumerSystemResult(
id=self._consumer_dto.id,
id=self.id,
timesteps=timesteps,
is_valid=is_valid,
power=power_usage,
Expand All @@ -128,7 +131,7 @@ def get_consumer_result(
operational_settings_results=operational_settings_result,
)

elif self._consumer_dto.component_type == ComponentType.PUMP:
elif self.component_type == ComponentType.PUMP:
# Using generic consumer result as pump has no specific results currently

inlet_rate_time_series = TimeSeriesStreamDayRate(
Expand Down Expand Up @@ -156,7 +159,7 @@ def get_consumer_result(
).reindex(new_time_vector=timesteps)

consumer_result = PumpResult(
id=self._consumer_dto.id,
id=self.id,
timesteps=timesteps,
is_valid=is_valid,
energy_usage=energy_usage,
Expand All @@ -166,7 +169,7 @@ def get_consumer_result(
outlet_pressure_bar=outlet_pressure_time_series,
operational_head=operational_head_time_series,
)
elif self._consumer_dto.component_type == ComponentType.COMPRESSOR:
elif self.component_type == ComponentType.COMPRESSOR:
# All energy_function_results should be CompressorTrainResult,
# if not the consumer should not have COMPRESSOR type.
if isinstance(aggregated_result.energy_function_result, CompressorTrainResult):
Expand Down Expand Up @@ -205,7 +208,7 @@ def get_consumer_result(
outlet_pressure_before_choking = [math.nan] * len(timesteps)

consumer_result = CompressorResult(
id=self._consumer_dto.id,
id=self.id,
timesteps=timesteps,
is_valid=is_valid,
energy_usage=energy_usage,
Expand All @@ -225,7 +228,7 @@ def get_consumer_result(

else:
consumer_result = GenericComponentResult(
id=self._consumer_dto.id,
id=self.id,
timesteps=timesteps,
is_valid=is_valid,
energy_usage=energy_usage,
Expand All @@ -240,9 +243,9 @@ def evaluate(
"""Warning! We are converting energy usage to NaN when the energy usage models has invalid timesteps. this will
probably be changed soon.
"""
logger.debug(f"Evaluating consumer: {self._consumer_dto.name}")
logger.debug(f"Evaluating consumer: {self.name}")
regularity = TemporalExpression.evaluate(
temporal_expression=TemporalModel(self._consumer_dto.regularity),
temporal_expression=self.regularity,
variables_map=variables_map,
)

Expand Down Expand Up @@ -276,7 +279,7 @@ def evaluate(
# By convention, we change remaining NaN-values to 0 regardless of extrapolation
energy_usage = np.nan_to_num(energy_usage)

if self._consumer_dto.consumes == ConsumptionType.FUEL:
if self.consumes == ConsumptionType.FUEL:
power_time_series = None
if aggregated_consumer_function_result.power is not None:
power = self.reindex_time_vector(
Expand All @@ -295,7 +298,7 @@ def evaluate(
unit=Unit.STANDARD_CUBIC_METER_PER_DAY,
)

elif self._consumer_dto.consumes == ConsumptionType.ELECTRICITY:
elif self.consumes == ConsumptionType.ELECTRICITY:
energy_usage_time_series = TimeSeriesStreamDayRate(
timesteps=variables_map.time_vector,
values=array_to_list(energy_usage),
Expand All @@ -304,7 +307,7 @@ def evaluate(

power_time_series = energy_usage_time_series.model_copy()
else:
raise ValueError(f"Consuming '{self._consumer_dto.consumes}' is not implemented.")
assert_never(self.consumes)

is_valid = TimeSeriesBoolean(
timesteps=variables_map.time_vector,
Expand All @@ -320,7 +323,7 @@ def evaluate(
aggregated_result=aggregated_consumer_function_result,
)

if self._consumer_dto.component_type in [ComponentType.PUMP_SYSTEM, ComponentType.COMPRESSOR_SYSTEM]:
if self.component_type in [ComponentType.PUMP_SYSTEM, ComponentType.COMPRESSOR_SYSTEM]:
model_results = self.map_model_result(aggregated_consumer_function_result)
else:
model_results = [self.map_model_result(model_result) for model_result in consumer_function_results]
Expand Down
16 changes: 10 additions & 6 deletions src/libecalc/core/models/generator.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
from typing import List

import numpy as np
from numpy.typing import NDArray
from scipy.interpolate import interp1d

from libecalc import dto
from libecalc.common.list.adjustment import transform_linear


class GeneratorModelSampled:
def __init__(
self,
data_transfer_object: dto.GeneratorSetSampled,
fuel_values: List[float],
power_values: List[float],
energy_usage_adjustment_constant: float,
energy_usage_adjustment_factor: float,
):
fuel_values = transform_linear(
np.array(data_transfer_object.fuel_values),
constant=data_transfer_object.energy_usage_adjustment_constant,
factor=data_transfer_object.energy_usage_adjustment_factor,
np.array(fuel_values),
constant=energy_usage_adjustment_constant,
factor=energy_usage_adjustment_factor,
)

self._func = interp1d(
data_transfer_object.power_values,
power_values,
fuel_values.tolist(),
fill_value=(min(fuel_values), max(fuel_values)),
bounds_error=False,
Expand Down
Loading

0 comments on commit 76a472e

Please sign in to comment.