# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
# SPDX-License-Identifier: MPL-2.0
import numpy as np
import numba as nb
from typing import Union
from VeraGridEngine.basic_structures import Vec, CxVec, CxMat
[docs]
@nb.njit(cache=True)
def get_overload_score(loading: Union[CxMat, CxVec], branches_cost: Vec, threshold=1.0) -> float:
"""
Compute overload score by multiplying the loadings above 100% by the associated branch cost.
:param loading: load results
:param branches_cost: all branch elements from studied grid
:param threshold: threshold for overload
:return: sum of all costs associated to branch overloads
"""
cost_ = float(0.0)
if loading.ndim == 1:
for i in range(loading.shape[0]):
absloading = np.abs(loading[i])
if absloading > threshold:
cost_ += (absloading - threshold) * float(branches_cost[i])
elif loading.ndim == 2:
for i in range(loading.shape[0]):
for j in range(loading.shape[1]):
absloading = np.abs(loading[i, j])
if absloading > threshold:
cost_ += (absloading - threshold) * branches_cost[j]
return cost_
[docs]
@nb.njit(cache=True)
def get_voltage_module_score(voltage: Union[CxVec, CxMat], vm_cost: Vec, vm_max: Vec, vm_min: Vec) -> float:
"""
Compute voltage module score by multiplying the voltages outside limits by the associated bus costs.
:param voltage: voltage results
:param vm_cost: Vm cost array
:param vm_max: maximum voltage
:param vm_min: minimum voltage
:return: sum of all costs associated to voltage module deviation
"""
cost_ = 0.0
if voltage.ndim == 1:
for i in range(voltage.shape[0]):
vm = np.abs(voltage[i])
if vm < vm_min[i]:
cost_ += vm_cost[i] * (vm_min[i] - vm)
elif vm > vm_max[i]:
cost_ += vm_cost[i] * (vm - vm_max[i])
elif voltage.ndim == 2:
for i in range(voltage.shape[0]):
for j in range(voltage.shape[1]):
vm = np.abs(voltage[i, j])
if vm < vm_min[j]:
cost_ += vm_cost[j] * (vm_min[j] - vm)
elif vm > vm_max[j]:
cost_ += vm_cost[j] * (vm - vm_max[j])
return cost_
[docs]
@nb.njit(cache=True)
def get_voltage_phase_score(voltage: Union[CxMat, CxVec], va_cost: Vec, va_max: Vec, va_min: Vec) -> float:
"""
Compute voltage phase score by multiplying the phases outside limits by the associated bus costs.
:param voltage: voltage results
:param va_cost: array of bus angles costs
:param va_max: maximum voltage angles
:param va_min: minimum voltage angles
:return: sum of all costs associated to voltage module deviation
"""
cost_ = 0.0
if voltage.ndim == 1:
for i in range(voltage.shape[0]):
va = np.angle(voltage[i])
if va < va_min[i]:
cost_ += va_cost[i] * (va_min[i] - va)
elif va > va_max[i]:
cost_ += va_cost[i] * (va - va_max[i])
elif voltage.ndim == 2:
for i in range(voltage.shape[0]):
for j in range(voltage.shape[1]):
va = np.angle(voltage[i, j])
if va < va_min[j]:
cost_ += va_cost[j] * (va_min[j] - va)
elif va > va_max[j]:
cost_ += va_cost[j] * (va - va_max[j])
return cost_
[docs]
class TechnoEconomicScores:
"""
InvestmentScores
"""
__slots__ = (
"capex_score",
"opex_score",
"losses_score",
"overload_score",
"voltage_module_score",
"voltage_angle_score",
)
def __init__(self) -> None:
"""
Constructor
"""
self.capex_score: float = 0.0
self.opex_score: float = 0.0
self.losses_score: float = 0.0
self.overload_score: float = 0.0
self.voltage_module_score: float = 0.0
self.voltage_angle_score: float = 0.0
@property
def financial_score(self) -> float:
"""
Get the financial score: CAPEX + OPEX
:return: float
"""
return self.capex_score + self.opex_score
@property
def tech_score(self):
return self.losses_score + self.overload_score + self.voltage_module_score + self.voltage_angle_score
[docs]
def arr(self) -> Vec:
"""
Return multidimensional metrics for the optimization
:return: array of 2 values
"""
return np.array([self.losses_score, self.overload_score, self.voltage_module_score, self.voltage_angle_score,
self.financial_score, self.tech_score])