Ablative config

This commit is contained in:
2026-03-03 20:30:29 +00:00
parent 386f6fe928
commit 585e66ceb4
16 changed files with 713 additions and 71 deletions

View File

@@ -1,6 +1,7 @@
import { useRef, useState, useEffect } from 'react'
import { createPortal } from 'react-dom'
import { useEngineDesign } from '../hooks/useEngineDesign.js'
import { PropellantModal } from '../components/PropellantModal.jsx'
import {
exportEngineJSON,
exportEngineOdt,
@@ -13,6 +14,7 @@ import ErrorBoundary from '../components/ErrorBoundary.jsx'
import { formatValue } from '../engine/format.js'
import { getUnitsForFamily } from '../engine/units.js'
import { ENGINE_FIELD_INFO } from '../engine/engineFieldInfo.js'
import { ABLATIVE_MATERIALS } from '../engine/knowledgebaseData.js'
/* ── Info popup ───────────────────────────────────────────────────── */
@@ -231,6 +233,7 @@ function ResultSection({ title, children }) {
export default function EnginePage() {
const importRef = useRef(null)
const [showPropellants, setShowPropellants] = useState(false)
const {
thermoInputs, setThermoInput,
chamber, setChamber,
@@ -246,6 +249,7 @@ export default function EnginePage() {
coolingResults: cr,
feedResults: fr,
loadDesign,
applyPropellant,
} = useEngineDesign()
function handleExportJSON() {
@@ -320,7 +324,17 @@ export default function EnginePage() {
{/* ── Left: Inputs ── */}
<div className="w-[420px] shrink-0 overflow-y-auto p-4 border-r border-slate-700">
<DesignSection title="Thermodynamic Inputs">
<DesignSection
title="Thermodynamic Inputs"
headerAction={
<button
onClick={() => setShowPropellants(true)}
className="px-2 py-1 text-xs bg-blue-700 hover:bg-blue-600 text-white rounded transition-colors whitespace-nowrap"
>
Load Propellant
</button>
}
>
<NumInput
label="Chamber Pressure (p₀)"
value={thermoInputs.p0}
@@ -577,6 +591,24 @@ export default function EnginePage() {
step="0.01"
/>
)}
{cooling.method === 'ablative' && (
<>
<SelectInput
label="Liner Material"
value={cooling.ablativeMaterial}
onChange={v => setCooling(c => ({ ...c, ablativeMaterial: v }))}
options={ABLATIVE_MATERIALS.map(m => ({ value: m.id, label: m.name }))}
/>
<NumInput
label="Initial Thickness"
value={cooling.ablativeThickness}
onChange={v => setCooling(c => ({ ...c, ablativeThickness: v }))}
units="mm"
step="1"
placeholder="10"
/>
</>
)}
</DesignSection>
<DesignSection title="Feed System">
@@ -694,6 +726,52 @@ export default function EnginePage() {
<ResultRow label="Est. Isp Penalty" value={cr.ispPenalty} unit="%" infoKey="ispPenalty_result" />
</>
)}
{cr.method === 'ablative' && cr.material && (
<>
<div className="text-sm mb-2">
<span className="text-slate-400">Material: </span>
<span className="text-slate-100 font-semibold">{cr.material.name}</span>
</div>
{cr.pressureFactor && Math.abs(cr.pressureFactor - 1.0) > 0.01 && (
<ResultRow label="Pressure Correction" value={cr.pressureFactor} unit="×" />
)}
<ResultRow label="Erosion Rate" value={cr.effectiveRate} unit="mm/s" />
<ResultRow label="Initial Thickness" value={cr.ablativeThickness} unit="mm" />
<div className="flex items-center gap-2 text-sm py-1.5">
<span className="text-slate-400 w-48 shrink-0">Eroded</span>
<span className="font-mono text-green-400">
{formatValue(cr.erosionMm)} mm
</span>
<span className="text-xs text-slate-500">
({formatValue(cr.erosionMmMin)}{formatValue(cr.erosionMmMax)} mm)
</span>
</div>
<div className="flex items-center gap-2 text-sm py-1.5">
<span className="text-slate-400 w-48 shrink-0">Remaining Thickness</span>
<span className={`font-mono font-semibold ${
cr.status === 'critical' ? 'text-red-400' :
cr.status === 'warning' ? 'text-amber-400' :
'text-green-400'
}`}>
{formatValue(cr.remainingMm)} mm
</span>
<span className="text-xs text-slate-500">
(worst: {formatValue(cr.remainingMmWorst)} mm)
</span>
</div>
{(cr.status === 'warning' || cr.status === 'critical') && (
<div className={`text-xs px-3 py-2 rounded mt-2 ${
cr.status === 'critical'
? 'bg-red-900/30 border border-red-700 text-red-200'
: 'bg-amber-900/30 border border-amber-700 text-amber-200'
}`}>
{cr.status === 'critical'
? '🚨 CRITICAL: Liner thickness below safe minimum!'
: '⚠️ WARNING: Liner thickness approaching minimum!'}
</div>
)}
</>
)}
{cr.note && (
<p className="text-xs text-slate-400 italic mt-1">{cr.note}</p>
)}
@@ -720,6 +798,14 @@ export default function EnginePage() {
</ResultSection>
</div>
</div>
{showPropellants && (
<PropellantModal
onClose={() => setShowPropellants(false)}
onApply={applyPropellant}
description="Select a propellant to pre-fill γ, R, T₀, O/F, and densities."
/>
)}
</div>
)
}