Everything current: pid-designer connections, the engine work after #72, feed-twin - #74
Open
Carlsaurus wants to merge 25 commits into
Open
Carlsaurus wants to merge 25 commits into
Carlsaurus wants to merge 25 commits into
Conversation
The contraction half-angle was a real optimizer variable but three places had 45
degrees baked in, so the chamber being scored was not the chamber being drawn:
- generate_nozzle pinned the entrance arc sweep at -135 deg, which puts a 15 deg
corner at the throat for any theta other than 45
- solved_chamber_plot (the Geometry tab's fast path) is a second copy of the
contour and had its own 45
- /api/geometry never passed theta at all
chamber_length_calc also credited a straight frustum all the way down to the
throat, but the cone stops at the tangency radius of the 1.5*R_t entrance arc and
that arc carries its own volume. Chambers came out 3.7-10.2% over the commanded
L*. Adds entrance_arc_volume and the r_tan = R_t(1 + 1.5(1 - cos theta)) term;
drawn L* now matches commanded to 0.000% at every theta and L* tested.
The existing volume test integrated a synthetic cylinder-plus-cone, which is the
same convention the arithmetic got wrong, so it could never have caught this. It
now integrates the contour the generator actually produces.
Cd was a free number we kept picking, and the chamber closure loop was also shrinking it 0.95x per spray violation -- wrong-signed, never converged, and it had walked an 8 kN design down to an effective 0.468 (0.6055 * 0.95^5). That is a 23% orifice-sizing error, so the holes were being drilled to fit a coefficient nobody measured. Cd is a property of the inlet you machine, so it is now derived from it: sharp drilled 0.80 (L/d 2-5, the jet reattaches) chamfered 0.84 rounded r/d 0.1 0.88 bellmouth r/d >=0.2 0.95 with cd_from_inlet_radius_ratio interpolating between them and cd_length_factor handling short and long lands (L/d -> 0 recovers the 0.61 thin-plate value, past 5 it falls off with friction). Lichtarowicz/Duggins/Markland for the L/d shape, Nurick for the inlet rounding. This makes Cd a per-orifice knob: fillet one side's inlet and it flows more for the same hole, which is a real lever on the momentum balance. Closure factor defaults to 1.0. Anything that wants the old behaviour has to ask for it explicitly.
The numba kernel used the legacy K*D32^2 while the Python path used the derived Spalding form, so the accelerated and reference solves were running different physics -- 9.7% apart on thrust for the same config. The A/B parity suite compares thrust, Pc and MR on configs where both happened to agree, so it passed. _tau_evap is now the single definition and all three call sites use it: the impinging solve, the pintle solve, and _spray_length_frac. Verified identical across NUMBA_DISABLE_JIT=1. _discharge() resolves the inlet-geometry Cd up front and sets use_geometry_cd=0, so the new Cd model cannot open a second Python/kernel split the same way.
Where the doublet ring pair sat radially was an exactly flat direction in the
Layer 1 objective. _layer1_derive_fuel_spacing solves the fuel pitch so the
standoff hits its target, and it does that by fixing the ring GAP -- so dr does
not depend on s_O, the pair slides radially at zero cost, and s_O fell onto its
own lower search bound.
The 8 kN design that came out of that passed all seven gates and could not be
built:
D_pitch_O 26.90 mm, so all 28 doublets impinged on a 41.79 mm circle inside a
127.00 mm bore -- narrower than the 49.57 mm throat, feeding 10.8% of the
chamber area. 1.44 mm LOX web. 24.83 mm clear at the axis against the 27.15 a
3/8-18 NPT igniter boss needs. Fuel at 69 deg from the axis: 35.4 mm of
passage through a 12.7 mm plate at 21 deg of incidence.
Six requirement keys, all defaulting to inert:
layer1_injector_center_clear_dia_m igniter boss / centre port
layer1_injector_min_web_m spacing >= d_jet alone allows zero land
layer1_injector_wall_clearance_m manifold land, room for a barrier row
layer1_injector_spray_radius_frac where the impingement circle sits
layer1_injector_min_face_incidence_deg drill entry angle, caps theta
layer1_injector_plate_thickness_m passage geometry
Radial extent is measured on the orifice's elliptical face trace (major axis
d/cos theta, 2.79x the drill at 69 deg), not the drill diameter.
Three things this took to actually hold:
- A hard block wired into one path is not a hard block.
_impinging_hard_geometry_blocks_eval only ran in the serial loop, not in
_compute_objective_value where the parallel workers score everything. The
limits with a search bound behind them held; the spray-radius band, which
has none, did not -- 2 of 3 seeds converged outside their own declared band
reporting ALL GATES PASS.
- A binary block in the worker is worse than useless. infeasibility_score += 1
puts the whole violating region on one flat plateau and CMA cannot descend
it; 2 of 3 seeds then never found the feasible set at all. Graded squared
misses instead (_impinging_face_infeasibility_terms).
- The tilt allowance was a constant while r_imp is an output. It is now
derivable from the geometry: the fan is only a hazard if it reaches the
liner, so the limit is atan((r_wall - r_imp)/(margin * L_chamber)).
Reported as resultant_tilt_breakeven_deg either way.
Geometry tab gets two engineering views of the element: a face view with the
pitch circles, the reserved centre circle, the impingement circle and the
elliptical hole traces, and a section through one doublet with the jets, the
standoff and the drilled passage broken into counterbore plus orifice land. The
injector panel reads the live backend config rather than the App-level prop --
after a Layer 1 run the chamber panel showed the optimised engine while the
injector above it still drew the pre-run ring.
scripts/design_audit.py re-checks every declared limit with no gate slack
forgiven (the tilt gate forgives 1.0 deg by default, the O/F gate 15%; three
candidates passed every gate while exceeding their own declared tilt allowance).
scripts/design_robustness.py sweeps Cd, eta_c* and throat growth -- and reports
the Cd it achieved, because inlet_geometry overrides Cd_inf and a sweep that
only sets Cd_inf silently does nothing.
configs/ethalox_8kN_SHIP.yaml is the 8 kN point: 8000.3 N, O/F 1.6576, Isp
238.71, 27 doublets at 40/50 deg, 127.000 mm bore, clean on the audit.
The chug verdict was riding on a d2-law vaporization lag. Against the test data the Leonardi 2017 double-time-lag form is 6.3x closer, so that is the default now and the d2 law stays available as time_lag_model: d2_law. Convection is off -- turning it on made it worse, not better, on the same data. Adds a root-locus view so the margin is something you can look at rather than a single number, and the glossary explains which lag produced the answer. docs/stability/chug-double-time-lag.md has the comparison.
NaN and Inf are legal model outputs -- a lag model that does not define K_v, a margin that is unbounded -- and the serializer was coercing them through a path that json.dumps then refused, so a valid run came back as a server error instead of a plot with a gap in it. They now serialize as null, which is what the charts already handle. np.integer was being caught by the same branch as np.floating and did not need to be; splitting them keeps integer ids exact. evaluate.py also takes the stability overrides (smd_um, time_lag_model) so the panel can ask "what would this look like on the other lag model" without editing the config.
Display strings only. The MR keys stay as they are -- they are a wire contract between the API, the CSV exports and the configs, and renaming them would break every saved file.
W_MOM was 30000 in default.yaml and 120000 in the 7000N doublet against a code default of 75. Those weights date from when the momentum-flux ratio R was the hard ablative guard; that guard was replaced by the spray resultant-tilt gate and the weights were never brought down, so R was effectively a hard constraint again -- and it outranked the actual O/F requirement (W_OF 20000). Measured on the 8 kN ethalox point: W_MOM 30000 gives O/F 2.077, Isp 230.5, of_check FALSE; W_MOM 1 gives O/F 1.6503, Isp 237.9, all seven gates pass. Every new design inherits default.yaml, so this was reaching designs that had nothing to do with the original ablative question. Also records critical_temperature on each propellant -- the chug time lag needs it and was falling back to a per-fluid assumption.
dev.sh printed the URLs the instant tmux had spawned the panes. Vite serves in about a second but importing backend.main takes several (numba warm plus the router graph), so opening the app inside that window hit a dead API. Wait for every declared port to accept a connection before printing. The app also probed /api/health exactly once on mount and gave up, so a reload in that same window left it stuck on "disconnected" until you reloaded again. It retries now. ConfigEditor gets hover text for the geometry knobs added with the chamber volume fix. All three default to null, which is the pre-fix behaviour, so a config that leaves them blank optimises as if the fix were not there -- worth saying out loud in the UI rather than in a commit message nobody reads.
`!engine/**` un-ignores everything under engine/, so `git add engine/` swept 607 CMake artifacts -- object files, generated makefiles, compiler probes -- into the index. The C sources are not in those directories, and no tracked file is affected by this.
New design point sized to three hard constraints, all exact: wet mass 81.6466 kg = 180.000 lb thrust 6405.4 N = 1440.0 lbf, so T/W = 8.0000 : 1 liquid propellant 11.000 L + COPV 5.000 L = 16.000 L With volume capped instead of mass, the objective is density impulse rho*Isp, not Isp, and with wet mass also capped dv = Isp*g0*ln(m_wet/(m_wet - m_prop)) where m_prop = rho_bulk*11 L. Swept O/F 1.30-2.50: the dv peak is at 1.85, not at the Isp peak of 1.675 -- but it is worth 0.45 %, and staying at the required 1.65 costs 0.62 % while saving 40 K of chamber temperature. The optimum is flat; 1.65 stands. Apogee 12765 ft at the modelled eta_c* 0.9504, 11500 ft at 0.8935. The modelled value is optimistic against the 0.87 published comparable, so expect the middle of that range. Both ends are inside the 4000-13000 ft window. ui/flight_sim.py drained the entire COPV over the burn (mdot = m_pressurant / burn_time) and handed it to a RocketPy tank as liquid_mass_flow_rate_out, which subtracts it from vehicle mass -- so the pressurant was being flown as propellant. It is not: it moves from the COPV into the ullage the departing propellant leaves behind and is still on board at burnout. The old line's own comment said "flows out to propellant tanks", which is the reason it must not be expelled. Measured A/B on this vehicle: apogee 12522 -> 12339 ft, so the bug inflated it 1.5 %. The ideal rocket equation says 51 m/s of phantom dv; the trajectory only sees a fraction of that because the mass comes off gradually and the flight is drag-dominated at Mach 0.87. Worse than the bias, draining the tank to exactly -0.000 kg made RocketPy raise outright, so the sim failed on most points of an efficiency sweep rather than answering wrongly. Only configs that actually set press_tank.initial_gas_mass were affected; leaving it unset modelled no pressurant at all. design_audit.py had 8000 N and O/F 1.65 hardcoded, so every 6405 N candidate reported FAILS: thrust. Targets now come from the config being audited.
Same three constraints, still exact: 180.000 lb wet, 8.0001:1, 11.000 L liquid + 5.000 L COPV = 16.000 L. RocketPy independently reports "Initial T/W ratio: 8.000" off its own mass model. O/F 1.5103, Pc 433.96 psia, Isp 237.11 s, 27 doublets at 41/43 deg, burn 3.854 s, impulse 24686 N.s, apogee 12404 ft modelled / 11170 ft at eta_c* 0.893. What 1.65 -> 1.50 costs and buys, measured both ways: Isp 238.72 -> 237.11 s impulse -1.6 % apogee 12767 -> 12404 ft -363 ft, -2.8 % Tc 3303 -> 3226 K -77 K q_throat 20.56 -> 19.20 MW/m2 -6.6 % (Bartz) included 88 -> 84 deg more face-heating margin fuel drill L/d 11.07 -> 9.08 counterbore no longer required The heat-flux gain is 6.6 %, not the 2.3 % the Tc drop alone implies: c* RISES 0.19 % moving off the peak and h_g ~ (Pc/c*)^0.8, so h_g falls 3.0 % while (T_aw - T_wall) falls 3.7 %. Both terms are doing work. Two more flight-sim defects, both found by specifying a 5 L COPV honestly: Fluid(name="GN2_COPV", density=200) was a number with nothing behind it. Real GN2 at 4500 psi / 293 K is 310 kg/m3 (CoolProp, Z = 1.150), so 200 under-states a charged bottle by 35 % and caps 5 L at 1.0 kg. A 5 L COPV holds 1.551 kg and RocketPy rejected the tank outright as overfilled. Volume now comes from free_volume_L -- the number the operator actually specifies, and the one a propellant-volume budget counts -- and density is mass/volume, which assumes nothing about fill pressure or gas species. initial_gas_mass=0.01 was then added ALONGSIDE the liquid, so the tank held m_pressurant + 0.01 kg: 0.0050322 m3 in a 0.0050000 m3 bottle, and RocketPy raised "Input Function image must be within the domain". The stub now comes out of the pressurant mass instead of on top of it, and the density carries 0.1 % of solver ullage so a tank filled to exactly its own volume does not fail on float equality. Mass is conserved exactly in both.
COPV fill 4500 -> 4000 psi: 1.551 -> 1.421 kg of GN2 on board (284.1 kg/m3, CoolProp Z = 1.116). Deliverable gas still 1.189 kg against the 0.638 kg the tanks swallow, 1.86x, and the bottle ends the burn near 2000 psi, so the regulator still holds. The 0.29 lb goes back into the structure budget. Mass model now carries hand-calculated component masses rather than the earlier allocations. Tanks at 9 lb each as specified; engine 18.58 lb from geometry (steel sleeve 7.06, injector 3.34, flanges 3.18, ablative liner 2.63, nozzle 1.61, bosses 0.55, graphite 0.21); feed dry 36.74 lb. Wet still closes at 180.000 lb and T/W at 8.0001:1, and since m_wet and m_prop are both unchanged the apogee is unchanged at 12404 ft modelled. Two findings recorded in the header: A bare aluminium nozzle does not survive this engine. Lumped heat sink with Bartz falling off as (At/A)^0.9 from 19.2 MW/m2 over a 3.854 s burn puts a 5 mm wall at 1818 K where the graphite insert ends and 1067 K at the exit, even after discounting Bartz to 0.6x, against a 933 K melt. Holding 400 K of rise needs 16-32 mm, and a wall that thick is not lumped anyway. Carrying 12 mm of ablative inside a 3 mm aluminium shell instead: +0.96 lb over the aluminium that does not work, against a 4.6-7.7 mm recession before blowing credit. The 6.5 in OD was never optimised for 6405 N -- frozen_parameters pins it, and it came from the 8 kN vehicle. 4.5 in bore is equally legal on Dc/Dt but comes out HEAVIER (14.08 vs 13.79 lb): L* is fixed at 1.0 m, so a narrower bore needs a longer barrel and the extra sleeve costs more than the diameter saves. The lever on chamber mass is L*, not diameter.
…face n_doublets pinned to 24 so the angular pitch is 360/24 = 15.000 deg exactly, instead of the 13.333 deg a 27-element ring was asking the machinist for. Ran the three clean divisors under the 30 cap; all pass every gate and audit CLEAN: n=20 (18 deg) O/F 1.4497 Isp 235.82 O/F 3.4 % low n=24 (15 deg) O/F 1.5045 Isp 236.99 on target <- shipped n=30 (12 deg) O/F 1.5138 Isp 237.18 more holes for +0.08 % Everything else holds exactly: 180.0000 lb wet, T/W 8.0000:1, liquid 11.000 L + COPV 5.000 L = 16.000 L, O/F 1.50, 4000 psi COPV, aluminium injector. Tanks now declare 0.90 fill (the 10 % ullage asked for): 6.2255 L LOX and 5.9967 L fuel, 1.6446 and 1.5842 US gal. RocketPy's tank model refuses to run above ~0.80, so the apogee numbers came off a larger envelope at identical propellant mass -- verified insensitive, 0.80/0.75/0.70 all return 12427 ft, because the trajectory depends on mass and not on how much empty tank surrounds it. Sleeve now runs 40 mm past the gas boundary so the injector plugs into it: 168.94 mm long against a 128.94 mm chamber. That also means the injector is sized to the sleeve BORE (O152.4) rather than the engine OD (O165.1), which takes more mass off the injector than the longer sleeve adds to the case. Engine 20.28 lb. Apogee 12427 ft modelled, 11192 ft at eta_c* 0.893. Airframe + recovery budget lands at 96.38 lb.
45 ft3 / 4500 psi 30-minute standard. The sheet quotes 10.40 lb FULL and that is full of breathing AIR, not oxygen, so the charge backs out cleanly: 3.37 lb of air at 1 atm / 70 F leaves 7.03 lb (3.188 kg) dry, in 4.619 L of water volume derived from charge mass over real air density at service pressure. That replaces 2.969 kg at 4.5 L in eight EngineDesign configs (no source field at all), 3.500 kg at 4.687 L in the feed-twin drawing (tagged estimated, reference literally said "weigh it"), and the 3.300 kg at 5.000 L this design was carrying. On kg/L those were -4.4 %, +8.2 % and -4.4 % against the real part. The real bottle is SMALLER than the assumed 5 L, so 0.381 L comes back into the propellant budget: liquid 11.000 -> 11.381 L, 10.646 -> 11.014 kg, and the COPV is 0.49 lb lighter installed. Constraints still exact: 180.0000 lb wet, 8.0000:1, liquid + COPV = 16.0000 L. Seed selection now on total impulse rather than Isp. The shipped seed delivers O/F 1.4993 against a 1.5000 load and strands 3 g; two seeds with higher Isp (237.15, 237.22) delivered 1.511-1.513, stranded 33-39 g of fuel, and produced LESS total impulse. Isp is not the figure of merit when the load ratio is fixed. Apogee 13208 ft at the modelled eta_c* 0.9499, which is 208 ft OVER the 13000 ft ceiling. It only gets there if the engine makes 0.95 against a 0.87 published comparable; the realistic 0.88-0.91 band lands 11900-12400 ft. De-loading 0.099 kg (tanks to 89.2 % instead of 90 %) puts the modelled nominal on 13000 exactly -- a fill-level call on the pad, not a redesign.
/api/evaluate already returns 61 diagnostic keys and ResultsDisplay rendered a
handful of them, so Forward Mode showed a strictly smaller picture of the same
engine than Layer 1 did. Nothing was missing from the backend -- it was a display
gap. Two new sections, no duplicates of what was already there:
Injector & Spray -- effective SMD (same mass-flux blend Layer 1 uses,
MR/(1+MR)*D32_O + 1/(1+MR)*D32_F), per-stream SMD, impingement angle, momentum
ratio R, effective injector area / A_throat (same Cd*A_geom definition Layer 1
computes), x*, jet-to-jet relative velocity, Cd and d_jet per stream, Weber
numbers, bulk velocities, element counts, spray quality.
Injector Geometry -- pitch circles, ring offset, standoff and L/d, impingement
circle and the chamber area it feeds, webs, centre clear, wall land, face
incidence. The solver does not return these; they are derived with the SAME
deriveInjectorLayout the Chamber Geometry drawing uses rather than a second copy
of the arithmetic.
Root locus had four layout defects, all measured off the rendered SVG:
- the zeta ray labels were placed at pad.t - 3, i.e. ABOVE the plot frame; they
floated in the gap under the subtitle, detached from the rays they name
- "sigma = 0" sat on the top frame line and fought those labels for the same
few pixels; moved to the bottom of the boundary, which is empty
- the start and end eta labels overlapped the zeta=0.5 label by 7.5 x 7.1 px;
split vertically, start below its point and end above
- the rotated Hz axis title laid out from x=283.8 to x=346.2 against a 320-wide
viewBox and was clipped; frame widened to 348 with r=54
And the real problem underneath them: yMin was pinned to 0 while this locus lives
at omega 400-460 rad/s, so the data occupied the top 11 % of the frame and a
shallow arc read as a flat line. Omega now zooms to the data, dropping to 0 only
when the data goes near it, with the zeta rays clipped to the axes since they
start off-frame.
The geometry behind the new connection tool, with no UI in it. A junction rides the run between the two things its two run lines connect, found from the drawing every time rather than remembered: a fixed fraction of the way along, put back there when either end moves, kept in place when only the routing changed, and slid along the run when dragged, because along the run is the only place it can be. Its lines are re-pointed at the faces the run actually uses there, and each half is handed the run's own corners on its side, so the two halves draw the run. A part put into one half shortens the run; nothing has to be told. A run can carry the corners a person put on it. `routeThrough` honours them exactly and still leaves each port along the port's own axis; a corner dragged behind its port steps across first rather than turning round through the symbol. `dragSegment` moves one segment across its axis and keeps both ends where they are, adding a stub and a corner at a port; `jogSegment` puts a detour in. Where a vertical line crosses a horizontal one it hops, in its own path, so the drawing states the distinction the code has always kept: crossing is not joining. Lines publish their corners to one small store and read each other's. A valve, a regulator or a disconnect goes *into* a line: the run breaks around it, turned to face the way the run goes, and deleting it heals the run, exactly as a mid-line tee does. Ports anchor where React Flow anchors them -- the handle's outer edge in its facing direction, not its centre; the centre was a three-pixel kink in every run a tee was put back on. A tee is never seated on a port nothing has measured: the fallback for one picks the face nearest the tee, which made the seat depend on itself.
The interaction layer over the new geometry. A tee's four ports covered the whole of the visible dot, so pressing on it drew a line and moving it meant finding an invisible halo. The ports still anchor lines but take no pointer: the dot drags (and slides along its run), and a dashed ring on hover pulls a new line out. A tee with one line on it is an open end, drawn hollow. Press anywhere on a line and pull, and you are drawing a branch: the dot riding the pointer is where the tee goes, and letting go on a port, a symbol, another line, a tee or empty canvas is what it joins. A press that does not move is a click. Alt-click, or the Junction tool, puts a bare tee in, and the tool is one-shot. A port drag let go on nothing leaves an open end. Every segment has a grip; drag it and the segment moves across, the ends stay on their ports, Alt-drag puts a detour in, and double-click routes the line automatically again. Drop a valve on a line and it goes into the line. Delete it and the line heals. Tees are re-seated after every change, once React Flow has measured the nodes, and never more than thirty times a second: a feedback that will not settle stops and says so rather than taking the page down.
The plan document gains a Phase 6 section describing the connection model and what is deliberately left out. The Vite proxy target follows PID_DESIGNER_API_PORT, the same override dev.sh already honours for the API, so a second checkout can run its own pair of servers.
… a tee Pulling from a tee onto a nearby run still knotted. Two causes, one underneath the other. A branch's face was picked by which side of the tee the other end's centre was on. For two tees on runs at nearly the same height that put `t` on one and `b` on the other, and the router can only join those with a five-segment S over one run and under the other. Both `t` is a three-segment hook. So `pointLines` now chooses the faces of every line touching a tee together, by trying each combination -- the two across the run, all four of an open end, a symbol's port as drawn -- and keeping the shortest route with the fewest corners, the current faces winning a tie. It runs after every seat. A pull released on a symbol's body picks the port by the same cost, so a port that faces away is not chosen for being nearest. Underneath that, the router treated every end as a symbol: a sixteen- pixel stub and a forty-four-pixel clearance. Two tees thirty pixels apart had no room between two stubs for the one crossbar that joins them, and went round both. A tee end now says what it is (`J_END`: six and fourteen), and -- the part that mattered -- the edge renderer applies it too, via `useNodesData`, so the route a face was chosen for is the route that gets drawn. Before that the chooser picked a Z and the renderer drew a loop. Also: a tee's pull ring is an SVG stroke with `pointer-events: stroke`. As a div it covered the dot, so after a hover, pressing the dot started a pull instead of a drag. Hand-placed corners now move with a box selection when both ends of their line move together. A tee refuses a pull back onto its own run. Hops keep two radii clear of a corner.
…the last engine The working state left after the 180 lb re-cut, committed as it stood. Three reporting defects sat downstream of the physics and survived the earlier pass, because each is correct on methalox and only wrong on a propellant whose fuel is the slower vaporizer: the vaporization card, radar and SMD slider were oxidizer-only and now follow the rate-limiting stream; `fallbacks_used` accumulated across runs and propellants and is now scoped per report; and Forward Mode kept the previous propellant's stability panel on screen, so results and sensitivity overrides are cleared when the engine identity changes (`lib/engineIdentity.ts`). Section 4b of the chug note records all three. Alongside: the optimizer router and Layer 1 carry the design-requirement merge and a reproducible hybrid seed, the time-varying solver takes an ambient, and two 6500 N ethalox configs join the shipped set. Each has a test.
#72 on main was a squash of the first nine commits of this branch; the branch then carried on for eight more -- the 180 lb re-cut at 8:1, the COPV specified off the MSA G1 sheet, real component masses, the rate-limiting-stream stability reporting, and the Forward Mode identity reset. Six files conflicted between the squashed copy and the later work, and in every one the later work is a rewrite of the earlier: the oxidizer-only SMD slider and vaporization profile became both-stream, the design audit's 8 kN thresholds became the 180 lb point's. The branch's side is taken throughout.
It loaded tests/golden/anchor_A_config_ethalox_pintle.yaml, and tests/golden/ is gitignored, so the test errored at setup on every machine but the one it was written on -- CI included. What it needs is the coupled ablative path on an ethalox engine, which the 180 lb point ships with. Both cases pass against it.
In CI both seeds returned the start point with f = 4.0 in under a fifth of a second, which is not two searches agreeing, it is no search. The assertion now reports the evaluation counts so the next failure says which.
…ends It compared the final best point of two seeds, and at a 600-evaluation budget that is a coin flip: the search often never improves on the start point at all. Seed 7 returns the start on every machine tried; seed 8 found something marginally better here and nothing on the CI runner, so "the two seeds landed in different places" passed or failed on luck. Whether the seed reaches the sampler is visible in the sequence of candidates instead: different seeds diverge on the first iteration after the start point, equal seeds reproduce every candidate, and neither depends on what the search then makes of them. Verified that both assertions can fail.
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.
One branch with every app at its newest, so main is the thing to work on.
pid-designer: the connection tool, rebuilt (
pid/connections)EngineDesign: the work that continued after #72
geometry-fixes; this carries the eight after it: the 180 lb re-cut at 8:1, the COPV specified off the MSA G1 sheet, real component masses, the injector face constrained and drawn, stability reporting that follows the rate-limiting stream, and Forward Mode clearing when the engine identity changes.feed-twin is unchanged since #57 and included as-is.
Gates on the merged tree: EngineDesign Python 596 passed, EngineDesign frontend tsc + 29 tests + build, pid-designer 384 frontend + 112 Python, physics benchmark all checks passed.