Component analyses

client.component_analyses runs a propeller, motor or battery independently of a full powertrain. It uses the same public API as the dashboard.

Submit a job

from thrustlab import Client

client = Client()  # THRUSTLAB_API_KEY
job = client.component_analyses.create(
    component_type="propeller",
    custom_spec={
        "diameter_m": 0.254, "pitch_m": 0.127,
        "num_blades": 2, "family": "thin_electric", "camber_pct": 3.2,
    },
    model="fast",
    inputs={"rpm": 6000.0, "v_axial_mps": 10.0, "v_edge_mps": 0.0},
)
resource = client.component_analyses.wait(job["id"], timeout=300)
if resource["status"] == "completed":
    print(resource["result"]["candidates"][0]["scenarios"][0]["metrics"])
else:
    print(resource["status"], resource.get("error"))

Provide exactly one of component_id, candidate_ids, or custom_spec. Catalog and private references follow the account's component visibility rules. Full propeller analysis requires blade geometry and section airfoils; a printed size alone supports Fast.

Operating inputs use SI. Custom component specs retain their documented nameplate conventions: Fast labels use metres, Full geometry arrays use metres/degrees while its diameter metadata uses inches, motor resistance is phase-to-phase ohms, and battery capacity uses mAh. The API rejects unsupported or conflicting inputs.

Modes and goals

mode accepts point, sweep, solve_target, optimize, sensitivity and, for propellers, compare_models. Supply variables for a sweep, sensitivity study or operating search. Variables name supported operating inputs or custom geometry transformations and carry min, max, steps, or an explicit values list.

job = client.component_analyses.create(
    component_type="propeller",
    candidate_ids=["comp_FIRST", "comp_SECOND"],
    model="full", mode="solve_target",
    inputs={"v_axial_mps": 20.0, "v_edge_mps": 0.0},
    variables=[{"parameter": "rpm", "min": 1000.0, "max": 15000.0, "steps": 25}],
    target={"metric": "thrust_n", "value": 10.0, "tolerance": 0.02},
    constraints=[{"metric": "power_w", "operator": "lte", "value": 300.0}],
    objective={"metric": "power_w", "direction": "minimize", "aggregation": "mean"},
    max_evaluations=256,
)

Replace the example IDs with accessible components. This compares shaft power at equal thrust. Constraints establish feasibility; an optional objective ranks feasible results. Without an objective, rank, objective_score and best_index are null. The result does not imply that the first feasible candidate is best.

Multiple flight/load conditions use labeled conditions with weights and operating-input overrides. All conditions must satisfy the constraints. Objectives explicitly choose weighted mean or conservative worst aggregation. Search is bounded by max_evaluations; inspect stopping_reason and failed evaluations. No global optimum is promised.

Motor and battery inputs

Motor analyses specify supply_voltage_v, an ESC configuration and shaft torque or power. Choose prescribed RPM or load-driven throttle; supplying both is rejected. No simulated battery or propeller is inserted.

Battery analyses specify one load: current_a, power_w, load_resistance_ohm, or a time-ordered load_profile starting at zero. Optional duration, timestep, initial charge/temperature, thermal and cutoff controls define a discharge trajectory. Cutoff and numerical failures are reported rather than silently extending a failed trajectory.

Dynamic component studies

Set evaluation="dynamic" and supply a component-specific dynamics object. Existing steady requests keep their behavior. Dynamic props support prescribed RPM/inflow histories or control_mode="shaft_torque"; motors integrate averaged dq currents, speed and two-node temperatures; batteries retain SOC, RC polarization and thermal state. Hold/linear profile rows start at zero and override controls cumulatively. The reporting interval is separate from internal step/work limits.

job = client.component_analyses.create(
    component_type="propeller", component_id="comp_your_propeller",
    model="full", evaluation="dynamic", mode="sweep",
    inputs={"shaft_torque_nm": 0.1},
    dynamics={
        "duration_s": 1.0, "control_mode": "shaft_torque",
        "initial_state": {"rpm": 0.0}, "wake_on": True,
        "output_event": {"metric": "thrust_n", "operator": "gte", "value": 2.0, "dwell_s": 0.01},
    },
    variables=[{"parameter": "shaft_torque_nm", "values": [0.05, 0.1, 0.15]}],
    objective={"metric": "time_to_target_s", "direction": "minimize", "aggregation": "mean"},
    max_evaluations=8,
)
result = client.component_analyses.wait(job["id"])

Dynamic metrics include terminal values, peak_, minimum_ and mean_ physical observables, energy_wh, shaft_energy_wh and time_to_target_s. Events are measured on accepted native states; unreached events are null and do not receive a score. Named axes can modify initial_state.<field>, profile_scale.<channel> and profile_offset.<channel>; a constant input axis cannot be overwritten by the same profile. Absolute-temperature profiles use offsets rather than Celsius scale factors. Torque-driven geometry searches require an explicit measured/fixed or similarity inertia policy.

Each scenario's trajectory contains time_s, observable channels, termination, partial status and integration count. Native continuation and calibrated catalog inputs remain private. Saved output is limited to 2,000 rows per trajectory and 20,000 per job; internal attempted work has separate per-trajectory/job limits, including failed work. Partial or unconverged histories remain inspectable and unranked. See the dynamic component guide for the model assumptions and time-constant controls.

Poll, cancel and retain results

current = client.component_analyses.retrieve(job["id"])
cancelled = client.component_analyses.cancel(job["id"])

wait() returns a terminal resource (completed, failed or cancelled). On timeout it returns SDK-only timed_out; the server job continues. Use on_progress to receive status snapshots. Cancellation is cooperative between evaluations and bounded native chunks. A running dynamic job reports cancelling until its worker stops and persists partial results; cancelling a completed job preserves its result.

The public endpoints are POST /v1/component_analyses, GET /v1/component_analyses/{job_id} and DELETE /v1/component_analyses/{job_id}. Requests and results are owner-scoped and retained for 24 hours from submission; expired or inaccessible jobs return 404. Export results you need to keep.