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.
@@ -0,0 +1,729 @@
1
+ # -*- coding: utf-8
2
+
3
+ """Module for custom components.
4
+
5
+ Components in this module:
6
+
7
+ - :func:`tespy.components.customs.orc_evaporator`
8
+
9
+ This file is part of project TESPy (github.com/oemof/tespy). It's copyrighted
10
+ by the contributors recorded in the version control history of the file,
11
+ available from its original location tespy/components/customs.py
12
+
13
+ SPDX-License-Identifier: MIT
14
+ """
15
+
16
+ import numpy as np
17
+
18
+ from tespy.components.components import component
19
+ from tespy.tools.data_containers import dc_cp
20
+ from tespy.tools.data_containers import dc_simple
21
+ from tespy.tools.fluid_properties import dh_mix_dpQ
22
+ from tespy.tools.fluid_properties import h_mix_pQ
23
+ from tespy.tools.fluid_properties import h_mix_pT
24
+ from tespy.tools.fluid_properties import s_mix_ph
25
+ from tespy.tools.fluid_properties import v_mix_ph
26
+
27
+ # %%
28
+
29
+
30
+ class orc_evaporator(component):
31
+ r"""Evaporator of the geothermal Organic Rankine Cycle (ORC).
32
+
33
+ Generally, the hot side of the geo-fluid from the geothermal wells deliver
34
+ two phases: steam and brine. In order to fully use the energy of the
35
+ geo-fluid, there are 2 inlets at the hot side.
36
+
37
+ The ORC evaporator represents counter current evaporators. Both, two hot
38
+ and one cold side of the evaporator, are simulated.
39
+
40
+ Equations
41
+
42
+ **mandatory equations**
43
+
44
+ - :func:`tespy.components.components.component.fluid_func`
45
+ - :func:`tespy.components.customs.orc_evaporator.mass_flow_func`
46
+
47
+ - :func:`tespy.components.customs.orc_evaporator.energy_func`
48
+
49
+ .. math::
50
+
51
+ 0 = p_{1,in} \cdot pr1 - p_{1,out}\\
52
+ 0 = p_{2,in} \cdot pr2 - p_{2,out}\\
53
+ 0 = p_{3,in} \cdot pr3 - p_{3,out}
54
+
55
+ - hot side steam :func:`tespy.components.components.component.zeta_func`
56
+ - hot side brine :func:`tespy.components.components.component.zeta_func`
57
+ - worling fluid :func:`tespy.components.components.component.zeta_func`
58
+
59
+ **mandatory equations at outlet of the steam
60
+ from geothermal heat source side**
61
+
62
+ .. math::
63
+
64
+ 0 = h_{1,out} - h\left(p, x=0 \right)\\
65
+ x: \text{vapour mass fraction}
66
+
67
+ **mandatory equations at outlet of the working fluid
68
+ of being evaporated side**
69
+
70
+ .. math::
71
+
72
+ 0 = h_{3,out} - h\left(p, x=1 \right)\\
73
+ x: \text{vapour mass fraction}
74
+
75
+ Inlets/Outlets
76
+
77
+ - in1, in2, in3 (index 1: steam from geothermal heat source,
78
+ index 2: brine from geothermal heat source,
79
+ index 3: working fluid of being evaporated)
80
+ - out1, out2, out3 (index 1: steam from geothermal heat source,
81
+ index 2: brine from geothermal heat source,
82
+ index 3: working fluid of being evaporated)
83
+
84
+ Image
85
+
86
+ .. image:: _images/orc_evaporator.svg
87
+ :scale: 100 %
88
+ :alt: alternative text
89
+ :align: center
90
+
91
+ Parameters
92
+ ----------
93
+ label : str
94
+ The label of the component.
95
+
96
+ design : list
97
+ List containing design parameters (stated as String).
98
+
99
+ offdesign : list
100
+ List containing offdesign parameters (stated as String).
101
+
102
+ design_path: str
103
+ Path to the components design case.
104
+
105
+ local_offdesign : boolean
106
+ Treat this component in offdesign mode in a design calculation.
107
+
108
+ local_design : boolean
109
+ Treat this component in design mode in an offdesign calculation.
110
+
111
+ char_warnings: boolean
112
+ Ignore warnings on default characteristics usage for this component.
113
+
114
+ printout: boolean
115
+ Include this component in the network's results printout.
116
+
117
+ Q : float/tespy.tools.data_containers.dc_cp
118
+ Heat transfer, :math:`Q/\text{W}`.
119
+
120
+ pr1 : float/tespy.tools.data_containers.dc_cp
121
+ Outlet to inlet pressure ratio at hot side 1 (steam),
122
+ :math:`pr/1`.
123
+
124
+ pr2 : float/tespy.tools.data_containers.dc_cp
125
+ Outlet to inlet pressure ratio at hot side 2 (brine),
126
+ :math:`pr/1`.
127
+
128
+ pr3 : float/tespy.tools.data_containers.dc_cp
129
+ Outlet to inlet pressure ratio at cold side (working fluid),
130
+ :math:`pr/1`.
131
+
132
+ zeta1 : float/tespy.tools.data_containers.dc_cp
133
+ Geometry independent friction coefficient at hot side 1 (steam),
134
+ :math:`\frac{\zeta}{D^4}/\frac{1}{\text{m}^4}`.
135
+
136
+ zeta2 : float/tespy.tools.data_containers.dc_cp
137
+ Geometry independent friction coefficient at hot side 2 (brine),
138
+ :math:`\frac{\zeta}{D^4}/\frac{1}{\text{m}^4}`.
139
+
140
+ zeta3 : float/tespy.tools.data_containers.dc_cp
141
+ Geometry independent friction coefficient at cold side (working fluid),
142
+ :math:`\frac{\zeta}{D^4}/\frac{1}{\text{m}^4}`.
143
+
144
+ subcooling : bool
145
+ Enable/disable subcooling at oulet of the hot side 1,
146
+ default value: disabled (False).
147
+
148
+ overheating : bool
149
+ Enable/disable overheating at oulet of the cold side,
150
+ default value: disabled (False).
151
+
152
+ Note
153
+ ----
154
+ The ORC evaporator has an additional equation for enthalpy at the outlet of
155
+ the geothermal steam: The fluid leaves the component in saturated liquid
156
+ state. If code:`subcooling` is activated (:code:`True`), it is possible to
157
+ specify the enthalpy at the outgoing connection manually.
158
+
159
+ Additionally, an equation for enthalpy at the outlet of the working fluid
160
+ is imposed: It leaves the component in saturated gas state. If
161
+ :code:`overheating` is enabled (:code:`True`), it is possible to specify
162
+ the enthalpy at the outgoing connection manually.
163
+
164
+ Example
165
+ -------
166
+ A two-phase geo-fluid is used as the heat source for evaporating the
167
+ working fluid. We calculate the mass flow of the working fluid with known
168
+ steam and brine mass flow.
169
+
170
+ >>> from tespy.connections import connection
171
+ >>> from tespy.networks import network
172
+ >>> from tespy.components import source, sink
173
+ >>> from tespy.components.customs import orc_evaporator
174
+ >>> fluids = ['water', 'Isopentane']
175
+ >>> nw = network(fluids=fluids, iterinfo=False)
176
+ >>> nw.set_attr(p_unit='bar', T_unit='C', h_unit='kJ / kg')
177
+ >>> evaporator = orc_evaporator('geothermal orc evaporator')
178
+ >>> evaporator.component()
179
+ 'orc_evaporator'
180
+ >>> source_wf = source('working fluid source')
181
+ >>> sink_wf = sink('working fluid sink')
182
+ >>> source_s = source('steam source')
183
+ >>> source_b = source('brine source')
184
+ >>> sink_s = sink('steam sink')
185
+ >>> sink_b = sink('brine sink')
186
+ >>> eva_wf_in = connection(source_wf, 'out1', evaporator, 'in3')
187
+ >>> eva_wf_out = connection(evaporator, 'out3', sink_wf, 'in1')
188
+ >>> eva_steam_in = connection(source_s, 'out1', evaporator, 'in1')
189
+ >>> eva_sink_s = connection(evaporator, 'out1', sink_s, 'in1')
190
+ >>> eva_brine_in = connection(source_b, 'out1', evaporator, 'in2')
191
+ >>> eva_sink_b = connection(evaporator, 'out2', sink_b, 'in1')
192
+ >>> nw.add_conns(eva_wf_in, eva_wf_out)
193
+ >>> nw.add_conns(eva_steam_in, eva_sink_s)
194
+ >>> nw.add_conns(eva_brine_in, eva_sink_b)
195
+
196
+ The orc working fluids leaves the evaporator in saturated steam state, the
197
+ geothermal steam leaves the component in staturated liquid state. We imply
198
+ the state of geothermal steam and brine with the corresponding mass flow as
199
+ well as the working fluid's state at the evaporator inlet. The pressure
200
+ ratio is specified for each of the three streams.
201
+
202
+ >>> evaporator.set_attr(pr1=0.95, pr2=0.98, pr3=0.99)
203
+ >>> eva_wf_in.set_attr(T=111, p=11,
204
+ ... fluid={'water': 0, 'Isopentane': 1})
205
+ >>> eva_steam_in.set_attr(T=147, p=4.3, m=20,
206
+ ... fluid={'water': 1, 'Isopentane': 0})
207
+ >>> eva_brine_in.set_attr(T=147, p=10.2, m=190,
208
+ ... fluid={'water': 1, 'Isopentane': 0})
209
+ >>> eva_sink_b.set_attr(T=117)
210
+ >>> nw.solve(mode='design')
211
+
212
+ Check the state of the steam and working fluid outlet:
213
+
214
+ >>> eva_wf_out.x.val
215
+ 1.0
216
+ >>> eva_sink_s.x.val
217
+ 0.0
218
+ """
219
+
220
+ @staticmethod
221
+ def component():
222
+ return 'orc_evaporator'
223
+
224
+ @staticmethod
225
+ def attr():
226
+ return {
227
+ 'Q': dc_cp(max_val=0),
228
+ 'pr1': dc_cp(max_val=1), 'pr2': dc_cp(max_val=1),
229
+ 'pr3': dc_cp(max_val=1),
230
+ 'zeta1': dc_cp(min_val=0), 'zeta2': dc_cp(min_val=0),
231
+ 'zeta3': dc_cp(min_val=0),
232
+ 'subcooling': dc_simple(val=False),
233
+ 'overheating': dc_simple(val=False),
234
+ 'SQ1': dc_simple(), 'SQ2': dc_simple(), 'SQ3': dc_simple(),
235
+ 'Sirr': dc_simple()
236
+ }
237
+
238
+ @staticmethod
239
+ def inlets():
240
+ return ['in1', 'in2', 'in3']
241
+
242
+ @staticmethod
243
+ def outlets():
244
+ return ['out1', 'out2', 'out3']
245
+
246
+ def comp_init(self, nw):
247
+
248
+ component.comp_init(self, nw)
249
+
250
+ # number of mandatroy equations for
251
+ # fluid balance: num_fl * 3
252
+ # mass flow: 3
253
+ # energy balance: 1
254
+ self.num_eq = self.num_nw_fluids * 3 + 3 + 1
255
+ # enthalpy hot side 1 outlet (if not subcooling): 1
256
+ if self.subcooling.val is False:
257
+ self.num_eq += 1
258
+ # enthalpy cold side outlet (if not overheating): 1
259
+ if self.overheating.val is False:
260
+ self.num_eq += 1
261
+ for var in [self.Q, self.pr1, self.pr2, self.pr3,
262
+ self.zeta1, self.zeta2, self.zeta3, ]:
263
+ if var.is_set is True:
264
+ self.num_eq += 1
265
+
266
+ self.jacobian = np.zeros((
267
+ self.num_eq,
268
+ self.num_i + self.num_o + self.num_vars,
269
+ self.num_nw_vars))
270
+
271
+ self.residual = np.zeros(self.num_eq)
272
+ pos = self.num_nw_fluids * 3
273
+ self.jacobian[0:pos] = self.fluid_deriv()
274
+ self.jacobian[pos:pos + 3] = self.mass_flow_deriv()
275
+
276
+ def equations(self):
277
+ r"""Calculate residual vector with results of equations."""
278
+ k = 0
279
+
280
+ ######################################################################
281
+ # equations for fluid balance
282
+ self.residual[k:k + self.num_nw_fluids * 3] = self.fluid_func()
283
+ k += self.num_nw_fluids * 3
284
+
285
+ ######################################################################
286
+ # equations for mass flow balance
287
+ self.residual[k:k + 3] = self.mass_flow_func()
288
+ k += 3
289
+
290
+ ######################################################################
291
+ # equations for energy balance
292
+ self.residual[k] = self.energy_func()
293
+ k += 1
294
+
295
+ ######################################################################
296
+ # equations for specified heat transfer
297
+ if self.Q.is_set:
298
+ self.residual[k] = (
299
+ self.inl[2].m.val_SI * (
300
+ self.outl[2].h.val_SI - self.inl[2].h.val_SI) - self.Q.val)
301
+ k += 1
302
+
303
+ ######################################################################
304
+ # equations for specified pressure ratio at hot side 1
305
+ if self.pr1.is_set:
306
+ self.residual[k] = (
307
+ self.pr1.val * self.inl[0].p.val_SI -
308
+ self.outl[0].p.val_SI)
309
+ k += 1
310
+
311
+ ######################################################################
312
+ # equations for specified pressure ratio at hot side 2
313
+ if self.pr2.is_set:
314
+ self.residual[k] = (
315
+ self.pr2.val * self.inl[1].p.val_SI -
316
+ self.outl[1].p.val_SI)
317
+ k += 1
318
+
319
+ ######################################################################
320
+ # equations for specified pressure ratio at cold side
321
+ if self.pr3.is_set:
322
+ self.residual[k] = (
323
+ self.pr3.val * self.inl[2].p.val_SI -
324
+ self.outl[2].p.val_SI)
325
+ k += 1
326
+
327
+ ######################################################################
328
+ # equations for specified zeta at hot side 1
329
+ if self.zeta1.is_set:
330
+ self.residual[k] = self.zeta_func(
331
+ zeta='zeta1', inconn=0, outconn=0)
332
+ k += 1
333
+
334
+ ######################################################################
335
+ # equations for specified zeta at hot side 2
336
+ if self.zeta2.is_set:
337
+ self.residual[k] = self.zeta_func(
338
+ zeta='zeta2', inconn=1, outconn=1)
339
+ k += 1
340
+
341
+ ######################################################################
342
+ # equations for specified zeta at cold side
343
+ if self.zeta3.is_set:
344
+ self.residual[k] = self.zeta_func(
345
+ zeta='zeta3', inconn=2, outconn=2)
346
+ k += 1
347
+
348
+ ######################################################################
349
+ # equation for saturated liquid at hot side 1 outlet
350
+ if self.subcooling.val is False:
351
+ o1 = self.outl[0].to_flow()
352
+ self.residual[k] = o1[2] - h_mix_pQ(o1, 0)
353
+ k += 1
354
+
355
+ ######################################################################
356
+ # equation for saturated gas at cold side outlet
357
+ if self.overheating.val is False:
358
+ o3 = self.outl[2].to_flow()
359
+ self.residual[k] = o3[2] - h_mix_pQ(o3, 1)
360
+ k += 1
361
+
362
+ def derivatives(self, increment_filter):
363
+ r"""Calculate matrix of partial derivatives for given equations."""
364
+ ######################################################################
365
+ # derivatives fluid and mass balance are static
366
+ k = self.num_nw_fluids * 3 + 3
367
+
368
+ ######################################################################
369
+ # derivatives for energy balance equation
370
+ # mat_deriv += self.energy_deriv()
371
+ for i in range(3):
372
+ self.jacobian[k, i, 0] = (
373
+ self.outl[i].h.val_SI - self.inl[i].h.val_SI)
374
+ self.jacobian[k, i, 2] = -self.inl[i].m.val_SI
375
+
376
+ self.jacobian[k, i + 3, 2] = self.inl[i].m.val_SI
377
+ k += 1
378
+
379
+ ######################################################################
380
+ # derivatives for specified heat transfer
381
+ if self.Q.is_set:
382
+ self.jacobian[k, 2, 0] = (
383
+ self.outl[2].h.val_SI - self.inl[2].h.val_SI)
384
+ self.jacobian[k, 2, 2] = -self.inl[2].m.val_SI
385
+ self.jacobian[k, 5, 2] = self.inl[2].m.val_SI
386
+ k += 1
387
+
388
+ ######################################################################
389
+ # derivatives for specified pressure ratio at hot side 1
390
+ if self.pr1.is_set:
391
+ self.jacobian[k, 0, 1] = self.pr1.val
392
+ self.jacobian[k, 3, 1] = -1
393
+ k += 1
394
+
395
+ ######################################################################
396
+ # derivatives for specified pressure ratio at hot side 2
397
+ if self.pr2.is_set:
398
+ self.jacobian[k, 1, 1] = self.pr2.val
399
+ self.jacobian[k, 4, 1] = -1
400
+ k += 1
401
+
402
+ ######################################################################
403
+ # derivatives for specified pressure ratio at cold side
404
+ if self.pr3.is_set:
405
+ self.jacobian[k, 2, 1] = self.pr3.val
406
+ self.jacobian[k, 5, 1] = -1
407
+ k += 1
408
+
409
+ ######################################################################
410
+ # derivatives for specified zeta at hot side 1
411
+ if self.zeta1.is_set:
412
+ f = self.zeta_func
413
+ if not increment_filter[0, 0]:
414
+ self.jacobian[k, 0, 0] = self.numeric_deriv(
415
+ f, 'm', 0, zeta='zeta1', inconn=0, outconn=0)
416
+ if not increment_filter[0, 1]:
417
+ self.jacobian[k, 0, 1] = self.numeric_deriv(
418
+ f, 'p', 0, zeta='zeta1', inconn=0, outconn=0)
419
+ if not increment_filter[0, 2]:
420
+ self.jacobian[k, 0, 2] = self.numeric_deriv(
421
+ f, 'h', 0, zeta='zeta1', inconn=0, outconn=0)
422
+ if not increment_filter[3, 1]:
423
+ self.jacobian[k, 3, 1] = self.numeric_deriv(
424
+ f, 'p', 3, zeta='zeta1', inconn=0, outconn=0)
425
+ if not increment_filter[3, 2]:
426
+ self.jacobian[k, 3, 2] = self.numeric_deriv(
427
+ f, 'h', 3, zeta='zeta1', inconn=0, outconn=0)
428
+ k += 1
429
+
430
+ ######################################################################
431
+ # derivatives for specified zeta at hot side 2
432
+ if self.zeta2.is_set:
433
+ f = self.zeta_func
434
+ if not increment_filter[1, 0]:
435
+ self.jacobian[k, 1, 0] = self.numeric_deriv(
436
+ f, 'm', 1, zeta='zeta2', inconn=1, outconn=1)
437
+ if not increment_filter[1, 1]:
438
+ self.jacobian[k, 1, 1] = self.numeric_deriv(
439
+ f, 'p', 1, zeta='zeta2', inconn=1, outconn=1)
440
+ if not increment_filter[1, 2]:
441
+ self.jacobian[k, 1, 2] = self.numeric_deriv(
442
+ f, 'h', 1, zeta='zeta2', inconn=1, outconn=1)
443
+ if not increment_filter[4, 1]:
444
+ self.jacobian[k, 4, 1] = self.numeric_deriv(
445
+ f, 'p', 4, zeta='zeta2', inconn=1, outconn=1)
446
+ if not increment_filter[4, 2]:
447
+ self.jacobian[k, 4, 2] = self.numeric_deriv(
448
+ f, 'h', 4, zeta='zeta2', inconn=1, outconn=1)
449
+ k += 1
450
+
451
+ ######################################################################
452
+ # derivatives for specified zeta at cold side
453
+ if self.zeta3.is_set:
454
+ f = self.zeta_func
455
+ if not increment_filter[2, 0]:
456
+ self.jacobian[k, 2, 0] = self.numeric_deriv(
457
+ f, 'm', 2, zeta='zeta3', inconn=2, outconn=2)
458
+ if not increment_filter[2, 1]:
459
+ self.jacobian[k, 2, 1] = self.numeric_deriv(
460
+ f, 'p', 2, zeta='zeta3', inconn=2, outconn=2)
461
+ if not increment_filter[2, 2]:
462
+ self.jacobian[k, 2, 2] = self.numeric_deriv(
463
+ f, 'h', 2, zeta='zeta3', inconn=2, outconn=2)
464
+ if not increment_filter[5, 1]:
465
+ self.jacobian[k, 5, 1] = self.numeric_deriv(
466
+ f, 'p', 5, zeta='zeta3', inconn=2, outconn=2)
467
+ if not increment_filter[5, 2]:
468
+ self.jacobian[k, 5, 2] = self.numeric_deriv(
469
+ f, 'h', 5, zeta='zeta3', inconn=2, outconn=2)
470
+ k += 1
471
+
472
+ ######################################################################
473
+ # derivatives for saturated liquid at hot side 1 outlet equation
474
+ if self.subcooling.val is False:
475
+ o1 = self.outl[0].to_flow()
476
+ self.jacobian[k, 3, 1] = -dh_mix_dpQ(o1, 0)
477
+ self.jacobian[k, 3, 2] = 1
478
+ k += 1
479
+
480
+ ######################################################################
481
+ # derivatives for saturated gas at cold side outlet 3 equation
482
+ if self.overheating.val is False:
483
+ o3 = self.outl[2].to_flow()
484
+ self.jacobian[k, 5, 1] = -dh_mix_dpQ(o3, 1)
485
+ self.jacobian[k, 5, 2] = 1
486
+ k += 1
487
+
488
+ def mass_flow_func(self):
489
+ r"""
490
+ Calculate the residual value of mass flow balance equations.
491
+
492
+ Returns
493
+ -------
494
+ residual : list
495
+ Vector with residual value for component's mass flow balance.
496
+
497
+ .. math::
498
+
499
+ 0 = \dot{m}_{in,i} - \dot{m}_{out,i} \;
500
+ \forall i \in inlets/outlets
501
+ """
502
+ residual = []
503
+ for i in range(self.num_i):
504
+ residual += [self.inl[i].m.val_SI - self.outl[i].m.val_SI]
505
+ return residual
506
+
507
+ def mass_flow_deriv(self):
508
+ r"""
509
+ Calculate the partial derivatives for all mass flow balance equations.
510
+
511
+ Returns
512
+ -------
513
+ deriv : list
514
+ Matrix with partial derivatives for the mass flow balance
515
+ equations.
516
+ """
517
+ deriv = np.zeros((self.num_i, 2 * self.num_i, self.num_nw_vars))
518
+ for i in range(self.num_i):
519
+ deriv[i, i, 0] = 1
520
+ for j in range(self.num_i):
521
+ deriv[j, j + self.num_i, 0] = -1
522
+ return deriv
523
+
524
+ def energy_func(self):
525
+ r"""
526
+ Equation for heat exchanger energy balance.
527
+
528
+ Returns
529
+ -------
530
+ res : float
531
+ Residual value of equation.
532
+
533
+ .. math::
534
+
535
+ \begin{split}
536
+ res = &
537
+ \dot{m}_{1,in} \cdot \left(h_{1,out} - h_{1,in} \right) \\
538
+ &+ \dot{m}_{2,in} \cdot \left(h_{2,out} - h_{2,in} \right) \\
539
+ &+ \dot{m}_{3,in} \cdot \left(h_{3,out} - h_{3,in} \right)
540
+ \end{split}
541
+ """
542
+ return (
543
+ self.inl[0].m.val_SI * (
544
+ self.outl[0].h.val_SI - self.inl[0].h.val_SI) +
545
+ self.inl[1].m.val_SI * (
546
+ self.outl[1].h.val_SI - self.inl[1].h.val_SI) +
547
+ self.inl[2].m.val_SI * (
548
+ self.outl[2].h.val_SI - self.inl[2].h.val_SI))
549
+
550
+ def bus_func(self, bus):
551
+ r"""
552
+ Calculate the value of the bus function.
553
+
554
+ Parameters
555
+ ----------
556
+ bus : tespy.connections.bus
557
+ TESPy bus object.
558
+
559
+ Returns
560
+ -------
561
+ val : float
562
+ Value of energy transfer :math:`\dot{E}`. This value is passed to
563
+ :py:meth:`tespy.components.components.component.calc_bus_value`
564
+ for value manipulation according to the specified characteristic
565
+ line of the bus.
566
+
567
+ .. math::
568
+
569
+ \dot{E} = \dot{m}_{3,in} \cdot \left(
570
+ h_{3,out} - h_{3,in} \right)
571
+ """
572
+ i = self.inl[2].to_flow()
573
+ o = self.outl[2].to_flow()
574
+ val = i[0] * (o[2] - i[2])
575
+
576
+ return val
577
+
578
+ def bus_deriv(self, bus):
579
+ r"""
580
+ Calculate the matrix of partial derivatives of the bus function.
581
+
582
+ Parameters
583
+ ----------
584
+ bus : tespy.connections.bus
585
+ TESPy bus object.
586
+
587
+ Returns
588
+ -------
589
+ mat_deriv : ndarray
590
+ Matrix of partial derivatives.
591
+ """
592
+ deriv = np.zeros((1, 6, self.num_nw_vars))
593
+ f = self.calc_bus_value
594
+ deriv[0, 2, 0] = self.numeric_deriv(f, 'm', 2, bus=bus)
595
+ deriv[0, 2, 2] = self.numeric_deriv(f, 'h', 2, bus=bus)
596
+ deriv[0, 5, 2] = self.numeric_deriv(f, 'h', 5, bus=bus)
597
+ return deriv
598
+
599
+ def initialise_source(self, c, key):
600
+ r"""
601
+ Return a starting value for pressure and enthalpy at outlet.
602
+
603
+ Parameters
604
+ ----------
605
+ c : tespy.connections.connection
606
+ Connection to perform initialisation on.
607
+
608
+ key : str
609
+ Fluid property to retrieve.
610
+
611
+ Returns
612
+ -------
613
+ val : float
614
+ Starting value for pressure/enthalpy in SI units.
615
+
616
+ .. math::
617
+
618
+ val = \begin{cases}
619
+ 10 \cdot 10^5 & \text{key = 'p'}\\
620
+ h\left(p, 473.15 \text{K} \right) &
621
+ \text{key = 'h' at outlet 1}\\
622
+ h\left(p, 473.15 \text{K} \right) &
623
+ \text{key = 'h' at outlet 2}\\
624
+ h\left(p, 523.15 \text{K} \right) &
625
+ \text{key = 'h' at outlet 3}
626
+ \end{cases}
627
+ """
628
+ if key == 'p':
629
+ return 10e5
630
+ elif key == 'h':
631
+ flow = c.to_flow()
632
+ if c.source_id == 'out1':
633
+ T = 200 + 273.15
634
+ return h_mix_pT(flow, T)
635
+ elif c.source_id == 'out2':
636
+ T = 200 + 273.15
637
+ return h_mix_pT(flow, T)
638
+ else:
639
+ T = 250 + 273.15
640
+ return h_mix_pT(flow, T)
641
+
642
+ def initialise_target(self, c, key):
643
+ r"""
644
+ Return a starting value for pressure and enthalpy at inlet.
645
+
646
+ Parameters
647
+ ----------
648
+ c : tespy.connections.connection
649
+ Connection to perform initialisation on.
650
+
651
+ key : str
652
+ Fluid property to retrieve.
653
+
654
+ Returns
655
+ -------
656
+ val : float
657
+ Starting value for pressure/enthalpy in SI units.
658
+
659
+ .. math::
660
+
661
+ val = \begin{cases}
662
+ 10 \cdot 10^5 & \text{key = 'p'}\\
663
+ h\left(p, 573.15 \text{K} \right) &
664
+ \text{key = 'h' at inlet 1}\\
665
+ h\left(p, 573.15 \text{K} \right) &
666
+ \text{key = 'h' at inlet 2}\\
667
+ h\left(p, 493.15 \text{K} \right) &
668
+ \text{key = 'h' at inlet 3}
669
+ \end{cases}
670
+ """
671
+ if key == 'p':
672
+ return 10e5
673
+ elif key == 'h':
674
+ flow = c.to_flow()
675
+ if c.target_id == 'in1':
676
+ T = 300 + 273.15
677
+ return h_mix_pT(flow, T)
678
+ elif c.target_id == 'in2':
679
+ T = 300 + 273.15
680
+ return h_mix_pT(flow, T)
681
+ else:
682
+ T = 220 + 273.15
683
+ return h_mix_pT(flow, T)
684
+
685
+ def calc_parameters(self):
686
+ r"""Postprocessing parameter calculation."""
687
+ # connection information
688
+ i1 = self.inl[0].to_flow()
689
+ i2 = self.inl[1].to_flow()
690
+ i3 = self.inl[2].to_flow()
691
+ o1 = self.outl[0].to_flow()
692
+ o2 = self.outl[1].to_flow()
693
+ o3 = self.outl[2].to_flow()
694
+
695
+ # specific volume
696
+ v_i1 = v_mix_ph(i1, T0=self.inl[0].T.val_SI)
697
+ v_i2 = v_mix_ph(i2, T0=self.inl[1].T.val_SI)
698
+ v_i3 = v_mix_ph(i3, T0=self.inl[2].T.val_SI)
699
+ v_o1 = v_mix_ph(o1, T0=self.outl[0].T.val_SI)
700
+ v_o2 = v_mix_ph(o2, T0=self.outl[1].T.val_SI)
701
+ v_o3 = v_mix_ph(o3, T0=self.outl[2].T.val_SI)
702
+
703
+ # specific entropy
704
+ s_i1 = s_mix_ph(i1, T0=self.inl[0].T.val_SI)
705
+ s_i2 = s_mix_ph(i2, T0=self.inl[1].T.val_SI)
706
+ s_i3 = s_mix_ph(i3, T0=self.inl[2].T.val_SI)
707
+ s_o1 = s_mix_ph(o1, T0=self.outl[0].T.val_SI)
708
+ s_o2 = s_mix_ph(o2, T0=self.outl[1].T.val_SI)
709
+ s_o3 = s_mix_ph(o3, T0=self.outl[2].T.val_SI)
710
+
711
+ # component parameters
712
+ self.Q.val = -i3[0] * (o3[2] - i3[2])
713
+
714
+ self.pr1.val = o1[1] / i1[1]
715
+ self.pr2.val = o2[1] / i2[1]
716
+ self.pr3.val = o3[1] / i3[1]
717
+ self.zeta1.val = ((i1[1] - o1[1]) * np.pi ** 2 /
718
+ (8 * i1[0] ** 2 * (v_i1 + v_o1) / 2))
719
+ self.zeta2.val = ((i2[1] - o2[1]) * np.pi ** 2 /
720
+ (8 * i2[0] ** 2 * (v_i2 + v_o2) / 2))
721
+ self.zeta3.val = ((i3[1] - o3[1]) * np.pi ** 2 /
722
+ (8 * i3[0] ** 2 * (v_i3 + v_o3) / 2))
723
+
724
+ self.SQ1.val = self.inl[0].m.val_SI * (s_o1 - s_i1)
725
+ self.SQ2.val = self.inl[1].m.val_SI * (s_o2 - s_i2)
726
+ self.SQ3.val = self.inl[2].m.val_SI * (s_o3 - s_i3)
727
+ self.Sirr.val = self.SQ1.val + self.SQ2.val + self.SQ3.val
728
+
729
+ self.check_parameter_bounds()