Support or add `calc_branch_flow_nfa`
In https://lanl-ansi.github.io/PowerModels.jl/stable/power-flow/#Branch-Flow-Values, there are calc_branch_flow_ac and calc_branch_flow_dc, but not for calc_branch_flow_nfa. But, this will not help I think, except calc_branch_flow_nfa just:
function calc_branch_flow_nfa(data::Dict{String,<:Any})
pm_data = get_pm_data(data)
return Dict(
"branch" => Dict(
i => Dict(
"pf" => branch["pf"],
"qf" => branch["qf"],
"pt" => branch["pt"],
"qt" => branch["qt"]
) for (i, branch) in data["branch"]
),
"per_unit" => pm_data["per_unit"],
"baseMVA" => pm_data["baseMVA"]
)
end
Usage is the same with the other:
update_data!(network, result["solution"])
flows = calc_branch_flow_nfa(network)
I'm not sure I fully understand what you are trying to do.
Would you like to be able to solve some problem (e.g. optimal power flow) with the NFAPowerModel and then afterwards calculate the branch flows?
As far as I can tell, the NFAPowerModel does not consider branches and their impedances in its model at all. Therefore, no branch flows can be calculated using this model (someone please correct me if I am wrong).
The code you are showing just seems to pass through data that was already in the network dictionary:
It takes the data dictionary, passes it through get_pm_data (which just strips away some data), then takes the branch results which were already in the data dictionary and copies them into a new dictionary.
Which problem (e.g. OPF) are you specifically solving? Maybe you would want a different model instead of the NFAPowerModel? (perhaps a DC power flow model like DCPPowerModel?)
Hi, @LKuhrmann. This is not an urgent bug, just a soft feature request.
NFAPowerModel still generate branch flow results because it also consider branch capacity.
Here is running matpower data (you might need to change path for this).
using PowerModels
using PrettyPrint
using Ipopt
network = PowerModels.parse_file("../../env/lib/python3.13/site-packages/matpower/data/case9.m")
pm = instantiate_model(network, NFAPowerModel, PowerModels.build_opf)
result = optimize_model!(pm, optimizer=Ipopt.Optimizer)
println(result["termination_status"])
if result["termination_status"] == LOCALLY_SOLVED
println(result["objective"])
PowerModels.print_summary(result["solution"])
end
function calc_branch_flow_nfa(data::Dict{String,<:Any})
network = get_pm_data(data)
return Dict(
"branch" => Dict(
i => Dict(
"pf" => branch["pf"],
"qf" => 0.0,
"pt" => branch["pt"],
"qt" => 0.0
) for (i, branch) in data["branch"]
),
"per_unit" => network["per_unit"],
"baseMVA" => network["baseMVA"]
)
end
update_data!(network, result["solution"])
flows = calc_branch_flow_nfa(network)
flows["branch"]
[:qf, :qt, :pf, :pt] is not exist in parse, but exist in result. I actually trying to use NFAPowerModel in Sienna, and in the middle of looking how I can extract the flow results.
Hi!
I see! I think I don't fully understand NFAPowerModel. Thanks for your example!
I saw that NFAPowerModel doesn't implement constraint_ohms_yt_from and it doesn't solve for power flow (unrelated bug?).
Also, there are only tests for OPB and OPF and they seem to have the same result in all cases. I then (incorrectly) assumed that it is a copper plate model, but that is not true.
If you want a dict that only contains "branch", "per_unit" and "baseMVA" as an excerpt from network, you can also use this one-liner: a = Dict((k,deepcopy(v)) for (k, v) in result["solution"] if k in ["branch", "per_unit", "baseMVA"])
In more general terms, perhaps there should be a function calc_branch_flow that gets the PowerModel and figures out how to calculate the branch flows itself. That would make the interface more Model agnostic.
The branch power is available via PowerModel object. From previous network as data source and pm as PowerModel instance:
using JuMP
function get_branch_flow_df(vars, network)
"""
Create a DataFrame of branch flows using network branch data.
Parameters:
- vars: JuMP variables dictionary (vars[:p])
- network: Network data dictionary containing branch information
Returns:
- DataFrame with branch flows
"""
data = []
for (branch_id_str, branch_data) in network["branch"]
branch_id = parse(Int, branch_id_str)
f_bus = branch_data["f_bus"]
t_bus = branch_data["t_bus"]
pf = get(vars[:p], (branch_id, f_bus, t_bus), 0.0) |> value
pt = get(vars[:p], (branch_id, t_bus, f_bus), 0.0) |> value
push!(data, (id=branch_id, f_bus=f_bus, t_bus=t_bus, pf=pf, pt=pt))
end
return DataFrame(data) |> df -> sort!(df, :id)
end
vars = var(pm)
get_branch_flow_df(vars, network)
Something similar can be used to get all object that expose :p (NFAPowerModel using :p variable, I'm not sure for the other). But this is extracting from model variable directly, not from network data. The one from network data is already answered in my previous comment.
If PowerModel interested in this as PR, I'll try to make it. But, my previous PR is rejected as out of scope, thus I'll not make it before confirmation.
Hi Folks! Let me provide some context to this.
First, the objective of calc_branch_flow_ac and calc_branch_flow_dc are to take as input the bus voltages and then compute as output the corresponding branch flow values, via the power flow equations (either the full AC or DC approximation variants).
The NFA model is short for "network flow approximation" it is also known as the "transportation model", https://lanl-ansi.github.io/PowerModels.jl/stable/formulation-details/#PowerModels.NFAPowerModel
In the NFA model there is no concept of bus voltages and power can flow freely on every branch of the network up to the branch flow limits. This model has some uses but is far removed from the physics of how power flows in the electric grid.
For these reasons, calc_branch_flow_nfa is not a well defined function following the conventions of the other two. If you would like to know suitable branch flows for the NFA model you should solve a OPF problem using the NFA formulation.
Hi @ccoffrin,
Yeah, I already know it. It is just that NFAPowerModel is a valid approach used by engineer to firstly check power balance. Since it didn't use any voltage data, it would be better to give a separate calc_branch_flow_nfa so that someone didn't accidentally use calc_branch_flow_dc and use the "old" voltage angle data (which is not updated by update_data! as far as I know).
This is not urgent, and just to add something. The simplest implementation will be what I described previously: https://github.com/lanl-ansi/PowerModels.jl/issues/977#issuecomment-3106814773