Skip to content

Conversation

@qluo0320github
Copy link
Collaborator

@qluo0320github qluo0320github commented Jun 24, 2025

Description

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

  • Feature

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

An example case "11_three_zones_CCS_solvent_storage" is used in test.

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
Copy link
Collaborator

Thanks @qluo0320github for reopening this PR! 🙌

"PoliciesFolder" => "policies",
"ObjScale" => 1)
"ObjScale" => 1,
"FlexibleCCS" => 1)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just curious — what’s the reason for using a settings flag rather than a resource attribute?

Copy link
Collaborator

@lbonaldo lbonaldo left a comment

Choose a reason for hiding this comment

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

Hey @qluo0320github! Thanks so much for adding this new resource. I’ve added a few comments to the code and rebased the branch onto the current develop. Would you mind running the example case again and seeing if the results make sense to you? Thank you!

end

if MultiStage == 1
@expression(EP, eExistingCap_CCS_SS[y in CCS_SOLVENT_STORAGE, i = 1:7], vEXISTINGCAP_CCS_SS[y,i])
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is multistage supported? I think we need to update the configure_ddp_dicts function in dual_dyanimic_programming.jl as well to make it run. For now, I added a check that throws an error if CCS_SOLVENT_STORAGE is used with both retrofit and multistage (as we did for allam cycle), but please let me if I'm wrong.

Comment on lines +159 to +190
@expression(EP, eCInv_CCS_SS[y in CCS_SOLVENT_STORAGE, i in 1:7],
if y in NEW_CAP_CCS_SS # Resources eligible for new capacity
if y in COMMIT_CCS_SS # Resource eligible for Unit commitment
solvent_storage_dict[y,"inv_cost"][i] * solvent_storage_dict[y,"cap_size"][i] * EP[:vCAP_CCS_SS][y, i]
else
solvent_storage_dict[y,"inv_cost"][i] * EP[:vCAP_CCS_SS][y, i]
end
else
0
end)

# Fixed O&M cost of each component in CCS_SOLVENT_STORAGE
@expression(EP, eCFom_CCS_SS[y in CCS_SOLVENT_STORAGE, i in 1:7],
solvent_storage_dict[y,"fom_cost"][i] * eTotalCap_CCS_SS[y,i])

# Total fixed cost expression - combines investment and fixed O&M costs
@expression(EP, eCFix_CCS_SS[y in CCS_SOLVENT_STORAGE, i in 1:7],
if y in NEW_CAP_CCS_SS
# For resources with new capacity: investment cost + fixed O&M cost
EP[:eCInv_CCS_SS][y, i] + EP[:eCFom_CCS_SS][y, i]
else
# For existing resources: only fixed O&M cost
EP[:eCFom_CCS_SS][y, i]
end)

# connect eCFix_CCS_SS_Plant to eCFix
@expression(EP, eCFix_CCS_SS_Plant[y in CCS_SOLVENT_STORAGE], sum(EP[:eCFix_CCS_SS][y,i] for i in 1:7))
@expression(EP, eTotalCFix_CCS_SS, sum(EP[:eCFix_CCS_SS_Plant][y] for y in CCS_SOLVENT_STORAGE))

# connect eCInv_CCS_SS_Plant to eCInv
@expression(EP, eCInv_CCS_SS_Plant[y in CCS_SOLVENT_STORAGE], sum(EP[:eCInv_CCS_SS][y,i] for i in 1:7))
@expression(EP, eTotalCInv_CCS_SS, sum(EP[:eCInv_CCS_SS_Plant][y] for y in CCS_SOLVENT_STORAGE))
Copy link
Collaborator

Choose a reason for hiding this comment

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

I've updated the file splitting eCFix = eCInv + eCFom. Could you please double check it makes sense to you?

Comment on lines +213 to +216
if MultiStage == 1
# Existing capacity variable is equal to existing capacity specified in the input file
@constraint(EP, cExistingCap_CCS_SS[y in CCS_SOLVENT_STORAGE, i in 1:7], EP[:vEXISTINGCAP_CCS_SS][y,i]== solvent_storage_dict[y, "existing_cap"][i])
end
Copy link
Collaborator

Choose a reason for hiding this comment

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

I believe this constraint is repeated (see line 118)

# sum to annual level
@expression(EP, eCVar_CCS_SS[y in CCS_SOLVENT_STORAGE], sum(EP[:eCVar_CCS_SS_unit][y,i,t] for i in 1:5 for t in 1:T))
# sum to zonal-annual level
@expression(EP, eZonalCVar_CCS_SS[z = 1:Z], sum(EP[:eCVar_CCS_SS][y] for y in intersect(CCS_SOLVENT_STORAGE, resources_in_zone_by_rid(gen, z))))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Pre-compute set outside @expression

Suggested change
@expression(EP, eZonalCVar_CCS_SS[z = 1:Z], sum(EP[:eCVar_CCS_SS][y] for y in intersect(CCS_SOLVENT_STORAGE, resources_in_zone_by_rid(gen, z))))
CCS_SOLVENT_STORAGE_BY_ZONE = map(1:Z) do z
return intersect(CCS_SOLVENT_STORAGE, resources_in_zone_by_rid(gen, z))
end
@expression(EP, eZonalCVar_CCS_SS[z = 1:Z],
sum(EP[:eCVar_CCS_SS][y]
for y in CCS_SOLVENT_STORAGE_BY_ZONE[z]))

@constraint(EP, cP[y in CCS_SOLVENT_STORAGE, t = 1:T], eP_CCS_SS[y, t] == EP[:vP][y,t])
@expression(EP, ePowerBalance_CCS_SS[t = 1:T, z = 1:Z],
sum(EP[:eP_CCS_SS][y, t] - EP[:vCHARGE_CCS_SS][y, t]
for y in intersect(CCS_SOLVENT_STORAGE, resources_in_zone_by_rid(gen, z))))
Copy link
Collaborator

Choose a reason for hiding this comment

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

similar to the previous comment:

Suggested change
for y in intersect(CCS_SOLVENT_STORAGE, resources_in_zone_by_rid(gen, z))))
for y in CCS_SOLVENT_STORAGE_BY_ZONE[z]))

Comment on lines +102 to +105
@constraint(EP,[y in COMMIT_CCS_SS, i in [gasturbine, absorber, compressor], t = 1:T],
EP[:vOutput_CCS_SS][y,i,t]-EP[:vOutput_CCS_SS][y,i,hoursbefore(p, t, 1)] <= solvent_storage_dict[y, "ramp_up"][i]*solvent_storage_dict[y, "cap_size"][i]*(vCOMMIT_CCS_SS[y,i,t]-vSTART_CCS_SS[y,i,t])
+ min(1,max(solvent_storage_dict[y, "min_power"][i],solvent_storage_dict[y, "ramp_up"][i]))*solvent_storage_dict[y, "cap_size"][i]*vSTART_CCS_SS[y,i,t]
-solvent_storage_dict[y, "min_power"][i]*solvent_storage_dict[y, "cap_size"][i]*vSHUT_CCS_SS[y,i,t])
Copy link
Collaborator

Choose a reason for hiding this comment

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

Two questions on this constraint:

  • do we need to include regulation and reserves terms?
  • can the max power output be different than 1 (e.g. inputs["pP_Max"])

tempCVar += eCVar_CCS_SS_zone
tempCTotal += eCFix_CCS_SS_zone + eCVar_CCS_SS_zone
if setup["UCommit"] >= 1 && !isempty(Y_ZONE_CCS_SS)
eCStart_CCS_SS = sum(value.(EP[:eCStart_CCS_SS][Y_ZONE_CCS_SS, :])) +
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we use the sum of (EP[:eCStart_CCS_SS][y,t] + EP[:eCStart_CCS_SS_steamturbine][y,t] + EP[:eCStart_CCS_SS_regenerator][y,t]) instead of EP[:eCStart_CCS_SS] only?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants