-
Notifications
You must be signed in to change notification settings - Fork 240
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
Add support for isothermal conditions in control volumes. #1558
Changes from 9 commits
1a58c7c
93c41b8
eaeabc4
70c3475
feee062
9476565
e482c37
6b9ea94
e54dcb5
88a0394
7f2d9f9
71245de
08e3983
05ee497
de153a7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -311,3 +311,33 @@ The :math:`\Delta P_{custom, t, x}` term allows the user to provide custom terms | |
`pressure_linking_constraint(t, x)`: | ||
|
||
This constraint is an internal constraint used to link the pressure terms in the StateBlocks into a single indexed variable. This is required as Pyomo.DAE requires a single indexed variable to create the associated DerivativeVars and their numerical expansions. | ||
|
||
|
||
Extended 1D Control Volume Class | ||
-------------------------------- | ||
|
||
The ExtendedControlVolume1DBlock block builds upon ControlVolume1DBlock by adding some new balance options. It is envisioned that this will | ||
merge with ControlVolume1DBlock, however to ensure backward compatibility these additions have been kept separate until unit models can | ||
be updated to restrict (or allow) these new options if necessary. The core functionality is the same as for ControlVolume1DBlock, with the | ||
addition of one extra energy balance type; isothermal. | ||
|
||
.. module:: idaes.core.base.extended_control_volume1d | ||
|
||
.. autoclass:: ExtendedControlVolume1DBlock | ||
:members: | ||
|
||
.. autoclass:: ExtendedControlVolume1DBlockData | ||
:members: | ||
|
||
add_isothermal_constraint | ||
^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
A constraint equating temperature along the length domain of the control volume is written. | ||
|
||
**Constraints** | ||
|
||
`enthalpy_balances(t)`: | ||
|
||
.. math:: T_{t, x-1} == T_{t, x} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Outdated. |
||
|
||
This constraint is skipped at the inlet to the control volume. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
################################################################################# | ||
# The Institute for the Design of Advanced Energy Systems Integrated Platform | ||
# Framework (IDAES IP) was produced under the DOE Institute for the | ||
# Design of Advanced Energy Systems (IDAES). | ||
# | ||
# Copyright (c) 2018-2024 by the software owners: The Regents of the | ||
# University of California, through Lawrence Berkeley National Laboratory, | ||
# National Technology & Engineering Solutions of Sandia, LLC, Carnegie Mellon | ||
# University, West Virginia University Research Corporation, et al. | ||
# All rights reserved. Please see the files COPYRIGHT.md and LICENSE.md | ||
# for full copyright and license information. | ||
################################################################################# | ||
""" | ||
0D Control Volume class with support for isothermal energy balance. | ||
""" | ||
|
||
__author__ = "Andrew Lee" | ||
|
||
# Import IDAES cores | ||
from idaes.core.base.control_volume0d import ControlVolume0DBlockData | ||
from idaes.core import declare_process_block_class | ||
from idaes.core.util.exceptions import ConfigurationError | ||
|
||
import idaes.logger as idaeslog | ||
|
||
_log = idaeslog.getLogger(__name__) | ||
|
||
|
||
@declare_process_block_class( | ||
"ExtendedControlVolume0DBlock", | ||
doc=""" | ||
ExtendedControlVolume0DBlock is an extension of the ControlVolume0D | ||
block with support for isothermal conditions in place of a formal | ||
energy balance.""", | ||
) | ||
class ExtendedControlVolume0DBlockData(ControlVolume0DBlockData): | ||
""" | ||
Extended 0-Dimensional (Non-Discretized) ControlVolume Class | ||
|
||
This class extends the existing ControlVolume0DBlockData class | ||
with support for isothermal energy balances. | ||
""" | ||
|
||
def add_isothermal_constraint( | ||
self, | ||
has_heat_of_reaction=False, | ||
has_heat_transfer=False, | ||
has_work_transfer=False, | ||
has_enthalpy_transfer=False, | ||
custom_term=None, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we add Type Hints here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might as well - if we don't do it now it probably won't get done. |
||
): | ||
""" | ||
This method constructs an isothermal constraint for the control volume. | ||
|
||
Arguments are supported for compatibility with other forms but must be False | ||
or None otherwise an Exception is raised. | ||
|
||
Args: | ||
has_heat_of_reaction: whether terms for heat of reaction should | ||
be included in enthalpy balance | ||
has_heat_transfer: whether terms for heat transfer should be | ||
included in enthalpy balances | ||
has_work_transfer: whether terms for work transfer should be | ||
included in enthalpy balances | ||
has_enthalpy_transfer: whether terms for enthalpy transfer due to | ||
mass transfer should be included in enthalpy balance. This | ||
should generally be the same as the has_mass_transfer | ||
argument in the material balance methods | ||
custom_term: a Python method which returns Pyomo expressions representing | ||
custom terms to be included in enthalpy balances. | ||
Method should accept time and phase list as arguments. | ||
|
||
Returns: | ||
Constraint object representing enthalpy balances | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be good to update this to reflect isothermal constraint. |
||
""" | ||
if has_heat_transfer: | ||
raise ConfigurationError( | ||
f"{self.name}: isothermal energy balance option requires that has_heat_transfer is False. " | ||
"If you are trying to solve for heat duty to achieve isothermal operation, please use " | ||
"a full energy balance and add a constraint to equate inlet and outlet temperatures." | ||
) | ||
if has_work_transfer: | ||
raise ConfigurationError( | ||
f"{self.name}: isothermal energy balance option requires that has_work_transfer is False. " | ||
"If you are trying to solve for work under isothermal operation, please use " | ||
"a full energy balance and add a constraint to equate inlet and outlet temperatures." | ||
) | ||
if has_enthalpy_transfer: | ||
raise ConfigurationError( | ||
f"{self.name}: isothermal energy balance option does not support enthalpy transfer." | ||
) | ||
if has_heat_of_reaction: | ||
raise ConfigurationError( | ||
f"{self.name}: isothermal energy balance option requires that has_heat_of_reaction is False. " | ||
"If you are trying to solve for heat duty to achieve isothermal operation, please use " | ||
"a full energy balance and add a constraint to equate inlet and outlet temperatures." | ||
) | ||
if custom_term is not None: | ||
raise ConfigurationError( | ||
f"{self.name}: isothermal energy balance option does not support custom terms." | ||
) | ||
|
||
# Add isothermal constraint | ||
@self.Constraint(self.flowsheet().time, doc="Energy balances") | ||
def enthalpy_balances(b, t): | ||
return b.properties_in[t].temperature == b.properties_out[t].temperature | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we rename this constraint to something that better describe what it does? Otherwise you might, for example, get enthalpy scaling on a constraint that should have temperature scaling. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I debated whether the change the name - my reason for keeping it the same was for consistency of naming between different configurations (so that the "energy balance" would always have the same name no matter what option you chose). However, I did not think about scaling, and having a descriptive name would be far more useful there for people doing custom scaling. I'll change it. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
################################################################################# | ||
# The Institute for the Design of Advanced Energy Systems Integrated Platform | ||
# Framework (IDAES IP) was produced under the DOE Institute for the | ||
# Design of Advanced Energy Systems (IDAES). | ||
# | ||
# Copyright (c) 2018-2024 by the software owners: The Regents of the | ||
# University of California, through Lawrence Berkeley National Laboratory, | ||
# National Technology & Engineering Solutions of Sandia, LLC, Carnegie Mellon | ||
# University, West Virginia University Research Corporation, et al. | ||
# All rights reserved. Please see the files COPYRIGHT.md and LICENSE.md | ||
# for full copyright and license information. | ||
################################################################################# | ||
""" | ||
1D Control Volume class with support for isothermal energy balance. | ||
""" | ||
|
||
__author__ = "Andrew Lee" | ||
|
||
# Import Pyomo libraries | ||
from pyomo.environ import Constraint | ||
|
||
# Import IDAES cores | ||
from idaes.core.base.control_volume1d import ControlVolume1DBlockData | ||
from idaes.core import declare_process_block_class | ||
from idaes.core.util.exceptions import ConfigurationError | ||
|
||
import idaes.logger as idaeslog | ||
|
||
_log = idaeslog.getLogger(__name__) | ||
|
||
|
||
@declare_process_block_class( | ||
"ExtendedControlVolume1DBlock", | ||
doc=""" | ||
ExtendedControlVolume1DBlock is an extension of the ControlVolume1D | ||
block with support for isothermal conditions in place of a formal | ||
energy balance.""", | ||
) | ||
class ExtendedControlVolume1DBlockData(ControlVolume1DBlockData): | ||
""" | ||
Extended 1-Dimensional ControlVolume Class | ||
|
||
This class extends the existing ControlVolume1DBlockData class | ||
with support for isothermal energy balances. | ||
""" | ||
|
||
def add_isothermal_constraint( | ||
self, | ||
has_heat_of_reaction=False, | ||
has_heat_transfer=False, | ||
has_work_transfer=False, | ||
has_enthalpy_transfer=False, | ||
custom_term=None, | ||
): | ||
""" | ||
This method constructs an isothermal constraint for the control volume. | ||
|
||
Arguments are supported for compatibility with other forms but must be False | ||
or None otherwise an Exception is raised. | ||
|
||
Args: | ||
has_heat_of_reaction: whether terms for heat of reaction should | ||
be included in enthalpy balance | ||
has_heat_transfer: whether terms for heat transfer should be | ||
included in enthalpy balances | ||
has_work_transfer: whether terms for work transfer should be | ||
included in enthalpy balances | ||
has_enthalpy_transfer: whether terms for enthalpy transfer due to | ||
mass transfer should be included in enthalpy balance. This | ||
should generally be the same as the has_mass_transfer | ||
argument in the material balance methods | ||
custom_term: a Python method which returns Pyomo expressions representing | ||
custom terms to be included in enthalpy balances. | ||
Method should accept time and phase list as arguments. | ||
|
||
Returns: | ||
Constraint object representing enthalpy balances | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be good to update this to reflect isothermal constraint. |
||
""" | ||
if has_heat_transfer: | ||
raise ConfigurationError( | ||
f"{self.name}: isothermal energy balance option requires that has_heat_transfer is False. " | ||
"If you are trying to solve for heat duty to achieve isothermal operation, please use " | ||
"a full energy balance and add a constraint to equate inlet and outlet temperatures." | ||
) | ||
if has_work_transfer: | ||
raise ConfigurationError( | ||
f"{self.name}: isothermal energy balance option requires that has_work_transfer is False. " | ||
"If you are trying to solve for work under isothermal operation, please use " | ||
"a full energy balance and add a constraint to equate inlet and outlet temperatures." | ||
) | ||
if has_enthalpy_transfer: | ||
raise ConfigurationError( | ||
f"{self.name}: isothermal energy balance option does not support enthalpy transfer. " | ||
) | ||
if has_heat_of_reaction: | ||
raise ConfigurationError( | ||
f"{self.name}: isothermal energy balance option requires that has_heat_of_reaction is False. " | ||
"If you are trying to solve for heat duty to achieve isothermal operation, please use " | ||
"a full energy balance and add a constraint to equate inlet and outlet temperatures." | ||
) | ||
if custom_term is not None: | ||
raise ConfigurationError( | ||
f"{self.name}: isothermal energy balance option does not support custom terms. " | ||
) | ||
|
||
# Add isothermal constraint | ||
@self.Constraint( | ||
self.flowsheet().time, self.length_domain, doc="Energy balances" | ||
) | ||
def enthalpy_balances(b, t, x): | ||
if x == b.length_domain.first(): | ||
return Constraint.Skip | ||
|
||
return ( | ||
b.properties[t, b.length_domain.prev(x)].temperature | ||
== b.properties[t, x].temperature | ||
Comment on lines
+116
to
+118
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like the constraint would be skipped at the outlet when using forward scheme, but what does this mean for temperature at the outlet? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, most of this is not needed as there is no partial derivative. The way this is written, it has to skip the first point no matter what. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I might be misunderstanding, but as written, it appears that for backwards transformation, temperature at the inlet (x=0) would be set equal to the temperature of the next element, and this equality would be propagated across adjacent pairs for the whole length domain. So the temperature for each element would be covered. However, for the forward transformation case, it seems that all elements except the last element would be covered by the equality constraint. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, now I think the issue is fixed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we also rename |
||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now outdated