Skip to content

Commit cf419a8

Browse files
authored
Flash and HDA Flowsheet Tutorial Updates (#160)
* Flash Unite and HDA Flowsheet Tutorial Revisions to reflect changes to initialization and scaling * Flash Unite and HDA Flowsheet Tutorial Revisions to reflect changes to initialization and scaling * Updated version numbers for GitHub Actions to version 4 * Revised both Tutorials based on feedback and ensured they both work as intended * Fixed spelling and ran Black * Fixing pytesting for earlier versions * Created config files to implement modular properties framework and initialized the HDA flowsheet files to work with the modular properties framework. Currently not working due to unit model initialization error. * Revised HDA Flowsheet to use the new State Definition FpTPxpc and add the necessary supporting files to make it work. Included a Python file of the flowsheet. * Flash Unite and HDA Flowsheet Tutorial Revisions to reflect changes to initialization and scaling * Flash Unite and HDA Flowsheet Tutorial Revisions to reflect changes to initialization and scaling * Revised both Tutorials based on feedback and ensured they both work as intended * Fixed spelling and ran Black * Fixing pytesting for earlier versions * Created config files to implement modular properties framework and initialized the HDA flowsheet files to work with the modular properties framework. Currently not working due to unit model initialization error. * Revised HDA Flowsheet to use the new State Definition FpTPxpc and add the necessary supporting files to make it work. Included a Python file of the flowsheet. * Revised and fixed the HDA tutorial to work with updated FpTPxpc state definition and initialization methods * Ran black * Ran black * Ran ideasx pre * Spelling fixes * Spelling * Fixed some testing and reverted `hda_ideal_VLE.py` back to its original state * Relaxes assertion check for reactor temperature due to result variance between python/os versions in testing and populated doc notebook files * Relaxes assertion check for reactor temperature due to result variance between python/os versions in testing * Relaxes assertion check for reactor temperature due to result variance between python/os versions in testing * Relaxes assertion check for reactor temperature due to result variance between python/os versions in testing * Fixed reactor outlet temperature to remove indeterminancy and adjusted exercise cells to accommodate * Changed reactor heat duty units from J to W and expanded explanation for fixing reactor outlet temperature
1 parent 8db87b5 commit cf419a8

18 files changed

Lines changed: 19874 additions & 8423 deletions

.github/workflows/core.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ jobs:
6969
runs-on: ubuntu-latest
7070
steps:
7171
- name: Checkout source
72-
uses: actions/checkout@v3
72+
uses: actions/checkout@v4
7373
- name: Run Spell Checker
7474
uses: crate-ci/typos@master
7575

@@ -95,7 +95,7 @@ jobs:
9595
- os: win64
9696
runner-image: windows-2022
9797
steps:
98-
- uses: actions/checkout@v3
98+
- uses: actions/checkout@v4
9999
- name: Set up Conda environment
100100
uses: conda-incubator/setup-miniconda@v2.2.0
101101
with:
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
import numpy as np
2+
from idaes.core.util.model_statistics import degrees_of_freedom
3+
from idaes.core.solvers import get_solver
4+
import idaes.logger as idaeslog
5+
6+
7+
def FpcTP_to_FpTPxpc(flow_mol_phase_comp):
8+
9+
flow_mol_phase = {
10+
"Vap": 0,
11+
"Liq": 0,
12+
}
13+
mole_frac_phase_comp = {}
14+
15+
for p, c in flow_mol_phase_comp.keys():
16+
flow_mol_phase[p] += flow_mol_phase_comp[(p, c)]
17+
for p, c in flow_mol_phase_comp.keys():
18+
if flow_mol_phase[p] == 0.0:
19+
mole_frac_phase_comp[p, c] = 0.0
20+
else:
21+
mole_frac_phase_comp[p, c] = flow_mol_phase_comp[(p, c)] / flow_mol_phase[p]
22+
23+
return flow_mol_phase, mole_frac_phase_comp
24+
25+
26+
def fix_inlet_states(m):
27+
28+
eps = 1e-5
29+
flow_mol_phase_comp_101 = {
30+
("Vap", "benzene"): eps,
31+
("Vap", "toluene"): eps,
32+
("Vap", "hydrogen"): eps,
33+
("Vap", "methane"): eps,
34+
("Liq", "benzene"): eps,
35+
("Liq", "toluene"): 0.30,
36+
}
37+
38+
flow_mol_phase_101, mole_frac_phase_comp_101 = FpcTP_to_FpTPxpc(
39+
flow_mol_phase_comp_101
40+
)
41+
42+
m.fs.I101.flow_mol_phase[0, "Vap"].fix(flow_mol_phase_101["Vap"])
43+
m.fs.I101.flow_mol_phase[0, "Liq"].fix(flow_mol_phase_101["Liq"])
44+
m.fs.I101.mole_frac_phase_comp[0, "Vap", "benzene"].fix(
45+
mole_frac_phase_comp_101["Vap", "benzene"]
46+
)
47+
m.fs.I101.mole_frac_phase_comp[0, "Vap", "toluene"].fix(
48+
mole_frac_phase_comp_101["Vap", "toluene"]
49+
)
50+
m.fs.I101.mole_frac_phase_comp[0, "Vap", "hydrogen"].fix(
51+
mole_frac_phase_comp_101["Vap", "hydrogen"]
52+
)
53+
m.fs.I101.mole_frac_phase_comp[0, "Vap", "methane"].fix(
54+
mole_frac_phase_comp_101["Vap", "methane"]
55+
)
56+
m.fs.I101.mole_frac_phase_comp[0, "Liq", "benzene"].fix(
57+
mole_frac_phase_comp_101["Liq", "benzene"]
58+
)
59+
m.fs.I101.mole_frac_phase_comp[0, "Liq", "toluene"].fix(
60+
mole_frac_phase_comp_101["Liq", "toluene"]
61+
)
62+
m.fs.I101.temperature.fix(303.2)
63+
m.fs.I101.pressure.fix(350000)
64+
65+
flow_mol_phase_comp_102 = {
66+
("Vap", "benzene"): eps,
67+
("Vap", "toluene"): eps,
68+
("Vap", "hydrogen"): 0.30,
69+
("Vap", "methane"): 0.02,
70+
("Liq", "benzene"): eps,
71+
("Liq", "toluene"): eps,
72+
}
73+
74+
flow_mol_phase_102, mole_frac_phase_comp_102 = FpcTP_to_FpTPxpc(
75+
flow_mol_phase_comp_102
76+
)
77+
78+
m.fs.I102.flow_mol_phase[0, "Vap"].fix(flow_mol_phase_102["Vap"])
79+
m.fs.I102.flow_mol_phase[0, "Liq"].fix(flow_mol_phase_102["Liq"])
80+
m.fs.I102.mole_frac_phase_comp[0, "Vap", "benzene"].fix(
81+
mole_frac_phase_comp_102["Vap", "benzene"]
82+
)
83+
m.fs.I102.mole_frac_phase_comp[0, "Vap", "toluene"].fix(
84+
mole_frac_phase_comp_102["Vap", "toluene"]
85+
)
86+
m.fs.I102.mole_frac_phase_comp[0, "Vap", "hydrogen"].fix(
87+
mole_frac_phase_comp_102["Vap", "hydrogen"]
88+
)
89+
m.fs.I102.mole_frac_phase_comp[0, "Vap", "methane"].fix(
90+
mole_frac_phase_comp_102["Vap", "methane"]
91+
)
92+
m.fs.I102.mole_frac_phase_comp[0, "Liq", "benzene"].fix(
93+
mole_frac_phase_comp_102["Liq", "benzene"]
94+
)
95+
m.fs.I102.mole_frac_phase_comp[0, "Liq", "toluene"].fix(
96+
mole_frac_phase_comp_102["Liq", "toluene"]
97+
)
98+
m.fs.I102.temperature.fix(303.2)
99+
m.fs.I102.pressure.fix(350000)
100+
101+
tear_guesses = {
102+
"flow_mol_phase": {
103+
(0, "Liq"): flow_mol_phase_101["Liq"],
104+
(0, "Vap"): flow_mol_phase_102["Vap"],
105+
},
106+
"mole_frac_phase_comp": {
107+
(0, "Liq", "benzene"): mole_frac_phase_comp_101["Liq", "benzene"],
108+
(0, "Liq", "toluene"): mole_frac_phase_comp_101["Liq", "toluene"],
109+
(0, "Vap", "benzene"): mole_frac_phase_comp_102["Vap", "benzene"],
110+
(0, "Vap", "toluene"): mole_frac_phase_comp_102["Vap", "toluene"],
111+
(0, "Vap", "methane"): mole_frac_phase_comp_102["Vap", "hydrogen"],
112+
(0, "Vap", "hydrogen"): mole_frac_phase_comp_102["Vap", "methane"],
113+
},
114+
"temperature": {0: 303},
115+
"pressure": {0: 350000},
116+
}
117+
118+
return tear_guesses
119+
120+
121+
def initialize_unit(unit):
122+
from idaes.core.util.exceptions import InitializationError
123+
import idaes.logger as idaeslog
124+
125+
optarg = {
126+
"nlp_scaling_method": "user-scaling",
127+
"OF_ma57_automatic_scaling": "yes",
128+
"max_iter": 1000,
129+
"tol": 1e-8,
130+
}
131+
132+
try:
133+
initializer = unit.default_initializer(solver_options=optarg)
134+
initializer.initialize(unit, output_level=idaeslog.INFO_LOW)
135+
except InitializationError:
136+
solver = get_solver(solver_options=optarg)
137+
solver.solve(unit)
138+
139+
140+
def manual_propagation(m, tear_guesses):
141+
from idaes.core.util.initialization import propagate_state
142+
143+
print(f"The DOF is {degrees_of_freedom(m)} initially")
144+
m.fs.s03_expanded.deactivate()
145+
print(f"The DOF is {degrees_of_freedom(m)} after deactivating the tear stream")
146+
147+
for k, v in tear_guesses.items():
148+
for k1, v1 in v.items():
149+
getattr(m.fs.s03.destination, k)[k1].fix(v1)
150+
151+
DOF_initial = degrees_of_freedom(m)
152+
153+
print(f"The DOF is {degrees_of_freedom(m)} after setting the tear stream")
154+
155+
optarg = {
156+
"nlp_scaling_method": "user-scaling",
157+
"OF_ma57_automatic_scaling": "yes",
158+
"max_iter": 300,
159+
# "tol": 1e-10,
160+
}
161+
162+
solver = get_solver(solver_options=optarg)
163+
164+
initialize_unit(m.fs.H101) # Initialize Heater
165+
propagate_state(m.fs.s04) # Establish connection between Heater and Reactor
166+
initialize_unit(m.fs.R101) # Initialize Reactor
167+
propagate_state(
168+
m.fs.s05
169+
) # Establish connection between Reactor and First Flash Unit
170+
initialize_unit(m.fs.F101) # Initialize First Flash Unit
171+
propagate_state(
172+
m.fs.s06
173+
) # Establish connection between First Flash Unit and Splitter
174+
propagate_state(
175+
m.fs.s07
176+
) # Establish connection between First Flash Unit and Second Flash Unit
177+
initialize_unit(m.fs.S101) # Initialize Splitter
178+
propagate_state(m.fs.s08) # Establish connection between Splitter and Compressor
179+
initialize_unit(m.fs.C101) # Initialize Compressor
180+
propagate_state(m.fs.s09) # Establish connection between Compressor and Mixer
181+
initialize_unit(m.fs.I101) # Initialize Toluene Inlet
182+
propagate_state(m.fs.s01) # Establish connection between Toluene Inlet and Mixer
183+
initialize_unit(m.fs.I102) # Initialize Hydrogen Inlet
184+
propagate_state(m.fs.s02) # Establish connection between Hydrogen Inlet and Mixer
185+
initialize_unit(m.fs.M101) # Initialize Mixer
186+
propagate_state(m.fs.s03) # Establish connection between Mixer and Heater
187+
solver.solve(m.fs.F102)
188+
propagate_state(
189+
m.fs.s10
190+
) # Establish connection between Second Flash Unit and Benzene Product
191+
propagate_state(
192+
m.fs.s11
193+
) # Establish connection between Second Flash Unit and Toluene Product
194+
propagate_state(m.fs.s12) # Establish connection between Splitter and Purge Product
195+
196+
optarg = {
197+
"nlp_scaling_method": "user-scaling",
198+
"OF_ma57_automatic_scaling": "yes",
199+
"max_iter": 300,
200+
"tol": 1e-8,
201+
}
202+
solver = get_solver("ipopt_v2", options=optarg)
203+
solver.solve(m, tee=False)
204+
205+
for k, v in tear_guesses.items():
206+
for k1, v1 in v.items():
207+
getattr(m.fs.H101.inlet, k)[k1].unfix()
208+
209+
m.fs.s03_expanded.activate()
210+
print(
211+
f"The DOF is {degrees_of_freedom(m)} after unfixing the values and reactivating the tear stream"
212+
)
213+
214+
215+
def automatic_propagation(m, tear_guesses):
216+
217+
from pyomo.network import SequentialDecomposition
218+
219+
seq = SequentialDecomposition()
220+
seq.options.select_tear_method = "heuristic"
221+
seq.options.tear_method = "Wegstein"
222+
seq.options.iterLim = 5
223+
224+
# Using the SD tool
225+
G = seq.create_graph(m)
226+
heuristic_tear_set = seq.tear_set_arcs(G, method="heuristic")
227+
order = seq.calculation_order(G)
228+
229+
# Pass the tear_guess to the SD tool
230+
seq.set_guesses_for(heuristic_tear_set[0].destination, tear_guesses)
231+
232+
print(f"Tear Stream starts at: {heuristic_tear_set[0].destination.name}")
233+
234+
for o in order:
235+
print(o[0].name)
236+
237+
seq.run(m, initialize_unit)

0 commit comments

Comments
 (0)