-
Notifications
You must be signed in to change notification settings - Fork 41
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Removing P as a decision variable in the OPF problem #430
Comments
It sounds like you will want to write a custom problem, with some additional constraints and a new objective function. In order to not completely rewrite all the the existing constraints that call for active power variables, probably the best way would be to add some equality constraints to them. Here's an example of a modified version of the OPF problem (mostly cut and paste, with minor additions): import PowerModelsDistribution as PMD
import JuMP
import Ipopt
function build_custom(pm::PMD.AbstractUnbalancedPowerModel)
PMD.variable_mc_bus_voltage(pm)
PMD.variable_mc_branch_power(pm)
PMD.variable_mc_transformer_power(pm)
PMD.variable_mc_switch_power(pm)
PMD.variable_mc_generator_power(pm)
PMD.variable_mc_load_power(pm)
PMD.variable_mc_storage_power(pm)
PMD.constraint_mc_model_voltage(pm)
for i in PMD.ids(pm, :ref_buses)
PMD.constraint_mc_theta_ref(pm, i)
end
# generators should be constrained before KCL, or Pd/Qd undefined
for id in PMD.ids(pm, :gen)
PMD.constraint_mc_generator_power(pm, id)
constraint_generator_active_power_dispatch(pm, id)
end
# loads should be constrained before KCL, or Pd/Qd undefined
for id in PMD.ids(pm, :load)
PMD.constraint_mc_load_power(pm, id)
end
for i in PMD.ids(pm, :bus)
PMD.constraint_mc_power_balance(pm, i)
end
for i in PMD.ids(pm, :storage)
PMD.constraint_storage_state(pm, i)
PMD.constraint_storage_complementarity_nl(pm, i)
PMD.constraint_mc_storage_losses(pm, i)
PMD.constraint_mc_storage_thermal_limit(pm, i)
constraint_storage_active_power_dispatch(pm, id)
end
for i in PMD.ids(pm, :branch)
PMD.constraint_mc_ohms_yt_from(pm, i)
PMD.constraint_mc_ohms_yt_to(pm, i)
PMD.constraint_mc_voltage_angle_difference(pm, i)
PMD.constraint_mc_thermal_limit_from(pm, i)
PMD.constraint_mc_thermal_limit_to(pm, i)
PMD.constraint_mc_ampacity_from(pm, i)
PMD.constraint_mc_ampacity_to(pm, i)
end
for i in PMD.ids(pm, :switch)
PMD.constraint_mc_switch_state(pm, i)
PMD.constraint_mc_switch_thermal_limit(pm, i)
PMD.constraint_mc_switch_ampacity(pm, i)
end
for i in PMD.ids(pm, :transformer)
PMD.constraint_mc_transformer_power(pm, i)
end
objective_custom_min(pm)
end From this example, you would need three more functions, two to add the power constraints, and one for your objective. function constraint_generator_active_power_dispatch(pm, i; nw=PMD.nw_id_default)
for (idx,c) in enumerate(PMD.ref(pm, nw, :gen, i, "connections"))
JuMP.@constraint(pm.model, PMD.var(pm, nw, :pg, i)[c] == PMD.ref(pm, nw, :gen, i, "pg")[idx])
end
end
function constraint_storage_active_power_dispatch(pm, i; nw=PMD.nw_id_default)
for (idx,c) in enumerate(PMD.ref(pm, nw, :storage, i, "connections"))
JuMP.@constraint(pm.model, PMD.var(pm, nw, :ps, i)[c] == PMD.ref(pm, nw, :storage, i, "ps")[idx])
end
end
function objective_custom_min(pm)
JuMP.@objective(pm.model, Min, sum(sum(sum(qg) for (i, qg) in PMD.var(pm, n, :qg)) + sum(sum(qs) for (i, qs) in PMD.var(pm, n, :qs)) for n in PMD.nw_ids(pm)))
end Here I have assumed that your desired active power setpoints are in the property "pg" on generators and "ps" on storage. Your objective function is similarly straightforward. In this example I minimized the reactive powers from storage and generators. Here is an example of this working, where I first find the power set points using OPF pmd_path = joinpath(dirname(pathof(PMD)), "..")
data = PMD.parse_file("$pmd_path/test/data/opendss/case3_unbalanced.dss")
r_opf = PMD.solve_mc_opf(data, PMD.ACPUPowerModel, Ipopt.Optimizer)
for t in ["voltage_source", "generator", "solar"]
for (i,obj) in get(r_opf["solution"], t, Dict())
data[t][i]["pg"] = obj["pg"]
end
end
for (i,obj) in get(r_opf["solution"], "storage", Dict())
data["storage"][i]["ps"] = obj["ps"]
end ...then run your custom problem. r_custom = PMD.solve_mc_model(data, PMD.ACPUPowerModel, Ipopt.Optimizer, build_custom) One final note is that I found a minor bug in the ENGINEERING to MATHEMATICAL model transformation where voltage sources were not pulling in the pg/qg values. I will put a fix in today for that which will be required for my above example to work as-is. |
_map_eng2math_voltage_source! was failing to pull the pg / qg values given in the engineering model to the math model Related #430
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
I'm solving the OPF problem using the function _PMD.solve_mc_opf(networkfile, model, ipopt_solver). If I want to fix P i.e. remove it as a decision variable, so my optimization problem solves only Q, how do I go about it? Also, is there an objective function that minimizes losses or penalty function? The objective function that I see only minimizes cost. Any help in the right direction will be greatly appreciated.
The text was updated successfully, but these errors were encountered: