Skip to content

Commit

Permalink
Merge pull request #32 from kingsleynweye/develop
Browse files Browse the repository at this point in the history
Updates to KPI definitions and calculation
  • Loading branch information
kingsleynweye authored Oct 2, 2023
2 parents e9bd42d + 826e1f1 commit da4310e
Show file tree
Hide file tree
Showing 16 changed files with 494 additions and 105 deletions.
2 changes: 1 addition & 1 deletion energy_flexibility_kpis/enumerations.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class BaseUnit(Enum):
KWH = ('kWh', 'kilowatt-hour')
KW = ('kW', 'kilowatt')
DOLLAR = ('%', 'dollar')
HOUR = ('hr', 'hour')
HOUR = ('h', 'hour')
MINUTE = ('min', 'minute')
SECOND = ('s', 'second')
MILLISECOND = ('ms', 'millisecond')
Expand Down
24 changes: 24 additions & 0 deletions energy_flexibility_kpis/kpi/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,24 @@ def calculate(
baseline_electric_power_profile: List[float] = None,
baseline_electricity_consumption_profile: List[float] = None,
baseline_natural_gas_consumption_profile: List[float] = None,
baseline_cost_profile: List[float] = None,
baseline_carbon_emissions_profile: List[float] = None,
baseline_carbon_intensity_profile: List[float] = None,
baseline_self_production_profile: List[float] = None,
flexible_electric_power_profile: List[float] = None,
flexible_electricity_consumption_profile: List[float] = None,
flexible_natural_gas_consumption_profile: List[float] = None,
flexible_cost_profile: List[float] = None,
flexible_carbon_emissions_profile: List[float] = None,
flexible_carbon_intensity_profile: List[float] = None,
flexible_self_production_profile: List[float] = None,
generic_electric_power_profile: List[float] = None,
generic_electricity_consumption_profile: List[float] = None,
generic_natural_gas_consumption_profile: List[float] = None,
generic_cost_profile: List[float] = None,
generic_carbon_emissions_profile: List[float] = None,
generic_carbon_intensity_profile: List[float] = None,
generic_self_production_profile: List[float] = None,
load_profile_peak_timestamp: Union[int, datetime.datetime, str] = None,
load_profile_valley_timestamp: Union[int, datetime.datetime, str] = None,
grid_peak_timestamp: Union[int, datetime.datetime, str] = None,
Expand All @@ -99,12 +111,24 @@ def calculate(
baseline_electric_power_profile=baseline_electric_power_profile,
baseline_electricity_consumption_profile=baseline_electricity_consumption_profile,
baseline_natural_gas_consumption_profile=baseline_natural_gas_consumption_profile,
baseline_cost_profile=baseline_cost_profile,
baseline_carbon_emissions_profile=baseline_carbon_emissions_profile,
baseline_carbon_intensity_profile=baseline_carbon_intensity_profile,
baseline_self_production_profile=baseline_self_production_profile,
flexible_electric_power_profile=flexible_electric_power_profile,
flexible_electricity_consumption_profile=flexible_electricity_consumption_profile,
flexible_natural_gas_consumption_profile=flexible_natural_gas_consumption_profile,
flexible_cost_profile=flexible_cost_profile,
flexible_carbon_emissions_profile=flexible_carbon_emissions_profile,
flexible_carbon_intensity_profile=flexible_carbon_intensity_profile,
flexible_self_production_profile=flexible_self_production_profile,
generic_electric_power_profile=generic_electric_power_profile,
generic_electricity_consumption_profile=generic_electricity_consumption_profile,
generic_natural_gas_consumption_profile=generic_natural_gas_consumption_profile,
generic_cost_profile=generic_cost_profile,
generic_carbon_emissions_profile=generic_carbon_emissions_profile,
generic_carbon_intensity_profile=generic_carbon_intensity_profile,
generic_self_production_profile=generic_self_production_profile,
load_profile_peak_timestamp=load_profile_peak_timestamp,
load_profile_valley_timestamp=load_profile_valley_timestamp,
grid_peak_timestamp=grid_peak_timestamp,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class FlexibilityMap(KPI):

NAME = 'flexibility map'
DEFINITION = __doc__
UNIT = Unit(numerator=[BaseUnit.KWH])
UNIT = Unit(numerator=[BaseUnit.KW, BaseUnit.HOUR])
CATEGORY = KPICategory.EF_DEMAND_PROFILE_RESHAPING
RELEVANCE = Relevance.MEDIUM
STAKEHOLDERS = [Stakeholder.GRID_OPERATOR, Stakeholder.BUILDING_OPERATOR]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,77 +1,164 @@
import datetime
from typing import List, Union
from scipy import integrate
from energy_flexibility_kpis.kpi.base import KPI
from energy_flexibility_kpis.enumerations import BaseUnit, Complexity, DOEFlexibilityCategory, KPICategory, PerformanceAspect, Relevance
from energy_flexibility_kpis.enumerations import Stakeholder, TemporalEvaluationWindow,TemporalResolution, SpatialResolution
from energy_flexibility_kpis.unit import Unit

class FlexibilitySavingsIndex(KPI):
"""The fraction of saved cost from a penalty-aware operational strategy (flexible)
compared with a penalty-ignorant operation strategy (baseline)."""

NAME = 'flexibility savings index'
DEFINITION = __doc__
UNIT = Unit(numerator=[BaseUnit.DIMENSIONLESS])
CATEGORY = KPICategory.EF_DEMAND_RESPONSE_COSTS_OR_SAVINGS
RELEVANCE = Relevance.HIGH
STAKEHOLDERS = [Stakeholder.BUILDING_OPERATOR, Stakeholder.BUILDING_OWNER]
COMPLEXITY = Complexity.LOW
NEED_BASELINE = True
TEMPORAL_EVALUATION_WINDOW = TemporalEvaluationWindow.UNSPECIFIED
TEMPORAL_RESOLUTION = TemporalResolution.UNSPECIFIED
SPATIAL_RESOLUTION = SpatialResolution.UNSPECIFIED
DOE_FLEXIBILITY_CATEGORY = [DOEFlexibilityCategory.EFFICIENCY, DOEFlexibilityCategory.LOAD_SHEDDING, DOEFlexibilityCategory.LOAD_SHIFTING]
PERFORMANCE_ASPECT = [PerformanceAspect.COST]

def __init__(self):
super().__init__()

@classmethod
def calculate(
cls,
baseline_cost_profile: List[float],
flexible_cost_profile: List[float],
timestamps: Union[List[int], List[datetime.datetime], List[str]] = None,
evaluation_start_timestamp: Union[int, datetime.datetime, str] = None,
evaluation_end_timestamp: Union[int, datetime.datetime, str] = None,
) -> float:
) -> List[float]:
_, vs = super().calculate(
baseline_cost_profile=baseline_cost_profile,
flexible_cost_profile=flexible_cost_profile,
timestamps=timestamps,
evaluation_start_timestamp=evaluation_start_timestamp,
evaluation_end_timestamp=evaluation_end_timestamp,
)

raise NotImplementedError
value = 1 - (vs.flexible_cost_profile.value[vs.evaluation_mask]/vs.baseline_cost_profile.value[vs.evaluation_mask])

return value

class CostOrEnergyDeviationRatio(KPI):
"""Flexibility is assessed by the energy consumption and cost deviations resulting from
DR measures with respect a reference scenario without DR."""

NAME = 'cost or energy deviation ratio'
DEFINITION = __doc__
UNIT = Unit(numerator=[BaseUnit.PERCENT])
CATEGORY = KPICategory.EF_DEMAND_RESPONSE_COSTS_OR_SAVINGS
RELEVANCE = Relevance.HIGH
STAKEHOLDERS = [Stakeholder.BUILDING_OPERATOR, Stakeholder.BUILDING_OWNER]
COMPLEXITY = Complexity.LOW
NEED_BASELINE = True
TEMPORAL_EVALUATION_WINDOW = TemporalEvaluationWindow.WHOLE_YEAR
TEMPORAL_RESOLUTION = TemporalResolution.UNSPECIFIED
SPATIAL_RESOLUTION = SpatialResolution.BUILDING_CLUSTER
DOE_FLEXIBILITY_CATEGORY = [DOEFlexibilityCategory.LOAD_SHEDDING, DOEFlexibilityCategory.LOAD_SHIFTING, DOEFlexibilityCategory.GENERATION]
PERFORMANCE_ASPECT = [PerformanceAspect.COST]

def __init__(self):
super().__init__()

@classmethod
def calculate(
cls,
timestamps: Union[List[int], List[datetime.datetime], List[str]] = None,
baseline_electric_power_profile: List[float],
baseline_cost_profile: List[float],
flexible_electric_power_profile: List[float],
flexible_cost_profile: List[float],
timestamps: Union[List[int], List[datetime.datetime], List[str]],
evaluation_start_timestamp: Union[int, datetime.datetime, str] = None,
evaluation_end_timestamp: Union[int, datetime.datetime, str] = None,
) -> float:
_, vs = super().calculate(
baseline_electric_power_profile=baseline_electric_power_profile,
baseline_cost_profile=baseline_cost_profile,
flexible_electric_power_profile=flexible_electric_power_profile,
flexible_cost_profile=flexible_cost_profile,
timestamps=timestamps,
evaluation_start_timestamp=evaluation_start_timestamp,
evaluation_end_timestamp=evaluation_end_timestamp,
)

raise NotImplementedError
dx = vs.get_temporal_resolution(BaseUnit.HOUR, value=vs.timestamps.value[vs.evaluation_mask])
cost_profile = vs.flexible_cost_profile.value[vs.evaluation_mask] - vs.baseline_cost_profile.value[vs.evaluation_mask]
electric_power_profile = vs.flexible_electric_power_profile.value[vs.evaluation_mask] - vs.baseline_electric_power_profile.value[vs.evaluation_mask]
cost_value = integrate.simpson(cost_profile, dx=dx)
electric_power_value = integrate.simpson(electric_power_profile, dx=dx)
value = cost_value/electric_power_value

return value

class RelativeOperationalCostOfADR(KPI):
"""Ratio between the total operational cost with ADR and the total operational
cost in the case of no ADR participation (only fuel cost)."""

NAME = 'relative operational cost of ADR'
DEFINITION = __doc__
UNIT = Unit(numerator=[BaseUnit.PERCENT])
CATEGORY = KPICategory.EF_DEMAND_RESPONSE_COSTS_OR_SAVINGS
RELEVANCE = Relevance.HIGH
STAKEHOLDERS = [Stakeholder.BUILDING_OPERATOR, Stakeholder.BUILDING_OWNER]
COMPLEXITY = Complexity.LOW
NEED_BASELINE = True
TEMPORAL_EVALUATION_WINDOW = TemporalEvaluationWindow.WHOLE_YEAR
TEMPORAL_RESOLUTION = TemporalResolution.UNSPECIFIED
SPATIAL_RESOLUTION = SpatialResolution.BUILDING_CLUSTER
DOE_FLEXIBILITY_CATEGORY = [DOEFlexibilityCategory.LOAD_SHEDDING, DOEFlexibilityCategory.LOAD_SHIFTING, DOEFlexibilityCategory.GENERATION]
PERFORMANCE_ASPECT = [PerformanceAspect.COST]

def __init__(self):
super().__init__()

@classmethod
def calculate(
cls,
baseline_cost_profile: List[float],
flexible_cost_profile: List[float],
timestamps: Union[List[int], List[datetime.datetime], List[str]] = None,
evaluation_start_timestamp: Union[int, datetime.datetime, str] = None,
evaluation_end_timestamp: Union[int, datetime.datetime, str] = None,
) -> float:
) -> List[float]:
_, vs = super().calculate(
baseline_cost_profile=baseline_cost_profile,
flexible_cost_profile=flexible_cost_profile,
timestamps=timestamps,
evaluation_start_timestamp=evaluation_start_timestamp,
evaluation_end_timestamp=evaluation_end_timestamp,
)

raise NotImplementedError
value = vs.flexible_cost_profile.value[vs.evaluation_mask]/vs.baseline_cost_profile.value[vs.evaluation_mask]

return value

class CostSavings(KPI):
"""Economic savings generated by the demand response of the building when
sold on the flexibility market."""

NAME = 'cost savings'
DEFINITION = __doc__
UNIT = Unit(numerator=[BaseUnit.DOLLAR])
CATEGORY = KPICategory.EF_DEMAND_RESPONSE_COSTS_OR_SAVINGS
RELEVANCE = Relevance.MEDIUM
STAKEHOLDERS = [Stakeholder.BUILDING_OPERATOR, Stakeholder.BUILDING_OWNER]
COMPLEXITY = Complexity.LOW
NEED_BASELINE = True
TEMPORAL_EVALUATION_WINDOW = TemporalEvaluationWindow.WHOLE_DAY
TEMPORAL_RESOLUTION = TemporalResolution.HOURLY
SPATIAL_RESOLUTION = SpatialResolution.UNSPECIFIED
DOE_FLEXIBILITY_CATEGORY = [DOEFlexibilityCategory.LOAD_SHEDDING, DOEFlexibilityCategory.LOAD_SHIFTING]
PERFORMANCE_ASPECT = [PerformanceAspect.COST]

def __init__(self):
super().__init__()

Expand All @@ -88,4 +175,4 @@ def calculate(
evaluation_end_timestamp=evaluation_end_timestamp,
)

raise NotImplementedError
raise NotImplementedError('Complex equation')
Original file line number Diff line number Diff line change
@@ -1,46 +1,97 @@
import datetime
from typing import List, Union
from scipy import integrate
from energy_flexibility_kpis.kpi.base import KPI
from energy_flexibility_kpis.enumerations import BaseUnit, Complexity, DOEFlexibilityCategory, KPICategory, PerformanceAspect, Relevance
from energy_flexibility_kpis.enumerations import Stakeholder, TemporalEvaluationWindow,TemporalResolution, SpatialResolution
from energy_flexibility_kpis.unit import Unit

class RelativeCO2EmissionsReduction(KPI):
"""CO2 emissions savings due to the demand response of the building."""

NAME = 'relative CO2 emissions reduction'
DEFINITION = __doc__
UNIT = Unit(numerator=[BaseUnit.PERCENT])
CATEGORY = KPICategory.EF_DEMAND_RESPONSE_EMISSION_OR_ENVIRONMENTAL_IMPACT
RELEVANCE = Relevance.MEDIUM
STAKEHOLDERS = [Stakeholder.GRID_OPERATOR, Stakeholder.POLICYMAKER, Stakeholder.UTILITY_COMPANY]
COMPLEXITY = Complexity.LOW
NEED_BASELINE = True
TEMPORAL_EVALUATION_WINDOW = TemporalEvaluationWindow.UNSPECIFIED
TEMPORAL_RESOLUTION = TemporalResolution.UNSPECIFIED
SPATIAL_RESOLUTION = SpatialResolution.UNSPECIFIED
DOE_FLEXIBILITY_CATEGORY = [DOEFlexibilityCategory.LOAD_SHEDDING, DOEFlexibilityCategory.LOAD_SHIFTING]
PERFORMANCE_ASPECT = [PerformanceAspect.EMISSION]

def __init__(self):
super().__init__()

@classmethod
def calculate(
cls,
#test
baseline_carbon_emissions_profile: List[float],
flexible_carbon_emissions_profile: List[float],
timestamps: Union[List[int], List[datetime.datetime], List[str]] = None,
evaluation_start_timestamp: Union[int, datetime.datetime, str] = None,
evaluation_end_timestamp: Union[int, datetime.datetime, str] = None,
) -> float:
) -> List[float]:
_, vs = super().calculate(
baseline_carbon_emissions_profile=baseline_carbon_emissions_profile,
flexible_carbon_emissions_profile=flexible_carbon_emissions_profile,
timestamps=timestamps,
evaluation_start_timestamp=evaluation_start_timestamp,
evaluation_end_timestamp=evaluation_end_timestamp,
)

raise NotImplementedError
value = (
vs.flexible_carbon_emissions_profile.value[vs.evaluation_mask]
- vs.baseline_carbon_emissions_profile.value[vs.evaluation_mask]
)*100.0/vs.baseline_carbon_emissions_profile.value[vs.evaluation_mask]

return value

class EnvironmentalSavings(KPI):
"""CO2 emissions savings due to the demand response of the building."""

NAME = 'environmental savings'
DEFINITION = __doc__
UNIT = Unit(numerator=[BaseUnit.KG_OF_CO2])
CATEGORY = KPICategory.EF_DEMAND_RESPONSE_EMISSION_OR_ENVIRONMENTAL_IMPACT
RELEVANCE = Relevance.MEDIUM
STAKEHOLDERS = [Stakeholder.GRID_OPERATOR, Stakeholder.POLICYMAKER, Stakeholder.UTILITY_COMPANY]
COMPLEXITY = Complexity.LOW
NEED_BASELINE = True
TEMPORAL_EVALUATION_WINDOW = TemporalEvaluationWindow.WHOLE_DAY
TEMPORAL_RESOLUTION = TemporalResolution.HOURLY
SPATIAL_RESOLUTION = SpatialResolution.UNSPECIFIED
DOE_FLEXIBILITY_CATEGORY = [DOEFlexibilityCategory.LOAD_SHEDDING, DOEFlexibilityCategory.LOAD_SHIFTING]
PERFORMANCE_ASPECT = [PerformanceAspect.EMISSION]

def __init__(self):
super().__init__()

@classmethod
def calculate(
cls,
timestamps: Union[List[int], List[datetime.datetime], List[str]] = None,
baseline_electric_power_profile: List[float],
flexible_electric_power_profile: List[float],
generic_carbon_intensity_profile: List[float],
timestamps: Union[List[int], List[datetime.datetime], List[str]],
evaluation_start_timestamp: Union[int, datetime.datetime, str] = None,
evaluation_end_timestamp: Union[int, datetime.datetime, str] = None,
) -> float:
_, vs = super().calculate(
baseline_electric_power_profile=baseline_electric_power_profile,
flexible_electric_power_profile=flexible_electric_power_profile,
generic_carbon_intensity_profile=generic_carbon_intensity_profile,
timestamps=timestamps,
evaluation_start_timestamp=evaluation_start_timestamp,
evaluation_end_timestamp=evaluation_end_timestamp,
)

raise NotImplementedError
electric_power_profile = vs.flexible_electric_power_profile.value[vs.evaluation_mask] - vs.baseline_electric_power_profile.value[vs.evaluation_mask]
carbon_emissions_profile = electric_power_profile*vs.generic_carbon_intensity_profile.value[vs.evaluation_mask]
dx = vs.get_temporal_resolution(BaseUnit.HOUR, value=vs.timestamps.value[vs.evaluation_mask])
value = integrate.simpson(carbon_emissions_profile, dx=dx)

return value
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class EnergySavingsOfDemandResponse(KPI):

NAME = 'energy savings of demand response'
DEFINITION = __doc__
UNIT = Unit(numerator=[BaseUnit.KWH])
UNIT = Unit(numerator=[BaseUnit.KW, BaseUnit.HOUR])
CATEGORY = KPICategory.EF_DEMAND_RESPONSE_ENERGY_EFFICIENCY
RELEVANCE = Relevance.MEDIUM
STAKEHOLDERS = [Stakeholder.BUILDING_OWNER, Stakeholder.BUILDING_OPERATOR]
Expand Down Expand Up @@ -175,4 +175,4 @@ def calculate(
evaluation_end_timestamp=evaluation_end_timestamp,
)

raise NotImplementedError('Variable are unclear')
raise NotImplementedError('Variables are unclear')
Loading

0 comments on commit da4310e

Please sign in to comment.