# 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
from typing import List
from VeraGridEngine.Devices.multi_circuit import MultiCircuit
from VeraGridEngine.Simulations.NTC.ntc_opf import run_linear_ntc_opf
from VeraGridEngine.Simulations.NTC.ntc_opf_strict import run_linear_ntc_opf_strict
from VeraGridEngine.Simulations.driver_template import DriverTemplate
from VeraGridEngine.Simulations.NTC.ntc_options import OptimalNetTransferCapacityOptions
from VeraGridEngine.Simulations.NTC.ntc_results import OptimalNetTransferCapacityResults
from VeraGridEngine.basic_structures import Logger
from VeraGridEngine.enumerations import SimulationTypes
[docs]
class OptimalNetTransferCapacityDriver(DriverTemplate):
__slots__ = (
"options",
"all_solved",
)
name = 'Optimal net transfer capacity'
tpe = SimulationTypes.OPF_NTC_run
def __init__(self,
grid: MultiCircuit,
options: OptimalNetTransferCapacityOptions):
"""
PowerFlowDriver class constructor
:param grid: MultiCircuit Object
:param options: OptimalNetTransferCapacityOptions
"""
DriverTemplate.__init__(self, grid=grid)
# Options to use
self.options: OptimalNetTransferCapacityOptions = options
self.all_solved = True
self.logger = Logger()
[docs]
def get_steps(self) -> List[int]:
"""
Get time steps list of strings
"""
return list()
[docs]
def opf(self) -> OptimalNetTransferCapacityResults:
"""
Run a power flow for every circuit
@return: OptimalPowerFlowResults object
"""
self.report_text('Compiling...')
self.report_text('Formulating NTC OPF...')
if self.options.strict_formulation:
opf_vars, model = run_linear_ntc_opf_strict(
grid=self.grid,
t=None,
solver_type=self.options.opf_options.mip_solver,
zonal_grouping=self.options.opf_options.zonal_grouping,
skip_generation_limits=self.options.skip_generation_limits,
consider_contingencies=self.options.consider_contingencies,
contingency_groups_used=self.grid.get_contingency_groups_active(),
lodf_threshold=self.options.lin_options.lodf_threshold,
bus_a1_idx=self.options.sending_bus_idx,
bus_a2_idx=self.options.receiving_bus_idx,
transfer_method=self.options.transfer_method,
monitor_only_ntc_load_rule_branches=self.options.monitor_only_ntc_load_rule_branches,
alpha_threshold=self.options.branch_exchange_sensitivity,
monitor_only_sensitive_branches=self.options.use_branch_exchange_sensitivity,
ntc_load_rule=self.options.branch_rating_contribution,
logger=self.logger,
progress_text=self.report_text,
progress_func=self.report_progress,
verbose=self.options.opf_options.verbose,
robust=self.options.opf_options.robust,
mip_framework=self.options.opf_options.mip_framework
)
else:
opf_vars, model = run_linear_ntc_opf(
grid=self.grid,
t=None,
solver_type=self.options.opf_options.mip_solver,
zonal_grouping=self.options.opf_options.zonal_grouping,
skip_generation_limits=self.options.skip_generation_limits,
consider_contingencies=self.options.consider_contingencies,
contingency_groups_used=self.grid.get_contingency_groups_active(),
lodf_threshold=self.options.lin_options.lodf_threshold,
bus_a1_idx=self.options.sending_bus_idx,
bus_a2_idx=self.options.receiving_bus_idx,
transfer_method=self.options.transfer_method,
monitor_only_ntc_load_rule_branches=self.options.monitor_only_ntc_load_rule_branches,
alpha_threshold=self.options.branch_exchange_sensitivity,
monitor_only_sensitive_branches=self.options.use_branch_exchange_sensitivity,
ntc_load_rule=self.options.branch_rating_contribution,
logger=self.logger,
progress_text=self.report_text,
progress_func=self.report_progress,
verbose=self.options.opf_options.verbose,
robust=self.options.opf_options.robust,
mip_framework=self.options.opf_options.mip_framework
)
# pack the results
self.results = OptimalNetTransferCapacityResults(
bus_names=self.grid.get_bus_names(),
branch_names=self.grid.get_branch_names(add_hvdc=False, add_vsc=False, add_switch=True),
hvdc_names=self.grid.get_hvdc_names(),
vsc_names=self.grid.get_vsc_names(),
contingency_group_names=self.grid.get_contingency_group_names()
)
self.results.voltage = opf_vars.get_voltages()[0, :]
self.results.Sbus = opf_vars.bus_vars.Pinj[0, :]
self.results.dSbus = opf_vars.bus_vars.delta_p[0, :]
self.results.bus_shadow_prices = opf_vars.bus_vars.shadow_prices[0, :]
self.results.nodal_balance = opf_vars.bus_vars.Pbalance[0, :]
self.results.Sf = opf_vars.branch_vars.flows[0, :]
self.results.St = -opf_vars.branch_vars.flows[0, :]
if not self.options.strict_formulation:
self.results.load_shedding = opf_vars.bus_vars.load_shedding[0, :]
self.results.overloads = (opf_vars.branch_vars.flow_slacks_pos[0, :]
- opf_vars.branch_vars.flow_slacks_neg[0, :])
self.results.loading = opf_vars.branch_vars.loading[0, :]
self.results.phase_shift = opf_vars.branch_vars.tap_angles[0, :]
self.results.rates = opf_vars.branch_vars.rates[0, :]
self.results.contingency_rates = opf_vars.branch_vars.contingency_rates[0, :]
self.results.alpha = opf_vars.branch_vars.alpha[0, :]
self.results.monitor_logic = opf_vars.branch_vars.monitor_logic[0, :]
self.results.contingency_flows_list = opf_vars.branch_vars.contingency_flow_data
self.results.strict_formulation = self.options.strict_formulation
self.results.hvdc_Pf = opf_vars.hvdc_vars.flows[0, :]
self.results.hvdc_loading = opf_vars.hvdc_vars.loading[0, :]
self.results.vsc_Pf = opf_vars.vsc_vars.flows[0, :]
self.results.vsc_loading = opf_vars.vsc_vars.loading[0, :]
self.results.sending_bus_idx = self.options.sending_bus_idx
self.results.receiving_bus_idx = self.options.receiving_bus_idx
self.results.inter_space_branches = opf_vars.branch_vars.inter_space_branches
self.results.inter_space_hvdc = opf_vars.hvdc_vars.inter_space_hvdc
self.results.inter_space_vsc = opf_vars.vsc_vars.inter_space_vsc
self.results.inter_area_flows = opf_vars.inter_area_flows[0]
self.results.structural_inter_area_flows = opf_vars.structural_ntc[0]
self.results.converged = opf_vars.acceptable_solution
if self.options.opf_options.report_formulation:
self.results.report_text = model.model_as_string()
self.report_text('Done!')
return self.results
[docs]
def run(self) -> None:
"""
Run this study
"""
self.tic()
self.opf()
self.toc()