Skip to content

Add multi-unit combined cycle gas turbine component#232

Open
jfrederik-nrel wants to merge 46 commits intoNatLabRockies:developfrom
jfrederik-nrel:feature/mu_ccgt
Open

Add multi-unit combined cycle gas turbine component#232
jfrederik-nrel wants to merge 46 commits intoNatLabRockies:developfrom
jfrederik-nrel:feature/mu_ccgt

Conversation

@jfrederik-nrel
Copy link
Copy Markdown
Collaborator

@jfrederik-nrel jfrederik-nrel commented Mar 7, 2026

Work in progress. Still to do:

  • Merge latest version of feature/mm-thermal back in
  • Execute sanity check of the control logic
  • Clean up code, ruff
  • Complete the example, possibly merge with existing example
  • Write documentation
  • Add tests

No reviews necessary yet at this time.

dzalkind and others added 29 commits March 4, 2026 14:37
This reverts commit 71f3c90, reversing
changes made to bb51e82.
@jfrederik-nrel
Copy link
Copy Markdown
Collaborator Author

jfrederik-nrel commented Mar 10, 2026

The only problem is that we currently do not define the fuel usage independently. Instead, the fuel usage is determined using the efficiency table and the power production, so it is a bit of a chicken or egg story in that sense.

I think the easiest solution would be to have the efficiency linked to the state of the thermal unit. If it's off, efficiency is NaN, if its starting up (burning fuel but not producing energy yet), it is 0, and finally, if it is producing energy, we use the interpolation we use now.

@misi9170
Copy link
Copy Markdown
Collaborator

Ah good point, yes, that makes sense!

@jfrederik-nrel
Copy link
Copy Markdown
Collaborator Author

The latest commit does exactly that @misi9170. This is what the efficiency look like now:

image

I feel like this still isn't perfect, but it might be sufficient for now.

Two notes:

  1. I'm now using np.nansum to plot the combined unit efficiency. This is needed as I want the efficiency to be a number if the gas turbine is producing power while the gas turbine is off. However, it has the downside that it also makes all NaN's zeros when both units are off. We might need to make that more sophisticated later.
  2. I haven't tested the changes to calculate_efficiency in thermal_component_base.py in any of the other examples yet

@jfrederik-nrel
Copy link
Copy Markdown
Collaborator Author

In the latest commit, I ended up choosing a middle way between both methods. It now uses some if-else logic to determine the efficiency based on the state of the gas and steam unit. I honestly don't love this solution, but believe it's currently the best way to model the efficiency given the limitations of the framework (see issue #234).

@jfrederik-nrel
Copy link
Copy Markdown
Collaborator Author

@genevievestarke @dzalkind it's ready for review now. I would particularly recommend taking a look at the new tests I formulated as this was a first time for me.

@dzalkind
Copy link
Copy Markdown
Collaborator

Very nice contribution, @jfrederik-nrel!

Which PR should this follow? #224? Should #225 also be included?

This might help the comparison in the meantime: https://github.com/misi9170/hercules/compare/feature/mm-thermal...jfrederik-nrel:hercules:feature/mu_ccgt?expand=1

| Power Fraction | HHV Net Efficiency |
|---------------|-------------------|
| 1.00 | 0.35 (35%) |
| 0.5o | 0.32 (32%) |
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Possibly just a typo with 0.5o

- file: thermal_component_base
- file: open_cycle_gas_turbine
- file: steam_turbine
- file: combined_cycle_plant
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not seeing this combined_cycle_plant.md file in docs

"For the combined cycle plant, one of the units must be an open cycle gas turbine."
)

if len(generic_units) != 2:
Copy link
Copy Markdown
Collaborator

@dzalkind dzalkind Mar 19, 2026

Choose a reason for hiding this comment

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

We may want to relax this constraint in some cases in the future, where multiple gas generators could be combined to feed a steam unit. We can probably work around this for now.

Will the code break in this case? I'm not sure we should change it now, but it might be good to know in the future.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, I was thinking about that too. What I think I will try to do on the short term (though perhaps not in this PR) is allow at least 2 (or perhaps more? From what I can find, 2 is currently the max) gas units powering a single steam unit. I don't think that would break anything, but definitely not excluding the possibility that it causes complications that I'm currently not overseeing.


# Update h_dict with outputs
h_dict[self.component_name]["power"] = self.power_output
# h_dict[self.component_name]["state"] = self.state.value
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do you want the state or is it not needed because each unit has its own state?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Each unit has it's own state. We would need to define a new state machine for the combined unit as it has complicated combined states like "gas turbine on, steam turbine off". I refrained from doing that and just left it at each unit having its own state

return h_dict

def control(self, power_setpoint):
""""""
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Maybe denote that this is the control of the combined plant?

1 - self.gas_power_ratio
) * power_setpoint

# TODO: we probably want to add an actual controller for the gas turbine
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It looks like you did this TODO?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

No I didn't. I meant that right now it uses the basic _control logic defined in the thermal_component_base class, as there is no control method defined in the open_cycle_gas_turbine class. What I meant here is that we might want to add that at some point, or else rename _control in thermal_component_base to control.

Copy link
Copy Markdown
Collaborator

@dzalkind dzalkind left a comment

Choose a reason for hiding this comment

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

Overall, this looks good to me. Nice job!

I like the power set point input addition. It might be nice to have that documented for future reference, along with making sure your new docs are included.

Otherwise, I left some small comments in the files; they are pretty minor and often for my understanding.

@jfrederik-nrel
Copy link
Copy Markdown
Collaborator Author

@dzalkind it should follow #224. #225 is related, but a stand-alone PR I'd say.

I merged #224 into this branch recently, so I don't expect any major merge conflicts.

Added the missing combined_cycle_plant doc.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants