Coverage for backend/ahuora-builder/src/ahuora_builder/custom/custom_heat_exchanger.py: 83%
101 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-07-22 05:22 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2026-07-22 05:22 +0000
3# Import Pyomo libraries
4from pyomo.environ import (
5 Block,
6 Var,
7 Param,
8 log,
9 Reference,
10 PositiveReals,
11 ExternalFunction,
12 units as pyunits,
13 check_optimal_termination,
14 value,
15)
16from pyomo.common.config import ConfigBlock, ConfigValue, In
17from pyomo.core.base.component import Component
19# Import IDAES cores
20from idaes.core import (
21 declare_process_block_class,
22 UnitModelBlockData,
23)
25import idaes.logger as idaeslog
26from idaes.core.util.functions import functions_lib
27from idaes.core.util.tables import create_stream_table_dataframe
28from idaes.models.unit_models.heater import (
29 _make_heater_config_block,
30 _make_heater_control_volume,
31)
33from idaes.core.util.misc import add_object_reference
34from idaes.core.util import scaling as iscale
35from idaes.core.solvers import get_solver
36from idaes.core.util.exceptions import ConfigurationError, InitializationError
37from idaes.core.initialization import SingleControlVolumeUnitInitializer
38from idaes.models.unit_models.heat_exchanger import HX0DInitializer, _make_heat_exchanger_config, HeatExchangerData, delta_temperature_underwood_callback
39from .inverted import add_inverted, initialise_inverted
40_log = idaeslog.getLogger(__name__)
43@declare_process_block_class("CustomHeatExchanger", doc="Simple 0D heat exchanger model.")
44class CustomHeatExchangerData(HeatExchangerData):
46 CONFIG = HeatExchangerData.CONFIG()
47 CONFIG.pop("delta_temperature_callback")
48 CONFIG.declare(
49 "delta_temperature_callback",
50 ConfigValue(
51 default=delta_temperature_underwood_callback,
52 description="Callback for for temperature difference calculations",
53 ),
54 )
56 def build(self,*args,**kwargs) -> None:
57 """
58 Begin building model.
59 """
60 super().build(*args,**kwargs)
61 # Add an inverted DeltaP
62 add_inverted(self.hot_side, "deltaP")
63 add_inverted(self.cold_side, "deltaP")
65 def initialize_build(
66 self,
67 state_args_1=None,
68 state_args_2=None,
69 outlvl=idaeslog.NOTSET,
70 solver=None,
71 optarg=None,
72 duty=None,
73 ):
74 """
75 Heat exchanger initialization method.
77 Args:
78 state_args_1 : a dict of arguments to be passed to the property
79 initialization for the hot side (see documentation of the specific
80 property package) (default = {}).
81 state_args_2 : a dict of arguments to be passed to the property
82 initialization for the cold side (see documentation of the specific
83 property package) (default = {}).
84 outlvl : sets output level of initialization routine
85 optarg : solver options dictionary object (default=None, use
86 default solver options)
87 solver : str indicating which solver to use during
88 initialization (default = None, use default solver)
89 duty : an initial guess for the amount of heat transferred. This
90 should be a tuple in the form (value, units), (default
91 = (1000 J/s))
93 Returns:
94 None
96 """
97 # So, when solving with a correct area, there can be problems
98 # That's because if the area's even slightly too large, it becomes infeasible
99 if not self.area.fixed:
100 self.area.value = self.area.value * 0.8
102 initialise_inverted(self.hot_side, "deltaP")
103 initialise_inverted(self.cold_side, "deltaP")
105 # Set solver options
106 init_log = idaeslog.getInitLogger(self.name, outlvl, tag="unit")
107 solve_log = idaeslog.getSolveLogger(self.name, outlvl, tag="unit")
109 # Create solver
110 opt = get_solver(solver, optarg)
112 flags1 = self.hot_side.initialize(
113 outlvl=outlvl, optarg=optarg, solver=solver, state_args=state_args_1
114 )
117 init_log.info_high("Initialization Step 1a (hot side) Complete.")
119 flags2 = self.cold_side.initialize(
120 outlvl=outlvl, optarg=optarg, solver=solver, state_args=state_args_2
121 )
122 init_log.info_high("Initialization Step 1b (cold side) Complete.")
123 # ---------------------------------------------------------------------
124 # Solve unit without heat transfer equation
125 self.heat_transfer_equation.deactivate()
126 # Deactivate any additional constraints added to constrain outlet temperature/vapor fraction etc during initialisation.
127 if hasattr( self.cold_side.properties_out, "_deactivate_additional_constraints"): 127 ↛ 129line 127 didn't jump to line 129 because the condition on line 127 was always true
128 self.cold_side.properties_out._deactivate_additional_constraints()
129 if hasattr( self.hot_side.properties_out, "_deactivate_additional_constraints"): 129 ↛ 133line 129 didn't jump to line 133 because the condition on line 129 was always true
130 self.hot_side.properties_out._deactivate_additional_constraints()
132 # Get side 1 and side 2 heat units, and convert duty as needed
133 s1_units = self.hot_side.heat.get_units()
134 s2_units = self.cold_side.heat.get_units()
136 # Check to see if heat duty is fixed
137 # WE will assume that if the first point is fixed, it is fixed at all points
138 if not self.cold_side.heat[self.flowsheet().time.first()].fixed: 138 ↛ 166line 138 didn't jump to line 166 because the condition on line 138 was always true
139 cs_fixed = False
140 if duty is None: 140 ↛ 155line 140 didn't jump to line 155 because the condition on line 140 was always true
141 # Assume 1000 J/s and check for unitless properties
142 if s1_units is None and s2_units is None: 142 ↛ 144line 142 didn't jump to line 144 because the condition on line 142 was never true
143 # Backwards compatibility for unitless properties
144 s1_duty = -1000
145 s2_duty = 1000
146 else:
147 s1_duty = pyunits.convert_value(
148 -1000, from_units=pyunits.W, to_units=s1_units
149 )
150 s2_duty = pyunits.convert_value(
151 1000, from_units=pyunits.W, to_units=s2_units
152 )
153 else:
154 # Duty provided with explicit units
155 s1_duty = -pyunits.convert_value(
156 duty[0], from_units=duty[1], to_units=s1_units
157 )
158 s2_duty = pyunits.convert_value(
159 duty[0], from_units=duty[1], to_units=s2_units
160 )
162 self.cold_side.heat.fix(s2_duty)
163 for i in self.hot_side.heat:
164 self.hot_side.heat[i].value = s1_duty
165 else:
166 cs_fixed = True
167 for i in self.hot_side.heat:
168 self.hot_side.heat[i].set_value(self.cold_side.heat[i])
169 with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc:
170 res = opt.solve(self, tee=slc.tee)
171 init_log.info_high("Initialization Step 2 {}.".format(idaeslog.condition(res)))
172 if not cs_fixed: 172 ↛ 176line 172 didn't jump to line 176 because the condition on line 172 was always true
173 self.cold_side.heat.unfix()
176 if hasattr( self.cold_side.properties_out, "_reactivate_additional_constraints"): 176 ↛ 178line 176 didn't jump to line 178 because the condition on line 176 was always true
177 self.cold_side.properties_out._reactivate_additional_constraints()
178 if hasattr( self.hot_side.properties_out, "_reactivate_additional_constraints"): 178 ↛ 180line 178 didn't jump to line 180 because the condition on line 178 was always true
179 self.hot_side.properties_out._reactivate_additional_constraints()
180 self.heat_transfer_equation.activate()
182 # ---------------------------------------------------------------------
183 # Solve unit
184 with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc:
185 res = opt.solve(self, tee=slc.tee)
186 init_log.info_high("Initialization Step 3 {}.".format(idaeslog.condition(res)))
187 # ---------------------------------------------------------------------
189 # Release Inlet state
190 self.hot_side.release_state(flags1, outlvl=outlvl)
191 self.cold_side.release_state(flags2, outlvl=outlvl)
193 init_log.info("Initialization Completed, {}".format(idaeslog.condition(res)))
195 if not check_optimal_termination(res):
196 raise InitializationError(
197 f"{self.name} failed to initialize successfully. Please check "
198 f"the output logs for more information."
199 )
202 def diagnose(self) -> list[tuple[Component, str]]:
203 """
204 Test a few common issues with the heat exchanger model and provide hints to the user.
205 returns a list with the variable the it is most relevant to and a message describing the issue
206 """
207 # if flow rates are drastically different and enthalpy rates are drastically different,
208 # this might be a problem.
209 problems = []
210 mass_flow_difference = value(self.hot_side.properties_in[0].flow_mass)/value(self.cold_side.properties_in[0].flow_mass)
211 if mass_flow_difference > 100:
212 problems.append(
213 (
214 self.hot_side.properties_in[0].flow_mass,
215 f"Mass flow rate on hot side is {mass_flow_difference:.2f} times higher than cold side. This may cause convergence issues. Consider adjusting the model or providing better initial guesses.",
216 )
217 )
218 elif mass_flow_difference < 0.01: 218 ↛ 219line 218 didn't jump to line 219 because the condition on line 218 was never true
219 problems.append(
220 (
221 self.cold_side.properties_in[0].flow_mass,
222 f"Mass flow rate on cold side is {1/mass_flow_difference:.2f} times higher than hot side. This may cause convergence issues. Consider adjusting the model or providing better initial guesses.",
223 )
224 )
225 hsi_temp = value(self.hot_side.properties_in[0].temperature) or 0
226 csi_temp = value(self.cold_side.properties_in[0].temperature) or 0
227 hso_temp = value(self.hot_side.properties_out[0].temperature) or 0
228 cso_temp = value(self.cold_side.properties_out[0].temperature) or 0
229 if hsi_temp < csi_temp:
230 # switch hot and cold side temps so hot side is always hotter.
231 hsi_temp, csi_temp = csi_temp, hsi_temp
232 hso_temp, cso_temp = cso_temp, hso_temp
234 if hsi_temp - cso_temp < 1e-1:
235 problems.append(
236 (
237 self.overall_heat_transfer_coefficient,
238 f"The heat exchanger has used all the heat available in the hot side. Temperature difference between HS inlet and CS outlet is ({hsi_temp - cso_temp:.2f} K). There needs to be a temperature difference to drive heat transfer. Perhaps there is insufficient energy in one of the streams.",
239 )
240 )
241 elif hso_temp - csi_temp < 1e-1: 241 ↛ 242line 241 didn't jump to line 242 because the condition on line 241 was never true
242 problems.append(
243 (
244 self.overall_heat_transfer_coefficient,
245 f"The heat exchanger has used all the cooling available in the cold side. Temperature difference between HS outlet and CS inlet is ({hso_temp - csi_temp:.2f} K). There needs to be a temperature difference to drive heat transfer. Perhaps there is insufficient cooling in the cold side or too much heat in the hot side.",
246 )
247 )
248 return problems
251 @staticmethod
252 def ahuora_metadata():
253 from ahuora_unit_ops.json_config import (
254 JsonAdapterArgConfig,
255 JsonFrontendConfig,
256 JsonGraphicObjectConfig,
257 JsonIdaesAdapterConfig,
258 JsonPortAdapterConfig,
259 JsonPortConfig,
260 JsonPortMapEntryConfig,
261 JsonPropertyConfig,
262 JsonPropertySetGroupConfig,
263 JsonUnitOpConfig,
264 JsonVariantConfig,
265 )
267 return (
268JsonUnitOpConfig(
269 key='heat_exchanger',
270 objectType='heatExchanger',
271 enumMember='HeatExchanger',
272 displayType='Heat Exchanger',
273 displayName='Heat Exchanger',
274 categoryPath=['chemical', 'heating_and_cooling'],
275 ports={
276 'coldInlet': JsonPortConfig(
277 displayName='Cold Inlet',
278 type='inlet',
279 streamType='stream',
280 many=False,
281 default=1,
282 minimum=1,
283 makeStream=True,
284 streamOffset=1,
285 streamName='CS',
286 ),
287 'hotInlet': JsonPortConfig(
288 displayName='Hot Inlet',
289 type='inlet',
290 streamType='stream',
291 many=False,
292 default=1,
293 minimum=1,
294 makeStream=True,
295 streamOffset=1,
296 streamName='HS',
297 ),
298 'coldOutlet': JsonPortConfig(
299 displayName='Cold Outlet',
300 type='outlet',
301 streamType='stream',
302 many=False,
303 default=1,
304 minimum=1,
305 makeStream=True,
306 streamOffset=1,
307 streamName='CS',
308 ),
309 'hotOutlet': JsonPortConfig(
310 displayName='Hot Outlet',
311 type='outlet',
312 streamType='stream',
313 many=False,
314 default=1,
315 minimum=1,
316 makeStream=True,
317 streamOffset=1,
318 streamName='HS',
319 ),
320 },
321 propertyPackagePorts={
322 'Cold Side': ['coldInlet', 'coldOutlet'],
323 'Hot Side': ['hotInlet', 'hotOutlet'],
324 },
325 graphicObject=JsonGraphicObjectConfig(
326 kind='unitop_graphic',
327 ),
328 indexSets=[],
329 properties={
330 'overall_heat_transfer_coefficient': JsonPropertyConfig(
331 propertySetGroup='default',
332 displayName='Heat Transfer Coefficient (U)',
333 indexSets=None,
334 sumToOne=False,
335 value=None,
336 unit=None,
337 unitType='heat_transf_coeff',
338 description=None,
339 type='numeric',
340 many=False,
341 default=1,
342 options={},
343 hasTimeIndex=True,
344 ),
345 'area': JsonPropertyConfig(
346 propertySetGroup='default',
347 displayName='Heat Exchange Area',
348 indexSets=None,
349 sumToOne=False,
350 value=None,
351 unit=None,
352 unitType='area',
353 description=None,
354 type='numeric',
355 many=False,
356 default=1,
357 options={},
358 hasTimeIndex=False,
359 ),
360 'heat_duty': JsonPropertyConfig(
361 propertySetGroup='default',
362 displayName='Heat Load',
363 indexSets=None,
364 sumToOne=False,
365 value=None,
366 unit=None,
367 unitType='heatflow',
368 description=None,
369 type='numeric',
370 many=False,
371 default=1,
372 options={},
373 hasTimeIndex=True,
374 ),
375 'cold_side.deltaP_inverted': JsonPropertyConfig(
376 propertySetGroup='default',
377 displayName='Pressure Drop (Cold Side)',
378 indexSets=None,
379 sumToOne=False,
380 value=0.0,
381 unit=None,
382 unitType='pressure',
383 description=None,
384 type='numeric',
385 many=False,
386 default=1,
387 options={},
388 hasTimeIndex=True,
389 ),
390 'hot_side.deltaP_inverted': JsonPropertyConfig(
391 propertySetGroup='default',
392 displayName='Pressure Drop (Hot Side)',
393 indexSets=None,
394 sumToOne=False,
395 value=0.0,
396 unit=None,
397 unitType='pressure',
398 description=None,
399 type='numeric',
400 many=False,
401 default=1,
402 options={},
403 hasTimeIndex=True,
404 ),
405 },
406 propertySetGroups={
407 'default': JsonPropertySetGroupConfig(
408 type='stateVars',
409 displayName='Properties',
410 stateVars=[
411 'overall_heat_transfer_coefficient',
412 'area',
413 'hot_side.deltaP_inverted',
414 'cold_side.deltaP_inverted',
415 ],
416 toggle=None,
417 ),
418 },
419 keyProperties=['heat_duty', 'logMeanTemperatureDifference', 'minimumTemperatureDifference'],
420 splitterFractionName=None,
421 idaesAdapter=JsonIdaesAdapterConfig(
422 constructor='ahuora_builder.custom.custom_heat_exchanger.CustomHeatExchanger',
423 args={
424 'hot_side': JsonAdapterArgConfig(
425 kind='dict',
426 args={
427 'property_package': JsonAdapterArgConfig(
428 kind='property_package',
429 label='Hot Side',
430 ),
431 'has_pressure_change': JsonAdapterArgConfig(
432 kind='constant',
433 value=True,
434 ),
435 },
436 ),
437 'cold_side': JsonAdapterArgConfig(
438 kind='dict',
439 args={
440 'property_package': JsonAdapterArgConfig(
441 kind='property_package',
442 label='Cold Side',
443 ),
444 'has_pressure_change': JsonAdapterArgConfig(
445 kind='constant',
446 value=True,
447 ),
448 },
449 ),
450 'dynamic': JsonAdapterArgConfig(
451 kind='constant',
452 value=False,
453 ),
454 },
455 ports=JsonPortAdapterConfig(
456 kind='port_map',
457 mapping={
458 'cold_side_inlet': JsonPortMapEntryConfig(
459 port='coldInlet',
460 inlet=True,
461 ),
462 'hot_side_inlet': JsonPortMapEntryConfig(
463 port='hotInlet',
464 inlet=True,
465 ),
466 'cold_side_outlet': JsonPortMapEntryConfig(
467 port='coldOutlet',
468 inlet=False,
469 ),
470 'hot_side_outlet': JsonPortMapEntryConfig(
471 port='hotOutlet',
472 inlet=False,
473 ),
474 },
475 ),
476 properties=None,
477 ),
478 frontend=JsonFrontendConfig(
479 showInPanel=True,
480 variant=JsonVariantConfig(
481 familyKey='heat_exchanger',
482 label='Heat Exchanger',
483 selectorLabel='Heat Exchanger Type',
484 default=True,
485 order=0,
486 preservePorts=True,
487 preserveGraphic=True,
488 ),
489 ),
490 ),
491JsonUnitOpConfig(
492 key='heat_exchanger_ntu',
493 objectType='heat_exchanger_ntu',
494 enumMember='NTUHeatExchanger',
495 displayType='NTU Heat Exchanger',
496 displayName='NTU Heat Exchanger',
497 categoryPath=['chemical', 'heating_and_cooling'],
498 ports={
499 'coldInlet': JsonPortConfig(
500 displayName='Cold Inlet',
501 type='inlet',
502 streamType='stream',
503 many=False,
504 default=1,
505 minimum=1,
506 makeStream=True,
507 streamOffset=1,
508 streamName='CS',
509 ),
510 'hotInlet': JsonPortConfig(
511 displayName='Hot Inlet',
512 type='inlet',
513 streamType='stream',
514 many=False,
515 default=1,
516 minimum=1,
517 makeStream=True,
518 streamOffset=1,
519 streamName='HS',
520 ),
521 'coldOutlet': JsonPortConfig(
522 displayName='Cold Outlet',
523 type='outlet',
524 streamType='stream',
525 many=False,
526 default=1,
527 minimum=1,
528 makeStream=True,
529 streamOffset=1,
530 streamName='CS',
531 ),
532 'hotOutlet': JsonPortConfig(
533 displayName='Hot Outlet',
534 type='outlet',
535 streamType='stream',
536 many=False,
537 default=1,
538 minimum=1,
539 makeStream=True,
540 streamOffset=1,
541 streamName='HS',
542 ),
543 },
544 propertyPackagePorts={
545 'Cold Side': ['coldInlet', 'coldOutlet'],
546 'Hot Side': ['hotInlet', 'hotOutlet'],
547 },
548 graphicObject=JsonGraphicObjectConfig(
549 kind='unitop_graphic',
550 ),
551 indexSets=[],
552 properties={
553 'heat_duty': JsonPropertyConfig(
554 propertySetGroup='default',
555 displayName='Heat Load',
556 indexSets=None,
557 sumToOne=False,
558 value=None,
559 unit=None,
560 unitType='heatflow',
561 description=None,
562 type='numeric',
563 many=False,
564 default=1,
565 options={},
566 hasTimeIndex=True,
567 ),
568 'effectiveness': JsonPropertyConfig(
569 propertySetGroup='default',
570 displayName='Effectiveness',
571 indexSets=None,
572 sumToOne=False,
573 value=None,
574 unit=None,
575 unitType='ratio',
576 description=None,
577 type='numeric',
578 many=False,
579 default=1,
580 options={},
581 hasTimeIndex=True,
582 ),
583 'cold_side.deltaP_inverted': JsonPropertyConfig(
584 propertySetGroup='default',
585 displayName='Pressure Drop (Cold Side)',
586 indexSets=None,
587 sumToOne=False,
588 value=0.0,
589 unit=None,
590 unitType='pressure',
591 description=None,
592 type='numeric',
593 many=False,
594 default=1,
595 options={},
596 hasTimeIndex=True,
597 ),
598 'hot_side.deltaP_inverted': JsonPropertyConfig(
599 propertySetGroup='default',
600 displayName='Pressure Drop (Hot Side)',
601 indexSets=None,
602 sumToOne=False,
603 value=0.0,
604 unit=None,
605 unitType='pressure',
606 description=None,
607 type='numeric',
608 many=False,
609 default=1,
610 options={},
611 hasTimeIndex=True,
612 ),
613 },
614 propertySetGroups={
615 'default': JsonPropertySetGroupConfig(
616 type='stateVars',
617 displayName='Properties',
618 stateVars=[
619 'heat_transfer_coefficient',
620 'area',
621 'effectiveness',
622 'cold_side.deltaP_inverted',
623 'hot_side.deltaP_inverted',
624 ],
625 toggle=None,
626 ),
627 },
628 keyProperties=['heat_duty', 'logMeanTemperatureDifference', 'minimumTemperatureDifference'],
629 splitterFractionName=None,
630 idaesAdapter=JsonIdaesAdapterConfig(
631 constructor='ahuora_builder.custom.SimpleEffectivenessHX_DH.HeatExchangerEffectiveness',
632 args={
633 'hot_side': JsonAdapterArgConfig(
634 kind='dict',
635 args={
636 'property_package': JsonAdapterArgConfig(
637 kind='property_package',
638 label='Hot Side',
639 ),
640 'has_pressure_change': JsonAdapterArgConfig(
641 kind='constant',
642 value=True,
643 ),
644 },
645 ),
646 'cold_side': JsonAdapterArgConfig(
647 kind='dict',
648 args={
649 'property_package': JsonAdapterArgConfig(
650 kind='property_package',
651 label='Cold Side',
652 ),
653 'has_pressure_change': JsonAdapterArgConfig(
654 kind='constant',
655 value=True,
656 ),
657 },
658 ),
659 'dynamic': JsonAdapterArgConfig(
660 kind='constant',
661 value=False,
662 ),
663 },
664 ports=JsonPortAdapterConfig(
665 kind='port_map',
666 mapping={
667 'cold_side_inlet': JsonPortMapEntryConfig(
668 port='coldInlet',
669 inlet=True,
670 ),
671 'hot_side_inlet': JsonPortMapEntryConfig(
672 port='hotInlet',
673 inlet=True,
674 ),
675 'cold_side_outlet': JsonPortMapEntryConfig(
676 port='coldOutlet',
677 inlet=False,
678 ),
679 'hot_side_outlet': JsonPortMapEntryConfig(
680 port='hotOutlet',
681 inlet=False,
682 ),
683 },
684 ),
685 properties=None,
686 ),
687 frontend=JsonFrontendConfig(
688 showInPanel=False,
689 variant=JsonVariantConfig(
690 familyKey='heat_exchanger',
691 label='NTU Heat Exchanger',
692 selectorLabel='Heat Exchanger Type',
693 default=False,
694 order=10,
695 preservePorts=True,
696 preserveGraphic=True,
697 ),
698 ),
699 ),
700JsonUnitOpConfig(
701 key='heat_exchanger_lc',
702 objectType='heat_exchanger_lc',
703 enumMember='LumpedCapacitanceHeatExchanger',
704 displayType='Lumped Capacitance Heat Exchanger',
705 displayName='Lumped Capacitance Heat Exchanger',
706 categoryPath=['chemical', 'heating_and_cooling'],
707 ports={
708 'coldInlet': JsonPortConfig(
709 displayName='Cold Inlet',
710 type='inlet',
711 streamType='stream',
712 many=False,
713 default=1,
714 minimum=1,
715 makeStream=True,
716 streamOffset=1,
717 streamName='CS',
718 ),
719 'hotInlet': JsonPortConfig(
720 displayName='Hot Inlet',
721 type='inlet',
722 streamType='stream',
723 many=False,
724 default=1,
725 minimum=1,
726 makeStream=True,
727 streamOffset=1,
728 streamName='HS',
729 ),
730 'coldOutlet': JsonPortConfig(
731 displayName='Cold Outlet',
732 type='outlet',
733 streamType='stream',
734 many=False,
735 default=1,
736 minimum=1,
737 makeStream=True,
738 streamOffset=1,
739 streamName='CS',
740 ),
741 'hotOutlet': JsonPortConfig(
742 displayName='Hot Outlet',
743 type='outlet',
744 streamType='stream',
745 many=False,
746 default=1,
747 minimum=1,
748 makeStream=True,
749 streamOffset=1,
750 streamName='HS',
751 ),
752 },
753 propertyPackagePorts={
754 'Cold Side': ['coldInlet', 'coldOutlet'],
755 'Hot Side': ['hotInlet', 'hotOutlet'],
756 },
757 graphicObject=JsonGraphicObjectConfig(
758 kind='unitop_graphic',
759 ),
760 indexSets=[],
761 properties={
762 'ua_cold_side': JsonPropertyConfig(
763 propertySetGroup='default',
764 displayName='Heat Transfer Coefficient (Cold Side)',
765 indexSets=None,
766 sumToOne=False,
767 value=0.0,
768 unit=None,
769 unitType='heat_transf_coeff',
770 description=None,
771 type='numeric',
772 many=False,
773 default=1,
774 options={},
775 hasTimeIndex=True,
776 ),
777 'ua_hot_side': JsonPropertyConfig(
778 propertySetGroup='default',
779 displayName='Heat Transfer Coefficient (Hot Side)',
780 indexSets=None,
781 sumToOne=False,
782 value=0.0,
783 unit=None,
784 unitType='heat_transf_coeff',
785 description=None,
786 type='numeric',
787 many=False,
788 default=1,
789 options={},
790 hasTimeIndex=True,
791 ),
792 'temperature_wall': JsonPropertyConfig(
793 propertySetGroup='default',
794 displayName='Average Wall Temperature',
795 indexSets=None,
796 sumToOne=False,
797 value=None,
798 unit=None,
799 unitType='temperature',
800 description=None,
801 type='numeric',
802 many=False,
803 default=1,
804 options={},
805 hasTimeIndex=True,
806 ),
807 'heat_duty': JsonPropertyConfig(
808 propertySetGroup='default',
809 displayName='Heat Load',
810 indexSets=None,
811 sumToOne=False,
812 value=None,
813 unit=None,
814 unitType='heatflow',
815 description=None,
816 type='numeric',
817 many=False,
818 default=1,
819 options={},
820 hasTimeIndex=True,
821 ),
822 },
823 propertySetGroups={
824 'default': JsonPropertySetGroupConfig(
825 type='stateVars',
826 displayName='Properties',
827 stateVars=['ua_cold_side', 'ua_hot_side', 'temperature_wall'],
828 toggle=None,
829 ),
830 },
831 keyProperties=['ua_cold_side', 'ua_hot_side', 'temperature_wall'],
832 splitterFractionName=None,
833 idaesAdapter=JsonIdaesAdapterConfig(
834 constructor='idaes.models.unit_models.heat_exchanger_lc.HeatExchangerLumpedCapacitance',
835 args={
836 'hot_side': JsonAdapterArgConfig(
837 kind='dict',
838 args={
839 'property_package': JsonAdapterArgConfig(
840 kind='property_package',
841 label='Hot Side',
842 ),
843 'has_pressure_change': JsonAdapterArgConfig(
844 kind='constant',
845 value=False,
846 ),
847 },
848 ),
849 'cold_side': JsonAdapterArgConfig(
850 kind='dict',
851 args={
852 'property_package': JsonAdapterArgConfig(
853 kind='property_package',
854 label='Cold Side',
855 ),
856 'has_pressure_change': JsonAdapterArgConfig(
857 kind='constant',
858 value=False,
859 ),
860 },
861 ),
862 'dynamic_heat_balance': JsonAdapterArgConfig(
863 kind='constant',
864 value=False,
865 ),
866 },
867 ports=JsonPortAdapterConfig(
868 kind='port_map',
869 mapping={
870 'cold_side_inlet': JsonPortMapEntryConfig(
871 port='coldInlet',
872 inlet=True,
873 ),
874 'hot_side_inlet': JsonPortMapEntryConfig(
875 port='hotInlet',
876 inlet=True,
877 ),
878 'cold_side_outlet': JsonPortMapEntryConfig(
879 port='coldOutlet',
880 inlet=False,
881 ),
882 'hot_side_outlet': JsonPortMapEntryConfig(
883 port='hotOutlet',
884 inlet=False,
885 ),
886 },
887 ),
888 properties=None,
889 ),
890 frontend=JsonFrontendConfig(
891 showInPanel=False,
892 variant=JsonVariantConfig(
893 familyKey='heat_exchanger',
894 label='Lumped Capacitance Heat Exchanger',
895 selectorLabel='Heat Exchanger Type',
896 default=False,
897 order=20,
898 preservePorts=True,
899 preserveGraphic=True,
900 ),
901 ),
902 ),
903JsonUnitOpConfig(
904 key='plate_heat_exchanger',
905 objectType='plate_heat_exchanger',
906 enumMember='PlateHeatExchanger',
907 displayType='Plate Heat Exchanger',
908 displayName='Plate Heat Exchanger',
909 categoryPath=['chemical', 'heating_and_cooling'],
910 ports={
911 'coldInlet': JsonPortConfig(
912 displayName='Cold Inlet',
913 type='inlet',
914 streamType='stream',
915 many=False,
916 default=1,
917 minimum=1,
918 makeStream=True,
919 streamOffset=1,
920 streamName='CS',
921 ),
922 'hotInlet': JsonPortConfig(
923 displayName='Hot Inlet',
924 type='inlet',
925 streamType='stream',
926 many=False,
927 default=1,
928 minimum=1,
929 makeStream=True,
930 streamOffset=1,
931 streamName='HS',
932 ),
933 'coldOutlet': JsonPortConfig(
934 displayName='Cold Outlet',
935 type='outlet',
936 streamType='stream',
937 many=False,
938 default=1,
939 minimum=1,
940 makeStream=True,
941 streamOffset=1,
942 streamName='CS',
943 ),
944 'hotOutlet': JsonPortConfig(
945 displayName='Hot Outlet',
946 type='outlet',
947 streamType='stream',
948 many=False,
949 default=1,
950 minimum=1,
951 makeStream=True,
952 streamOffset=1,
953 streamName='HS',
954 ),
955 },
956 propertyPackagePorts={
957 'Cold Side': ['coldInlet', 'coldOutlet'],
958 'Hot Side': ['hotInlet', 'hotOutlet'],
959 },
960 graphicObject=JsonGraphicObjectConfig(
961 kind='unitop_graphic',
962 ),
963 indexSets=[],
964 properties={
965 'area': JsonPropertyConfig(
966 propertySetGroup='default',
967 displayName='Heat Exchange Area',
968 indexSets=None,
969 sumToOne=False,
970 value=None,
971 unit=None,
972 unitType='area',
973 description=None,
974 type='numeric',
975 many=False,
976 default=1,
977 options={},
978 hasTimeIndex=False,
979 ),
980 'plate_length': JsonPropertyConfig(
981 propertySetGroup='default',
982 displayName='Plate Length',
983 indexSets=None,
984 sumToOne=False,
985 value=None,
986 unit=None,
987 unitType='distance',
988 description=None,
989 type='numeric',
990 many=False,
991 default=1,
992 options={},
993 hasTimeIndex=True,
994 ),
995 'plate_width': JsonPropertyConfig(
996 propertySetGroup='default',
997 displayName='Plate Width',
998 indexSets=None,
999 sumToOne=False,
1000 value=None,
1001 unit=None,
1002 unitType='distance',
1003 description=None,
1004 type='numeric',
1005 many=False,
1006 default=1,
1007 options={},
1008 hasTimeIndex=True,
1009 ),
1010 'plate_thickness': JsonPropertyConfig(
1011 propertySetGroup='default',
1012 displayName='Plate Thickness',
1013 indexSets=None,
1014 sumToOne=False,
1015 value=None,
1016 unit=None,
1017 unitType='distance',
1018 description=None,
1019 type='numeric',
1020 many=False,
1021 default=1,
1022 options={},
1023 hasTimeIndex=True,
1024 ),
1025 'plate_pact_length': JsonPropertyConfig(
1026 propertySetGroup='default',
1027 displayName='Compressed Plate Pact Length',
1028 indexSets=None,
1029 sumToOne=False,
1030 value=None,
1031 unit=None,
1032 unitType='distance',
1033 description=None,
1034 type='numeric',
1035 many=False,
1036 default=1,
1037 options={},
1038 hasTimeIndex=True,
1039 ),
1040 'port_diameter': JsonPropertyConfig(
1041 propertySetGroup='default',
1042 displayName='Port Diameter',
1043 indexSets=None,
1044 sumToOne=False,
1045 value=None,
1046 unit=None,
1047 unitType='diameter',
1048 description=None,
1049 type='numeric',
1050 many=False,
1051 default=1,
1052 options={},
1053 hasTimeIndex=True,
1054 ),
1055 'plate_therm_cond': JsonPropertyConfig(
1056 propertySetGroup='default',
1057 displayName='Plate Thermal Conductivity',
1058 indexSets=None,
1059 sumToOne=False,
1060 value=None,
1061 unit=None,
1062 unitType='thermalConductivity',
1063 description=None,
1064 type='numeric',
1065 many=False,
1066 default=1,
1067 options={},
1068 hasTimeIndex=True,
1069 ),
1070 },
1071 propertySetGroups={
1072 'default': JsonPropertySetGroupConfig(
1073 type='stateVars',
1074 displayName='Properties',
1075 stateVars=[
1076 'area',
1077 'plate_length',
1078 'plate_width',
1079 'plate_thickness',
1080 'plate_pact_length',
1081 'port_diameter',
1082 'plate_therm_cond',
1083 ],
1084 toggle=None,
1085 ),
1086 },
1087 keyProperties=['port_diameter', 'plate_therm_cond'],
1088 splitterFractionName=None,
1089 idaesAdapter=JsonIdaesAdapterConfig(
1090 constructor='idaes.models_extra.column_models.plate_heat_exchanger.PlateHeatExchanger',
1091 args={
1092 'hot_side': JsonAdapterArgConfig(
1093 kind='dict',
1094 args={
1095 'property_package': JsonAdapterArgConfig(
1096 kind='property_package',
1097 label='Hot Side',
1098 ),
1099 'has_pressure_change': JsonAdapterArgConfig(
1100 kind='constant',
1101 value=True,
1102 ),
1103 },
1104 ),
1105 'cold_side': JsonAdapterArgConfig(
1106 kind='dict',
1107 args={
1108 'property_package': JsonAdapterArgConfig(
1109 kind='property_package',
1110 label='Cold Side',
1111 ),
1112 'has_pressure_change': JsonAdapterArgConfig(
1113 kind='constant',
1114 value=True,
1115 ),
1116 },
1117 ),
1118 'dynamic': JsonAdapterArgConfig(
1119 kind='constant',
1120 value=False,
1121 ),
1122 },
1123 ports=JsonPortAdapterConfig(
1124 kind='port_map',
1125 mapping={
1126 'cold_side_inlet': JsonPortMapEntryConfig(
1127 port='coldInlet',
1128 inlet=True,
1129 ),
1130 'hot_side_inlet': JsonPortMapEntryConfig(
1131 port='hotInlet',
1132 inlet=True,
1133 ),
1134 'cold_side_outlet': JsonPortMapEntryConfig(
1135 port='coldOutlet',
1136 inlet=False,
1137 ),
1138 'hot_side_outlet': JsonPortMapEntryConfig(
1139 port='hotOutlet',
1140 inlet=False,
1141 ),
1142 },
1143 ),
1144 properties=None,
1145 ),
1146 frontend=JsonFrontendConfig(
1147 showInPanel=False,
1148 variant=JsonVariantConfig(
1149 familyKey='heat_exchanger',
1150 label='Plate Heat Exchanger',
1151 selectorLabel='Heat Exchanger Type',
1152 default=False,
1153 order=30,
1154 preservePorts=True,
1155 preserveGraphic=True,
1156 ),
1157 ),
1158 ),
1159 )