---
title: "Estimate Dynamic V1"
method: POST
path: "/v1/dynamic-simulations/estimate"
tags: ["v1", "dynamic-simulations"]
---

# Estimate Dynamic V1

`POST /v1/dynamic-simulations/estimate`

## Request

- Base URL: `https://thrustlab.com`
- URL: `https://thrustlab.com/v1/dynamic-simulations/estimate`
- Auth: HTTP bearer

Estimate the credit cost + run duration WITHOUT running the solver.

Coulombic only — resolves the battery capacity + reads the schedule mean
throttle and returns in milliseconds (no diffrax compile, no Celery dispatch;).

``estimated_credits`` MUST equal what create will actually reserve — the
3xR cap unit (create_dynamic_simulation: ``credits = 3 * R``), NOT the
coulombic minutes. The coulombic figure only sizes ``estimated_duration_s``
(the worker's first integrate window); quoting it here under-stated the
charge for any multi-rotor run (e.g. a count-4 quad: quoted 2, billed 12).

## Request body

- DynamicEstimateBody: Body for ``POST /v1/dynamic-simulations/estimate``. Accepts the full create body's fields (so the wizard can POST the same payload to /estimate as it would to create) — but the estimate path consumes ONLY the battery + termination + schedule to compute the coulombic duration, never the solver. ``extra="forbid"`` keeps it a strict subset-compatible body.
  - `ambient_temp_c` number
  - `battery_charge_pct` number
  - `battery_component_id` string, nullable
  - `battery_esc_wire_resistance_mohm` number
  - `battery_initial_temp_c` number, nullable
  - `battery_topology` PackTopologyIn
    - `bus_wire_resistance_mohm` number
    - `cross_charge_warning_acknowledged` boolean
    - `root` union, required
      - PackLeafIn
        - `battery_component_id` string, required
        - `branch_wire_resistance_mohm` number
        - `id` string, required
        - `initial_soc_pct` number, nullable
        - `kind` 'leaf'
      - PackSeriesIn
        - `children` union[], required
          - union
            - PackLeafIn
              - …
            - PackSeriesIn (recursive)
            - PackParallelIn
              - …
        - `kind` 'series'
      - PackParallelIn
        - `branches` union[], required
          - union
            - PackLeafIn
              - …
            - PackSeriesIn
              - …
            - PackParallelIn (recursive)
        - `kind` 'parallel'
        - `voltage_match_tolerance_v` number, required
    - `schema` 1
  - `density_kg_m3` number, nullable
  - `motor_initial_temp_c` number, nullable
  - `name` string, nullable
  - `project_id` string, nullable
  - `rotor_groups` DynamicRotorGroupIn[], nullable
    - `count` integer
    - `esc_motor_wire_resistance_mohm` number
    - `esc_pwm_frequency_khz` number: ESC switching frequency (kHz)
    - `esc_resistance_mohm` number
    - `esc_sync_rectification` boolean: Synchronous rectification (comp_pwm); default ON
    - `esc_timing` 'low' | 'medium' | 'high' | 'auto': Six-step commutation advance preset (low 7.5°/medium 15°/high 22.5°/auto duty-scheduled); FOC ignores it
    - `esc_type` 'foc' | 'six_step': ESC commutation type. Selects K_volt, copper_mult, iron_mult, the six-step commutation-advance physics and the throttle->duty map — not a cosmetic flag. Single-sourced (ESC-7): app/constants_esc.py.
    - `label` string, required
    - `motor_component_id` string, nullable
    - `motor_cooling_source` 'cowling' | 'prop_exit_velocity' | 'custom': Per-rotor-group motor cooling mode. cowling = still air; prop_exit_velocity = forced convection from the prop slipstream (default); custom = fixed velocity or direct thermal resistance.
    - `motor_cooling_velocity_m_s` number, nullable: Custom motor cooling convection velocity (m/s); only used when motor_cooling_source=custom.
    - `motor_r_th` number, nullable: Custom direct motor thermal resistance override (K/W); only used when motor_cooling_source=custom.
    - `propeller_component_id` string, nullable
    - `propeller_label` PropellerLabel: The printed size of a propeller — everything the Fast model reads. This is the whole input the Fast model takes for a rotor: no station arrays, no section airfoils, nothing that has to be measured. It is accepted ONLY with ``propeller_model == "fast"``; the Full model needs blade geometry and refuses a label rather than silently switching models.
      - `camber_pct` number: Section camber as a percentage of chord. The default is the reference model's 3.2 % (zero-lift angle -3.5 deg), so a label with no camber given reproduces the published closed-form numbers.
      - `chord_ratio` number, nullable: Chord at 75 % radius divided by diameter. Required when family == 'custom'; rejected otherwise (the family preset is the value).
      - `diameter_m` number, required: Propeller diameter in metres.
      - `family` 'thin_electric' | 'sport' | 'slow_flyer' | 'multirotor' | 'custom', required: Planform family, which sets the chord ratio at 75 % radius (thin_electric 0.065, sport 0.07, slow_flyer 0.10, multirotor 0.11). Use 'custom' to give chord_ratio directly.
      - `max_rpm` integer, nullable
      - `num_blades` integer, required: Number of blades.
      - `pitch_m` number, required: Geometric pitch in metres.
      - `weight_g` number, nullable
  - `rotors` DynamicRotorIn[], nullable: One entry per rotor. Preferred over rotor_groups, and required for coaxial stacks: set coax_stack_id to the same non-zero value on the rotors that share an axis, give each a distinct coax_position_m, and set opposite rotation_sense values for a contra-rotating stack. Supply either rotors or rotor_groups, not both.
    - `coax_position_m` number: Position along the coaxial stack axis in metres, increasing downstream (in the direction the slipstream travels). The spacing between two rotors is the difference of their positions. Ignored when coax_stack_id is 0.
    - `coax_stack_id` integer: Coaxial stack this rotor belongs to. 0 (default) means the rotor is not stacked and is simulated in isolation. Rotors sharing a non-zero value form one contra-rotating or co-rotating stack.
    - `esc_motor_wire_resistance_mohm` number
    - `esc_pwm_frequency_khz` number: ESC switching frequency (kHz)
    - `esc_resistance_mohm` number
    - `esc_sync_rectification` boolean: Synchronous rectification (comp_pwm); default ON
    - `esc_timing` 'low' | 'medium' | 'high' | 'auto': Six-step commutation advance preset (low 7.5°/medium 15°/high 22.5°/auto duty-scheduled); FOC ignores it
    - `esc_type` 'foc' | 'six_step': ESC commutation type. Selects K_volt, copper_mult, iron_mult, the six-step commutation-advance physics and the throttle->duty map — not a cosmetic flag. Single-sourced (ESC-7): app/constants_esc.py.
    - `label` string, required
    - `motor_component_id` string, nullable
    - `motor_cooling_source` 'cowling' | 'prop_exit_velocity' | 'custom': Per-rotor-group motor cooling mode. cowling = still air; prop_exit_velocity = forced convection from the prop slipstream (default); custom = fixed velocity or direct thermal resistance.
    - `motor_cooling_velocity_m_s` number, nullable: Custom motor cooling convection velocity (m/s); only used when motor_cooling_source=custom.
    - `motor_r_th` number, nullable: Custom direct motor thermal resistance override (K/W); only used when motor_cooling_source=custom.
    - `propeller_component_id` string, nullable
    - `propeller_label` PropellerLabel: The printed size of a propeller — everything the Fast model reads. This is the whole input the Fast model takes for a rotor: no station arrays, no section airfoils, nothing that has to be measured. It is accepted ONLY with ``propeller_model == "fast"``; the Full model needs blade geometry and refuses a label rather than silently switching models.
      - `camber_pct` number: Section camber as a percentage of chord. The default is the reference model's 3.2 % (zero-lift angle -3.5 deg), so a label with no camber given reproduces the published closed-form numbers.
      - `chord_ratio` number, nullable: Chord at 75 % radius divided by diameter. Required when family == 'custom'; rejected otherwise (the family preset is the value).
      - `diameter_m` number, required: Propeller diameter in metres.
      - `family` 'thin_electric' | 'sport' | 'slow_flyer' | 'multirotor' | 'custom', required: Planform family, which sets the chord ratio at 75 % radius (thin_electric 0.065, sport 0.07, slow_flyer 0.10, multirotor 0.11). Use 'custom' to give chord_ratio directly.
      - `max_rpm` integer, nullable
      - `num_blades` integer, required: Number of blades.
      - `pitch_m` number, required: Geometric pitch in metres.
      - `weight_g` number, nullable
    - `rotation_sense` 1 | -1: Direction of rotation about the downstream axis: 1 or -1. Opposite values in one stack give a contra-rotating pair, which recovers swirl energy and is the usual choice. Only read by coaxial pairing — a single rotor's value does not affect its own performance.
  - `schedule` ScheduleIn: The control schedule — a ``segments`` list OR a per-rotor ``csv_text``. ``mode`` is the discriminator. ``segments`` is required for ``mode=segments``; ``csv_text`` (+ optional ``interpolation``) for ``mode=csv``. The cross-field requirement is enforced server-side by the schedule compiler (``validate_schedule``) so a malformed schedule 4xx's BEFORE any reservation.
    - `csv_text` string, nullable
    - `interpolation` 'linear' | 'hold', nullable
    - `mode` 'segments' | 'csv', required
    - `segments` SegmentIn[], nullable
      - `airspeed_ramp` 'step' | 'linear'
      - `airspeed_target` number
      - `duration_s` number, nullable
      - `per_group` object, required
      - `throttle_ramp` 'step' | 'linear'
      - `until_depleted` boolean
      - `vertical_speed_ramp` 'step' | 'linear'
      - `vertical_speed_target` number
  - `termination` TerminationIn, required: Run-termination policy: the SOC / cell-voltage depletion cutoffs and the fixed-duration vs until-depleted run mode.
    - `cell_voltage_cutoff_v` number, nullable
    - `max_duration_s` number, nullable
    - `mode` 'fixed' | 'until_depleted'
    - `soc_cutoff_pct` number

## Response `200`

Successful Response

- DynamicEstimateResource
  - `estimated_credits` integer, required
  - `estimated_duration_s` number, required
  - `object` 'dynamic_estimate'

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/thrustlab/apis/thrustlab-api.md) · [All operations](https://skmtc.dev/thrustlab/apis/thrustlab-api/llms.txt) · [OpenAPI document](https://skmtc.dev/thrustlab/apis/thrustlab-api/revisions/c19717e52529?raw)
