Skip to content

Conversation

sambuddhac
Copy link
Collaborator

@sambuddhac sambuddhac commented Nov 13, 2024

Description

This PR attempts to introduce bidirectional asymmetric transmission lines into the system. Such lines have different values of MW flow limits, loss percentage, and capacity expansion limits along the two directions. While doing so, this PR also ensures that there are also symmetric lines present in the system along with the asymmetric lines and modifies the code in such a way that backwards compatibility is maintained with the existing cases.

What type of PR is this? (check all applicable)

  • Feature
  • Bug Fix
  • Documentation Update
  • Code Refactor
  • Performance Improvements

Related Tickets & Documents

Checklist

  • Code changes are sufficiently documented; i.e. new functions contain docstrings and .md files under /docs/src have been updated if necessary.
  • The latest changes on the target branch have been incorporated, so that any conflicts are taken care of before merging. This can be accomplished either by merging in the target branch (e.g. 'git merge develop') or by rebasing on top of the target branch (e.g. 'git rebase develop'). Please do not hesitate to reach out to the GenX development team if you need help with this.
  • Code has been tested to ensure all functionality works as intended.
  • CHANGELOG.md has been updated (if this is a 'notable' change).
  • I consent to the release of this PR's code under the GNU General Public license.

How this can be tested

Post-approval checklist for GenX core developers

After the PR is approved

  • Check that the latest changes on the target branch are incorporated, either via merge or rebase
  • Remember to squash and merge if incorporating into develop

@lbonaldo lbonaldo added the enhancement New feature or request label Dec 3, 2024
@lbonaldo lbonaldo force-pushed the asymmetric_line_flow branch from 0002caf to 0e224c5 Compare December 3, 2024 23:31
@lbonaldo lbonaldo added this to the v0.4.2 milestone Dec 4, 2024
@sambuddhac sambuddhac changed the title Asymmetric line flow (Refined) #Work in Progress; DO NOT MERGE YET !!! Asymmetric line flow (Refined) #Work in Progress; READY FOR REVIEW !!! Dec 17, 2024
@sambuddhac
Copy link
Collaborator Author

sambuddhac commented Dec 17, 2024

Hi @JesseJenkins and @lbonaldo , I have pushed all the modifications to the code base for this PR. Few things to note: First of all, as you will see, it's way more lengthy and complicated than what Jesse suggested to me previously. I first tried with the simpler version. But, was getting bidirectional flows on the asymmetric lines with Qian's example case even when we restricted the flow limits to zero in one of the directions for the asymmetric lines.

With some pondering, I realized that I was doing wrong indexing on the constraints and decision variables. So, I had to do a bit more digging and refining the code. The fundamental reasons for which the code is so much lengthier are: 1) I have both symmetric as well as asymmetric lines in a system and 2) In order to maintain backward compatibility with the existing example cases (As you will observe, I had to go to lengths to split the data-frame into two parts and assign separate variables and constraint names so that things stay clean and correct. I could not find an easier way to do this. I'll highly appreciate any feedback on this).

We tested with @qluo0320github 's example cases and it seems the results are sensible (?) However, I still get discrepancies when I benchmark against an actually symmetric system versus a "simulated" symmetric system (in which the asymmetric lines have the same flow limits, loss percentages, and transmission buildout in both directions). I am attaching hereby all the relevant flow comparison
Flow_Comparisons.xlsx
result file (the names should be self-explanatory) here for you to take a look.

I scrutinized the code several times, but couldn't spot any flaw so far. I would appreciate if you could point some obvious bug (if at all) that I might have missed. Also, I would appreciate taking a look at the quadratic loss formulation. I believe I did it right, but am not totally certain. Thank you so much !!! I have also updated most of the doc pages; I will wrap up parts of the transmission.jl doc page and make another push to close this.

@sambuddhac sambuddhac marked this pull request as ready for review December 17, 2024 15:02
@sambuddhac sambuddhac changed the title Asymmetric line flow (Refined) #Work in Progress; READY FOR REVIEW !!! Asymmetric line flow (Refined); READY FOR REVIEW !!! Dec 18, 2024
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remaining comments which cannot be posted as a review comment to avoid GitHub Rate Limit

JuliaFormatter

[JuliaFormatter] reported by reviewdog 🐶

if setup["asymmetrical_trans_flow_limit"] ==1


[JuliaFormatter] reported by reviewdog 🐶

cMaxFlow_out_asym[l = 1:L_asym, t = 1:T], vFLOW[(l+L_sym), t] <= EP[:eAvail_Trans_Cap_Pos][l] #Change these with Auxiliary
cMaxFlow_in_asym[l = 1:L_asym, t = 1:T], vFLOW[(l+L_sym), t] >= -EP[:eAvail_Trans_Cap_Neg][l] #Change these with Auxiliary


[JuliaFormatter] reported by reviewdog 🐶

begin
cMaxFlow_out[l = 1:L, t = 1:T], vFLOW[l, t] <= EP[:eAvail_Trans_Cap][l]
cMaxFlow_in[l = 1:L, t = 1:T], vFLOW[l, t] >= -EP[:eAvail_Trans_Cap][l]
end)


[JuliaFormatter] reported by reviewdog 🐶


[JuliaFormatter] reported by reviewdog 🐶

if setup["asymmetrical_trans_flow_limit"] ==1


[JuliaFormatter] reported by reviewdog 🐶

inputs["pPercent_Loss_Pos"][l] * (vTAUX_POS_ASYM[l, t]) + inputs["pPercent_Loss_Neg"][l] * (vTAUX_NEG_ASYM[l, t])


[JuliaFormatter] reported by reviewdog 🐶

vTAUX_POS_ASYM[l, t] - vTAUX_NEG_ASYM[l, t] == vFLOW[(l+L_sym), t]


[JuliaFormatter] reported by reviewdog 🐶

vTAUX_POS_ASYM[l, t] + vTAUX_NEG_ASYM[l, t] <= min(EP[:eAvail_Trans_Cap_Pos][l], EP[:eAvail_Trans_Cap_Neg][l])


[JuliaFormatter] reported by reviewdog 🐶

vTAUX_NEG[l, t] <= EP[:eAvail_Trans_Cap][l] - vPROD_TRANSCAP_ON[l, t]


[JuliaFormatter] reported by reviewdog 🐶

vTAUX_NEG_ASYM[l, t] <= EP[:eAvail_Trans_Cap_Neg][l] - vPROD_TRANSCAP_ON_NEG_ASYM[l, t]


[JuliaFormatter] reported by reviewdog 🐶

vTAUX_POS_ASYM[l, t] <= EP[:eAvail_Trans_Cap_Pos][l] - vPROD_TRANSCAP_ON_POS_ASYM[l, t]


[JuliaFormatter] reported by reviewdog 🐶

(1 - vTAUX_POS_ON_POS_ASYM[l, t]) * inputs["pTrans_Max_Possible_Pos"][l]


[JuliaFormatter] reported by reviewdog 🐶

(1 - vTAUX_POS_ON_NEG_ASYM[l, t]) * inputs["pTrans_Max_Possible_Neg"][l]


[JuliaFormatter] reported by reviewdog 🐶

vTAUX_NEG[l, t] <= EP[:eAvail_Trans_Cap][l] - vPROD_TRANSCAP_ON[l, t]


[JuliaFormatter] reported by reviewdog 🐶

if setup["asymmetrical_trans_flow_limit"] ==1


[JuliaFormatter] reported by reviewdog 🐶

sum((2 * s - 1) * (inputs["pTrans_Max_Possible"][l] / TRANS_LOSS_SEGS) *
vTAUX_POS[l, s, t] for s in 1:TRANS_LOSS_SEGS)) +


[JuliaFormatter] reported by reviewdog 🐶

sum((2 * s - 1) * (inputs["pTrans_Max_Possible"][l] / TRANS_LOSS_SEGS) *
vTAUX_NEG[l, s, t] for s in 1:TRANS_LOSS_SEGS)))


[JuliaFormatter] reported by reviewdog 🐶

sum(vTAUX_POS[l, s, t] for s in 1:TRANS_LOSS_SEGS) - vTAUX_POS[l, 0, t] ==


[JuliaFormatter] reported by reviewdog 🐶

sum(vTAUX_NEG[l, s, t] for s in 1:TRANS_LOSS_SEGS) - vTAUX_NEG[l, 0, t] ==


[JuliaFormatter] reported by reviewdog 🐶

cTAuxOrderPos2[l in LOSS_LINES_SYM, s = 1:(TRANS_LOSS_SEGS - 1), t = 1:T],


[JuliaFormatter] reported by reviewdog 🐶

cTAuxOrderNeg2[l in LOSS_LINES_SYM, s = 1:(TRANS_LOSS_SEGS - 1), t = 1:T],


[JuliaFormatter] reported by reviewdog 🐶

sum((2 * s - 1) * (inputs["pTrans_Max_Possible_Pos"][l] / TRANS_LOSS_SEGS) *
vTAUX_POS_ASYM[l, s, t] for s in 1:TRANS_LOSS_SEGS)) +


[JuliaFormatter] reported by reviewdog 🐶

sum((2 * s - 1) * (inputs["pTrans_Max_Possible_Neg"][l] / TRANS_LOSS_SEGS) *
vTAUX_NEG_ASYM[l, s, t] for s in 1:TRANS_LOSS_SEGS)))


[JuliaFormatter] reported by reviewdog 🐶

sum(vTAUX_POS_ASYM[l, s, t] for s in 1:TRANS_LOSS_SEGS) - vTAUX_POS_ASYM[l, 0, t] ==
vFLOW[(l+L_sym), t]


[JuliaFormatter] reported by reviewdog 🐶

sum(vTAUX_NEG_ASYM[l, s, t] for s in 1:TRANS_LOSS_SEGS) - vTAUX_NEG_ASYM[l, 0, t] ==
-vFLOW[(l+L_sym), t]


[JuliaFormatter] reported by reviewdog 🐶

cTAuxMaxNeg_asym[l in LOSS_LINES_ASYM, s = 1:TRANS_LOSS_SEGS, t = 1:T],


[JuliaFormatter] reported by reviewdog 🐶

cTAuxOrderPos1_asym[l in LOSS_LINES_ASYM, s = 1:TRANS_LOSS_SEGS, t = 1:T],


[JuliaFormatter] reported by reviewdog 🐶

cTAuxOrderNeg1_asym[l in LOSS_LINES_ASYM, s = 1:TRANS_LOSS_SEGS, t = 1:T],


[JuliaFormatter] reported by reviewdog 🐶

cTAuxOrderPos2_asym[l in LOSS_LINES_ASYM, s = 1:(TRANS_LOSS_SEGS - 1), t = 1:T],


[JuliaFormatter] reported by reviewdog 🐶

cTAuxOrderNeg2_asym[l in LOSS_LINES_ASYM, s = 1:(TRANS_LOSS_SEGS - 1), t = 1:T],


[JuliaFormatter] reported by reviewdog 🐶

inputs["pTrans_Max_Possible_Pos"][l] * (1 - vTAUX_POS_ON_ASYM[l, 1, t])


[JuliaFormatter] reported by reviewdog 🐶

inputs["pTrans_Max_Possible_Neg"][l] * (1 - vTAUX_NEG_ON_ASYM[l, 1, t])


[JuliaFormatter] reported by reviewdog 🐶

sum((2 * s - 1) * (inputs["pTrans_Max_Possible"][l] / TRANS_LOSS_SEGS) *
vTAUX_POS[l, s, t] for s in 1:TRANS_LOSS_SEGS)) +


[JuliaFormatter] reported by reviewdog 🐶

sum((2 * s - 1) * (inputs["pTrans_Max_Possible"][l] / TRANS_LOSS_SEGS) *
vTAUX_NEG[l, s, t] for s in 1:TRANS_LOSS_SEGS)))


[JuliaFormatter] reported by reviewdog 🐶

sum(vTAUX_POS[l, s, t] for s in 1:TRANS_LOSS_SEGS) - vTAUX_POS[l, 0, t] ==


[JuliaFormatter] reported by reviewdog 🐶

sum(vTAUX_NEG[l, s, t] for s in 1:TRANS_LOSS_SEGS) - vTAUX_NEG[l, 0, t] ==


[JuliaFormatter] reported by reviewdog 🐶

cTAuxOrderPos2[l in LOSS_LINES, s = 1:(TRANS_LOSS_SEGS - 1), t = 1:T],


[JuliaFormatter] reported by reviewdog 🐶

cTAuxOrderNeg2[l in LOSS_LINES, s = 1:(TRANS_LOSS_SEGS - 1), t = 1:T],

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remaining comments which cannot be posted as a review comment to avoid GitHub Rate Limit

JuliaFormatter

[JuliaFormatter] reported by reviewdog 🐶

cTAuxOrderNeg2_asym[l in LOSS_LINES_ASYM, s = 1:(TRANS_LOSS_SEGS - 1), t = 1:T],


[JuliaFormatter] reported by reviewdog 🐶

inputs["pTrans_Max_Possible_Pos"][l] * (1 - vTAUX_POS_ON_ASYM[l, 1, t])


[JuliaFormatter] reported by reviewdog 🐶

inputs["pTrans_Max_Possible_Neg"][l] * (1 - vTAUX_NEG_ON_ASYM[l, 1, t])


[JuliaFormatter] reported by reviewdog 🐶

sum((2 * s - 1) * (inputs["pTrans_Max_Possible"][l] / TRANS_LOSS_SEGS) *
vTAUX_POS[l, s, t] for s in 1:TRANS_LOSS_SEGS)) +


[JuliaFormatter] reported by reviewdog 🐶

sum((2 * s - 1) * (inputs["pTrans_Max_Possible"][l] / TRANS_LOSS_SEGS) *
vTAUX_NEG[l, s, t] for s in 1:TRANS_LOSS_SEGS)))


[JuliaFormatter] reported by reviewdog 🐶

sum(vTAUX_POS[l, s, t] for s in 1:TRANS_LOSS_SEGS) - vTAUX_POS[l, 0, t] ==


[JuliaFormatter] reported by reviewdog 🐶

sum(vTAUX_NEG[l, s, t] for s in 1:TRANS_LOSS_SEGS) - vTAUX_NEG[l, 0, t] ==


[JuliaFormatter] reported by reviewdog 🐶

cTAuxOrderPos2[l in LOSS_LINES, s = 1:(TRANS_LOSS_SEGS - 1), t = 1:T],


[JuliaFormatter] reported by reviewdog 🐶

cTAuxOrderNeg2[l in LOSS_LINES, s = 1:(TRANS_LOSS_SEGS - 1), t = 1:T],

@GenXProject GenXProject modified the milestones: v0.4.2, v0.4.3 Jan 16, 2025
sambuddhac and others added 10 commits January 27, 2025 16:30
@qluo0320github
Copy link
Collaborator

qluo0320github commented Feb 27, 2025 via email

@sambuddhac
Copy link
Collaborator Author

@lbonaldo this is now fully ready (except that one tiny part where we might want to finalize the set of input example cases; what I can also do, is to change the Neg in all of them and re-number the cases, I can do that tomorrow). Also, I rebased with develop, but, there's a very tiny little update on the changelog that was giving me a hard time. Maybe, it can be more easily fixed when you finally merge it on develop. Also, @qluo0320github , this might be a good time to do another round of testing and see if there's any further issues. If yes, please lmk. If everything looks good, this can be merged totally.

@sambuddhac
Copy link
Collaborator Author

@lbonaldo , @qluo0320github , @JesseJenkins , done with everything on this PR. It should now be fully polished and ready to be merged. Please take a look and see if there's any more suggestions.

@sambuddhac sambuddhac requested a review from lbonaldo March 4, 2025 19:47
…ample cases to test multistage asymmetric builds
Add warning for potential issues with asymmetric flow in multistage capacity expansion.
@Copilot Copilot AI review requested due to automatic review settings September 11, 2025 13:10
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces bidirectional asymmetric transmission lines into the GenX power system optimization model. The implementation allows transmission lines to have different flow limits, loss percentages, and capacity expansion limits in each direction, while maintaining backward compatibility with existing symmetric transmission lines.

Key changes:

  • Added support for asymmetric transmission flow limits with separate positive and negative direction parameters
  • Modified transmission modeling constraints and variables to handle both symmetric and asymmetric lines
  • Updated data loading and output writing functions to support the new asymmetric line parameters

Reviewed Changes

Copilot reviewed 239 out of 300 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/model/core/transmission/transmission.jl Core transmission modeling with asymmetric flow limits and loss calculations
src/model/core/transmission/investment_transmission.jl Investment planning for asymmetric transmission capacity expansion
src/load_inputs/load_network_data.jl Data loading logic for asymmetric line parameters
src/write_outputs/transmission/*.jl Output writing functions updated for asymmetric transmission results
src/multi_stage/dual_dynamic_programming.jl Multi-stage modeling support for asymmetric transmission
src/configure_settings/configure_settings.jl Added asymmetric transmission flow limit setting
Example systems and test cases Multiple example cases demonstrating asymmetric transmission functionality

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

LOSS_LINES_ASYM = inputs["LOSS_LINES_ASYM"] # Lines for which loss coefficients apply (are non-zero);


LOSS_LINES_ASYM = inputs["LOSS_LINES_ASYM"] # Lines for which loss coefficients apply (are non-zero);
Copy link

Copilot AI Sep 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is a duplicate of line 8. The redundant assignment should be removed.

Suggested change
LOSS_LINES_ASYM = inputs["LOSS_LINES_ASYM"] # Lines for which loss coefficients apply (are non-zero);

Copilot uses AI. Check for mistakes.

return models_d, stats_d, inputs_d
end

using Infiltrator
Copy link

Copilot AI Sep 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The using Infiltrator statement appears to be debugging code that should be removed from production code.

Suggested change
using Infiltrator

Copilot uses AI. Check for mistakes.

# and the associated linking constraint name (c) as a value
for (e, c) in start_cap_d
for y in keys(EP_cur[c])
@infiltrate # Use Infiltrator to inspect the keys of the constraint
Copy link

Copilot AI Sep 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This infiltrator debugging macro should be removed from production code.

Suggested change
@infiltrate # Use Infiltrator to inspect the keys of the constraint

Copilot uses AI. Check for mistakes.

@infiltrate # Use Infiltrator to inspect the keys of the constraint
# Set the right hand side value of the linking initial capacity constraint in the current stage to the value of the available capacity variable solved for in the previous stages
if length(EP_prev[e]) != length(EP_cur[c]) # Check if the previous stage has a value for the variable
@error "The lengths of the dictionary EP_prev and EP_cur should be the same" # Skip if there is no value for the variable in the previous stage
Copy link

Copilot AI Sep 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message is unclear. It should specify which dictionaries are being compared and what the actual lengths are. Consider: @error \"Length mismatch between EP_prev[$(e)] ($(length(EP_prev[e]))) and EP_cur[$(c)] ($(length(EP_cur[c])))\"

Suggested change
@error "The lengths of the dictionary EP_prev and EP_cur should be the same" # Skip if there is no value for the variable in the previous stage
@error "Length mismatch between EP_prev[$(e)] ($(length(EP_prev[e]))) and EP_cur[$(c)] ($(length(EP_cur[c])))"

Copilot uses AI. Check for mistakes.

Comment on lines +26 to +27
for i in eachindex(EXPANSION_LINES_ASYM)
asym_line_index = EXPANSION_LINES_ASYM[i]
Copy link

Copilot AI Sep 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loop uses eachindex(EXPANSION_LINES_ASYM) but then accesses EXPANSION_LINES_ASYM[i] inside the loop. This is unnecessarily complex. Consider using for asym_line_index in EXPANSION_LINES_ASYM for clarity.

Suggested change
for i in eachindex(EXPANSION_LINES_ASYM)
asym_line_index = EXPANSION_LINES_ASYM[i]
for (i, asym_line_index) in enumerate(EXPANSION_LINES_ASYM)

Copilot uses AI. Check for mistakes.

Comment on lines +70 to +72
#@variable(EP, vTRANSMAX[l in SYMMETRIC_EXPANSION_LINES]>=0)
#@variable(EP, vTRANSMAX_Pos[l in EXPANSION_LINES_ASYM]>=0)
#@variable(EP, vTRANSMAX_Neg[l in EXPANSION_LINES_ASYM]>=0)
Copy link

Copilot AI Sep 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These commented-out variable declarations should be removed as they are not needed and create confusion.

Suggested change
#@variable(EP, vTRANSMAX[l in SYMMETRIC_EXPANSION_LINES]>=0)
#@variable(EP, vTRANSMAX_Pos[l in EXPANSION_LINES_ASYM]>=0)
#@variable(EP, vTRANSMAX_Neg[l in EXPANSION_LINES_ASYM]>=0)

Copilot uses AI. Check for mistakes.

Comment on lines 361 to +376
if c == :cExistingTransCap
set_normalized_rhs(EP_cur[c][y], value(EP_prev[e][y]))
elseif c == :cExistingTransCapPos
set_normalized_rhs(EP_cur[c][y], value(EP_prev[e][y]))
elseif c == :cExistingTransCapNeg
set_normalized_rhs(EP_cur[c][y], value(EP_prev[e][y]))
Copy link

Copilot AI Sep 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The three conditional branches have identical logic. This code duplication should be consolidated into a single conditional check for transmission capacity constraints.

Suggested change
if c == :cExistingTransCap
set_normalized_rhs(EP_cur[c][y], value(EP_prev[e][y]))
elseif c == :cExistingTransCapPos
set_normalized_rhs(EP_cur[c][y], value(EP_prev[e][y]))
elseif c == :cExistingTransCapNeg
set_normalized_rhs(EP_cur[c][y], value(EP_prev[e][y]))
if c in (:cExistingTransCap, :cExistingTransCapPos, :cExistingTransCapNeg)
set_normalized_rhs(EP_cur[c][y], value(EP_prev[e][y]))

Copilot uses AI. Check for mistakes.

Comment on lines +310 to +311
cTAuxNeg2UB_asym[l in LOSS_LINES_ASYM, t = 1:T],
vTAUX_NEG_ASYM[l, t] <= vPROD_TRANSCAP_ON_NEG_ASYM[l, t]
Copy link

Copilot AI Sep 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two constraints create a logical inconsistency. Constraint 306 requires vTAUX_NEG_ASYM[l, t] <= EP[:eAvail_Trans_Cap_Neg][l] - vPROD_TRANSCAP_ON_NEG_ASYM[l, t] while constraint 309 requires vTAUX_NEG_ASYM[l, t] <= vPROD_TRANSCAP_ON_NEG_ASYM[l, t]. Combined, these would force 2 * vPROD_TRANSCAP_ON_NEG_ASYM[l, t] >= EP[:eAvail_Trans_Cap_Neg][l], which may not be the intended behavior.

Suggested change
cTAuxNeg2UB_asym[l in LOSS_LINES_ASYM, t = 1:T],
vTAUX_NEG_ASYM[l, t] <= vPROD_TRANSCAP_ON_NEG_ASYM[l, t]
# cTAuxNeg2UB_asym[l in LOSS_LINES_ASYM, t = 1:T],
# vTAUX_NEG_ASYM[l, t] <= vPROD_TRANSCAP_ON_NEG_ASYM[l, t]

Copilot uses AI. Check for mistakes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants