drzg15's picture
information buttoms
fb19a97
/** HPIChart — GCC with diamond markers for selected heat pumps. */
import Plot from 'react-plotly.js';
import { useAnalysisStore } from '../../store/analysisStore';
import { useUIStore } from '../../store/uiStore';
import ChartHelpButton from '../ui/ChartHelpButton';
const HP_COLORS = [
'#00CC96', '#AB63FA', '#FFA15A', '#19D3F3',
'#FF6692', '#B6E880', '#FF97FF', '#FECB52',
];
export default function HPIChart() {
const hpiResult = useAnalysisStore((s) => s.hpiResult);
const pinchResult = useAnalysisStore((s) => s.pinchResult);
const selectedHPTypes = useAnalysisStore((s) => s.selectedHPTypes);
const theme = useUIStore((s) => s.theme);
const isDark = theme === 'dark';
if (!hpiResult || !pinchResult) return null;
const gccH = hpiResult.gcc_data.H;
const gccT = hpiResult.gcc_data.T;
// GCC segment traces
const traces: any[] = [];
const heatCascade = pinchResult.heat_cascade;
for (let i = 0; i < gccH.length - 1; i++) {
let color = 'gray';
if (i < heatCascade.length) {
const dh = heatCascade[i].deltaH ?? 0;
if (dh > 0) color = 'red';
else if (dh < 0) color = 'blue';
}
traces.push({
x: [gccH[i], gccH[i + 1]],
y: [gccT[i], gccT[i + 1]],
mode: 'lines+markers' as const,
line: { color, width: 2 },
marker: { size: 5 },
showlegend: false,
hovertemplate: `T: %{y:.1f}°C<br>H: %{x:.1f} kW<extra></extra>`,
});
}
// Diamond markers for selected HP types
const feasible = hpiResult.integrations.filter((r) => r.feasible);
const allHPNames = feasible.map((r) => r.hp_type);
const displayed = selectedHPTypes.length > 0
? feasible.filter((r) => selectedHPTypes.includes(r.hp_type))
: feasible.slice(0, 1); // default: first
displayed.forEach((hp) => {
const globalIdx = allHPNames.indexOf(hp.hp_type);
const color = HP_COLORS[globalIdx % HP_COLORS.length];
const hpName = hp.hp_type;
if (hp.source_points.H.length > 0) {
traces.push({
x: hp.source_points.H,
y: hp.source_points.T,
mode: 'markers' as const,
marker: {
size: 12,
color,
symbol: 'diamond',
line: { width: 2, color },
},
name: `${hpName} - Source`,
legendgroup: hpName,
hovertemplate: `<b>${hpName} - Source</b><br>T: %{y:.1f}°C<br>Q: %{x:.1f} kW<br>COP: ${hp.COP?.toFixed(2)}<extra></extra>`,
});
}
if (hp.sink_points.H.length > 0) {
traces.push({
x: hp.sink_points.H,
y: hp.sink_points.T,
mode: 'markers' as const,
marker: {
size: 12,
color,
symbol: 'diamond-open',
line: { width: 2, color },
},
name: `${hpName} - Sink`,
legendgroup: hpName,
hovertemplate: `<b>${hpName} - Sink</b><br>T: %{y:.1f}°C<br>Q: %{x:.1f} kW<br>COP: ${hp.COP?.toFixed(2)}<extra></extra>`,
});
}
});
const layout: any = {
title: { text: 'Heat Pump Integration - Grand Composite Curve', font: { size: 14, color: isDark ? '#F8FAFC' : '#1A1C1E' } },
xaxis: {
title: 'Net ΔH (kW)',
gridcolor: isDark ? '#334155' : '#E2E8F0',
tickfont: { color: isDark ? '#94A3B8' : '#5F6368' },
titlefont: { color: isDark ? '#F8FAFC' : '#1A1C1E' }
},
yaxis: {
title: 'Shifted Temperature (°C)',
rangemode: 'tozero',
gridcolor: isDark ? '#334155' : '#E2E8F0',
tickfont: { color: isDark ? '#94A3B8' : '#5F6368' },
titlefont: { color: isDark ? '#F8FAFC' : '#1A1C1E' }
},
height: 400,
paper_bgcolor: 'rgba(0,0,0,0)',
plot_bgcolor: 'rgba(0,0,0,0)',
margin: { l: 220, r: 20, t: 40, b: 50 },
hovermode: 'closest' as const,
showlegend: true,
legend: {
x: -0.15, y: 1.0,
xanchor: 'right', yanchor: 'top',
font: { size: 10, color: isDark ? '#F8FAFC' : '#1A1C1E' },
itemsizing: 'constant'
},
shapes: [
{
type: 'line',
x0: 0, x1: 1, xref: 'paper',
y0: pinchResult.pinch_temperature,
y1: pinchResult.pinch_temperature,
line: { dash: 'dash', color: 'gray', width: 1 },
},
{
type: 'line',
x0: 0, x1: 0,
y0: 0, y1: 1, yref: 'paper',
line: { color: isDark ? '#94A3B8' : 'black', width: 1 },
opacity: 0.3,
},
],
annotations: [
{
x: 1, xref: 'paper', xanchor: 'right',
y: pinchResult.pinch_temperature,
text: `Pinch: ${pinchResult.pinch_temperature.toFixed(1)}°C`,
showarrow: false,
font: { size: 11, color: isDark ? '#60A5FA' : 'gray' },
},
],
};
return (
<div className="pa-chart" style={{ position: 'relative' }}>
<ChartHelpButton
title="Heat Pump Integration"
description={
<>
<p>Visualizes how a heat pump bridges the temperature gap to lift heat from <strong>below the pinch</strong> (source) to <strong>above the pinch</strong> (sink).</p>
<p>The solid diamonds represent the heat pump source, and hollow diamonds represent the sink.</p>
<p>Heat pumps reduce both external heating and cooling demands simultaneously.</p>
</>
}
/>
<Plot
data={traces}
layout={layout}
config={{ responsive: true }}
style={{ width: '100%' }}
/>
</div>
);
}