HeatTransPlan / frontend /src /components /analysis /CompositeCurvesChart.tsx
drzg15's picture
information buttoms
fb19a97
/** CompositeCurvesChart — hot/cold composite curves via Plotly. */
import Plot from 'react-plotly.js';
import { useAnalysisStore } from '../../store/analysisStore';
import { useUIStore } from '../../store/uiStore';
import ChartHelpButton from '../ui/ChartHelpButton';
export default function CompositeCurvesChart() {
const pinchResult = useAnalysisStore((s) => s.pinchResult);
const showShifted = useAnalysisStore((s) => s.showShifted);
const theme = useUIStore((s) => s.theme);
const isDark = theme === 'dark';
if (!pinchResult) return null;
const diagram = showShifted
? pinchResult.shifted_composite_diagram
: pinchResult.composite_diagram;
const titleText = showShifted ? 'Shifted Composite Curves' : 'Composite Curves';
const traces: any[] = [
{
x: diagram.hot.H,
y: diagram.hot.T,
mode: 'lines+markers' as const,
name: 'Hot streams (Heat sources)',
line: { color: 'red', width: 2 },
marker: { size: 6 },
},
{
x: diagram.cold.H,
y: diagram.cold.T,
mode: 'lines+markers' as const,
name: 'Cold streams (Heat sinks)',
line: { color: 'blue', width: 2 },
marker: { size: 6 },
},
];
const layout: any = {
title: { text: titleText, font: { size: 14, color: isDark ? '#F8FAFC' : '#1A1C1E' } },
xaxis: {
title: 'Enthalpy H (kW)',
rangemode: 'tozero',
gridcolor: isDark ? '#334155' : '#E2E8F0',
tickfont: { color: isDark ? '#94A3B8' : '#5F6368' },
titlefont: { color: isDark ? '#F8FAFC' : '#1A1C1E' }
},
yaxis: {
title: 'Temperature T (°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: 60, r: 20, t: 40, b: 50 },
legend: {
x: 0.01, y: 0.99,
xanchor: 'left', yanchor: 'top',
font: { color: isDark ? '#F8FAFC' : '#1A1C1E' }
},
hovermode: 'closest' as const,
shapes: [
{
type: 'line',
x0: 0, x1: 1, xref: 'paper',
y0: pinchResult.pinch_temperature,
y1: pinchResult.pinch_temperature,
line: { dash: 'dash', color: 'gray', width: 1 },
},
],
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={titleText}
description={
<>
<p>The <strong>horizontal overlap</strong> between the Hot (Red) and Cold (Blue) lines represents the maximum heat that can be recovered internally.</p>
<p>The remaining gaps on the left and right show the external heating and cooling utility we still need to provide.</p>
<p>The vertical distance between the curves represents the temperature difference driving the heat transfer.</p>
</>
}
/>
<Plot
data={traces}
layout={layout}
config={{ responsive: true }}
style={{ width: '100%' }}
/>
</div>
);
}