tespy 0.3.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- TESPy-0.3.0.data/data/src/tespy/data/char_lines.json +213 -0
- TESPy-0.3.0.data/data/src/tespy/data/char_maps.json +52 -0
- TESPy-0.3.0.dist-info/DESCRIPTION.rst +117 -0
- TESPy-0.3.0.dist-info/METADATA +156 -0
- TESPy-0.3.0.dist-info/RECORD +34 -0
- TESPy-0.3.0.dist-info/WHEEL +6 -0
- TESPy-0.3.0.dist-info/metadata.json +1 -0
- TESPy-0.3.0.dist-info/top_level.txt +1 -0
- tespy/__init__.py +27 -0
- tespy/components/__init__.py +28 -0
- tespy/components/basics.py +507 -0
- tespy/components/combustion.py +3291 -0
- tespy/components/components.py +813 -0
- tespy/components/customs.py +729 -0
- tespy/components/heat_exchangers.py +3116 -0
- tespy/components/nodes.py +1890 -0
- tespy/components/piping.py +536 -0
- tespy/components/reactors.py +965 -0
- tespy/components/subsystems.py +89 -0
- tespy/components/turbomachinery.py +1726 -0
- tespy/connections.py +1062 -0
- tespy/data/__init__.py +1 -0
- tespy/data/char_lines.json +213 -0
- tespy/data/char_maps.json +52 -0
- tespy/networks/__init__.py +3 -0
- tespy/networks/network_reader.py +660 -0
- tespy/networks/networks.py +3001 -0
- tespy/tools/__init__.py +13 -0
- tespy/tools/characteristics.py +663 -0
- tespy/tools/data_containers.py +429 -0
- tespy/tools/fluid_properties.py +1763 -0
- tespy/tools/global_vars.py +15 -0
- tespy/tools/helpers.py +592 -0
- tespy/tools/logger.py +216 -0
|
@@ -0,0 +1,1890 @@
|
|
|
1
|
+
# -*- coding: utf-8
|
|
2
|
+
|
|
3
|
+
"""Module for components of type node.
|
|
4
|
+
|
|
5
|
+
Components in this module:
|
|
6
|
+
|
|
7
|
+
- :func:`tespy.components.nodes.droplet_separator`
|
|
8
|
+
- :func:`tespy.components.nodes.drum`
|
|
9
|
+
- :func:`tespy.components.nodes.node`
|
|
10
|
+
- :func:`tespy.components.nodes.merge`
|
|
11
|
+
- :func:`tespy.components.nodes.splitter`
|
|
12
|
+
- :func:`tespy.components.nodes.separator`
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
This file is part of project TESPy (github.com/oemof/tespy). It's copyrighted
|
|
16
|
+
by the contributors recorded in the version control history of the file,
|
|
17
|
+
available from its original location tespy/components/nodes.py
|
|
18
|
+
|
|
19
|
+
SPDX-License-Identifier: MIT
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import logging
|
|
23
|
+
|
|
24
|
+
import numpy as np
|
|
25
|
+
|
|
26
|
+
from tespy.components.components import component
|
|
27
|
+
from tespy.tools.data_containers import dc_simple
|
|
28
|
+
from tespy.tools.fluid_properties import T_mix_ph
|
|
29
|
+
from tespy.tools.fluid_properties import dh_mix_dpQ
|
|
30
|
+
from tespy.tools.fluid_properties import dT_mix_dph
|
|
31
|
+
from tespy.tools.fluid_properties import dT_mix_pdh
|
|
32
|
+
from tespy.tools.fluid_properties import dT_mix_ph_dfluid
|
|
33
|
+
from tespy.tools.fluid_properties import h_mix_pQ
|
|
34
|
+
from tespy.tools.helpers import num_fluids
|
|
35
|
+
|
|
36
|
+
# %%
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class node(component):
|
|
40
|
+
r"""
|
|
41
|
+
Class node is the parent class for splitter, separator and merge.
|
|
42
|
+
|
|
43
|
+
Equations
|
|
44
|
+
|
|
45
|
+
**mandatory equations**
|
|
46
|
+
|
|
47
|
+
- :func:`tespy.components.components.component.mass_flow_func`
|
|
48
|
+
|
|
49
|
+
.. math::
|
|
50
|
+
|
|
51
|
+
0 = p_{in,1} - p_{out,i}\\
|
|
52
|
+
\forall i \in \mathrm{outlets}
|
|
53
|
+
|
|
54
|
+
**additional equations**
|
|
55
|
+
|
|
56
|
+
- :func:`tespy.components.nodes.splitter.additional_equations`
|
|
57
|
+
- :func:`tespy.components.nodes.separator.additional_equations`
|
|
58
|
+
- :func:`tespy.components.nodes.merge.additional_equations`
|
|
59
|
+
|
|
60
|
+
Inlets/Outlets
|
|
61
|
+
|
|
62
|
+
- specify number of outlets with :code:`num_in` (default value: 2)
|
|
63
|
+
- specify number of outlets with :code:`num_out` (default value: 2)
|
|
64
|
+
|
|
65
|
+
Image
|
|
66
|
+
|
|
67
|
+
.. image:: _images/node.svg
|
|
68
|
+
:scale: 100 %
|
|
69
|
+
:alt: alternative text
|
|
70
|
+
:align: center
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
----------
|
|
74
|
+
label : str
|
|
75
|
+
The label of the component.
|
|
76
|
+
|
|
77
|
+
design : list
|
|
78
|
+
List containing design parameters (stated as String).
|
|
79
|
+
|
|
80
|
+
offdesign : list
|
|
81
|
+
List containing offdesign parameters (stated as String).
|
|
82
|
+
|
|
83
|
+
design_path: str
|
|
84
|
+
Path to the components design case.
|
|
85
|
+
|
|
86
|
+
local_offdesign : boolean
|
|
87
|
+
Treat this component in offdesign mode in a design calculation.
|
|
88
|
+
|
|
89
|
+
local_design : boolean
|
|
90
|
+
Treat this component in design mode in an offdesign calculation.
|
|
91
|
+
|
|
92
|
+
char_warnings: boolean
|
|
93
|
+
Ignore warnings on default characteristics usage for this component.
|
|
94
|
+
|
|
95
|
+
printout: boolean
|
|
96
|
+
Include this component in the network's results printout.
|
|
97
|
+
|
|
98
|
+
num_in : float/tespy.tools.data_containers.dc_simple
|
|
99
|
+
Number of inlets for this component, default value: 2.
|
|
100
|
+
|
|
101
|
+
num_out : float/tespy.tools.data_containers.dc_simple
|
|
102
|
+
Number of outlets for this component, default value: 2.
|
|
103
|
+
|
|
104
|
+
Note
|
|
105
|
+
----
|
|
106
|
+
- Node: Fluid composition and enthalpy at all **outgoing** connections
|
|
107
|
+
(mass flow leaves the node) is result of mixture of the properties of
|
|
108
|
+
the incoming connections (mass flow enters node).
|
|
109
|
+
Incoming and outgoing connections can be a result of the calculation and
|
|
110
|
+
are not identical to the inlets and outlets!
|
|
111
|
+
- Splitter: Fluid composition and enthalpy at all outlets is the same as
|
|
112
|
+
the inlet's properties.
|
|
113
|
+
- Separator: Fluid composition is variable for all outlets, temperature at
|
|
114
|
+
all outlets is the same as the inlet's temperature.
|
|
115
|
+
- Merge: Fluid composition and enthalpy at outlet is result of mixture of
|
|
116
|
+
the inlet's properties.
|
|
117
|
+
|
|
118
|
+
Example
|
|
119
|
+
-------
|
|
120
|
+
The node can serve as merge or as splitter at the same time: The sum of
|
|
121
|
+
all mass flow going into the node is identical to the mass flow leaving it.
|
|
122
|
+
All incoming fluids are mixed in the node (mass flow weighted fluid mass
|
|
123
|
+
fractions and enthalpy). All outgoing fluids have the composition of the
|
|
124
|
+
mixture at the mixtures enthalpy/temperature.
|
|
125
|
+
|
|
126
|
+
>>> from tespy.components import sink, source, node
|
|
127
|
+
>>> from tespy.connections import connection
|
|
128
|
+
>>> from tespy.networks import network
|
|
129
|
+
>>> import shutil
|
|
130
|
+
>>> import numpy as np
|
|
131
|
+
>>> fluid_list = ['O2', 'N2']
|
|
132
|
+
>>> nw = network(fluids=fluid_list, p_unit='bar', T_unit='C',
|
|
133
|
+
... h_unit='kJ / kg', iterinfo=False)
|
|
134
|
+
>>> so1 = source('source1')
|
|
135
|
+
>>> so2 = source('source2')
|
|
136
|
+
>>> si1 = sink('sink1')
|
|
137
|
+
>>> si2 = sink('sink2')
|
|
138
|
+
>>> n = node('node', num_in=2, num_out=2)
|
|
139
|
+
>>> n.component()
|
|
140
|
+
'node'
|
|
141
|
+
>>> inc1 = connection(so1, 'out1', n, 'in1')
|
|
142
|
+
>>> inc2 = connection(so2, 'out1', n, 'in2')
|
|
143
|
+
>>> outg1 = connection(n, 'out1', si1, 'in1')
|
|
144
|
+
>>> outg2 = connection(n, 'out2', si2, 'in1')
|
|
145
|
+
>>> nw.add_conns(inc1, inc2, outg1, outg2)
|
|
146
|
+
|
|
147
|
+
2 kg/s of pure oxygen is mixed with 5 kg/s mixture (50 % nitrogen and
|
|
148
|
+
50 % oxygen). One of the outgoing connections should be at 3 kg/s.
|
|
149
|
+
|
|
150
|
+
>>> inc1.set_attr(fluid={'O2': 1, 'N2': 0}, p=1, T=20, m=2)
|
|
151
|
+
>>> inc2.set_attr(fluid={'O2': 0.5, 'N2': 0.5}, T=50, m=5)
|
|
152
|
+
>>> outg1.set_attr(m=3)
|
|
153
|
+
>>> nw.solve('design')
|
|
154
|
+
>>> (round(outg1.fluid.val['O2'], 3), round(outg1.fluid.val['N2'], 3))
|
|
155
|
+
(0.643, 0.357)
|
|
156
|
+
>>> round(outg1.T.val, 1)
|
|
157
|
+
41.8
|
|
158
|
+
|
|
159
|
+
Now calculate at what mass flow of the 50/50 mixture the total
|
|
160
|
+
oxygen fraction of the nodes outlets will be at 80 %.
|
|
161
|
+
|
|
162
|
+
>>> inc2.set_attr(m=np.nan)
|
|
163
|
+
>>> outg1.set_attr(fluid={'O2': 0.8})
|
|
164
|
+
>>> nw.solve('design')
|
|
165
|
+
>>> round(inc2.m.val_SI, 3)
|
|
166
|
+
1.333
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
@staticmethod
|
|
170
|
+
def component():
|
|
171
|
+
return 'node'
|
|
172
|
+
|
|
173
|
+
@staticmethod
|
|
174
|
+
def attr():
|
|
175
|
+
return {'num_in': dc_simple(), 'num_out': dc_simple()}
|
|
176
|
+
|
|
177
|
+
def inlets(self):
|
|
178
|
+
if self.num_in.is_set:
|
|
179
|
+
return ['in' + str(i + 1) for i in range(self.num_in.val)]
|
|
180
|
+
else:
|
|
181
|
+
self.set_attr(num_in=2)
|
|
182
|
+
return self.inlets()
|
|
183
|
+
|
|
184
|
+
def outlets(self):
|
|
185
|
+
if self.num_out.is_set:
|
|
186
|
+
return ['out' + str(i + 1) for i in range(self.num_out.val)]
|
|
187
|
+
else:
|
|
188
|
+
self.set_attr(num_out=2)
|
|
189
|
+
return self.outlets()
|
|
190
|
+
|
|
191
|
+
def comp_init(self, nw):
|
|
192
|
+
|
|
193
|
+
component.comp_init(self, nw)
|
|
194
|
+
|
|
195
|
+
# number of mandatroy equations for
|
|
196
|
+
# mass flow: 1
|
|
197
|
+
# pressure: number of inlets + number of outlets - 1
|
|
198
|
+
# the number of fluid and enthalpy equations is variable for a node!
|
|
199
|
+
|
|
200
|
+
self.num_eq = 1 + self.num_i + self.num_o - 1
|
|
201
|
+
|
|
202
|
+
# number of fluid equations
|
|
203
|
+
self.num_eq += self.num_nw_fluids * self.num_o
|
|
204
|
+
# number of enthalpy equations
|
|
205
|
+
self.num_eq += self.num_o
|
|
206
|
+
|
|
207
|
+
self.jacobian = np.zeros((
|
|
208
|
+
self.num_eq,
|
|
209
|
+
self.num_i + self.num_o + self.num_vars,
|
|
210
|
+
self.num_nw_vars))
|
|
211
|
+
|
|
212
|
+
self.residual = np.zeros(self.num_eq)
|
|
213
|
+
self.jacobian[0:1] = self.mass_flow_deriv()
|
|
214
|
+
self.jacobian[1:self.num_i + self.num_o] = self.pressure_deriv()
|
|
215
|
+
|
|
216
|
+
def equations(self):
|
|
217
|
+
r"""Calculate residual vector with results of equations."""
|
|
218
|
+
k = 0
|
|
219
|
+
######################################################################
|
|
220
|
+
# eqation for mass flow balance
|
|
221
|
+
self.residual[k] = self.mass_flow_func()
|
|
222
|
+
k += 1
|
|
223
|
+
|
|
224
|
+
######################################################################
|
|
225
|
+
# equations for pressure
|
|
226
|
+
inl = []
|
|
227
|
+
if self.num_i > 1:
|
|
228
|
+
inl = self.inl[1:]
|
|
229
|
+
for c in inl + self.outl:
|
|
230
|
+
self.residual[k] = self.inl[0].p.val_SI - c.p.val_SI
|
|
231
|
+
k += 1
|
|
232
|
+
|
|
233
|
+
######################################################################
|
|
234
|
+
# additional eqations
|
|
235
|
+
self.additional_equations(k)
|
|
236
|
+
|
|
237
|
+
def derivatives(self, increment_filter):
|
|
238
|
+
r"""Calculate partial derivatives for given equations."""
|
|
239
|
+
######################################################################
|
|
240
|
+
# derivatives for mass and pressure are static
|
|
241
|
+
k = self.num_i + self.num_o
|
|
242
|
+
######################################################################
|
|
243
|
+
# additional derivatives
|
|
244
|
+
self.additional_derivatives(increment_filter, k)
|
|
245
|
+
|
|
246
|
+
def additional_equations(self, k):
|
|
247
|
+
r"""
|
|
248
|
+
Calculate the residual vector of the additional equations.
|
|
249
|
+
|
|
250
|
+
Equations
|
|
251
|
+
|
|
252
|
+
**mandatroy equations**
|
|
253
|
+
|
|
254
|
+
.. math::
|
|
255
|
+
|
|
256
|
+
0 = \sum_i \left(\dot{m}_{i} \cdot x_{i,j}\right) - x_{o,j}
|
|
257
|
+
\cdot \sum_i \dot{m}_{i}\\
|
|
258
|
+
\forall j \in \text{fluids}\\
|
|
259
|
+
\forall o \in \text{outgoing mass flows}\\
|
|
260
|
+
\text{i: incoming mass flows}
|
|
261
|
+
|
|
262
|
+
.. math::
|
|
263
|
+
|
|
264
|
+
0 = \sum_i \dot{m}_{i} \cdot h_{i}
|
|
265
|
+
- \sum_o \dot{m}_{o} \cdot h_{o}
|
|
266
|
+
\forall o \in \text{outgoing mass flows}\\
|
|
267
|
+
\text{i: incoming mass flows}
|
|
268
|
+
"""
|
|
269
|
+
######################################################################
|
|
270
|
+
# check for incoming/outgoing mass flows in inlets and outlets
|
|
271
|
+
loc = 0
|
|
272
|
+
# total incoming enthalpy
|
|
273
|
+
h = 0
|
|
274
|
+
# total incoming mass flow (constant within every iteration)
|
|
275
|
+
self.m_inc = 0
|
|
276
|
+
|
|
277
|
+
self.inc = []
|
|
278
|
+
self.outg = []
|
|
279
|
+
for c in self.inl:
|
|
280
|
+
# incoming
|
|
281
|
+
if c.m.val_SI >= 0:
|
|
282
|
+
self.inc += [[c, loc]]
|
|
283
|
+
self.m_inc += c.m.val_SI
|
|
284
|
+
h += c.m.val_SI * c.h.val_SI
|
|
285
|
+
# outgoing
|
|
286
|
+
else:
|
|
287
|
+
self.outg += [[c, loc]]
|
|
288
|
+
loc += 1
|
|
289
|
+
|
|
290
|
+
for c in self.outl:
|
|
291
|
+
# inconming
|
|
292
|
+
if c.m.val_SI < 0:
|
|
293
|
+
self.inc += [[c, loc]]
|
|
294
|
+
self.m_inc -= c.m.val_SI
|
|
295
|
+
h -= c.m.val_SI * c.h.val_SI
|
|
296
|
+
# outgoing
|
|
297
|
+
else:
|
|
298
|
+
self.outg += [[c, loc]]
|
|
299
|
+
loc += 1
|
|
300
|
+
|
|
301
|
+
self.jacobian = self.jacobian[0:k]
|
|
302
|
+
self.jacobian = np.append(self.jacobian, np.zeros((
|
|
303
|
+
len(self.outg) * (self.num_nw_fluids + 1),
|
|
304
|
+
self.num_i + self.num_o + self.num_vars,
|
|
305
|
+
self.num_nw_vars)), axis=0)
|
|
306
|
+
|
|
307
|
+
######################################################################
|
|
308
|
+
# equations for fluid composition
|
|
309
|
+
|
|
310
|
+
for fluid in self.nw_fluids:
|
|
311
|
+
m = 0
|
|
312
|
+
for i in self.inc:
|
|
313
|
+
m += abs(i[0].m.val_SI) * i[0].fluid.val[fluid]
|
|
314
|
+
for o in self.outg:
|
|
315
|
+
self.residual[k] = m - o[0].fluid.val[fluid] * self.m_inc
|
|
316
|
+
k += 1
|
|
317
|
+
|
|
318
|
+
######################################################################
|
|
319
|
+
# equations for energy balance
|
|
320
|
+
for o in self.outg:
|
|
321
|
+
self.residual[k] = h - o[0].h.val_SI * self.m_inc
|
|
322
|
+
k += 1
|
|
323
|
+
|
|
324
|
+
def additional_derivatives(self, increment_filter, k):
|
|
325
|
+
r"""Calculate partial derivatives for given additional equations."""
|
|
326
|
+
######################################################################
|
|
327
|
+
# derivatives for fluid balance equations
|
|
328
|
+
j = 0
|
|
329
|
+
for fluid in self.nw_fluids:
|
|
330
|
+
for o in self.outg:
|
|
331
|
+
self.jacobian[k, o[1], j + 3] = -self.m_inc
|
|
332
|
+
for i in self.inc:
|
|
333
|
+
self.jacobian[k, i[1], 0] = -i[0].fluid.val[fluid]
|
|
334
|
+
self.jacobian[k, i[1], j + 3] = -abs(i[0].m.val_SI)
|
|
335
|
+
k += 1
|
|
336
|
+
j += 1
|
|
337
|
+
|
|
338
|
+
######################################################################
|
|
339
|
+
# derivatives for energy balance equations
|
|
340
|
+
for o in self.outg:
|
|
341
|
+
self.jacobian[k, o[1], 2] = -self.m_inc
|
|
342
|
+
for i in self.inc:
|
|
343
|
+
self.jacobian[k, i[1], 0] = i[0].h.val_SI - o[0].h.val_SI
|
|
344
|
+
self.jacobian[k, i[1], 2] = abs(i[0].m.val_SI)
|
|
345
|
+
k += 1
|
|
346
|
+
|
|
347
|
+
logging.info(str(self.jacobian))
|
|
348
|
+
|
|
349
|
+
def pressure_deriv(self):
|
|
350
|
+
r"""
|
|
351
|
+
Calculate partial derivatives for all pressure equations.
|
|
352
|
+
|
|
353
|
+
Returns
|
|
354
|
+
-------
|
|
355
|
+
deriv : list
|
|
356
|
+
Matrix with partial derivatives for the fluid equations.
|
|
357
|
+
"""
|
|
358
|
+
deriv = np.zeros((
|
|
359
|
+
self.num_i + self.num_o - 1,
|
|
360
|
+
self.num_i + self.num_o,
|
|
361
|
+
self.num_nw_vars))
|
|
362
|
+
|
|
363
|
+
inl = []
|
|
364
|
+
if self.num_i > 1:
|
|
365
|
+
inl = self.inl[1:]
|
|
366
|
+
for k in range(len(inl + self.outl)):
|
|
367
|
+
deriv[k, 0, 1] = 1
|
|
368
|
+
deriv[k, k + 1, 1] = -1
|
|
369
|
+
return deriv
|
|
370
|
+
|
|
371
|
+
def initialise_fluids(self, nw):
|
|
372
|
+
r"""
|
|
373
|
+
Fluid initialisation for fluid mixture at outlet of the node.
|
|
374
|
+
|
|
375
|
+
Parameters
|
|
376
|
+
----------
|
|
377
|
+
nw : tespy.networks.network
|
|
378
|
+
Network using this component object.
|
|
379
|
+
"""
|
|
380
|
+
num_fl = {}
|
|
381
|
+
for o in self.outl:
|
|
382
|
+
num_fl[o] = num_fluids(o.fluid.val)
|
|
383
|
+
|
|
384
|
+
for i in self.inl:
|
|
385
|
+
num_fl[i] = num_fluids(i.fluid.val)
|
|
386
|
+
|
|
387
|
+
ls = []
|
|
388
|
+
if any(num_fl.values()) and not all(num_fl.values()):
|
|
389
|
+
for conn, num in num_fl.items():
|
|
390
|
+
if num == 1:
|
|
391
|
+
ls += [conn]
|
|
392
|
+
|
|
393
|
+
for c in ls:
|
|
394
|
+
for fluid in nw.fluids:
|
|
395
|
+
for o in self.outl:
|
|
396
|
+
if not o.fluid.val_set[fluid]:
|
|
397
|
+
o.fluid.val[fluid] = c.fluid.val[fluid]
|
|
398
|
+
for i in self.inl:
|
|
399
|
+
if not i.fluid.val_set[fluid]:
|
|
400
|
+
i.fluid.val[fluid] = c.fluid.val[fluid]
|
|
401
|
+
|
|
402
|
+
@staticmethod
|
|
403
|
+
def initialise_source(c, key):
|
|
404
|
+
r"""
|
|
405
|
+
Return a starting value for pressure and enthalpy at outlet.
|
|
406
|
+
|
|
407
|
+
Parameters
|
|
408
|
+
----------
|
|
409
|
+
c : tespy.connections.connection
|
|
410
|
+
Connection to perform initialisation on.
|
|
411
|
+
|
|
412
|
+
key : str
|
|
413
|
+
Fluid property to retrieve.
|
|
414
|
+
|
|
415
|
+
Returns
|
|
416
|
+
-------
|
|
417
|
+
val : float
|
|
418
|
+
Starting value for pressure/enthalpy in SI units.
|
|
419
|
+
|
|
420
|
+
.. math::
|
|
421
|
+
|
|
422
|
+
val = \begin{cases}
|
|
423
|
+
10^5 & \text{key = 'p'}\\
|
|
424
|
+
5 \cdot 10^5 & \text{key = 'h'}
|
|
425
|
+
\end{cases}
|
|
426
|
+
"""
|
|
427
|
+
if key == 'p':
|
|
428
|
+
return 1e5
|
|
429
|
+
elif key == 'h':
|
|
430
|
+
return 5e5
|
|
431
|
+
|
|
432
|
+
@staticmethod
|
|
433
|
+
def initialise_target(c, key):
|
|
434
|
+
r"""
|
|
435
|
+
Return a starting value for pressure and enthalpy at inlet.
|
|
436
|
+
|
|
437
|
+
Parameters
|
|
438
|
+
----------
|
|
439
|
+
c : tespy.connections.connection
|
|
440
|
+
Connection to perform initialisation on.
|
|
441
|
+
|
|
442
|
+
key : str
|
|
443
|
+
Fluid property to retrieve.
|
|
444
|
+
|
|
445
|
+
Returns
|
|
446
|
+
-------
|
|
447
|
+
val : float
|
|
448
|
+
Starting value for pressure/enthalpy in SI units.
|
|
449
|
+
|
|
450
|
+
.. math::
|
|
451
|
+
|
|
452
|
+
val = \begin{cases}
|
|
453
|
+
10^5 & \text{key = 'p'}\\
|
|
454
|
+
5 \cdot 10^5 & \text{key = 'h'}
|
|
455
|
+
\end{cases}
|
|
456
|
+
"""
|
|
457
|
+
if key == 'p':
|
|
458
|
+
return 1e5
|
|
459
|
+
elif key == 'h':
|
|
460
|
+
return 5e5
|
|
461
|
+
|
|
462
|
+
# %%
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
class droplet_separator(component):
|
|
466
|
+
r"""
|
|
467
|
+
Separate liquid phase from gas phase of a single fluid.
|
|
468
|
+
|
|
469
|
+
Equations
|
|
470
|
+
|
|
471
|
+
**mandatory equations**
|
|
472
|
+
|
|
473
|
+
- :func:`tespy.components.nodes.droplet_separator.fluid_func`
|
|
474
|
+
- :func:`tespy.components.components.component.mass_flow_func`
|
|
475
|
+
|
|
476
|
+
.. math::
|
|
477
|
+
|
|
478
|
+
0 = \dot{m}_{in} \cdot h_{in} -
|
|
479
|
+
\sum_j \left(\dot{m}_{j,out} \cdot h_{j,out} \right)\\
|
|
480
|
+
\forall j \in outlet
|
|
481
|
+
|
|
482
|
+
0 = p_{in} - p_{out,i}\\
|
|
483
|
+
\forall i \in \mathrm{outlets}
|
|
484
|
+
|
|
485
|
+
0 = h_{1,out} - h\left(p, x=1 \right)
|
|
486
|
+
|
|
487
|
+
0 = h_{2,out} - h\left(p, x=0 \right)\\
|
|
488
|
+
x: \text{vapour mass fraction}
|
|
489
|
+
|
|
490
|
+
Inlets/Outlets
|
|
491
|
+
|
|
492
|
+
- in1
|
|
493
|
+
- out1, out2 (index 1: gas phase, index 2: liquid phase)
|
|
494
|
+
|
|
495
|
+
Image
|
|
496
|
+
|
|
497
|
+
.. image:: _images/droplet_separator.svg
|
|
498
|
+
:scale: 100 %
|
|
499
|
+
:alt: alternative text
|
|
500
|
+
:align: center
|
|
501
|
+
|
|
502
|
+
Parameters
|
|
503
|
+
----------
|
|
504
|
+
label : str
|
|
505
|
+
The label of the component.
|
|
506
|
+
|
|
507
|
+
design : list
|
|
508
|
+
List containing design parameters (stated as String).
|
|
509
|
+
|
|
510
|
+
offdesign : list
|
|
511
|
+
List containing offdesign parameters (stated as String).
|
|
512
|
+
|
|
513
|
+
design_path: str
|
|
514
|
+
Path to the components design case.
|
|
515
|
+
|
|
516
|
+
local_offdesign : boolean
|
|
517
|
+
Treat this component in offdesign mode in a design calculation.
|
|
518
|
+
|
|
519
|
+
local_design : boolean
|
|
520
|
+
Treat this component in design mode in an offdesign calculation.
|
|
521
|
+
|
|
522
|
+
char_warnings: boolean
|
|
523
|
+
Ignore warnings on default characteristics usage for this component.
|
|
524
|
+
|
|
525
|
+
printout: boolean
|
|
526
|
+
Include this component in the network's results printout.
|
|
527
|
+
|
|
528
|
+
Note
|
|
529
|
+
----
|
|
530
|
+
If you are using a drum in a network with multiple fluids, it is likely
|
|
531
|
+
the fluid propagation causes trouble. If this is the case, try to
|
|
532
|
+
specify the fluid composition at another connection of your network.
|
|
533
|
+
|
|
534
|
+
This component assumes, that the fluid composition between outlet 1 and
|
|
535
|
+
inlet 2 does not change, thus there is no equation for the fluid mass
|
|
536
|
+
fraction at the inlet 2!
|
|
537
|
+
|
|
538
|
+
Example
|
|
539
|
+
-------
|
|
540
|
+
The droplet separator separates gas from liquid phase. From a stream of
|
|
541
|
+
water the liquid phase will be separated.
|
|
542
|
+
|
|
543
|
+
>>> from tespy.components import sink, source, droplet_separator
|
|
544
|
+
>>> from tespy.connections import connection
|
|
545
|
+
>>> from tespy.networks import network
|
|
546
|
+
>>> from tespy.tools.fluid_properties import Q_ph, T_bp_p
|
|
547
|
+
>>> import shutil
|
|
548
|
+
>>> nw = network(fluids=['water'], T_unit='C', p_unit='bar',
|
|
549
|
+
... h_unit='kJ / kg', iterinfo=False)
|
|
550
|
+
>>> so = source('two phase inflow')
|
|
551
|
+
>>> sig = sink('gas outflow')
|
|
552
|
+
>>> sil = sink('liquid outflow')
|
|
553
|
+
>>> ds = droplet_separator('droplet separator')
|
|
554
|
+
>>> ds.component()
|
|
555
|
+
'droplet separator'
|
|
556
|
+
>>> so_ds = connection(so, 'out1', ds, 'in1')
|
|
557
|
+
>>> ds_sig = connection(ds, 'out1', sig, 'in1')
|
|
558
|
+
>>> ds_sil = connection(ds, 'out2', sil, 'in1')
|
|
559
|
+
>>> nw.add_conns(so_ds, ds_sig, ds_sil)
|
|
560
|
+
|
|
561
|
+
We specify the fluid's state at the inlet. At the gas outflow saturated
|
|
562
|
+
gas enthalpy is expected, at the liquid gas outflow saturated liquid
|
|
563
|
+
enthalpy. The mass flow at the outlets is expected to split according to
|
|
564
|
+
the vapor mass fraction:
|
|
565
|
+
|
|
566
|
+
.. math::
|
|
567
|
+
|
|
568
|
+
\dot{m}_\mathrm{out,1} = \frac{h_\mathrm{in} - h'}{h'' - h'} \cdot
|
|
569
|
+
\dot{m}_\mathrm{in}
|
|
570
|
+
|
|
571
|
+
\dot{m}_\mathrm{out,2} = \left(1 - \frac{h_\mathrm{in} - h'}{h'' - h'}
|
|
572
|
+
\right) \cdot \dot{m}_\mathrm{in}
|
|
573
|
+
|
|
574
|
+
>>> so_ds.set_attr(fluid={'water': 1}, p=1, h=1500, m=10)
|
|
575
|
+
>>> nw.solve('design')
|
|
576
|
+
>>> Q_in = Q_ph(so_ds.p.val_SI, so_ds.h.val_SI, 'water')
|
|
577
|
+
>>> round(Q_in * so_ds.m.val_SI, 6) == round(ds_sig.m.val_SI, 6)
|
|
578
|
+
True
|
|
579
|
+
>>> round((1 - Q_in) * so_ds.m.val_SI, 6) == round(ds_sil.m.val_SI, 6)
|
|
580
|
+
True
|
|
581
|
+
>>> Q_ph(ds_sig.p.val_SI, ds_sig.h.val_SI, 'water')
|
|
582
|
+
1.0
|
|
583
|
+
>>> Q_ph(ds_sil.p.val_SI, ds_sil.h.val_SI, 'water')
|
|
584
|
+
0.0
|
|
585
|
+
|
|
586
|
+
In a different setup, we unset pressure and enthalpy and specify gas
|
|
587
|
+
temperature and mass flow instead. The temperature specification must yield
|
|
588
|
+
the corresponding boiling point pressure and the mass flow must yield the
|
|
589
|
+
inlet enthalpy. The inlet vapor mass fraction must be equal to fraction of
|
|
590
|
+
gas mass flow to inlet mass flow (0.95 in this example).
|
|
591
|
+
|
|
592
|
+
>>> so_ds.set_attr(fluid={'water': 1}, p=None, h=None, T=150, m=10)
|
|
593
|
+
>>> ds_sig.set_attr(m=9.5)
|
|
594
|
+
>>> nw.solve('design')
|
|
595
|
+
>>> round(Q_ph(so_ds.p.val_SI, so_ds.h.val_SI, 'water'), 6)
|
|
596
|
+
0.95
|
|
597
|
+
>>> T_boil = T_bp_p(so_ds.to_flow())
|
|
598
|
+
>>> round(T_boil, 6) == round(so_ds.T.val_SI, 6)
|
|
599
|
+
True
|
|
600
|
+
"""
|
|
601
|
+
|
|
602
|
+
@staticmethod
|
|
603
|
+
def component():
|
|
604
|
+
return 'droplet separator'
|
|
605
|
+
|
|
606
|
+
@staticmethod
|
|
607
|
+
def inlets():
|
|
608
|
+
return ['in1']
|
|
609
|
+
|
|
610
|
+
@staticmethod
|
|
611
|
+
def outlets():
|
|
612
|
+
return ['out1', 'out2']
|
|
613
|
+
|
|
614
|
+
def comp_init(self, nw):
|
|
615
|
+
|
|
616
|
+
component.comp_init(self, nw)
|
|
617
|
+
|
|
618
|
+
# number of mandatroy equations for
|
|
619
|
+
# fluid balance: num_fl * 2
|
|
620
|
+
# mass flow: 1
|
|
621
|
+
# pressure: 2
|
|
622
|
+
# enthalpy: 1
|
|
623
|
+
# saturated liquid outlet: 1
|
|
624
|
+
# saturated gas outlet: 1
|
|
625
|
+
self.num_eq = self.num_nw_fluids * 2 + 6
|
|
626
|
+
|
|
627
|
+
self.jacobian = np.zeros((
|
|
628
|
+
self.num_eq,
|
|
629
|
+
self.num_i + self.num_o + self.num_vars,
|
|
630
|
+
self.num_nw_vars))
|
|
631
|
+
|
|
632
|
+
self.residual = np.zeros(self.num_eq)
|
|
633
|
+
pos = self.num_nw_fluids * 2
|
|
634
|
+
self.jacobian[0:pos] = self.fluid_deriv()
|
|
635
|
+
self.jacobian[pos:pos + 1] = self.mass_flow_deriv()
|
|
636
|
+
self.jacobian[pos + 1:pos + 3] = self.pressure_deriv()
|
|
637
|
+
|
|
638
|
+
def equations(self):
|
|
639
|
+
r"""Calculate residual vector with results of equations."""
|
|
640
|
+
k = 0
|
|
641
|
+
######################################################################
|
|
642
|
+
# eqations for fluid balance
|
|
643
|
+
self.residual[k:k + self.num_nw_fluids * 2] = self.fluid_func()
|
|
644
|
+
k += self.num_nw_fluids * 2
|
|
645
|
+
|
|
646
|
+
######################################################################
|
|
647
|
+
# eqations for mass flow balance
|
|
648
|
+
self.residual[k] = self.mass_flow_func()
|
|
649
|
+
k += 1
|
|
650
|
+
|
|
651
|
+
######################################################################
|
|
652
|
+
# eqations for pressure
|
|
653
|
+
p = self.inl[0].p.val_SI
|
|
654
|
+
for c in self.outl:
|
|
655
|
+
self.residual[k] = p - c.p.val_SI
|
|
656
|
+
k += 1
|
|
657
|
+
|
|
658
|
+
######################################################################
|
|
659
|
+
# eqations for enthalpy
|
|
660
|
+
val = 0
|
|
661
|
+
for i in self.inl:
|
|
662
|
+
val += i.m.val_SI * i.h.val_SI
|
|
663
|
+
for o in self.outl:
|
|
664
|
+
val -= o.m.val_SI * o.h.val_SI
|
|
665
|
+
self.residual[k] = val
|
|
666
|
+
k += 1
|
|
667
|
+
|
|
668
|
+
######################################################################
|
|
669
|
+
# eqations for staturated fluid state at outlets
|
|
670
|
+
self.residual[k] = h_mix_pQ(
|
|
671
|
+
self.outl[0].to_flow(), 1) - self.outl[0].h.val_SI
|
|
672
|
+
k += 1
|
|
673
|
+
self.residual[k] = h_mix_pQ(
|
|
674
|
+
self.outl[1].to_flow(), 0) - self.outl[1].h.val_SI
|
|
675
|
+
k += 1
|
|
676
|
+
|
|
677
|
+
def derivatives(self, increment_filter):
|
|
678
|
+
r"""Calculate partial derivatives for given equations."""
|
|
679
|
+
######################################################################
|
|
680
|
+
# derivatives fluid, mass flow and pressure balance are static
|
|
681
|
+
k = self.num_nw_fluids * 2 + 3
|
|
682
|
+
|
|
683
|
+
######################################################################
|
|
684
|
+
# derivatives for energy balance equation
|
|
685
|
+
self.jacobian[k, 0, 0] = self.inl[0].h.val_SI
|
|
686
|
+
self.jacobian[k, 0, 2] = self.inl[0].m.val_SI
|
|
687
|
+
j = 0
|
|
688
|
+
for outl in self.outl:
|
|
689
|
+
self.jacobian[k, j + 1, 0] = -outl.h.val_SI
|
|
690
|
+
self.jacobian[k, j + 1, 2] = -outl.m.val_SI
|
|
691
|
+
j += 1
|
|
692
|
+
k += 1
|
|
693
|
+
|
|
694
|
+
######################################################################
|
|
695
|
+
# derivatives of equations for saturated states at outlets
|
|
696
|
+
self.jacobian[k, 1, 1] = dh_mix_dpQ(self.outl[0].to_flow(), 0)
|
|
697
|
+
self.jacobian[k, 1, 2] = -1
|
|
698
|
+
k += 1
|
|
699
|
+
self.jacobian[k, 2, 1] = dh_mix_dpQ(self.outl[1].to_flow(), 1)
|
|
700
|
+
self.jacobian[k, 2, 2] = -1
|
|
701
|
+
k += 1
|
|
702
|
+
|
|
703
|
+
def fluid_func(self):
|
|
704
|
+
r"""
|
|
705
|
+
Calculate the vector of residual values for fluid balance equations.
|
|
706
|
+
|
|
707
|
+
Returns
|
|
708
|
+
-------
|
|
709
|
+
residual : list
|
|
710
|
+
Vector of residual values for component's fluid balance.
|
|
711
|
+
|
|
712
|
+
.. math::
|
|
713
|
+
|
|
714
|
+
0 = fluid_{i,in_1} - fluid_{i,out_{j}}\\
|
|
715
|
+
\forall i \in \mathrm{fluid}, \; \forall j \in inlets
|
|
716
|
+
|
|
717
|
+
"""
|
|
718
|
+
residual = []
|
|
719
|
+
|
|
720
|
+
for o in self.outl:
|
|
721
|
+
for fluid, x in self.inl[0].fluid.val.items():
|
|
722
|
+
residual += [x - o.fluid.val[fluid]]
|
|
723
|
+
return residual
|
|
724
|
+
|
|
725
|
+
def fluid_deriv(self):
|
|
726
|
+
r"""
|
|
727
|
+
Calculate partial derivatives for all fluid balance equations.
|
|
728
|
+
|
|
729
|
+
Returns
|
|
730
|
+
-------
|
|
731
|
+
deriv : ndarray
|
|
732
|
+
Matrix with partial derivatives for the fluid equations.
|
|
733
|
+
"""
|
|
734
|
+
deriv = np.zeros((2 * self.num_nw_fluids, 3, self.num_nw_vars))
|
|
735
|
+
for k in range(2):
|
|
736
|
+
for i in range(self.num_nw_fluids):
|
|
737
|
+
deriv[i + k * self.num_nw_fluids, 0, i + 3] = 1
|
|
738
|
+
deriv[i + k * self.num_nw_fluids, k + 1, i + 3] = -1
|
|
739
|
+
return deriv
|
|
740
|
+
|
|
741
|
+
def pressure_deriv(self):
|
|
742
|
+
r"""
|
|
743
|
+
Calculate partial derivatives for pressure equations.
|
|
744
|
+
|
|
745
|
+
Returns
|
|
746
|
+
-------
|
|
747
|
+
deriv : list
|
|
748
|
+
Matrix with partial derivatives for the pressure equations.
|
|
749
|
+
"""
|
|
750
|
+
deriv = np.zeros((2, 3, self.num_nw_vars))
|
|
751
|
+
for k in range(2):
|
|
752
|
+
deriv[k, 0, 1] = 1
|
|
753
|
+
deriv[k, k + 1, 1] = -1
|
|
754
|
+
return deriv
|
|
755
|
+
|
|
756
|
+
@staticmethod
|
|
757
|
+
def initialise_source(c, key):
|
|
758
|
+
r"""
|
|
759
|
+
Return a starting value for pressure and enthalpy at outlet.
|
|
760
|
+
|
|
761
|
+
Parameters
|
|
762
|
+
----------
|
|
763
|
+
c : tespy.connections.connection
|
|
764
|
+
Connection to perform initialisation on.
|
|
765
|
+
|
|
766
|
+
key : str
|
|
767
|
+
Fluid property to retrieve.
|
|
768
|
+
|
|
769
|
+
Returns
|
|
770
|
+
-------
|
|
771
|
+
val : float
|
|
772
|
+
Starting value for pressure/enthalpy in SI units.
|
|
773
|
+
|
|
774
|
+
.. math::
|
|
775
|
+
|
|
776
|
+
val = \begin{cases}
|
|
777
|
+
10^6 & \text{key = 'p'}\\
|
|
778
|
+
h\left(p, x=1 \right) & \text{key = 'h' at outlet 1}\\
|
|
779
|
+
h\left(p, x=0 \right) & \text{key = 'h' at outlet 2}
|
|
780
|
+
\end{cases}
|
|
781
|
+
"""
|
|
782
|
+
if key == 'p':
|
|
783
|
+
return 10e5
|
|
784
|
+
elif key == 'h':
|
|
785
|
+
if c.source_id == 'out1':
|
|
786
|
+
return h_mix_pQ(c.to_flow(), 1)
|
|
787
|
+
else:
|
|
788
|
+
return h_mix_pQ(c.to_flow(), 0)
|
|
789
|
+
|
|
790
|
+
@staticmethod
|
|
791
|
+
def initialise_target(c, key):
|
|
792
|
+
r"""
|
|
793
|
+
Return a starting value for pressure and enthalpy at inlet.
|
|
794
|
+
|
|
795
|
+
Parameters
|
|
796
|
+
----------
|
|
797
|
+
c : tespy.connections.connection
|
|
798
|
+
Connection to perform initialisation on.
|
|
799
|
+
|
|
800
|
+
key : str
|
|
801
|
+
Fluid property to retrieve.
|
|
802
|
+
|
|
803
|
+
Returns
|
|
804
|
+
-------
|
|
805
|
+
val : float
|
|
806
|
+
Starting value for pressure/enthalpy in SI units.
|
|
807
|
+
|
|
808
|
+
.. math::
|
|
809
|
+
|
|
810
|
+
val = \begin{cases}
|
|
811
|
+
10^6 & \text{key = 'p'}\\
|
|
812
|
+
h\left(p, x=0.5 \right) & \text{key = 'h' at inlet 1}
|
|
813
|
+
\end{cases}
|
|
814
|
+
"""
|
|
815
|
+
if key == 'p':
|
|
816
|
+
return 10e5
|
|
817
|
+
elif key == 'h':
|
|
818
|
+
return h_mix_pQ(c.to_flow(), 0.5)
|
|
819
|
+
|
|
820
|
+
# %%
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
class drum(component):
|
|
824
|
+
r"""
|
|
825
|
+
A drum separates saturated gas from saturated liquid.
|
|
826
|
+
|
|
827
|
+
Equations
|
|
828
|
+
|
|
829
|
+
**mandatory equations**
|
|
830
|
+
|
|
831
|
+
- :func:`tespy.components.nodes.drum.fluid_func`
|
|
832
|
+
- :func:`tespy.components.components.component.mass_flow_func`
|
|
833
|
+
|
|
834
|
+
.. math::
|
|
835
|
+
|
|
836
|
+
0 = \sum_i \left(\dot{m}_{i,in} \cdot h_{i,in} \right) -
|
|
837
|
+
\sum_j \left(\dot{m}_{j,out} \cdot h_{j,out} \right)\\
|
|
838
|
+
\forall i \in inlets, \; \forall j \in outlet
|
|
839
|
+
|
|
840
|
+
0 = p_{in,1} - p_{out,i}\\
|
|
841
|
+
\forall i \in \mathrm{outlets}
|
|
842
|
+
|
|
843
|
+
0 = h_{1,out} - h\left(p, x=0 \right)
|
|
844
|
+
|
|
845
|
+
0 = h_{2,out} - h\left(p, x=1 \right)\\
|
|
846
|
+
x: \text{vapour mass fraction}
|
|
847
|
+
|
|
848
|
+
Inlets/Outlets
|
|
849
|
+
|
|
850
|
+
- in1, in2 (index 1: from economiser, index 2: from evaporator)
|
|
851
|
+
- out1, out2 (index 1: to evaporator, index 2: to superheater)
|
|
852
|
+
|
|
853
|
+
Image
|
|
854
|
+
|
|
855
|
+
.. image:: _images/drum.svg
|
|
856
|
+
:scale: 100 %
|
|
857
|
+
:alt: alternative text
|
|
858
|
+
:align: center
|
|
859
|
+
|
|
860
|
+
Parameters
|
|
861
|
+
----------
|
|
862
|
+
label : str
|
|
863
|
+
The label of the component.
|
|
864
|
+
|
|
865
|
+
design : list
|
|
866
|
+
List containing design parameters (stated as String).
|
|
867
|
+
|
|
868
|
+
offdesign : list
|
|
869
|
+
List containing offdesign parameters (stated as String).
|
|
870
|
+
|
|
871
|
+
design_path: str
|
|
872
|
+
Path to the components design case.
|
|
873
|
+
|
|
874
|
+
local_offdesign : boolean
|
|
875
|
+
Treat this component in offdesign mode in a design calculation.
|
|
876
|
+
|
|
877
|
+
local_design : boolean
|
|
878
|
+
Treat this component in design mode in an offdesign calculation.
|
|
879
|
+
|
|
880
|
+
char_warnings: boolean
|
|
881
|
+
Ignore warnings on default characteristics usage for this component.
|
|
882
|
+
|
|
883
|
+
printout: boolean
|
|
884
|
+
Include this component in the network's results printout.
|
|
885
|
+
|
|
886
|
+
Note
|
|
887
|
+
----
|
|
888
|
+
If you are using a drum in a network with multiple fluids, it is likely
|
|
889
|
+
the fluid propagation causes trouble. If this is the case, try to
|
|
890
|
+
specify the fluid composition at another connection of your network.
|
|
891
|
+
|
|
892
|
+
This component assumes, that the fluid composition between outlet 1 and
|
|
893
|
+
inlet 2 does not change, thus there is no equation for the fluid mass
|
|
894
|
+
fraction at the inlet 2!
|
|
895
|
+
|
|
896
|
+
Example
|
|
897
|
+
-------
|
|
898
|
+
The drum separates saturated gas from saturated liquid. The liquid phase is
|
|
899
|
+
transported to an evaporator, the staturated gas phase is extracted from
|
|
900
|
+
the drum. In this example ammonia is evaporated using ambient air. A
|
|
901
|
+
characteristic function is applied for the heat transfer coefficient of the
|
|
902
|
+
evaporator. It is possible to load the char_line with the function
|
|
903
|
+
:code:`load_default_char` from the default lines. We want to use the
|
|
904
|
+
'EVAPORATING FLUID' lines of the heat exchanger.
|
|
905
|
+
|
|
906
|
+
>>> from tespy.components import sink, source, drum, pump, heat_exchanger
|
|
907
|
+
>>> from tespy.connections import connection, ref
|
|
908
|
+
>>> from tespy.networks import network
|
|
909
|
+
>>> from tespy.tools.characteristics import char_line
|
|
910
|
+
>>> from tespy.tools.characteristics import load_default_char as ldc
|
|
911
|
+
>>> import shutil
|
|
912
|
+
>>> import numpy as np
|
|
913
|
+
>>> nw = network(fluids=['NH3', 'air'], T_unit='C', p_unit='bar',
|
|
914
|
+
... h_unit='kJ / kg', iterinfo=False)
|
|
915
|
+
>>> fa = source('feed ammonia')
|
|
916
|
+
>>> amb_in = source('air inlet')
|
|
917
|
+
>>> amb_out = sink('air outlet')
|
|
918
|
+
>>> s = sink('steam')
|
|
919
|
+
>>> dr = drum('drum')
|
|
920
|
+
>>> dr.component()
|
|
921
|
+
'drum'
|
|
922
|
+
>>> ev = heat_exchanger('evaporator')
|
|
923
|
+
>>> erp = pump('evaporator reciculation pump')
|
|
924
|
+
>>> f_dr = connection(fa, 'out1', dr, 'in1')
|
|
925
|
+
>>> dr_erp = connection(dr, 'out1', erp, 'in1')
|
|
926
|
+
>>> erp_ev = connection(erp, 'out1', ev, 'in2')
|
|
927
|
+
>>> ev_dr = connection(ev, 'out2', dr, 'in2')
|
|
928
|
+
>>> dr_s = connection(dr, 'out2', s, 'in1')
|
|
929
|
+
>>> nw.add_conns(f_dr, dr_erp, erp_ev, ev_dr, dr_s)
|
|
930
|
+
>>> amb_ev = connection(amb_in, 'out1', ev, 'in1')
|
|
931
|
+
>>> ev_amb = connection(ev, 'out1', amb_out, 'in1')
|
|
932
|
+
>>> nw.add_conns(amb_ev, ev_amb)
|
|
933
|
+
|
|
934
|
+
The ambient air enters the evaporator at 30 °C. The pinch point temperature
|
|
935
|
+
difference (ttd_l) of the evaporator is at 5 K, and 1 MW of heat should be
|
|
936
|
+
transferred. State of ammonia at the inlet is at -5 °C and 5 bar. From this
|
|
937
|
+
design it is possible to calculate offdesign performance at 75 % part load.
|
|
938
|
+
|
|
939
|
+
>>> char1 = ldc('heat exchanger', 'kA_char1', 'DEFAULT',
|
|
940
|
+
... char_line)
|
|
941
|
+
>>> char2 = ldc('heat exchanger', 'kA_char2', 'EVAPORATING FLUID',
|
|
942
|
+
... char_line)
|
|
943
|
+
>>> ev.set_attr(pr1=0.999, pr2=0.99, ttd_l=5, kA_char1=char1,
|
|
944
|
+
... kA_char2=char2, design=['pr1', 'ttd_l'],
|
|
945
|
+
... offdesign=['zeta1', 'kA_char'])
|
|
946
|
+
>>> ev.set_attr(Q=-1e6)
|
|
947
|
+
>>> erp.set_attr(eta_s=0.8)
|
|
948
|
+
>>> f_dr.set_attr(p=5, T=-5)
|
|
949
|
+
>>> erp_ev.set_attr(m=ref(f_dr, 4, 0), fluid={'air': 0, 'NH3': 1})
|
|
950
|
+
>>> amb_ev.set_attr(fluid={'air': 1, 'NH3': 0}, T=30)
|
|
951
|
+
>>> ev_amb.set_attr(p=1)
|
|
952
|
+
>>> nw.solve('design')
|
|
953
|
+
>>> nw.save('tmp')
|
|
954
|
+
>>> round(ev_amb.T.val - erp_ev.T.val ,1)
|
|
955
|
+
5.0
|
|
956
|
+
>>> round(f_dr.h.val, 1)
|
|
957
|
+
322.7
|
|
958
|
+
>>> round(dr_erp.h.val, 1)
|
|
959
|
+
364.9
|
|
960
|
+
>>> round(ev_dr.h.val, 1)
|
|
961
|
+
687.2
|
|
962
|
+
>>> round(f_dr.m.val, 2)
|
|
963
|
+
0.78
|
|
964
|
+
>>> ev.set_attr(Q=-0.75e6)
|
|
965
|
+
>>> nw.solve('offdesign', design_path='tmp')
|
|
966
|
+
>>> round(f_dr.m.val, 2)
|
|
967
|
+
0.58
|
|
968
|
+
>>> round(ev_amb.T.val - erp_ev.T.val ,1)
|
|
969
|
+
3.0
|
|
970
|
+
>>> shutil.rmtree('./tmp', ignore_errors=True)
|
|
971
|
+
"""
|
|
972
|
+
|
|
973
|
+
@staticmethod
|
|
974
|
+
def component():
|
|
975
|
+
return 'drum'
|
|
976
|
+
|
|
977
|
+
@staticmethod
|
|
978
|
+
def inlets():
|
|
979
|
+
return ['in1', 'in2']
|
|
980
|
+
|
|
981
|
+
@staticmethod
|
|
982
|
+
def outlets():
|
|
983
|
+
return ['out1', 'out2']
|
|
984
|
+
|
|
985
|
+
def comp_init(self, nw):
|
|
986
|
+
|
|
987
|
+
component.comp_init(self, nw)
|
|
988
|
+
|
|
989
|
+
# number of mandatroy equations for
|
|
990
|
+
# fluid balance: num_fl * 2
|
|
991
|
+
# mass flow: 1
|
|
992
|
+
# pressure: 3
|
|
993
|
+
# enthalpy: 1
|
|
994
|
+
# saturated liquid outlet: 1
|
|
995
|
+
# saturated gas outlet: 1
|
|
996
|
+
self.num_eq = self.num_nw_fluids * 2 + 7
|
|
997
|
+
|
|
998
|
+
self.jacobian = np.zeros((
|
|
999
|
+
self.num_eq,
|
|
1000
|
+
self.num_i + self.num_o + self.num_vars,
|
|
1001
|
+
self.num_nw_vars))
|
|
1002
|
+
|
|
1003
|
+
self.residual = np.zeros(self.num_eq)
|
|
1004
|
+
pos = self.num_nw_fluids * 2
|
|
1005
|
+
self.jacobian[0:pos] = self.fluid_deriv()
|
|
1006
|
+
self.jacobian[pos:pos + 1] = self.mass_flow_deriv()
|
|
1007
|
+
self.jacobian[pos + 1:pos + 4] = self.pressure_deriv()
|
|
1008
|
+
|
|
1009
|
+
def equations(self):
|
|
1010
|
+
r"""Calculate residual vector with results of equations."""
|
|
1011
|
+
k = 0
|
|
1012
|
+
######################################################################
|
|
1013
|
+
# eqations for fluid balance
|
|
1014
|
+
self.residual[k:k + self.num_nw_fluids * 2] = self.fluid_func()
|
|
1015
|
+
k += self.num_nw_fluids * 2
|
|
1016
|
+
|
|
1017
|
+
######################################################################
|
|
1018
|
+
# eqations for mass flow balance
|
|
1019
|
+
self.residual[k] = self.mass_flow_func()
|
|
1020
|
+
k += 1
|
|
1021
|
+
|
|
1022
|
+
######################################################################
|
|
1023
|
+
# eqations for pressure
|
|
1024
|
+
p = self.inl[0].p.val_SI
|
|
1025
|
+
for c in [self.inl[1]] + self.outl:
|
|
1026
|
+
self.residual[k] = p - c.p.val_SI
|
|
1027
|
+
k += 1
|
|
1028
|
+
|
|
1029
|
+
######################################################################
|
|
1030
|
+
# eqations for enthalpy
|
|
1031
|
+
val = 0
|
|
1032
|
+
for i in self.inl:
|
|
1033
|
+
val += i.m.val_SI * i.h.val_SI
|
|
1034
|
+
for o in self.outl:
|
|
1035
|
+
val -= o.m.val_SI * o.h.val_SI
|
|
1036
|
+
self.residual[k] = val
|
|
1037
|
+
k += 1
|
|
1038
|
+
|
|
1039
|
+
######################################################################
|
|
1040
|
+
# eqations for staturated fluid state at outlets
|
|
1041
|
+
self.residual[k] = h_mix_pQ(
|
|
1042
|
+
self.outl[0].to_flow(), 0) - self.outl[0].h.val_SI
|
|
1043
|
+
k += 1
|
|
1044
|
+
self.residual[k] = h_mix_pQ(
|
|
1045
|
+
self.outl[1].to_flow(), 1) - self.outl[1].h.val_SI
|
|
1046
|
+
k += 1
|
|
1047
|
+
|
|
1048
|
+
def derivatives(self, increment_filter):
|
|
1049
|
+
r"""Calculate partial derivatives for given equations."""
|
|
1050
|
+
######################################################################
|
|
1051
|
+
# derivatives fluid, mass flow and pressure balance are static
|
|
1052
|
+
k = self.num_nw_fluids * 2 + 4
|
|
1053
|
+
|
|
1054
|
+
######################################################################
|
|
1055
|
+
# derivatives for energy balance equation
|
|
1056
|
+
i = 0
|
|
1057
|
+
for inl in self.inl:
|
|
1058
|
+
self.jacobian[k, i, 0] = inl.h.val_SI
|
|
1059
|
+
self.jacobian[k, i, 2] = inl.m.val_SI
|
|
1060
|
+
i += 1
|
|
1061
|
+
j = 0
|
|
1062
|
+
for outl in self.outl:
|
|
1063
|
+
self.jacobian[k, j + i, 0] = -outl.h.val_SI
|
|
1064
|
+
self.jacobian[k, j + i, 2] = -outl.m.val_SI
|
|
1065
|
+
j += 1
|
|
1066
|
+
k += 1
|
|
1067
|
+
|
|
1068
|
+
######################################################################
|
|
1069
|
+
# derivatives of equations for saturated states at outlets
|
|
1070
|
+
self.jacobian[k, 2, 1] = dh_mix_dpQ(self.outl[0].to_flow(), 0)
|
|
1071
|
+
self.jacobian[k, 2, 2] = -1
|
|
1072
|
+
k += 1
|
|
1073
|
+
self.jacobian[k, 3, 1] = dh_mix_dpQ(self.outl[1].to_flow(), 1)
|
|
1074
|
+
self.jacobian[k, 3, 2] = -1
|
|
1075
|
+
k += 1
|
|
1076
|
+
|
|
1077
|
+
def fluid_func(self):
|
|
1078
|
+
r"""
|
|
1079
|
+
Calculate the vector of residual values for fluid balance equations.
|
|
1080
|
+
|
|
1081
|
+
Returns
|
|
1082
|
+
-------
|
|
1083
|
+
residual : list
|
|
1084
|
+
Vector of residual values for component's fluid balance.
|
|
1085
|
+
|
|
1086
|
+
.. math::
|
|
1087
|
+
|
|
1088
|
+
0 = fluid_{i,in_1} - fluid_{i,out_{j}}\\
|
|
1089
|
+
\forall i \in \mathrm{fluid}, \; \forall j \in inlets
|
|
1090
|
+
|
|
1091
|
+
"""
|
|
1092
|
+
residual = []
|
|
1093
|
+
|
|
1094
|
+
for o in self.outl:
|
|
1095
|
+
for fluid, x in self.inl[0].fluid.val.items():
|
|
1096
|
+
residual += [x - o.fluid.val[fluid]]
|
|
1097
|
+
return residual
|
|
1098
|
+
|
|
1099
|
+
def fluid_deriv(self):
|
|
1100
|
+
r"""
|
|
1101
|
+
Calculate partial derivatives for all fluid balance equations.
|
|
1102
|
+
|
|
1103
|
+
Returns
|
|
1104
|
+
-------
|
|
1105
|
+
deriv : ndarray
|
|
1106
|
+
Matrix with partial derivatives for the fluid equations.
|
|
1107
|
+
"""
|
|
1108
|
+
deriv = np.zeros((2 * self.num_nw_fluids, 4, self.num_nw_vars))
|
|
1109
|
+
for k in range(2):
|
|
1110
|
+
for i in range(self.num_nw_fluids):
|
|
1111
|
+
deriv[i + k * self.num_nw_fluids, 0, i + 3] = 1
|
|
1112
|
+
deriv[i + k * self.num_nw_fluids, k + 2, i + 3] = -1
|
|
1113
|
+
return deriv
|
|
1114
|
+
|
|
1115
|
+
def pressure_deriv(self):
|
|
1116
|
+
r"""
|
|
1117
|
+
Calculate partial derivatives for pressure equations.
|
|
1118
|
+
|
|
1119
|
+
Returns
|
|
1120
|
+
-------
|
|
1121
|
+
deriv : list
|
|
1122
|
+
Matrix with partial derivatives for the pressure equations.
|
|
1123
|
+
"""
|
|
1124
|
+
deriv = np.zeros((3, 4, self.num_nw_vars))
|
|
1125
|
+
for k in range(3):
|
|
1126
|
+
deriv[k, 0, 1] = 1
|
|
1127
|
+
deriv[k, k + 1, 1] = -1
|
|
1128
|
+
return deriv
|
|
1129
|
+
|
|
1130
|
+
@staticmethod
|
|
1131
|
+
def initialise_source(c, key):
|
|
1132
|
+
r"""
|
|
1133
|
+
Return a starting value for pressure and enthalpy at outlet.
|
|
1134
|
+
|
|
1135
|
+
Parameters
|
|
1136
|
+
----------
|
|
1137
|
+
c : tespy.connections.connection
|
|
1138
|
+
Connection to perform initialisation on.
|
|
1139
|
+
|
|
1140
|
+
key : str
|
|
1141
|
+
Fluid property to retrieve.
|
|
1142
|
+
|
|
1143
|
+
Returns
|
|
1144
|
+
-------
|
|
1145
|
+
val : float
|
|
1146
|
+
Starting value for pressure/enthalpy in SI units.
|
|
1147
|
+
|
|
1148
|
+
.. math::
|
|
1149
|
+
|
|
1150
|
+
val = \begin{cases}
|
|
1151
|
+
10^6 & \text{key = 'p'}\\
|
|
1152
|
+
h\left(p, x=0 \right) & \text{key = 'h' at outlet 1}\\
|
|
1153
|
+
h\left(p, x=1 \right) & \text{key = 'h' at outlet 2}
|
|
1154
|
+
\end{cases}
|
|
1155
|
+
"""
|
|
1156
|
+
if key == 'p':
|
|
1157
|
+
return 10e5
|
|
1158
|
+
elif key == 'h':
|
|
1159
|
+
if c.source_id == 'out1':
|
|
1160
|
+
return h_mix_pQ(c.to_flow(), 0)
|
|
1161
|
+
else:
|
|
1162
|
+
return h_mix_pQ(c.to_flow(), 1)
|
|
1163
|
+
|
|
1164
|
+
@staticmethod
|
|
1165
|
+
def initialise_target(c, key):
|
|
1166
|
+
r"""
|
|
1167
|
+
Return a starting value for pressure and enthalpy at inlet.
|
|
1168
|
+
|
|
1169
|
+
Parameters
|
|
1170
|
+
----------
|
|
1171
|
+
c : tespy.connections.connection
|
|
1172
|
+
Connection to perform initialisation on.
|
|
1173
|
+
|
|
1174
|
+
key : str
|
|
1175
|
+
Fluid property to retrieve.
|
|
1176
|
+
|
|
1177
|
+
Returns
|
|
1178
|
+
-------
|
|
1179
|
+
val : float
|
|
1180
|
+
Starting value for pressure/enthalpy in SI units.
|
|
1181
|
+
|
|
1182
|
+
.. math::
|
|
1183
|
+
|
|
1184
|
+
val = \begin{cases}
|
|
1185
|
+
10^6 & \text{key = 'p'}\\
|
|
1186
|
+
h\left(p, x=0 \right) & \text{key = 'h' at inlet 1}\\
|
|
1187
|
+
h\left(p, x=0.7 \right) & \text{key = 'h' at inlet 2}
|
|
1188
|
+
\end{cases}
|
|
1189
|
+
"""
|
|
1190
|
+
if key == 'p':
|
|
1191
|
+
return 10e5
|
|
1192
|
+
elif key == 'h':
|
|
1193
|
+
if c.target_id == 'in1':
|
|
1194
|
+
return h_mix_pQ(c.to_flow(), 0)
|
|
1195
|
+
else:
|
|
1196
|
+
return h_mix_pQ(c.to_flow(), 0.7)
|
|
1197
|
+
|
|
1198
|
+
# %%
|
|
1199
|
+
|
|
1200
|
+
|
|
1201
|
+
class merge(node):
|
|
1202
|
+
r"""
|
|
1203
|
+
The component node is the parent class for splitter, separator and merge.
|
|
1204
|
+
|
|
1205
|
+
Equations
|
|
1206
|
+
|
|
1207
|
+
**mandatory equations**
|
|
1208
|
+
|
|
1209
|
+
- :func:`tespy.components.components.component.mass_flow_func`
|
|
1210
|
+
|
|
1211
|
+
.. math::
|
|
1212
|
+
|
|
1213
|
+
0 = p_{in} - p_{out,i} \;
|
|
1214
|
+
\forall i \in \mathrm{outlets}
|
|
1215
|
+
|
|
1216
|
+
**additional equations**
|
|
1217
|
+
|
|
1218
|
+
- :func:`tespy.components.components.merge.additional_equations`
|
|
1219
|
+
|
|
1220
|
+
Inlets/Outlets
|
|
1221
|
+
|
|
1222
|
+
- specify number of outlets with :code:`num_in` (default value: 2)
|
|
1223
|
+
- out1
|
|
1224
|
+
|
|
1225
|
+
Image
|
|
1226
|
+
|
|
1227
|
+
.. image:: _images/merge.svg
|
|
1228
|
+
:scale: 100 %
|
|
1229
|
+
:alt: alternative text
|
|
1230
|
+
:align: center
|
|
1231
|
+
|
|
1232
|
+
Parameters
|
|
1233
|
+
----------
|
|
1234
|
+
label : str
|
|
1235
|
+
The label of the component.
|
|
1236
|
+
|
|
1237
|
+
design : list
|
|
1238
|
+
List containing design parameters (stated as String).
|
|
1239
|
+
|
|
1240
|
+
offdesign : list
|
|
1241
|
+
List containing offdesign parameters (stated as String).
|
|
1242
|
+
|
|
1243
|
+
design_path: str
|
|
1244
|
+
Path to the components design case.
|
|
1245
|
+
|
|
1246
|
+
local_offdesign : boolean
|
|
1247
|
+
Treat this component in offdesign mode in a design calculation.
|
|
1248
|
+
|
|
1249
|
+
local_design : boolean
|
|
1250
|
+
Treat this component in design mode in an offdesign calculation.
|
|
1251
|
+
|
|
1252
|
+
char_warnings: boolean
|
|
1253
|
+
Ignore warnings on default characteristics usage for this component.
|
|
1254
|
+
|
|
1255
|
+
printout: boolean
|
|
1256
|
+
Include this component in the network's results printout.
|
|
1257
|
+
|
|
1258
|
+
num_in : float/tespy.tools.data_containers.dc_simple
|
|
1259
|
+
Number of inlets for this component, default value: 2.
|
|
1260
|
+
|
|
1261
|
+
Example
|
|
1262
|
+
-------
|
|
1263
|
+
The merge mixes a specified number of mass flows and has a single outlet.
|
|
1264
|
+
At the outlet, fluid composition and enthalpy are calculated by mass
|
|
1265
|
+
weighted fluid composition and enthalpy of the inlets.
|
|
1266
|
+
|
|
1267
|
+
>>> from tespy.components import sink, source, merge
|
|
1268
|
+
>>> from tespy.connections import connection
|
|
1269
|
+
>>> from tespy.networks import network
|
|
1270
|
+
>>> import shutil
|
|
1271
|
+
>>> import numpy as np
|
|
1272
|
+
>>> fluid_list = ['O2', 'N2']
|
|
1273
|
+
>>> nw = network(fluids=fluid_list, p_unit='bar', iterinfo=False)
|
|
1274
|
+
>>> so1 = source('source1')
|
|
1275
|
+
>>> so2 = source('source2')
|
|
1276
|
+
>>> so3 = source('source3')
|
|
1277
|
+
>>> si1 = sink('sink')
|
|
1278
|
+
>>> m = merge('merge', num_in=3)
|
|
1279
|
+
>>> m.component()
|
|
1280
|
+
'merge'
|
|
1281
|
+
>>> inc1 = connection(so1, 'out1', m, 'in1')
|
|
1282
|
+
>>> inc2 = connection(so2, 'out1', m, 'in2')
|
|
1283
|
+
>>> inc3 = connection(so3, 'out1', m, 'in3')
|
|
1284
|
+
>>> outg = connection(m, 'out1', si1, 'in1')
|
|
1285
|
+
>>> nw.add_conns(inc1, inc2, inc3, outg)
|
|
1286
|
+
|
|
1287
|
+
A merge with three inlets mixes air (simplified) with pure nitrogen and
|
|
1288
|
+
pure oxygen. All gases enter the component at the same temperature. As
|
|
1289
|
+
mixing effects are not considered, the outlet temperature should thus be
|
|
1290
|
+
similar to the three inlet temperatures (difference might occur due to
|
|
1291
|
+
rounding in fluid property functions, let's check it for two different
|
|
1292
|
+
temperatures). It is e.g. possible to find the required mass flow of pure
|
|
1293
|
+
nitrogen given the nitrogen mass fraction in the outlet.
|
|
1294
|
+
|
|
1295
|
+
>>> T = 293.15
|
|
1296
|
+
>>> inc1.set_attr(fluid={'O2': 0.23, 'N2': 0.77}, p=1, T=T, m=5)
|
|
1297
|
+
>>> inc2.set_attr(fluid={'O2': 1, 'N2':0}, T=T, m=5)
|
|
1298
|
+
>>> inc3.set_attr(fluid={'O2': 0, 'N2': 1}, T=T)
|
|
1299
|
+
>>> outg.set_attr(fluid={'N2': 0.4})
|
|
1300
|
+
>>> nw.solve('design')
|
|
1301
|
+
>>> round(inc3.m.val_SI, 2)
|
|
1302
|
+
0.25
|
|
1303
|
+
>>> abs(round((outg.T.val_SI - T) / T, 5)) < 0.01
|
|
1304
|
+
True
|
|
1305
|
+
>>> T = 173.15
|
|
1306
|
+
>>> inc1.set_attr(T=T)
|
|
1307
|
+
>>> inc2.set_attr(T=T)
|
|
1308
|
+
>>> inc3.set_attr(T=T)
|
|
1309
|
+
>>> nw.solve('design')
|
|
1310
|
+
>>> abs(round((outg.T.val_SI - T) / T, 5)) < 0.01
|
|
1311
|
+
True
|
|
1312
|
+
"""
|
|
1313
|
+
|
|
1314
|
+
@staticmethod
|
|
1315
|
+
def component():
|
|
1316
|
+
return 'merge'
|
|
1317
|
+
|
|
1318
|
+
@staticmethod
|
|
1319
|
+
def attr():
|
|
1320
|
+
return {'num_in': dc_simple()}
|
|
1321
|
+
|
|
1322
|
+
def inlets(self):
|
|
1323
|
+
if self.num_in.is_set:
|
|
1324
|
+
return ['in' + str(i + 1) for i in range(self.num_in.val)]
|
|
1325
|
+
else:
|
|
1326
|
+
self.set_attr(num_in=2)
|
|
1327
|
+
return self.inlets()
|
|
1328
|
+
|
|
1329
|
+
@staticmethod
|
|
1330
|
+
def outlets():
|
|
1331
|
+
return ['out1']
|
|
1332
|
+
|
|
1333
|
+
def comp_init(self, nw):
|
|
1334
|
+
|
|
1335
|
+
component.comp_init(self, nw)
|
|
1336
|
+
|
|
1337
|
+
# number of mandatroy equations for
|
|
1338
|
+
# mass flow: 1
|
|
1339
|
+
# pressure: number of inlets + number of outlets - 1
|
|
1340
|
+
# fluid: number of fluids
|
|
1341
|
+
# enthalpy: 1
|
|
1342
|
+
|
|
1343
|
+
self.num_eq = self.num_i + self.num_o + self.num_nw_fluids + 1
|
|
1344
|
+
|
|
1345
|
+
self.jacobian = np.zeros((
|
|
1346
|
+
self.num_eq,
|
|
1347
|
+
self.num_i + self.num_o + self.num_vars,
|
|
1348
|
+
self.num_nw_vars))
|
|
1349
|
+
|
|
1350
|
+
self.residual = np.zeros(self.num_eq)
|
|
1351
|
+
self.jacobian[0:1] = self.mass_flow_deriv()
|
|
1352
|
+
end = self.num_i + self.num_o
|
|
1353
|
+
self.jacobian[1:end] = self.pressure_deriv()
|
|
1354
|
+
|
|
1355
|
+
def additional_equations(self, k):
|
|
1356
|
+
r"""
|
|
1357
|
+
Calculate results of additional equations.
|
|
1358
|
+
|
|
1359
|
+
Equations
|
|
1360
|
+
|
|
1361
|
+
**mandatroy equations**
|
|
1362
|
+
|
|
1363
|
+
.. math::
|
|
1364
|
+
|
|
1365
|
+
0 = \dot{m}_{in_{j}} \cdot fluid_{i,in_{j}} -
|
|
1366
|
+
\dot {m}_{out} \cdot fluid_{i,out} \\
|
|
1367
|
+
\forall i \in \mathrm{fluid}, \; \forall j \in inlets
|
|
1368
|
+
|
|
1369
|
+
.. math::
|
|
1370
|
+
|
|
1371
|
+
0 = h_{in} - h_{out,i} \;
|
|
1372
|
+
\forall i \in \mathrm{outlets}\\
|
|
1373
|
+
"""
|
|
1374
|
+
######################################################################
|
|
1375
|
+
# equations for fluid balance
|
|
1376
|
+
for fluid, x in self.outl[0].fluid.val.items():
|
|
1377
|
+
res = -x * self.outl[0].m.val_SI
|
|
1378
|
+
for i in self.inl:
|
|
1379
|
+
res += i.fluid.val[fluid] * i.m.val_SI
|
|
1380
|
+
self.residual[k] = res
|
|
1381
|
+
k += 1
|
|
1382
|
+
|
|
1383
|
+
######################################################################
|
|
1384
|
+
# equation for energy balance
|
|
1385
|
+
h_res = -self.outl[0].m.val_SI * self.outl[0].h.val_SI
|
|
1386
|
+
for i in self.inl:
|
|
1387
|
+
h_res += i.m.val_SI * i.h.val_SI
|
|
1388
|
+
self.residual[k] = h_res
|
|
1389
|
+
k += 1
|
|
1390
|
+
|
|
1391
|
+
def additional_derivatives(self, increment_filter, k):
|
|
1392
|
+
r"""Calculate partial derivatives for given additional equations."""
|
|
1393
|
+
######################################################################
|
|
1394
|
+
# derivatives for fluid balance equations
|
|
1395
|
+
i = 0
|
|
1396
|
+
for fluid, x in self.outl[0].fluid.val.items():
|
|
1397
|
+
j = 0
|
|
1398
|
+
for inl in self.inl:
|
|
1399
|
+
self.jacobian[k, j, 0] = inl.fluid.val[fluid]
|
|
1400
|
+
self.jacobian[k, j, i + 3] = inl.m.val_SI
|
|
1401
|
+
j += 1
|
|
1402
|
+
self.jacobian[k, j, 0] = -x
|
|
1403
|
+
self.jacobian[k, j, i + 3] = -self.outl[0].m.val_SI
|
|
1404
|
+
i += 1
|
|
1405
|
+
k += 1
|
|
1406
|
+
|
|
1407
|
+
######################################################################
|
|
1408
|
+
# derivatives for energy balance equations
|
|
1409
|
+
self.jacobian[k, self.num_i, 0] = -self.outl[0].h.val_SI
|
|
1410
|
+
self.jacobian[k, self.num_i, 2] = -self.outl[0].m.val_SI
|
|
1411
|
+
j = 0
|
|
1412
|
+
for i in self.inl:
|
|
1413
|
+
self.jacobian[k, j, 0] = i.h.val_SI
|
|
1414
|
+
self.jacobian[k, j, 2] = i.m.val_SI
|
|
1415
|
+
j += 1
|
|
1416
|
+
k += 1
|
|
1417
|
+
|
|
1418
|
+
# %%
|
|
1419
|
+
|
|
1420
|
+
|
|
1421
|
+
class separator(node):
|
|
1422
|
+
r"""
|
|
1423
|
+
A separator separates fluid components from a mass flow.
|
|
1424
|
+
|
|
1425
|
+
Equations
|
|
1426
|
+
|
|
1427
|
+
**mandatory equations**
|
|
1428
|
+
|
|
1429
|
+
- :func:`tespy.components.components.component.mass_flow_func`
|
|
1430
|
+
|
|
1431
|
+
.. math::
|
|
1432
|
+
|
|
1433
|
+
0 = p_{in} - p_{out,i} \;
|
|
1434
|
+
\forall i \in \mathrm{outlets}
|
|
1435
|
+
|
|
1436
|
+
**additional equations**
|
|
1437
|
+
|
|
1438
|
+
- :func:`tespy.components.nodes.separator.additional_equations`
|
|
1439
|
+
|
|
1440
|
+
Inlets/Outlets
|
|
1441
|
+
|
|
1442
|
+
- in1
|
|
1443
|
+
- specify number of outlets with :code:`num_out` (default value: 2)
|
|
1444
|
+
|
|
1445
|
+
Image
|
|
1446
|
+
|
|
1447
|
+
.. image:: _images/split.svg
|
|
1448
|
+
:scale: 100 %
|
|
1449
|
+
:alt: alternative text
|
|
1450
|
+
:align: center
|
|
1451
|
+
|
|
1452
|
+
Note
|
|
1453
|
+
----
|
|
1454
|
+
Fluid separation requires power and cooling, equations have not been
|
|
1455
|
+
implemented, yet!
|
|
1456
|
+
|
|
1457
|
+
Parameters
|
|
1458
|
+
----------
|
|
1459
|
+
label : str
|
|
1460
|
+
The label of the component.
|
|
1461
|
+
|
|
1462
|
+
design : list
|
|
1463
|
+
List containing design parameters (stated as String).
|
|
1464
|
+
|
|
1465
|
+
offdesign : list
|
|
1466
|
+
List containing offdesign parameters (stated as String).
|
|
1467
|
+
|
|
1468
|
+
design_path: str
|
|
1469
|
+
Path to the components design case.
|
|
1470
|
+
|
|
1471
|
+
local_offdesign : boolean
|
|
1472
|
+
Treat this component in offdesign mode in a design calculation.
|
|
1473
|
+
|
|
1474
|
+
local_design : boolean
|
|
1475
|
+
Treat this component in design mode in an offdesign calculation.
|
|
1476
|
+
|
|
1477
|
+
char_warnings: boolean
|
|
1478
|
+
Ignore warnings on default characteristics usage for this component.
|
|
1479
|
+
|
|
1480
|
+
printout: boolean
|
|
1481
|
+
Include this component in the network's results printout.
|
|
1482
|
+
|
|
1483
|
+
num_out : float/tespy.tools.data_containers.dc_simple
|
|
1484
|
+
Number of outlets for this component, default value: 2.
|
|
1485
|
+
|
|
1486
|
+
Example
|
|
1487
|
+
-------
|
|
1488
|
+
The separator is used to split up a single mass flow into a specified
|
|
1489
|
+
number of different parts at identical pressure and temperature but
|
|
1490
|
+
different fluid composition. Fluids can be separated from each other.
|
|
1491
|
+
|
|
1492
|
+
>>> from tespy.components import sink, source, separator
|
|
1493
|
+
>>> from tespy.connections import connection
|
|
1494
|
+
>>> from tespy.networks import network
|
|
1495
|
+
>>> import shutil
|
|
1496
|
+
>>> import numpy as np
|
|
1497
|
+
>>> fluid_list = ['O2', 'N2']
|
|
1498
|
+
>>> nw = network(fluids=fluid_list, p_unit='bar', T_unit='C',
|
|
1499
|
+
... iterinfo=False)
|
|
1500
|
+
>>> so = source('source')
|
|
1501
|
+
>>> si1 = sink('sink1')
|
|
1502
|
+
>>> si2 = sink('sink2')
|
|
1503
|
+
>>> s = separator('separator', num_out=2)
|
|
1504
|
+
>>> s.component()
|
|
1505
|
+
'separator'
|
|
1506
|
+
>>> inc = connection(so, 'out1', s, 'in1')
|
|
1507
|
+
>>> outg1 = connection(s, 'out1', si1, 'in1')
|
|
1508
|
+
>>> outg2 = connection(s, 'out2', si2, 'in1')
|
|
1509
|
+
>>> nw.add_conns(inc, outg1, outg2)
|
|
1510
|
+
|
|
1511
|
+
An Air (simplified) mass flow of 5 kg/s is split up into two mass flows.
|
|
1512
|
+
One mass flow of 1 kg/s containing 10 % oxygen and 90 % nitrogen leaves the
|
|
1513
|
+
separator. It is possible to calculate the fluid composition of the second
|
|
1514
|
+
mass flow. Specify starting values for the second mass flow fluid
|
|
1515
|
+
composition for calculation stability.
|
|
1516
|
+
|
|
1517
|
+
>>> inc.set_attr(fluid={'O2': 0.23, 'N2': 0.77}, p=1, T=20, m=5)
|
|
1518
|
+
>>> outg1.set_attr(fluid={'O2': 0.1, 'N2': 0.9}, m=1)
|
|
1519
|
+
>>> outg2.set_attr(fluid0={'O2': 0.5, 'N2': 0.5})
|
|
1520
|
+
>>> nw.solve('design')
|
|
1521
|
+
>>> outg2.fluid.val['O2']
|
|
1522
|
+
0.2625
|
|
1523
|
+
|
|
1524
|
+
In the same way, it is possible to specify one of the fluid components in
|
|
1525
|
+
the second mass flow instead of the first mass flow. The solver will find
|
|
1526
|
+
the mass flows matching the desired composition. 65 % of the mass flow
|
|
1527
|
+
will leave the separator at the second outlet the case of 30 % oxygen
|
|
1528
|
+
mass fraction for this outlet.
|
|
1529
|
+
|
|
1530
|
+
>>> outg1.set_attr(m=np.nan)
|
|
1531
|
+
>>> outg2.set_attr(fluid={'O2': 0.3})
|
|
1532
|
+
>>> nw.solve('design')
|
|
1533
|
+
>>> outg2.fluid.val['O2']
|
|
1534
|
+
0.3
|
|
1535
|
+
>>> round(outg2.m.val_SI / inc.m.val_SI, 2)
|
|
1536
|
+
0.65
|
|
1537
|
+
"""
|
|
1538
|
+
|
|
1539
|
+
@staticmethod
|
|
1540
|
+
def component():
|
|
1541
|
+
return 'separator'
|
|
1542
|
+
|
|
1543
|
+
@staticmethod
|
|
1544
|
+
def attr():
|
|
1545
|
+
return {'num_out': dc_simple()}
|
|
1546
|
+
|
|
1547
|
+
@staticmethod
|
|
1548
|
+
def inlets():
|
|
1549
|
+
return ['in1']
|
|
1550
|
+
|
|
1551
|
+
def outlets(self):
|
|
1552
|
+
if self.num_out.is_set:
|
|
1553
|
+
return ['out' + str(i + 1) for i in range(self.num_out.val)]
|
|
1554
|
+
else:
|
|
1555
|
+
self.set_attr(num_out=2)
|
|
1556
|
+
return self.outlets()
|
|
1557
|
+
|
|
1558
|
+
def comp_init(self, nw):
|
|
1559
|
+
|
|
1560
|
+
component.comp_init(self, nw)
|
|
1561
|
+
|
|
1562
|
+
# number of mandatroy equations for
|
|
1563
|
+
# mass flow: 1
|
|
1564
|
+
# pressure: number of inlets + number of outlets - 1
|
|
1565
|
+
# fluid: number of fluids
|
|
1566
|
+
# enthalpy: number of outlets
|
|
1567
|
+
|
|
1568
|
+
self.num_eq = self.num_i + self.num_o * 2 + self.num_nw_fluids
|
|
1569
|
+
|
|
1570
|
+
self.jacobian = np.zeros((
|
|
1571
|
+
self.num_eq,
|
|
1572
|
+
self.num_i + self.num_o + self.num_vars,
|
|
1573
|
+
self.num_nw_vars))
|
|
1574
|
+
|
|
1575
|
+
self.residual = np.zeros(self.num_eq)
|
|
1576
|
+
self.jacobian[0:1] = self.mass_flow_deriv()
|
|
1577
|
+
end = self.num_i + self.num_o
|
|
1578
|
+
self.jacobian[1:end] = self.pressure_deriv()
|
|
1579
|
+
|
|
1580
|
+
def additional_equations(self, k):
|
|
1581
|
+
r"""
|
|
1582
|
+
Calculate results of additional equations.
|
|
1583
|
+
|
|
1584
|
+
Equations
|
|
1585
|
+
|
|
1586
|
+
**mandatroy equations**
|
|
1587
|
+
|
|
1588
|
+
.. math:: 0 = fluid_{i,in} - fluid_{i,out_{j}} \;
|
|
1589
|
+
\forall i \in \mathrm{fluid}, \; \forall j \in outlets
|
|
1590
|
+
|
|
1591
|
+
.. math::
|
|
1592
|
+
|
|
1593
|
+
0 = T_{in} - T_{out,i} \;
|
|
1594
|
+
\forall i \in \mathrm{outlets}
|
|
1595
|
+
"""
|
|
1596
|
+
######################################################################
|
|
1597
|
+
# equations for fluid balance
|
|
1598
|
+
for fluid, x in self.inl[0].fluid.val.items():
|
|
1599
|
+
res = x * self.inl[0].m.val_SI
|
|
1600
|
+
for o in self.outl:
|
|
1601
|
+
res -= o.fluid.val[fluid] * o.m.val_SI
|
|
1602
|
+
self.residual[k] = res
|
|
1603
|
+
k += 1
|
|
1604
|
+
|
|
1605
|
+
######################################################################
|
|
1606
|
+
# equations for energy balance
|
|
1607
|
+
for o in self.outl:
|
|
1608
|
+
self.residual[k] = (
|
|
1609
|
+
T_mix_ph(self.inl[0].to_flow(), T0=self.inl[0].T.val_SI) -
|
|
1610
|
+
T_mix_ph(o.to_flow(), T0=o.T.val_SI))
|
|
1611
|
+
k += 1
|
|
1612
|
+
|
|
1613
|
+
def additional_derivatives(self, increment_filter, k):
|
|
1614
|
+
r"""Calculate partial derivatives for given additional equations."""
|
|
1615
|
+
######################################################################
|
|
1616
|
+
# derivatives for fluid balance equations
|
|
1617
|
+
i = 0
|
|
1618
|
+
for fluid in self.nw_fluids:
|
|
1619
|
+
j = 0
|
|
1620
|
+
for o in self.outl:
|
|
1621
|
+
self.jacobian[k, j + 1, 0] = -o.fluid.val[fluid]
|
|
1622
|
+
self.jacobian[k, j + 1, i + 3] = -o.m.val_SI
|
|
1623
|
+
j += 1
|
|
1624
|
+
self.jacobian[k, 0, 0] = self.inl[0].fluid.val[fluid]
|
|
1625
|
+
self.jacobian[k, 0, i + 3] = self.inl[0].m.val_SI
|
|
1626
|
+
k += 1
|
|
1627
|
+
i += 1
|
|
1628
|
+
|
|
1629
|
+
######################################################################
|
|
1630
|
+
# derivatives for energy balance equations
|
|
1631
|
+
i = self.inl[0].to_flow()
|
|
1632
|
+
j = 0
|
|
1633
|
+
for o in self.outl:
|
|
1634
|
+
o = o.to_flow()
|
|
1635
|
+
self.jacobian[k, 0, 1] = dT_mix_dph(i)
|
|
1636
|
+
self.jacobian[k, 0, 2] = dT_mix_pdh(i)
|
|
1637
|
+
self.jacobian[k, 0, 3:] = dT_mix_ph_dfluid(i)
|
|
1638
|
+
self.jacobian[k, j + 1, 1] = -dT_mix_dph(o)
|
|
1639
|
+
self.jacobian[k, j + 1, 2] = -dT_mix_pdh(o)
|
|
1640
|
+
self.jacobian[k, j + 1, 3:] = -1 * dT_mix_ph_dfluid(o)
|
|
1641
|
+
j += 1
|
|
1642
|
+
k += 1
|
|
1643
|
+
|
|
1644
|
+
@staticmethod
|
|
1645
|
+
def initialise_fluids(nw):
|
|
1646
|
+
r"""
|
|
1647
|
+
Fluid initialisation for fluid mixture at outlet of the node.
|
|
1648
|
+
|
|
1649
|
+
Parameters
|
|
1650
|
+
----------
|
|
1651
|
+
nw : tespy.networks.network
|
|
1652
|
+
Network using this component object.
|
|
1653
|
+
"""
|
|
1654
|
+
return
|
|
1655
|
+
|
|
1656
|
+
# %%
|
|
1657
|
+
|
|
1658
|
+
|
|
1659
|
+
class splitter(node):
|
|
1660
|
+
r"""
|
|
1661
|
+
Split up a mass flow in parts of identical enthalpy and fluid composition.
|
|
1662
|
+
|
|
1663
|
+
Equations
|
|
1664
|
+
|
|
1665
|
+
**mandatory equations**
|
|
1666
|
+
|
|
1667
|
+
- :func:`tespy.components.components.component.mass_flow_func`
|
|
1668
|
+
|
|
1669
|
+
.. math::
|
|
1670
|
+
|
|
1671
|
+
0 = p_{in} - p_{out,i} \;
|
|
1672
|
+
\forall i \in \mathrm{outlets}
|
|
1673
|
+
|
|
1674
|
+
**additional equations**
|
|
1675
|
+
|
|
1676
|
+
- :func:`tespy.components.nodes.splitter.additional_equations`
|
|
1677
|
+
|
|
1678
|
+
Inlets/Outlets
|
|
1679
|
+
|
|
1680
|
+
- in1
|
|
1681
|
+
- specify number of outlets with :code:`num_out` (default value: 2)
|
|
1682
|
+
|
|
1683
|
+
Image
|
|
1684
|
+
|
|
1685
|
+
.. image:: _images/split.svg
|
|
1686
|
+
:scale: 100 %
|
|
1687
|
+
:alt: alternative text
|
|
1688
|
+
:align: center
|
|
1689
|
+
|
|
1690
|
+
Parameters
|
|
1691
|
+
----------
|
|
1692
|
+
label : str
|
|
1693
|
+
The label of the component.
|
|
1694
|
+
|
|
1695
|
+
design : list
|
|
1696
|
+
List containing design parameters (stated as String).
|
|
1697
|
+
|
|
1698
|
+
offdesign : list
|
|
1699
|
+
List containing offdesign parameters (stated as String).
|
|
1700
|
+
|
|
1701
|
+
design_path: str
|
|
1702
|
+
Path to the components design case.
|
|
1703
|
+
|
|
1704
|
+
local_offdesign : boolean
|
|
1705
|
+
Treat this component in offdesign mode in a design calculation.
|
|
1706
|
+
|
|
1707
|
+
local_design : boolean
|
|
1708
|
+
Treat this component in design mode in an offdesign calculation.
|
|
1709
|
+
|
|
1710
|
+
char_warnings: boolean
|
|
1711
|
+
Ignore warnings on default characteristics usage for this component.
|
|
1712
|
+
|
|
1713
|
+
printout: boolean
|
|
1714
|
+
Include this component in the network's results printout.
|
|
1715
|
+
|
|
1716
|
+
num_out : float/tespy.tools.data_containers.dc_simple
|
|
1717
|
+
Number of outlets for this component, default value: 2.
|
|
1718
|
+
|
|
1719
|
+
Example
|
|
1720
|
+
-------
|
|
1721
|
+
A splitter is used to split up a single mass flow into a specified number
|
|
1722
|
+
of different parts at identical pressure, enthalpy and fluid composition.
|
|
1723
|
+
|
|
1724
|
+
>>> from tespy.components import sink, source, splitter
|
|
1725
|
+
>>> from tespy.connections import connection
|
|
1726
|
+
>>> from tespy.networks import network
|
|
1727
|
+
>>> import shutil
|
|
1728
|
+
>>> import numpy as np
|
|
1729
|
+
>>> fluid_list = ['O2', 'N2']
|
|
1730
|
+
>>> nw = network(fluids=fluid_list, p_unit='bar', T_unit='C',
|
|
1731
|
+
... iterinfo=False)
|
|
1732
|
+
>>> so = source('source')
|
|
1733
|
+
>>> si1 = sink('sink1')
|
|
1734
|
+
>>> si2 = sink('sink2')
|
|
1735
|
+
>>> si3 = sink('sink3')
|
|
1736
|
+
>>> s = splitter('splitter', num_out=3)
|
|
1737
|
+
>>> s.component()
|
|
1738
|
+
'splitter'
|
|
1739
|
+
>>> inc = connection(so, 'out1', s, 'in1')
|
|
1740
|
+
>>> outg1 = connection(s, 'out1', si1, 'in1')
|
|
1741
|
+
>>> outg2 = connection(s, 'out2', si2, 'in1')
|
|
1742
|
+
>>> outg3 = connection(s, 'out3', si3, 'in1')
|
|
1743
|
+
>>> nw.add_conns(inc, outg1, outg2, outg3)
|
|
1744
|
+
|
|
1745
|
+
An Air (simplified) mass flow is split up into three mass flows. The total
|
|
1746
|
+
incoming mass flow is 5 kg/s, 3 kg/s and 1 kg/s respectively are leaving
|
|
1747
|
+
the splitter into the first two outlets. The residual mass flow will
|
|
1748
|
+
drain in the last outlet. Temperature and fluid composition will not
|
|
1749
|
+
change.
|
|
1750
|
+
|
|
1751
|
+
>>> inc.set_attr(fluid={'O2': 0.23, 'N2': 0.77}, p=1, T=20, m=5)
|
|
1752
|
+
>>> outg1.set_attr(m=3)
|
|
1753
|
+
>>> outg2.set_attr(m=1)
|
|
1754
|
+
>>> nw.solve('design')
|
|
1755
|
+
>>> round(outg3.m.val_SI, 1)
|
|
1756
|
+
1.0
|
|
1757
|
+
>>> round(inc.T.val, 1)
|
|
1758
|
+
20.0
|
|
1759
|
+
>>> round(outg3.T.val, 1)
|
|
1760
|
+
20.0
|
|
1761
|
+
"""
|
|
1762
|
+
|
|
1763
|
+
@staticmethod
|
|
1764
|
+
def component():
|
|
1765
|
+
return 'splitter'
|
|
1766
|
+
|
|
1767
|
+
@staticmethod
|
|
1768
|
+
def attr():
|
|
1769
|
+
return {'num_out': dc_simple()}
|
|
1770
|
+
|
|
1771
|
+
@staticmethod
|
|
1772
|
+
def inlets():
|
|
1773
|
+
return ['in1']
|
|
1774
|
+
|
|
1775
|
+
def outlets(self):
|
|
1776
|
+
if self.num_out.is_set:
|
|
1777
|
+
return ['out' + str(i + 1) for i in range(self.num_out.val)]
|
|
1778
|
+
else:
|
|
1779
|
+
self.set_attr(num_out=2)
|
|
1780
|
+
return self.outlets()
|
|
1781
|
+
|
|
1782
|
+
def comp_init(self, nw):
|
|
1783
|
+
|
|
1784
|
+
component.comp_init(self, nw)
|
|
1785
|
+
|
|
1786
|
+
# number of mandatroy equations for
|
|
1787
|
+
# mass flow: 1
|
|
1788
|
+
# pressure: number of inlets + number of outlets - 1
|
|
1789
|
+
# fluid: number of outlets * number of fluid_set
|
|
1790
|
+
# enthalpy: number of outlets
|
|
1791
|
+
|
|
1792
|
+
self.num_eq = self.num_i + self.num_o * (2 + self.num_nw_fluids)
|
|
1793
|
+
|
|
1794
|
+
self.jacobian = np.zeros((
|
|
1795
|
+
self.num_eq,
|
|
1796
|
+
self.num_i + self.num_o + self.num_vars,
|
|
1797
|
+
self.num_nw_vars))
|
|
1798
|
+
|
|
1799
|
+
self.residual = np.zeros(self.num_eq)
|
|
1800
|
+
self.jacobian[0:1] = self.mass_flow_deriv()
|
|
1801
|
+
end = self.num_i + self.num_o
|
|
1802
|
+
self.jacobian[1:end] = self.pressure_deriv()
|
|
1803
|
+
start = end
|
|
1804
|
+
end = start + self.num_o * self.num_nw_fluids
|
|
1805
|
+
self.jacobian[start:end] = self.fluid_deriv()
|
|
1806
|
+
start = end
|
|
1807
|
+
end = start + self.num_o
|
|
1808
|
+
self.jacobian[start:end] = self.enthalpy_deriv()
|
|
1809
|
+
|
|
1810
|
+
def additional_equations(self, k):
|
|
1811
|
+
r"""
|
|
1812
|
+
Calculate results of additional equations.
|
|
1813
|
+
|
|
1814
|
+
Equations
|
|
1815
|
+
|
|
1816
|
+
**mandatroy equations**
|
|
1817
|
+
|
|
1818
|
+
.. math:: 0 = fluid_{i,in} - fluid_{i,out_{j}} \;
|
|
1819
|
+
\forall i \in \mathrm{fluid}, \; \forall j \in outlets
|
|
1820
|
+
|
|
1821
|
+
.. math::
|
|
1822
|
+
0 = h_{in} - h_{out,i} \;
|
|
1823
|
+
\forall i \in \mathrm{outlets}\\
|
|
1824
|
+
"""
|
|
1825
|
+
######################################################################
|
|
1826
|
+
# equations for fluid balance
|
|
1827
|
+
for o in self.outl:
|
|
1828
|
+
for fluid, x in self.inl[0].fluid.val.items():
|
|
1829
|
+
self.residual[k] = x - o.fluid.val[fluid]
|
|
1830
|
+
k += 1
|
|
1831
|
+
|
|
1832
|
+
######################################################################
|
|
1833
|
+
# equations for energy balance
|
|
1834
|
+
for o in self.outl:
|
|
1835
|
+
self.residual[k] = self.inl[0].h.val_SI - o.h.val_SI
|
|
1836
|
+
k += 1
|
|
1837
|
+
|
|
1838
|
+
def additional_derivatives(self, increment_filter, k):
|
|
1839
|
+
r"""Calculate partial derivatives for given additional equations."""
|
|
1840
|
+
return
|
|
1841
|
+
|
|
1842
|
+
def fluid_deriv(self):
|
|
1843
|
+
r"""
|
|
1844
|
+
Calculate partial derivatives for all fluid balance equations.
|
|
1845
|
+
|
|
1846
|
+
Returns
|
|
1847
|
+
-------
|
|
1848
|
+
deriv : list
|
|
1849
|
+
Matrix with partial derivatives for the fluid equations.
|
|
1850
|
+
"""
|
|
1851
|
+
deriv = np.zeros((self.num_nw_fluids * self.num_o, 1 + self.num_o,
|
|
1852
|
+
self.num_nw_vars))
|
|
1853
|
+
k = 0
|
|
1854
|
+
for o in self.outl:
|
|
1855
|
+
i = 0
|
|
1856
|
+
for fluid in self.nw_fluids:
|
|
1857
|
+
deriv[i + k * self.num_nw_fluids, 0, i + 3] = 1
|
|
1858
|
+
deriv[i + k * self.num_nw_fluids, k + 1, i + 3] = -1
|
|
1859
|
+
i += 1
|
|
1860
|
+
k += 1
|
|
1861
|
+
return deriv
|
|
1862
|
+
|
|
1863
|
+
def enthalpy_deriv(self):
|
|
1864
|
+
r"""
|
|
1865
|
+
Calculate partial derivatives for enthalpy balance equation.
|
|
1866
|
+
|
|
1867
|
+
Returns
|
|
1868
|
+
-------
|
|
1869
|
+
deriv : list
|
|
1870
|
+
Matrix of partial derivatives.
|
|
1871
|
+
"""
|
|
1872
|
+
deriv = np.zeros((self.num_o, 1 + self.num_o, self.num_nw_vars))
|
|
1873
|
+
k = 0
|
|
1874
|
+
for o in self.outl:
|
|
1875
|
+
deriv[k, 0, 2] = 1
|
|
1876
|
+
deriv[k, k + 1, 2] = -1
|
|
1877
|
+
k += 1
|
|
1878
|
+
return deriv
|
|
1879
|
+
|
|
1880
|
+
@staticmethod
|
|
1881
|
+
def initialise_fluids(nw):
|
|
1882
|
+
r"""
|
|
1883
|
+
Fluid initialisation for fluid mixture at outlet of the node.
|
|
1884
|
+
|
|
1885
|
+
Parameters
|
|
1886
|
+
----------
|
|
1887
|
+
nw : tespy.networks.network
|
|
1888
|
+
Network using this component object.
|
|
1889
|
+
"""
|
|
1890
|
+
return
|