Skip to content

Commit

Permalink
Merge branch 'dispatches_cleanup' of https://github.com/MarcusHolly/d…
Browse files Browse the repository at this point in the history
…ispatches into WIP_fe_models
  • Loading branch information
bpaul4 committed Dec 22, 2023
2 parents 0fe948f + 0b52a0e commit c2b58b4
Show file tree
Hide file tree
Showing 58 changed files with 38,493 additions and 45,587 deletions.
8 changes: 7 additions & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ version: 2
sphinx:
fail_on_warning: true

build:
os: "ubuntu-22.04"
tools:
python: "3.8"

python:
version: "3.8"
install:
- requirements: requirements-dev.txt
- method: pip
path: .
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

"""
This script describes a multiperiod class to build an object for the
integrated ultra-supercritical power plant and motlen-salt based
integrated ultra-supercritical power plant and molten-salt based
thermal energy storage model.
"""

Expand All @@ -34,7 +34,7 @@ def __init__(
):
"""
Arguments:
horizon::Int64 - number of time points to use for associated multi-period model
horizon: Int64 - number of time points to use for associated multi-period model
Returns:
Float64: Value of power output in last time step
Expand All @@ -46,11 +46,11 @@ def __init__(

def populate_model(self, b, horizon):
"""
Create a integrated ultra-supercritical power plant and molten salt
Create an integrated ultra-supercritical power plant and molten salt
thermal energy storage model using the `MultiPeriod` package.
Arguments:
blk: this is an empty block passed in from eithe a bidder or tracker
blk: this is an empty block passed in from either a bidder or tracker
Returns:
None
Expand Down Expand Up @@ -157,7 +157,7 @@ def get_last_delivered_power(b, last_implemented_time_step):
step
Returns:
Float64: Value of power output in last time step
Float64: Value of power output in the last time step
"""
blk = b
return pyo.value(blk.P_T[last_implemented_time_step])
Expand Down Expand Up @@ -220,7 +220,6 @@ def record_results(self, b, date=None, hour=None, **kwargs):
result_dict["Date"] = date
result_dict["Hour"] = hour


# simulation inputs
result_dict["Horizon [hr]"] = int(t)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@

# import multiperiod object and rankine example
from idaes.apps.multiperiod.multiperiod import MultiPeriodModel
from idaes.apps.multiperiod.examples.simple_rankine_cycle import (
create_model, set_inputs, initialize_model,
close_flowsheet_loop, add_operating_cost)
##############################################################################
# DISPATCHES was produced under the DOE Design Integration and Synthesis
# Platform to Advance Tightly Coupled Hybrid Energy Systems program (DISPATCHES),
# and is copyright (c) 2021 by the software owners: The Regents of the University
# of California, through Lawrence Berkeley National Laboratory, National
# Technology & Engineering Solutions of Sandia, LLC, Alliance for Sustainable
# Energy, LLC, Battelle Energy Alliance, LLC, University of Notre Dame du Lac, et
# al. All rights reserved.
#
# Please see the files COPYRIGHT.md and LICENSE.md for full copyright and license
# information, respectively. Both files are also available online at the URL:
# "https://github.com/gmlc-dispatches/dispatches".
#
##############################################################################

from idaes.apps.grid_integration.multiperiod.multiperiod import (
MultiPeriodModel)

import pyomo.environ as pyo
from pyomo.environ import (Block, Param, Constraint, Objective, Reals,
NonNegativeReals, TransformationFactory, Expression,
maximize, RangeSet, value, log, exp, Var)
from pyomo.environ import (Param, Reals,
NonNegativeReals,
value, Var)
from pyomo.util.infeasible import (log_infeasible_constraints,
log_close_to_bounds)
import numpy as np
import copy
# from random import random

from idaes.core.util.model_statistics import degrees_of_freedom

from dispatches.models.fossil_case.ultra_supercritical_plant.storage import (
usc_storage_nlp_mp as usc)
from dispatches.case_studies.fossil_case.ultra_supercritical_plant.storage import (
integrated_storage_with_ultrasupercritical_power_plant as usc)

# For plots
from matplotlib import pyplot as plt
Expand Down Expand Up @@ -54,13 +64,13 @@ def create_ss_rankine_model():
m.rankine.fs.ess_hp_split.split_fraction[0, "to_hxc"].unfix()
m.rankine.fs.ess_bfp_split.split_fraction[0, "to_hxd"].unfix()
for salt_hxc in [m.rankine.fs.hxc]:
salt_hxc.inlet_1.unfix()
salt_hxc.inlet_2.flow_mass.unfix() # kg/s, 1 DOF
salt_hxc.shell_inlet.unfix()
salt_hxc.tube_inlet.flow_mass.unfix() # kg/s, 1 DOF
salt_hxc.area.unfix() # 1 DOF

for salt_hxd in [m.rankine.fs.hxd]:
salt_hxd.inlet_2.unfix()
salt_hxd.inlet_1.flow_mass.unfix() # kg/s, 1 DOF
salt_hxd.tube_inlet.unfix()
salt_hxd.shell_inlet.flow_mass.unfix() # kg/s, 1 DOF
salt_hxd.area.unfix() # 1 DOF

for unit in [m.rankine.fs.cooler]:
Expand All @@ -71,9 +81,9 @@ def create_ss_rankine_model():
# m.rankine.fs.salt_hot_temperature = 831
m.rankine.fs.hxc.area.fix(1904) # 1904
m.rankine.fs.hxd.area.fix(1095) # 1095
m.rankine.fs.hxc.outlet_2.temperature[0].fix(831)
m.rankine.fs.hxd.inlet_1.temperature[0].fix(831)
m.rankine.fs.hxd.outlet_1.temperature[0].fix(513.15)
m.rankine.fs.hxc.tube_outlet.temperature[0].fix(831)
m.rankine.fs.hxd.shell_inlet.temperature[0].fix(831)
m.rankine.fs.hxd.shell_outlet.temperature[0].fix(513.15)

return m

Expand Down Expand Up @@ -176,16 +186,16 @@ def constraint_salt_inventory_hot(b):
return (
b1.salt_inventory_hot ==
b1.previous_salt_inventory_hot
+ 3600*b1.fs.hxc.inlet_2.flow_mass[0]
- 3600*b1.fs.hxd.inlet_1.flow_mass[0])
+ 3600*b1.fs.hxc.tube_inlet.flow_mass[0]
- 3600*b1.fs.hxd.shell_inlet.flow_mass[0])

@b1.fs.Constraint(doc="Inventory balance at the end of the time period")
def constraint_salt_inventory_cold(b):
return (
b1.salt_inventory_cold ==
b1.previous_salt_inventory_cold
- 3600*b1.fs.hxc.inlet_2.flow_mass[0]
+ 3600*b1.fs.hxd.inlet_1.flow_mass[0])
- 3600*b1.fs.hxc.tube_inlet.flow_mass[0]
+ 3600*b1.fs.hxd.shell_inlet.flow_mass[0])

# @b1.fs.Constraint(doc="Maximum salt inventory at any time")
# def constraint_salt_inventory(b):
Expand Down Expand Up @@ -348,11 +358,11 @@ def get_rankine_periodic_variable_pairs(b1, b2):
value(blks[c].rankine.fs.plant_power_out[0]),
value(blks[c].rankine.fs.es_turbine.work_mechanical[0])*(-1e-6)))
print(' Salt from HXC (kg) [kg/s]: {:.4f} [{:.4f}]'.format(
value(blks[c].rankine.fs.hxc.outlet_2.flow_mass[0]) * 3600,
value(blks[c].rankine.fs.hxc.outlet_2.flow_mass[0])))
value(blks[c].rankine.fs.hxc.tube_outlet.flow_mass[0]) * 3600,
value(blks[c].rankine.fs.hxc.tube_outlet.flow_mass[0])))
print(' Salt from HXD (kg) [kg/s]: {:.4f} [{:.4f}]'.format(
value(blks[c].rankine.fs.hxd.outlet_1.flow_mass[0]) * 3600,
value(blks[c].rankine.fs.hxd.outlet_1.flow_mass[0])))
value(blks[c].rankine.fs.hxd.shell_outlet.flow_mass[0]) * 3600,
value(blks[c].rankine.fs.hxd.shell_outlet.flow_mass[0])))
print(' HXC Duty (MW): {:.4f}'.format(
value(blks[c].rankine.fs.hxc.heat_duty[0]) * 1e-6))
print(' HXD Duty (MW): {:.4f}'.format(
Expand All @@ -362,9 +372,9 @@ def get_rankine_periodic_variable_pairs(b1, b2):
print(' Split fraction to HXD: {:.4f}'.format(
value(blks[c].rankine.fs.ess_bfp_split.split_fraction[0, "to_hxd"])))
print(' Steam flow HXC (mol/s): {:.4f}'.format(
value(blks[c].rankine.fs.hxc.outlet_1.flow_mol[0])))
value(blks[c].rankine.fs.hxc.shell_outlet.flow_mol[0])))
print(' Steam flow HXD (mol/s): {:.4f}'.format(
value(blks[c].rankine.fs.hxd.outlet_2.flow_mol[0])))
value(blks[c].rankine.fs.hxd.tube_outlet.flow_mol[0])))
print(' Makeup water flow: {:.6f}'.format(
value(blks[c].rankine.fs.condenser_mix.makeup.flow_mol[0])))
print(' Delta T in HXC (K): {:.4f}'.format(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,40 @@

# import multiperiod object and rankine example
from multiperiod import MultiPeriodModel
# from idaes.apps.multiperiod.examples.simple_rankine_cycle import (
# create_model, set_inputs, initialize_model,
# close_flowsheet_loop, add_operating_cost)
##############################################################################
# DISPATCHES was produced under the DOE Design Integration and Synthesis
# Platform to Advance Tightly Coupled Hybrid Energy Systems program (DISPATCHES),
# and is copyright (c) 2021 by the software owners: The Regents of the University
# of California, through Lawrence Berkeley National Laboratory, National
# Technology & Engineering Solutions of Sandia, LLC, Alliance for Sustainable
# Energy, LLC, Battelle Energy Alliance, LLC, University of Notre Dame du Lac, et
# al. All rights reserved.
#
# Please see the files COPYRIGHT.md and LICENSE.md for full copyright and license
# information, respectively. Both files are also available online at the URL:
# "https://github.com/gmlc-dispatches/dispatches".
#
##############################################################################

from idaes.apps.grid_integration.multiperiod.multiperiod import (
MultiPeriodModel)

import pyomo.environ as pyo
from pyomo.environ import (Block, Param, Constraint, Objective, Reals,
NonNegativeReals, TransformationFactory, Expression,
maximize, RangeSet, value, log, exp, Var)
from pyomo.util.infeasible import (log_infeasible_constraints,
log_close_to_bounds)
from pyomo.environ import (Param, Reals,
NonNegativeReals,
value, Var)
from pyomo.util.infeasible import log_close_to_bounds
import numpy as np
import copy
# from random import random
from idaes.core.util.model_statistics import degrees_of_freedom

import usc_storage_nlp_mp_new_area as usc
from dispatches.case_studies.fossil_case.ultra_supercritical_plant.storage import (
integrated_storage_with_ultrasupercritical_power_plant as usc)

# For plots
from matplotlib import pyplot as plt
import matplotlib
matplotlib.rc('font', size=24)
plt.rc('axes', titlesize=24)

method = "with_efficiency" # options: with_efficiency and without_efficiency
import logging
logging.getLogger('pyomo.repn.plugins.nl_writer').setLevel(logging.ERROR)

max_power = 436 # in MW
min_power = int(0.65 * max_power) # 283 in MW
max_power_storage = 29 # in MW
Expand All @@ -33,7 +43,8 @@
min_power_total = min_power + min_power_storage
min_storage_heat_duty = 0.01 # in MW
max_storage_heat_duty = 150 # in MW
load_from_file = 'initialized_usc_storage_mlp_mp.json'
# TODO: Verify that this is the right data file
load_from_file = '../final_report/initialized_usc_storage_nlp_mp_unfixed_area.json'

# Add number of days and hours per week
number_days = 1
Expand Down Expand Up @@ -91,8 +102,7 @@
def create_ss_rankine_model():

m = pyo.ConcreteModel()
m.rankine = usc.main(method=method,
max_power=max_power,
m.rankine = usc.main(max_power=max_power,
load_from_file=load_from_file)

# Set bounds for plant power
Expand Down Expand Up @@ -123,13 +133,13 @@ def create_ss_rankine_model():
m.rankine.fs.ess_hp_split.split_fraction[0, "to_hxc"].unfix()
m.rankine.fs.ess_bfp_split.split_fraction[0, "to_hxd"].unfix()
for salt_hxc in [m.rankine.fs.hxc]:
salt_hxc.inlet_1.unfix()
salt_hxc.inlet_2.flow_mass.unfix() # kg/s, 1 DOF
salt_hxc.shell_inlet.unfix()
salt_hxc.tube_inlet.flow_mass.unfix() # kg/s, 1 DOF
salt_hxc.area.unfix() # 1 DOF

for salt_hxd in [m.rankine.fs.hxd]:
salt_hxd.inlet_2.unfix()
salt_hxd.inlet_1.flow_mass.unfix() # kg/s, 1 DOF
salt_hxd.tube_inlet.unfix()
salt_hxd.shell_inlet.flow_mass.unfix() # kg/s, 1 DOF
salt_hxd.area.unfix() # 1 DOF

for unit in [m.rankine.fs.cooler]:
Expand All @@ -139,9 +149,9 @@ def create_ss_rankine_model():
# Fix storage heat exchangers area and salt temperatures
m.rankine.fs.hxc.area.fix(1904)
m.rankine.fs.hxd.area.fix(2830)
m.rankine.fs.hxc.outlet_2.temperature[0].fix(831)
m.rankine.fs.hxd.inlet_1.temperature[0].fix(831)
m.rankine.fs.hxd.outlet_1.temperature[0].fix(513.15)
m.rankine.fs.hxc.tube_outlet.temperature[0].fix(831)
m.rankine.fs.hxd.shell_inlet.temperature[0].fix(831)
m.rankine.fs.hxd.shell_outlet.temperature[0].fix(513.15)

return m

Expand Down Expand Up @@ -204,8 +214,8 @@ def constraint_salt_inventory_hot(b):
return (
b1.salt_inventory_hot ==
b1.previous_salt_inventory_hot
+ (3600*b1.fs.hxc.inlet_2.flow_mass[0]
- 3600*b1.fs.hxd.inlet_1.flow_mass[0]) * scaling_factor
+ (3600*b1.fs.hxc.tube_inlet.flow_mass[0]
- 3600*b1.fs.hxd.shell_inlet.flow_mass[0]) * scaling_factor
)

@b1.fs.Constraint(doc="Maximum salt inventory at any time")
Expand Down Expand Up @@ -316,6 +326,9 @@ def get_rankine_periodic_variable_pairs(b1, b2):
# Plot results
n_weeks = 1
opt = pyo.SolverFactory('ipopt')
opt.options = {
"max_iter": 100,
}
hot_tank_level = []
cold_tank_level = []
net_power = []
Expand Down Expand Up @@ -365,7 +378,7 @@ def get_rankine_periodic_variable_pairs(b1, b2):
print(' Revenue ($): {:.4f}'.format(value(blks[c].revenue) / scaling_factor))
print(' Operating cost ($): {:.4f}'.format(value(blks[c].operating_cost) / scaling_factor))
print(' Specific Operating cost ($/MWh): {:.4f}'.format(
(value(blks[c].operating_cost) /scaling_factor) / value(blks[c].rankine.fs.net_power)))
(value(blks[c].operating_cost) / scaling_factor) / value(blks[c].rankine.fs.net_power)))
print(' Cycle efficiency (%): {:.4f}'.format(
value(blks[c].rankine.fs.cycle_efficiency)))
print(' Boiler efficiency (%): {:.4f}'.format(
Expand All @@ -377,9 +390,9 @@ def get_rankine_periodic_variable_pairs(b1, b2):
print(' Previous salt inventory (mton): {:.4f}'.format(
(value(blks[c].rankine.previous_salt_inventory_hot) / scaling_factor) * 1e-3))
print(' Salt from HXC (mton): {:.4f}'.format(
value(blks[c].rankine.fs.hxc.outlet_2.flow_mass[0]) * 3600 * 1e-3))
value(blks[c].rankine.fs.hxc.tube_outlet.flow_mass[0]) * 3600 * 1e-3))
print(' Salt from HXD (mton): {:.4f}'.format(
value(blks[c].rankine.fs.hxd.outlet_1.flow_mass[0]) * 3600 * 1e-3))
value(blks[c].rankine.fs.hxd.shell_outlet.flow_mass[0]) * 3600 * 1e-3))
print(' HXC Duty (MW): {:.4f}'.format(
value(blks[c].rankine.fs.hxc.heat_duty[0]) * 1e-6))
print(' HXD Duty (MW): {:.4f}'.format(
Expand All @@ -389,13 +402,13 @@ def get_rankine_periodic_variable_pairs(b1, b2):
print(' Split fraction to HXD: {:.4f}'.format(
value(blks[c].rankine.fs.ess_bfp_split.split_fraction[0, "to_hxd"])))
print(' Salt flow HXC (kg/s): {:.4f}'.format(
value(blks[c].rankine.fs.hxc.outlet_2.flow_mass[0])))
value(blks[c].rankine.fs.hxc.tube_outlet.flow_mass[0])))
print(' Salt flow HXD (kg/s): {:.4f}'.format(
value(blks[c].rankine.fs.hxd.outlet_1.flow_mass[0])))
value(blks[c].rankine.fs.hxd.shell_outlet.flow_mass[0])))
print(' Steam flow HXC (mol/s): {:.4f}'.format(
value(blks[c].rankine.fs.hxc.outlet_1.flow_mol[0])))
value(blks[c].rankine.fs.hxc.shell_outlet.flow_mol[0])))
print(' Steam flow HXD (mol/s): {:.4f}'.format(
value(blks[c].rankine.fs.hxd.outlet_2.flow_mol[0])))
value(blks[c].rankine.fs.hxd.tube_outlet.flow_mol[0])))
print(' Delta T in HXC (kg): {:.4f}'.format(
value(blks[c].rankine.fs.hxc.delta_temperature_in[0])))
print(' Delta T out HXC (kg): {:.4f}'.format(
Expand Down
Loading

0 comments on commit c2b58b4

Please sign in to comment.