Add pinch diagram plot for HeatExchangerNetwork - #255
Open
sarangbhagwat wants to merge 6 commits into
Open
Conversation
HeatExchangerNetwork had no way to visualize the synthesized network. Add hxn_synthesis.plot_pinch_diagram (pure function, returns fig/ax) and a HeatExchangerNetwork.plot_pinch_diagram wrapper that draw the standard diagram: cold streams (blue, left to right) above hot streams (red, right to left) with inlet/outlet T [degC] and H [kJ/hr], one vertical connector with its duty per HXprocess, a dashed pinch line separating cold-side from hot-side exchangers, and circles marking utility exchangers whose duty exceeds Qmin. Design notes: - The hot and cold stream of each exchanger are found from the stream life cycles by identity, not by parsing HX_<i>_<j>_<side> IDs. - Which side of the pinch an exchanger belongs to comes from the new attributes new_HXs_hot_side / new_HXs_cold_side stored in _cost (only their concatenation new_HXs was kept before). - Columns on each side are ordered by a topological sort of the exchangers under the constraint that every stream meets them in its flow direction (hot streams reversed); contradictory constraints fall back to synthesis order. This keeps the diagram readable and deterministic. - Artists carry gids (HX:<ID>, Util:<ID>) so tests can check the drawing structurally. Validation: tests/test_hxn.py gains an ordering unit test (including the hot-stream reversal and the cyclic fallback) and a structural test on the class doctest system; docstring example added. tests/test_hxn.py + hxn doctests: 17 passed. Rendered the doctest system and the full sugarcane network (HXN.units=None) and checked them against the reference figure. Canonical suite: 74 failed / 476 passed / 62 skipped; the 3 failures beyond the previous run (test_oilcane_O6/O8/O9, test_tire_modeling) also fail on the parent commit bbea705 with this change stashed - they come from the sibling biorefineries/thermosteam clone state, not from biosteam. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…am ID Stream indices alone do not say which process stream a row of the pinch diagram is. Add a label next to each index, '<unit> - <auxiliary> (<ID>)', with one toggle per part on plot_pinch_diagram: show_units (the unit that owns the stream's original heat exchanger, i.e. the main unit for auxiliary exchangers), show_auxiliary_units (the exchanger's name within that unit, e.g. 'condenser' or a dotted path for nested auxiliaries), and show_stream_IDs (the original exchanger's inlet stream ID; unnamed inlets add nothing). All default to True. The owner comes from Unit.owner and the auxiliary name from a recursive search of get_auxiliary_units_with_names(), so no unit or stream IDs are parsed (unlike get_original_hxs_associated_with_streams, which scans for '.' in IDs and special-cases unit classes). plot_pinch_diagram takes the per-stream original exchangers as a new original_hxs argument (required when any label part is on); HeatExchangerNetwork.plot_pinch_diagram passes original_heat_exchangers. Labels carry gid 'Label:<index>'. Validation: new test_pinch_diagram_stream_labels (helper composition, figure labels, toggles off -> no labels); tests/test_hxn.py + hxn doctests 18 passed. Rendered the full sugarcane network to check nested auxiliary names. Canonical suite 74 failed / 477 passed / 62 skipped, same pre-existing failure set as the previous commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New show_legend toggle (default True) on plot_pinch_diagram draws a three-column legend below the axes with proxy handles for the six symbols: cold stream, hot stream, process heat exchange, hot utility, cold utility, and the pinch line. Placed with bbox_to_anchor below the axes so it never overlaps the stream rows; savefig already uses bbox_inches='tight'. Validation: new test_pinch_diagram_legend (entries and order; toggle off -> no legend); tests/test_hxn.py + hxn doctests 19 passed; sugarcane network re-rendered. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Code-review fixes for the pinch diagram: - Utility markers were colored by the stream they sit on, so hot utilities (on cold streams) were blue and cold utilities red - the opposite of the reference figure and of the diagram's own legend. Color them by utility type. test_pinch_diagram_legend now checks every Util marker's edge color against the legend handle for its type, so the legend and drawing cannot drift apart again. - Stream labels were drawn at the same zorder as the connectors and ran through the hot-side columns; draw them above with a white box. - Remove the dead empty-life-cycle branch (H = nan), which _format_H could not format anyway: every stream always has its utility stage. - HeatExchangerNetwork.plot_pinch_diagram raises a clear RuntimeError when called before simulation instead of an AttributeError from _get_stream_life_cycles; regression test added. - Close the figure in the docstring example; NumPy-style Returns section; comment documenting the gid contract used by the tests. tests/test_hxn.py + hxn doctests: 20 passed; sugarcane network re-rendered. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…er circles The unit/stream label sat lower than the bold index (both were anchored with va='bottom' at different font sizes) and its white box could cover exchanger and utility circles on the stream line. Put index and label on a shared baseline (va='baseline') raised to yi + 0.25 so both sit in line and clear of the circles. tests/test_hxn.py + hxn doctests: 20 passed; sugarcane network re-rendered. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
With a shared baseline the smaller label still reads slightly low next to the larger bold index; raise its baseline by 0.08 so the two appear vertically centered on each other. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Member
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Adds a way to visualize a synthesized heat exchanger network as a pinch diagram (the standard figure of cold streams above hot streams, exchanger connectors, utilities, and the pinch line).
hxn_synthesis.plot_pinch_diagram(...)— pure plotting function, returns(fig, ax).HeatExchangerNetwork.plot_pinch_diagram(file=None, **kwargs)— thin wrapper.Stacked on #254 (which is stacked on #253); retarget to
masteronce those merge. Only the three commits51a38fba,bd508d76,60fd3303belong to this PR.The diagram
T [°C]andH [kJ/hr]at both ends and the stream index in bold at the inlet.HXprocess, with a boxedΔHlabel; cold-side exchangers left of a dashed pinch line, hot-side exchangers right of it, with shaded "Cold side" / "Hot side" backgrounds.Qmin(red = hot utility on cold streams, blue = cold utility on hot streams).<unit> - <auxiliary> (<stream ID>), e.g.D303 - condenser (vapor), toggled byshow_units,show_auxiliary_units,show_stream_IDs(all defaultTrue).show_legend=True).Design notes
StreamLifeCycles by identity, and the side of the pinch from two new additive attributesHXN.new_HXs_hot_side/new_HXs_cold_sidestored in_cost— no parsing ofHX_<i>_<j>_<side>IDs.Unit.ownerand a recursive search ofget_auxiliary_units_with_names()(works for nested auxiliaries), rather than the ID-scanning inget_original_hxs_associated_with_streams.HX:<ID>,Util:<ID>,Label:<i>) so the tests check the figure structurally.Validation
tests/test_hxn.py: column-ordering unit test (incl. hot-stream reversal and cyclic fallback), structural test on the class doctest system, label composition/toggle test, legend test.tests/test_hxn.py+ hxn doctests: 19 passed. Docstring example added.HXN.units=None) and checked them against a reference pinch figure.pytest . --disable-numba=1 -m "not slow"): 74 failed / 477 passed / 62 skipped. All failures pre-exist on the parent commitbbea7059with this change stashed (baseline items plus siblingbiorefineries/thermosteamclone drift:test_oilcane_O6/O8/O9,test_tire_modeling).🤖 Generated with Claude Code