Skip to content

Commit

Permalink
refactor: use PriorityOptimizer outside ConsumerSystem
Browse files Browse the repository at this point in the history
ConsumerSystem should not be restricted to only work with priorities.
Trying to strip ConsumerSystem down to the essentials, although this
leads to a bit more setup in ecalc.py
  • Loading branch information
jsolaas committed Nov 16, 2023
1 parent 16b9ccc commit f1af9e6
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 57 deletions.
6 changes: 4 additions & 2 deletions src/libecalc/common/priority_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,16 @@ def optimize(
We process each timestep separately.
It will default to the last priority if all settings fails
Args:
timesteps: The timesteps that we want to figure out which priority to use for.
priorities: Dict of priorities, key is used to identify the priority in the results.
evaluator: The evaluator function gives a list of results back, each result with its own unique id.
Returns:
PriorityOptimizerResult: result containing priorities used and a map of the calculated results. The keys of
the results map are the timestep used, the priority index and the id of the result.
PriorityOptimizerResult: result containing priorities used and a list of the results merged on priorities
used,
"""
is_valid = TimeSeriesBoolean(timesteps=timesteps, values=[False] * len(timesteps), unit=Unit.NONE)
Expand Down
91 changes: 39 additions & 52 deletions src/libecalc/core/consumers/consumer_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,15 @@
import networkx as nx
import numpy as np

from libecalc.common.priorities import Priorities
from libecalc.common.priority_optimizer import EvaluatorResult, PriorityOptimizer
from libecalc.common.priority_optimizer import EvaluatorResult
from libecalc.common.stream_conditions import StreamConditions
from libecalc.common.utils.rates import (
TimeSeriesInt,
)
from libecalc.core.consumers.base import BaseConsumer
from libecalc.core.consumers.compressor import Compressor
from libecalc.core.consumers.factory import create_consumer
from libecalc.core.consumers.pump import Pump
from libecalc.core.result import ConsumerSystemResult, EcalcModelResult
from libecalc.core.result import ComponentResult, ConsumerSystemResult, EcalcModelResult
from libecalc.dto.components import ConsumerComponent

Consumer = TypeVar("Consumer", bound=Union[Compressor, Pump])
Expand All @@ -33,7 +31,7 @@ class SystemComponentConditions(Protocol):
crossover: List[Crossover]


class ConsumerSystem(BaseConsumer):
class ConsumerSystem:
"""
A system of possibly interdependent consumers and or other consumer systems.
Expand Down Expand Up @@ -100,62 +98,51 @@ def _get_stream_conditions_adjusted_for_crossover(

return adjusted_stream_conditions

def evaluate(
def evaluator(
self, timestep: datetime, system_stream_condition: Dict[str, List[StreamConditions]]
) -> List[EvaluatorResult]:
"""
Function to evaluate the consumers in the system given stream conditions
Args:
timestep:
system_stream_condition:
Returns: list of evaluator results
"""
stream_conditions_for_timestep = {
component_id: [stream_condition.get_subset_for_timestep(timestep) for stream_condition in stream_conditions]
for component_id, stream_conditions in system_stream_condition.items()
}
adjusted_system_stream_conditions = self._get_stream_conditions_adjusted_for_crossover(
stream_conditions=stream_conditions_for_timestep,
)
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,
)
for consumer_result_for_priority in consumer_results_for_priority
]

def get_system_result(
self,
timesteps: List[datetime],
system_stream_conditions_priorities: Priorities[Dict[str, List[StreamConditions]]],
consumer_results: List[ComponentResult],
operational_settings_used: TimeSeriesInt,
) -> EcalcModelResult:
"""
Evaluating a consumer system that may be composed of both consumers and other consumer systems. It will default
to the last operational setting if all settings fails.
Get system result based on consumer results
Notes:
- We use 1-indexed operational settings output. We should consider changing this to default 0-index, and
only convert when presenting results to the end-user.
"""

optimizer = PriorityOptimizer()

def evaluator(
timestep: datetime, system_stream_condition: Dict[str, List[StreamConditions]]
) -> List[EvaluatorResult]:
stream_conditions_for_timestep = {
component_id: [
stream_condition.get_subset_for_timestep(timestep) for stream_condition in stream_conditions
]
for component_id, stream_conditions in system_stream_condition.items()
}
adjusted_system_stream_conditions = self._get_stream_conditions_adjusted_for_crossover(
stream_conditions=stream_conditions_for_timestep,
)
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,
)
for consumer_result_for_priority in consumer_results_for_priority
]

optimizer_result = optimizer.optimize(
timesteps=timesteps, priorities=system_stream_conditions_priorities, evaluator=evaluator
)

consumer_results = optimizer_result.priority_results

# Convert to legacy compatible operational_settings_used
priorities_to_int_map = {
priority_name: index + 1 for index, priority_name in enumerate(system_stream_conditions_priorities.keys())
}
operational_settings_used = TimeSeriesInt(
timesteps=optimizer_result.priorities_used.timesteps,
values=[priorities_to_int_map[priority_name] for priority_name in optimizer_result.priorities_used.values],
unit=optimizer_result.priorities_used.unit,
)

consumer_system_result = ConsumerSystemResult(
id=self.id,
is_valid=reduce(
Expand Down
31 changes: 28 additions & 3 deletions src/libecalc/core/ecalc.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import libecalc.dto.components
from libecalc import dto
from libecalc.common.list.list_utils import elementwise_sum
from libecalc.common.priority_optimizer import PriorityOptimizer
from libecalc.common.utils.rates import TimeSeriesInt
from libecalc.core.consumers.consumer_system import ConsumerSystem
from libecalc.core.consumers.direct_emitter import DirectEmitter
from libecalc.core.consumers.generator_set import Genset
Expand Down Expand Up @@ -58,14 +60,37 @@ def evaluate_energy_usage(self, variables_map: dto.VariablesMap) -> Dict[str, Ec
consumers=component_dto.consumers,
component_conditions=component_dto.component_conditions,
)

evaluated_stream_conditions = component_dto.evaluate_stream_conditions(
variables_map=variables_map,
)
system_result = consumer_system.evaluate(
timesteps=variables_map.time_vector, system_stream_conditions_priorities=evaluated_stream_conditions
optimizer = PriorityOptimizer()

optimizer_result = optimizer.optimize(
timesteps=variables_map.time_vector,
priorities=evaluated_stream_conditions,
evaluator=consumer_system.evaluator,
)

# Convert to legacy compatible operational_settings_used
priorities_to_int_map = {
priority_name: index + 1 for index, priority_name in enumerate(evaluated_stream_conditions.keys())
}
operational_settings_used = TimeSeriesInt(
timesteps=optimizer_result.priorities_used.timesteps,
values=[
priorities_to_int_map[priority_name]
for priority_name in optimizer_result.priorities_used.values
],
unit=optimizer_result.priorities_used.unit,
)

system_result = consumer_system.get_system_result(
consumer_results=optimizer_result.priority_results,
operational_settings_used=operational_settings_used,
)
consumer_results[component_dto.id] = system_result
for consumer_result in system_result.sub_components:
for consumer_result in optimizer_result.priority_results:
consumer_results[consumer_result.id] = EcalcModelResult(
component_result=consumer_result,
sub_components=[],
Expand Down

0 comments on commit f1af9e6

Please sign in to comment.