Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8c0d860
Draw the chamber the convergent angle actually asks for
Carlsaurus Sep 15, 2026
c6b8fb3
Size orifice Cd from the hole shape instead of guessing it
Carlsaurus Sep 15, 2026
39b74fd
One evaporation model, shared by the kernel and the Python reference
Carlsaurus Sep 15, 2026
e0fb952
Constrain the injector face, and draw it
Carlsaurus Sep 15, 2026
a5345fd
Chug stability on a double time lag, picked by measurement
Carlsaurus Sep 15, 2026
c12d4a3
Stop the API turning a legitimate NaN into a 500
Carlsaurus Sep 15, 2026
e1fc275
Say O/F, not mixture ratio
Carlsaurus Sep 15, 2026
5136429
Bring the shipped configs back in line with the code
Carlsaurus Sep 15, 2026
07a6940
Stop the dev stack racing its own startup banner
Carlsaurus Sep 15, 2026
27f4081
Ignore the native kernel's build output
Carlsaurus Sep 15, 2026
c7ffbea
180 lb vehicle at 8:1, and stop flying the pressurant as propellant
Carlsaurus Sep 16, 2026
3bfb717
Re-cut the 180 lb point at O/F 1.50, and size the COPV from the config
Carlsaurus Sep 16, 2026
08492a8
COPV to 4000 psi, and real component masses instead of allocations
Carlsaurus Sep 16, 2026
30f8bac
24 doublets on a 15 deg pitch, tanks at 10 % ullage, sleeve past the …
Carlsaurus Sep 16, 2026
ba50bed
Spec the COPV off the MSA G1 sheet instead of three disagreeing guesses
Carlsaurus Sep 16, 2026
a4f3b16
Forward Mode shows the whole engine, and fix the root locus layout
Carlsaurus Sep 16, 2026
0761670
A tee rides its run, a line can be routed by hand, and crossings hop
Carlsaurus Sep 20, 2026
a1ad5b3
The dot moves, the ring connects, and a line can be pulled out of a line
Carlsaurus Sep 20, 2026
8386d88
Phase 6 notes, and a dev proxy that follows the API port override
Carlsaurus Sep 20, 2026
52c89f1
A line's faces are chosen by the route they make, and a tee routes as…
Carlsaurus Sep 20, 2026
6cfcd55
Stability reports the rate-limiting stream, and Forward Mode forgets …
Carlsaurus Sep 20, 2026
13db5e1
Merge geometry-fixes: the engine work that continued after #72
Carlsaurus Sep 20, 2026
db25bb7
The ambient-pressure test runs against a shipped config
Carlsaurus Sep 20, 2026
770d7da
The seed test says how many evaluations the search spent
Carlsaurus Sep 20, 2026
acc1931
The seed test judges the candidates the search samples, not where it …
Carlsaurus Sep 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion EngineDesign/backend/routers/evaluate.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
class StabilityOverrides(BaseModel):
"""Optional forward-mode knobs for rich stability re-evaluation."""
eta_inj_O: float | None = Field(default=None, gt=0, le=0.6, description="Oxidizer ΔP_inj/Pc")
smd_um: float | None = Field(default=None, gt=0, le=200, description="Oxidizer spray SMD [µm]")
smd_um: float | None = Field(default=None, gt=0, le=400, description="Oxidizer spray SMD [µm]")
smd_F_um: float | None = Field(default=None, gt=0, le=400, description="Fuel spray SMD [µm]")
eta_inj_F: float | None = Field(default=None, gt=0, le=0.6, description="Fuel ΔP_inj/Pc")
n_interaction: float | None = Field(default=None, gt=0, le=2, description="Combustion interaction index n")
chi_acoustic: float | None = Field(default=None, gt=0, le=1, description="Acoustic sensitive-fraction χ")
time_lag_model: Literal["leonardi_dtl", "d2_law"] | None = Field(
Expand Down
61 changes: 42 additions & 19 deletions EngineDesign/backend/routers/optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,43 @@ def layer1_design_is_valid(results: Dict[str, Any]) -> tuple[bool, list]:
if (key.endswith("_check_passed") or key.endswith("_gate_passed")) and passed is False:
reasons.append(f"{key} = False")
return (not reasons), reasons


def merge_design_requirements(
old: Optional[Dict[str, Any]], incoming: Dict[str, Any]
) -> Dict[str, Any]:
"""Lay a (possibly partial) requirements payload over the requirements already loaded.

A key ABSENT from ``incoming`` keeps its current value. A key sent as an explicit
``None`` is cleared. Those are different intents -- a form that only knows a dozen
fields must not, by not mentioning them, reset the ~100 ``layer1_*`` knobs, the injector
face limits and the pinned seed to schema defaults. It did: the save route rebuilt
``design_requirements`` from the payload alone, and the next run optimised a different
problem than the one the file described. ``frozen_parameters`` merges key by key with the
same null-clears rule, as it already did.
"""
merged: Dict[str, Any] = dict(old or {})
prev_fp = merged.get("frozen_parameters")
old_fp: Dict[str, Any] = (
{k: v for k, v in prev_fp.items() if v is not None} if isinstance(prev_fp, dict) else {}
)
for key, value in incoming.items():
if key != "frozen_parameters":
merged[key] = value
if "frozen_parameters" in incoming:
new_fp = incoming.get("frozen_parameters")
fp = dict(old_fp)
for k, v in (new_fp.items() if isinstance(new_fp, dict) else ()):
if v is None:
fp.pop(k, None)
else:
fp[k] = v
merged["frozen_parameters"] = fp if fp else None
elif old_fp:
merged["frozen_parameters"] = old_fp
elif "frozen_parameters" in merged:
merged["frozen_parameters"] = None
return merged
from engine.pipeline.config_schemas import DesignRequirementsConfig
from engine.optimizer.layers.layer1_static_optimization import run_layer1_optimization
from engine.optimizer.layers.layer2_pressure import run_layer2_pressure
Expand Down Expand Up @@ -132,26 +169,12 @@ async def save_design_requirements(
)

try:
# Merge frozen_parameters so a partial UI payload cannot silently drop YAML pins.
req_in = dict(request.requirements)
# Overlay the payload on what is loaded; a partial payload must not reset the rest.
old_dr = session.app_state.config.design_requirements
old_fp: dict = {}
if old_dr is not None and old_dr.frozen_parameters is not None:
old_fp = old_dr.frozen_parameters.model_dump(exclude_none=True)
if "frozen_parameters" not in req_in:
if old_fp:
req_in["frozen_parameters"] = old_fp
else:
new_fp = req_in.get("frozen_parameters")
if not isinstance(new_fp, dict):
new_fp = {}
merged_fp = dict(old_fp)
for k, v in new_fp.items():
if v is None:
merged_fp.pop(k, None)
else:
merged_fp[k] = v
req_in["frozen_parameters"] = merged_fp if merged_fp else None
req_in = merge_design_requirements(
old_dr.model_dump() if old_dr is not None else None,
dict(request.requirements),
)

# Validate requirements using Pydantic
requirements = DesignRequirementsConfig(**req_in)
Expand Down
11 changes: 8 additions & 3 deletions EngineDesign/configs/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,14 @@ fluids:
specific_heat: 2300.0
thermal_conductivity: 0.15
temperature: 90.0
latent_heat: null
boiling_point: null
molecular_weight: null
# Stability-only inputs (droplet vaporization lag); the forward performance path does not read
# them, so filling them in cannot move thrust/Isp or the golden anchors. Left null, the chug
# model silently substituted these same handbook numbers on every evaluation of the default
# config and reported three fallbacks it should never have needed.
latent_heat: 213000.0 # J/kg, NIST oxygen at 1 atm
boiling_point: 90.19 # K, NIST oxygen at 1 atm
molecular_weight: 32.0 # g/mol
bulk_modulus_pa: 1500000000.0 # order-of-magnitude LOX; refine via water-hammer test T5
injector:
type: impinging
geometry:
Expand Down
Loading
Loading