> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dalus.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Quickstart guide for Dalus

# Quickstart (10 minutes)

Welcome! In this Quickstart, we'll guide you through building your first model in Dalus.

We're going to build a model of a satellite with 3 reaction wheels, design a simple attitude control system, and then verify a key performance parameter, `settling_time`, for that control system.

Whether you're new to system modeling or just getting started with Dalus, this hands-on example will help you get up to speed quickly and confidently.

## Step 1: Create a New Model

Start by creating a new model in the Dalus dashboard. Give your model a descriptive name, such as "Satellite Quickstart". This model is the isolated, collaborative environment that will contain your system's requirements, architecture, and analysis all in one place.

<img src="https://mintcdn.com/dalus/nVXGKHGeIs6KRnCF/images/quickstart/q1.png?fit=max&auto=format&n=nVXGKHGeIs6KRnCF&q=85&s=e7fe774b1aa50219b7ec9cf399ffbddf" alt="Creating a new model in Dalus" width="578" height="271" data-path="images/quickstart/q1.png" />

## Step 2: Define Requirements

Satellites need to respond to new attitude commands from the ground promptly to face the necessary direction for imaging, telemetry, etc.. Let's add a requirement that specifies the maximum allowable time to complete a commanded attitude maneuver:

> **Requirement:**
> The Attitude Control and Determination Subsystem (ACDS) shall settle to within 0.1 degrees of the commanded attitude within 120 seconds after maneuver initiation.

<img src="https://mintcdn.com/dalus/nVXGKHGeIs6KRnCF/images/quickstart/q2.png?fit=max&auto=format&n=nVXGKHGeIs6KRnCF&q=85&s=cdd3bea608fa23d38cdc9c1b4412519b" alt="Adding requirements in Dalus" width="1413" height="811" data-path="images/quickstart/q2.png" />

## Step 3: Build the Structure

Now let's define the basic structure of our satellite. We'll start by adding the main parts we'll be focusing on for this quickstart:

1. **Add** the **Satellite** as the root part of your model.
2. **Double-Click** into the Satellite and add these **subparts:**
   * **Reaction Wheels (x3):** Add three reaction wheel components to represent the 3-axis control system.
   * **Attitude Control & Determination System (ACDS):** Add the Attitude Control and Determination Subsystem as a separate part.

<img src="https://mintcdn.com/dalus/nVXGKHGeIs6KRnCF/images/quickstart/q3.gif?s=705b83e86ad4449c882c1b6b630a6ad4" alt="Building the structure in Dalus" width="1436" height="808" data-path="images/quickstart/q3.gif" />

## Step 4: Add Part Attributes

Now that we have the foundation, let's assign some attributes. Attributes are values like "mass" or "moment of inertia" that describe a part's physical properties and are accessible by any analysis we do.

Add the satellite's mass-moments of inertia as attributes in the **Right Sidebar**:

| Attribute Name | Value | Unit                   |
| -------------- | ----- | ---------------------- |
| l\_xx          | 10    | kilogram meter squared |
| l\_yy          | 10    | kilogram meter squared |
| l\_zz          | 10    | kilogram meter squared |

<img src="https://mintcdn.com/dalus/nVXGKHGeIs6KRnCF/images/quickstart/q4.png?fit=max&auto=format&n=nVXGKHGeIs6KRnCF&q=85&s=28f29e1aa482c5d21627e629435a300d" alt="Adding attributes in Dalus" width="1145" height="614" data-path="images/quickstart/q4.png" />

## Step 5: Configure an Action (Analysis)

In this step, we'll implement the action **Point Satellite** inside the **Satellite** part, which takes a commanded attitude and calculates how long it takes (among other things) to reach that attitude from its initial attitude (0,0,0).

1. First, switch to the **Action view** in the Dalus interface. This will allow you to create and configure actions for your model, in this case, for the Satellite part.
2. **Add** an action and name it "Point Satellite".
3. **Double-click** on the action and copy and paste this python code inside, which implements a simple Proportional-Derivative controller in 3 axes:

<img src="https://mintcdn.com/dalus/nVXGKHGeIs6KRnCF/images/quickstart/q5.gif?s=57466681b2e12482bba08e7ea66db3c8" alt="Creating an action in Dalus" width="1436" height="808" data-path="images/quickstart/q5.gif" />

```python [expandable] theme={null}
import numpy as np


def main():
    # Satellite inertia (from attributes)
    I_xx = getInput("l_xx")
    I_yy = getInput("l_yy")
    I_zz = getInput("l_zz")
    I = np.array([I_xx, I_yy, I_zz])

    # Control gains and limits
    Kp = np.array([0.1, 0.1, 0.1])
    Kd = np.array([1.5, 1.5, 1.5])
    torque_cmd_max = 0.02

    # Reaction wheel parameters
    J_rw = np.array([1.0, 1.0, 1.0])  # kg·m²
    efficiency = 0.9

    # Initial attitude (deg) and desired attitude (deg)
    a_x, a_y, a_z = 0.0, 0.0, 0.0
    a_cmd_x, a_cmd_y, a_cmd_z = getInput("a_cmd_x"), getInput("a_cmd_y"), getInput("a_cmd_z")

    attitude = np.deg2rad([a_x, a_y, a_z])
    desired_attitude = np.deg2rad([a_cmd_x, a_cmd_y, a_cmd_z])

    # Simulation settings
    dt = 0.1
    settling_threshold_deg = 0.1

    # Histories (use lists so the run can extend indefinitely)
    times = []
    error_norm_hist = []
    torque_hist = []
    power_hist = []

    omega = np.zeros(3)
    omega_rw = np.zeros(3)

    prev_error = desired_attitude - attitude
    initial_error_norm = np.linalg.norm(prev_error)
    settling_time = None

    t = 0.0
    while True:
        error = desired_attitude - attitude
        error_dot = (error - prev_error) / dt
        prev_error = error

        # PD control with saturation
        torque_cmd = Kp * error + Kd * error_dot
        torque_cmd = np.clip(torque_cmd, -torque_cmd_max, torque_cmd_max)

        # Update satellite dynamics
        alpha = torque_cmd / I
        omega += alpha * dt
        attitude += omega * dt

        # Reaction wheel dynamics & power
        alpha_rw = torque_cmd / J_rw
        omega_rw += alpha_rw * dt
        power = np.abs(torque_cmd * omega_rw) / efficiency

        # Record histories
        times.append(t)
        error_norm_val = np.linalg.norm(error)
        error_norm_hist.append(error_norm_val)
        torque_hist.append(torque_cmd)
        power_hist.append(power)

        # Settling check
        if np.rad2deg(error_norm_val) < settling_threshold_deg:
            settling_time = t
            break

        t += dt

    # Convert histories to arrays for analysis
    times = np.asarray(times)
    error_norm_hist = np.asarray(error_norm_hist)
    torque_hist = np.asarray(torque_hist)
    power_hist = np.asarray(power_hist)

    # Rise time (10% to 90% of initial error)
    e0 = initial_error_norm
    rise_time = np.nan
    idx_start = np.where(error_norm_hist <= 0.9 * e0)[0]
    idx_end = np.where(error_norm_hist <= 0.1 * e0)[0]
    if len(idx_start) > 0 and len(idx_end) > 0:
        t_rise_start = times[idx_start[0]]
        t_rise_end = times[idx_end[0]]
        if t_rise_end >= t_rise_start:
            rise_time = t_rise_end - t_rise_start

    # Total energy consumed by wheels
    total_power = np.sum(power_hist, axis=1)
    total_energy = np.trapz(total_power, times)  # Joules

    # Results
    if np.isnan(rise_time):
        print("Rise time: N/A (threshold not met during run)")
    else:
        print(f"Rise time: {rise_time:.1f} s")

    print(f"Settling time: {settling_time:.1f} s")

    setOutput("settling_time", settling_time)

    roll_deg, pitch_deg, yaw_deg = np.rad2deg(attitude)
    print(f"Final attitude: roll={roll_deg:.2f} deg, pitch={pitch_deg:.2f} deg, yaw={yaw_deg:.2f} deg")


if __name__ == "__main__":
    main()
```

The script uses the satellite's inertia values as inputs. Add these to the action via the **Right Sidebar → Inputs**:

| Input Variable | Value | Unit  |
| -------------- | ----- | ----- |
| l\_xx          | 10    | kg·m² |
| l\_yy          | 10    | kg·m² |
| l\_zz          | 10    | kg·m² |

You'll also notice we're grabbing commanded attitude variables: `a_cmd_x`, `a_cmd_y`, and `a_cmd_z`. Add these as inputs as well:

| Input Variable | Value | Unit |
| -------------- | ----- | ---- |
| a\_cmd\_x      | 50    | deg  |
| a\_cmd\_y      | 50    | deg  |
| a\_cmd\_z      | 50    | deg  |

<img src="https://mintcdn.com/dalus/nVXGKHGeIs6KRnCF/images/quickstart/q6.png?fit=max&auto=format&n=nVXGKHGeIs6KRnCF&q=85&s=23406c2f2ce50ca858e7860cfa461b01" alt="Adding input variables" width="558" height="741" data-path="images/quickstart/q6.png" />

**Finally**, we need to set the `settling_time`; this is the key performance parameter we are evaluating. Go back to the **Satellite** part and add it as an attribute.

| Attribute Name | Value | Unit |
| -------------- | ----- | ---- |
| settling\_time | 0     | s    |

<img src="https://mintcdn.com/dalus/nVXGKHGeIs6KRnCF/images/quickstart/q7.png?fit=max&auto=format&n=nVXGKHGeIs6KRnCF&q=85&s=32af27a9916d08180436be005f500318" alt="Adding settling_time attribute" width="1073" height="459" data-path="images/quickstart/q7.png" />

Then add `settling_time` as an **Output** in the **Right Sidebar** for the **Point Satellite** action. Click the **Run** button to execute the analysis—the script will calculate and output the settling time value.

<img src="https://mintcdn.com/dalus/nVXGKHGeIs6KRnCF/images/quickstart/q8.gif?s=871175cdb96e3e406ef33db87ff9b2a1" alt="Adding settling_time output and running the action" width="1436" height="808" data-path="images/quickstart/q8.gif" />

You should see output similar to this:

```
Execution:
Rise time: 26.3 s
Settling time: 44.9 s
Final attitude: roll=49.99 deg, pitch=49.99 deg, yaw=49.99 deg
```

<img src="https://mintcdn.com/dalus/nVXGKHGeIs6KRnCF/images/quickstart/q9.png?fit=max&auto=format&n=nVXGKHGeIs6KRnCF&q=85&s=c1f50805cf9dd44bcc968e6258c618af" alt="Execution results" width="818" height="267" data-path="images/quickstart/q9.png" />

## Step 6: Add a Requirement Constraint and Test

Now, let's return to our requirements and add a constraint to the ACDS settling time requirement:

1. Go to the requirements view and locate the ACDS settling time requirement.
2. Click on its status, then under **Add constraint**, select the **Attributes** dropdown.
3. Choose `settling_time` and set the limit to `< 120 seconds`.
4. You'll see the requirement status immediately update based on its current value.

<img src="https://mintcdn.com/dalus/nVXGKHGeIs6KRnCF/images/quickstart/q10.gif?s=d7ac3380de24c7318a9bc46530eedf5a" alt="Adding and testing constraints" width="1436" height="808" data-path="images/quickstart/q10.gif" />

Experiment a bit. Increase the moments of inertia (e.g., `l_xx`) in the action's inputs and re-run the simulation. Try to get the requirement to fail!

<img src="https://mintcdn.com/dalus/nVXGKHGeIs6KRnCF/images/quickstart/q11.png?fit=max&auto=format&n=nVXGKHGeIs6KRnCF&q=85&s=af09d57f05fae804d35deb151b691ff9" alt="Failed requirement" width="489" height="419" data-path="images/quickstart/q11.png" />

## Step 7: Create a Simple State Machine

Next, let's create a simple state machine to **trigger** our **Point Satellite** action instead of manually running it.

1. Switch to the **State view**.
2. Define two states: `STANDBY` and `SURVEILLING`.
3. Select the `STANDBY` state and enable **Entry State** in the Right Sidebar to make it the initial state.

<img src="https://mintcdn.com/dalus/nVXGKHGeIs6KRnCF/images/quickstart/q12.gif?s=34fd2be02c729b34c76cf04019bfb08a" alt="Creating a state machine" width="1436" height="808" data-path="images/quickstart/q12.gif" />

4. Connect them to add a **transition** between them. Name it anything.
5. In the **Point Satellite** **transition**, select the **Point Satellite** action as an **effect**. This causes the action to execute whenever you transition from Standby to Surveilling.
6. Use the states dropdown to first enter `STANDBY`. Then, switch to the Action view and transition to `SURVEILLING` to observe the effect taking place.

<img src="https://mintcdn.com/dalus/nVXGKHGeIs6KRnCF/images/quickstart/q13.gif?s=365ae5e9edb1dfacefdf26c038768d6d" alt="Triggering the state transition" width="1436" height="808" data-path="images/quickstart/q13.gif" />

Although simple, this powerful principle can be used to orchestrate many actions for a complex state machine. You can also add **transition guards**—boolean expressions that prevent the transition if any evaluate to true.

## Next Steps

Ready to take your model to the next level? Here are a few ways you can extend and improve your satellite model:

1. **Add a Hazard:**
   * Identify a potential failure mode, such as "Reaction wheel failure causes loss of attitude control."
   * Document the hazard severity, likelihood, and mitigation strategies.
   * See [Hazards](/feature-guides/hazards) for more details.

2. **Create a Test Case:**
   * Define a test case to verify the settling time requirement under different initial conditions.
   * Specify test inputs (e.g., commanded attitude of 90° on each axis) and expected outcomes.
   * See [Test Cases](/feature-guides/test-cases) for more details.

3. **Run a Trade Study:**
   * Compare different control gain configurations (`Kp`, `Kd`) to find the optimal balance between settling time and power consumption.
   * Use the trade study feature to evaluate alternatives side-by-side.
   * See [Trade Studies](/feature-guides/trade-studies) for more details.

4. **Parameterize Control Gains & Reaction Wheel Moments:**
   * Add the proportional (`Kp`) and derivative (`Kd`) gains as inputs to the **Point Satellite** action instead of hard-coding them in the script.
   * Similarly, add the Reaction Wheel moments (`J_rw_x`, `J_rw_y`, `J_rw_z`) as inputs. This organizes your parameters logically, making them easy to find and tune as your model grows in complexity.

5. **Model Command Inputs from Ground Control:**
   * Add a new part called **Ground Control** at the same level as the **Satellite**.
   * Connect the parts together to form an *interface* and add *connections* for `a_cmd_x`, `a_cmd_y`, and `a_cmd_z` instead of assigning them as inputs to the action.
   * In your script, use `getConnection` to access these command variables instead of `getInput`.

6. **Set and Reset the Satellite's Current Attitude:**
   * Add `attitude_x`, `attitude_y`, and `attitude_z` attributes (0 deg) to the **Satellite** in the **Right Sidebar → Attributes**.
   * Update the **Point Satellite** script to fetch these values and use them as our initial attitude instead of hard-coding the initial attitude to `(0,0,0)` (see line 22).
   * At the bottom of the **Point Satellite** script, use `setOutput` to write back the final `attitude_x`, `attitude_y`, and `attitude_z` (degrees).
   * In the actions view inside **Satellite**, create an action named "Reset Attitude" that sets the three attitude attributes back to `0`.
   * In the states view inside **Satellite**, add a **Reset Satellite** transition from `SURVEILLING` to `STANDBY`, and set `STANDBY`’s **Entry Action** to execute **Reset Attitude**.
   * Now, transition the **Satellite** back and forth between `SURVEILLING` and `STANDBY` to see its attitude change.

These improvements will make your model more realistic and modular. See if you can implement them—and experiment with different values and configurations to see how your system responds!
