Grand tour dashboard

Requires the viz extra for the widgets and the mdao/smt extras for the sizing and verdict strips (pip install "longeron[viz,mdao,smt]").

The grand-tour dashboard: every analysis seam on one linked surface.

One grand_dashboard() call composes the house widgets over a drone model (plus the multi-mission sizing model) into a single reactive dashboard – the capability finale of the tutorial series (tutorial 9):

  • a structure diagram pane (longeron.diagrams.structure_diagram(), compact toolbar on) – the linked-selection hub;

  • a 3D CAD pane (longeron.widgets.viewer3d.mesh_viewer()), config-keyed through longeron.analysis.link.bind_config_view() (tutorial 7’s seam, promoted): clicking ANY craft in the diagram – a build configuration, a fleet airframe shell, or the mission catalog’s variant usages – renders THAT craft in the pane, while selections inside the shown craft highlight per M0 individual; a translucent view cone follows the camera what-if sliders over the home assembly;

  • a requirements scoreboard pane (longeron.analysis.scoreboard.scoreboard(), Voronoi tessellation) scoring the model’s own requirement hierarchy, repainted live as the measured geometry changes;

  • a camera what-if card – elevation/azimuth sliders that re-run the CAD-native occlusion measure (longeron.analysis.geometry. occlusion_report(), deterministic mesh engine) on every move;

  • an OpenMDAO sizing strip – the sizing part’s generated Problem (longeron.analysis.mdao.build_problem()), re-run live from a loiter-speed slider, with a one-click driver run that snaps the slider to the optimum;

  • a Z3 verdict strip – requirement-consistency cards (longeron.analysis.smt.to_smt()): the design point’s SAT witness beside an impossible what-if’s UNSAT conflict core;

  • a Cesium mission pane (longeron.widgets.mission3d) flying the drone’s own geometry through its recorded state-machine execution over satellite imagery (offline front-ends degrade to a printed note; the dashboard composes regardless).

WIRING MAP – everything reacts to everything, kernel-side, through traitlets observers (so the whole surface works headless):

  • diagram click -> 3D highlight (and mesh pick -> diagram selection);

  • diagram click on another craft -> the 3D pane bakes and shows THAT craft (the camera what-if keeps measuring the home assembly);

  • diagram click on a requirement -> scoreboard selection (and a scoreboard cell click -> diagram selection);

  • camera sliders -> occlusion re-measure -> the view cone repaints, the obstructing parts highlight in 3D, the occlusion readout lists them, the scoreboard recolors, and the header score updates;

  • loiter slider -> run_model -> the sizing cards repaint; the maximize button runs the optimization driver and snaps the slider.

Requires the viz extra for the widgets and the mdao/smt extras for the sizing and verdict strips: pip install "longeron[viz,mdao,smt]".

longeron.analysis.grand.ATLANTA_LOOP: tuple[tuple[float, float, float], ...] = ((33.7813, -84.3833, 350.0), (33.7885, -84.3785, 390.0), (33.79, -84.3695, 380.0), (33.7838, -84.369, 360.0), (33.777, -84.3825, 350.0))

default mission route: a small loop over Piedmont Park, midtown Atlanta – (lat, lon, alt m MSL); the ground there sits ~300 m MSL

longeron.analysis.grand.FLIGHT_EVENTS: tuple[Any, ...] = (2.0, 'launch', 6.0, 'airborne', 150.0, 'low_battery', 10.0)

default event feed for the flight state machine replay (the Interpreter.simulate protocol: numbers advance the clock)

longeron.analysis.grand.drone_scene(model, assembly='Rotorcraft::QuadCopter')[source]

Bake the tagged per-instance mesh for a drone-family assembly.

Interprets assembly at M0 (longeron.m0.interpret()), sizes the parametric mesh from the population’s own attribute values (longeron.analysis.geometry.drone_geometry(), split_instances=True), and stamps every rendered part with its M0 individual id (tutorial 7’s identity keys). The rotor population picks the frame family – the CONFIG-KEYED rendering seam: a motors population fans out one arm per member (the quad and the hexa), frontMotors + tailMotor build the three-arm tricopter with its tail boom, and upperMotors + lowerMotors stack the coaxial pairs (uppers take motor1..4 / the first propeller individuals, lowers follow). The assembly must have the MultiRotor build-family shape of the DeepScout program (examples/deepscout): chassis, battery, and propellers slots, one of the rotor-population shapes above, plus an optional camera whose placement/boresight attributes drive the occlusion checks. Pair with longeron.analysis.link.owning_config() to resolve a diagram selection anywhere inside a configuration to the configuration this function renders. Returns (mesh, part_map).

Return type:

tuple[dict[str, Any], dict[str, str]]

longeron.analysis.grand.grand_dashboard(model, sizing=None, *, assembly='Rotorcraft::QuadCopter', states='DeepScout::FlightStates', sizer='ScoutSizing::IsrPrime', station_requirement='ScoutSizing::IsrStation', station_var='stationMinutes', loiter_var='loiterSpeed', what_if_station=420.0, values=None, waypoints=((33.7813, -84.3833, 350.0), (33.7885, -84.3785, 390.0), (33.79, -84.3695, 380.0), (33.7838, -84.369, 360.0), (33.777, -84.3825, 350.0)), events=(2.0, 'launch', 6.0, 'airborne', 150.0, 'low_battery', 10.0), ground_alt=300.0, imagery='satellite')[source]

The grand-tour dashboard (an ipywidgets VBox) – one call.

model carries the drone: its structure feeds the diagram, its interpreted M0 population sizes the 3D mesh, its requirement hierarchy is the scoreboard, and its states machine flies the Cesium mission over waypoints. sizing (default: model itself) carries the continuous side: sizer becomes the OpenMDAO problem behind the loiter slider, and station_requirement the Z3 consistency cards – the what-if card demands station_var >= what_if_station with loiter_var freed, an impossible floor whose UNSAT core names the binding constraints. values injects extra measured scoreboard bindings (e.g. performance measures computed through the interpreter); the live occlusion and disc-overlap measures are merged on top.

See the module docstring for the pane list and the wiring map. The returned layout exposes every piece for scripting and tests: .diagram, .viewer, .board (+ .scoreboard, the current Scoreboard), .elevation / .azimuth / .readout / .report, .loiter / .optimize / .problem / .optimum, .smt_sat / .smt_what_if, .mission / .track, .mesh / .part_map / .camera, .header, .config_view (the ConfigViewBinding behind the config-keyed 3D pane), and .unlink (drops the diagram <-> 3D binding).

Return type:

Any

longeron.analysis.grand.scene_for(model, config, *, interpreter=None, tilt_deg=None)[source]

Bake the tagged scene for ANY renderable craft definition.

One dispatcher over both DeepScout craft families. A fleet airframe shell – a definition whose own attributes evaluate to the Airframe geometry knobs (wingSpan, wingArea, taper, fuselageLength, motorCount, armCount) – renders through longeron.analysis.geometry.airframe_geometry() at nominal display propulsion. Every part carries the definition’s qualified name as its identity key (selecting the def lights the whole craft), EXCEPT the clickable internals: a rendered battery, fc, or camera part whose shell declares the matching part usage (battery / flightController / camera – every DeepScout fleet shell does) carries THAT usage’s qualified name, so picking the battery in the scene selects the battery element, not just the craft. Anything else goes to drone_scene(): the MultiRotor build family bakes from its own M0 population with per-individual identity keys, and non-assembly shapes fail loudly with AnalysisError.

This is the config-keyed rendering seam behind longeron.analysis.link.bind_config_view(): resolve a selection to its owning configuration, hand the configuration here, swap the viewer to the result. tilt_deg is the tilt-rotor family’s conversion state (hover 90 .. cruise 0, the scene-level knob behind tilt_viewer()); shells that declare no pivot chain ignore it. Returns (mesh, part_map).

Return type:

tuple[dict[str, Any], dict[str, str]]

longeron.analysis.grand.tilt_viewer(model, config='TiltRotors::TiltTriWing', *, tilt_deg=90.0, width_px=640, height_px=420)[source]

A mesh viewer with the tilt-rotor conversion affordance.

One longeron.widgets.viewer3d.mesh_viewer() over one ipywidgets slider: the slider commands the conversion state, sweeping hover (90 deg) to cruise (0), and every move re-bakes the scene through scene_for() at that tilt – the tip pods and the nose unit rotate about the pivots the MODEL declares, so the slider drives the same geometry the interference gate samples. The returned VBox exposes the pieces for scripting and tests: .viewer (the mesh viewer), .tilt (the slider), and .part_map. Requires the viz extra.

Return type:

Any

longeron.analysis.grand.view_cone_part(camera, *, length, name='viewCone', segments=48, color='#7a5d8c', opacity=0.25)[source]

A translucent view-cone mesh part for the 3D scene.

Apex at the camera position, axis along the azimuth/elevation boresight, half-angle fieldOfView / 2, reaching length metres – the same construction longeron.analysis.geometry. occlusion_report() measures, here as a display part (append it to a scene’s parts; never to the analysis mesh, where it would count as an obstruction). camera uses the attribute names of the DeepScout program’s ScoutParts::F450Kit::Camera part. Untagged, so linked selection ignores it.

Return type:

dict[str, Any]