Trajectories
This commit is contained in:
319
docs/SOLVER.md
Normal file
319
docs/SOLVER.md
Normal file
@@ -0,0 +1,319 @@
|
||||
# Solver Guide
|
||||
|
||||
## Overview
|
||||
|
||||
The **Equation Solver** is a constraint-based computational engine that automatically solves rocket propulsion equations. Rather than manually plugging numbers into formulas, users specify constraints (inputs), and the solver propagates solutions through an interconnected equation network.
|
||||
|
||||
**Key Concept**: Drag variables onto the workspace, set values, and the solver automatically computes unknowns.
|
||||
|
||||
## User Interface
|
||||
|
||||
### Variable Palette (Left)
|
||||
- Searchable list of all variables
|
||||
- Organized by category (thrust, Isp, chamber, etc.)
|
||||
- Drag variables onto workspace
|
||||
|
||||
### Workspace (Center)
|
||||
- Drop zone for constraint cards
|
||||
- Cards show variable name, unit, input field
|
||||
- Set constraint by entering value
|
||||
- Results auto-compute below
|
||||
|
||||
### Results Panel (Right)
|
||||
- Shows all computed values
|
||||
- Organized by category
|
||||
- Clickable to copy values
|
||||
- Unit selector for each variable
|
||||
|
||||
## Constraint Propagation Algorithm
|
||||
|
||||
The solver implements **greedy propagation**:
|
||||
|
||||
### Step-by-Step
|
||||
|
||||
1. **User Input**: Sets constraint on variable `x`
|
||||
2. **Find Equations**: Locates all equations referencing `x`
|
||||
3. **Attempt Solve**: For each equation, tries to solve for unknowns
|
||||
- If equation has 1 unknown: solve directly
|
||||
- If equation has 2+ unknowns: skip (not enough constraints)
|
||||
4. **Update State**: Newly-solved variables are added to known set
|
||||
5. **Recurse**: Repeat step 2 with newly-known variables
|
||||
6. **Stop**: When no new variables can be computed
|
||||
|
||||
### Example
|
||||
|
||||
**Given:**
|
||||
- `thrust = massFlowRate * exitVelocity`
|
||||
- `exitVelocity = sqrt(2 * Cp * (T0 - Te))`
|
||||
- `massFlowRate = densityO2 * densityFuel * burnRate`
|
||||
|
||||
**User Constraints:**
|
||||
- `thrust = 50000 N`
|
||||
- `massFlowRate = 100 kg/s`
|
||||
|
||||
**Solver Flow:**
|
||||
```
|
||||
1. Set thrust = 50000
|
||||
2. Find equations with thrust: Eq1
|
||||
3. Eq1 has 2 unknowns (massFlowRate, exitVelocity) → skip
|
||||
4. Set massFlowRate = 100
|
||||
5. Find equations with massFlowRate: Eq1, Eq3
|
||||
6. Eq1 now has 1 unknown (exitVelocity) → solve: exitVelocity = 500 m/s
|
||||
7. Find equations with exitVelocity: Eq2
|
||||
8. Eq2 has 2 unknowns (T0, Te) → skip
|
||||
9. No new variables → done
|
||||
```
|
||||
|
||||
## Variable Definitions
|
||||
|
||||
Variables are defined in `src/engine/variables.js` with metadata:
|
||||
|
||||
```javascript
|
||||
{
|
||||
id: 'thrust',
|
||||
name: 'Thrust',
|
||||
unit: 'N',
|
||||
category: 'performance',
|
||||
type: 'number'
|
||||
}
|
||||
```
|
||||
|
||||
**Categories:**
|
||||
- `thrust` — Force (N, lbf)
|
||||
- `isp` — Specific impulse (s, m/s)
|
||||
- `chamber` — Chamber conditions (P, T, density)
|
||||
- `nozzle` — Nozzle geometry (area, diameter, angle)
|
||||
- `propellant` — Fuel/oxidizer properties
|
||||
- `flow` — Mass flow rate, burn rate
|
||||
- `thermal` — Heat, temperature
|
||||
- `structural` — Stress, strain, thickness
|
||||
|
||||
## Equation Library
|
||||
|
||||
Equations are defined in `src/engine/equations.js`. Each equation:
|
||||
|
||||
1. **Specifies variables** it relates
|
||||
2. **Implements forward solver** (compute output given inputs)
|
||||
3. **Implements per-variable solvers** (solve for each variable given others)
|
||||
|
||||
### Example Equation
|
||||
|
||||
```javascript
|
||||
{
|
||||
id: 'thrustEquation',
|
||||
name: 'Thrust (Momentum)',
|
||||
variables: ['thrust', 'massFlowRate', 'exitVelocity'],
|
||||
equation: 'thrust = massFlowRate * exitVelocity',
|
||||
forward: (inputs) => {
|
||||
return inputs.massFlowRate * inputs.exitVelocity
|
||||
},
|
||||
solvers: {
|
||||
exitVelocity: (inputs) => {
|
||||
return inputs.thrust / inputs.massFlowRate
|
||||
},
|
||||
thrust: (inputs) => {
|
||||
return inputs.massFlowRate * inputs.exitVelocity
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Transcendental Equations (Bisection)
|
||||
|
||||
For equations with no closed-form solution (e.g., Mach from area ratio), the solver uses **bisection**:
|
||||
|
||||
```javascript
|
||||
// In numerics.js
|
||||
function bisection(fn, target, min, max, tolerance) {
|
||||
while (max - min > tolerance) {
|
||||
const mid = (min + max) / 2
|
||||
const fMid = fn(mid)
|
||||
if (fMid < target) {
|
||||
min = mid
|
||||
} else {
|
||||
max = mid
|
||||
}
|
||||
}
|
||||
return (min + max) / 2
|
||||
}
|
||||
```
|
||||
|
||||
Example: Finding Mach number from area ratio
|
||||
```javascript
|
||||
solvers: {
|
||||
machNumber: (inputs) => {
|
||||
const fn = (M) => isentropicAreaRatio(M, inputs.gamma)
|
||||
return bisection(fn, inputs.areaRatio, 0.01, 10, 1e-6)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Equations
|
||||
|
||||
### Thrust
|
||||
```
|
||||
thrust = mass_flow_rate * exit_velocity
|
||||
thrust = chamber_pressure * throat_area * CF
|
||||
```
|
||||
|
||||
### Specific Impulse
|
||||
```
|
||||
Isp = exit_velocity / g
|
||||
Isp_vac = exit_velocity / g
|
||||
Isp_sl = (exit_velocity - P_exit / mass_flow_rate * area_exit) / g
|
||||
```
|
||||
|
||||
### Chamber Conditions (Isentropic Flow)
|
||||
```
|
||||
T_exit = T_chamber * (P_exit / P_chamber)^((gamma - 1) / gamma)
|
||||
rho_exit = rho_chamber * (P_exit / P_chamber)^(1 / gamma)
|
||||
```
|
||||
|
||||
### Nozzle Area Ratio
|
||||
```
|
||||
A_exit / A_throat = (2 / (gamma + 1)) * (1 + (gamma - 1)/2 * M_exit^2))^((gamma + 1) / (2 * (gamma - 1))) / M_exit
|
||||
```
|
||||
(requires bisection to invert for M)
|
||||
|
||||
### Characteristic Velocity
|
||||
```
|
||||
c_star = P_chamber * A_throat / mass_flow_rate
|
||||
```
|
||||
|
||||
### Thrust Coefficient
|
||||
```
|
||||
CF = sqrt(2 * gamma^2 / (gamma - 1) * (2 / (gamma + 1))^((gamma + 1) / (gamma - 1)) * (1 - (P_exit / P_chamber)^((gamma - 1) / gamma)))
|
||||
```
|
||||
|
||||
## Workflow Examples
|
||||
|
||||
### Example 1: Determine Exit Velocity from Isp
|
||||
|
||||
**Goal:** Calculate exit velocity given Isp
|
||||
|
||||
**Steps:**
|
||||
1. Drag `specificImpulse` onto workspace
|
||||
2. Set `specificImpulse = 300 s`
|
||||
3. Solver computes `exitVelocity = 300 * 9.81 ≈ 2943 m/s`
|
||||
|
||||
### Example 2: Find Chamber Pressure from Thrust
|
||||
|
||||
**Goal:** Find chamber pressure needed for 50 kN thrust
|
||||
|
||||
**Steps:**
|
||||
1. Drag `thrust`, `throatDiameter`, `massFlowRate` onto workspace
|
||||
2. Set:
|
||||
- `thrust = 50000 N`
|
||||
- `throatDiameter = 0.1 m`
|
||||
- `massFlowRate = 100 kg/s`
|
||||
3. Solver finds equations with these variables
|
||||
4. Uses thrust equation to find thrust coefficient needed
|
||||
5. Inverts nozzle area ratio with bisection to find Mach
|
||||
6. Uses isentropic relations to find chamber pressure
|
||||
7. Displays `chamberPressure = X bar`
|
||||
|
||||
### Example 3: Design for Specific Isp
|
||||
|
||||
**Goal:** Find propellant combination for Isp = 350 s
|
||||
|
||||
**Steps:**
|
||||
1. Drag `specificImpulse`, `chamberTemperature`, `exhaustGamma` onto workspace
|
||||
2. Set `specificImpulse = 350 s`
|
||||
3. Click on knowledgebase to find propellant with T0 ≈ 3500 K
|
||||
4. Enter that temperature
|
||||
5. Solver computes `exitVelocity`, then works backward to required conditions
|
||||
6. Compare against available propellants
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### Unit Conversion
|
||||
Every variable card has a unit selector. Setting a variable automatically converts:
|
||||
```
|
||||
thrust = 50000 N → 11240.44 lbf
|
||||
thrust = 11240.44 lbf → 50000 N
|
||||
```
|
||||
|
||||
### Importing Engine Design
|
||||
Engine design results can be fed into the solver:
|
||||
- File → Import Engine JSON
|
||||
- Automatically populates thrust, Isp, chamber pressure, etc.
|
||||
- Enables end-to-end rocket design workflow
|
||||
|
||||
### Variable Categories
|
||||
Filter variables by category to reduce clutter:
|
||||
- Thrust (F, Isp, c*)
|
||||
- Chamber (P, T, density)
|
||||
- Nozzle (area, geometry)
|
||||
- Propellant (fuel/oxidizer properties)
|
||||
- Flow (mass flow, burn rate)
|
||||
|
||||
## Limitations & Gotchas
|
||||
|
||||
1. **No Circular Dependencies**: If equations form a cycle, solver may loop infinitely
|
||||
- Solver stops after N iterations to prevent this
|
||||
- Design equations to be acyclic where possible
|
||||
|
||||
2. **Insufficient Constraints**: If variables are underdetermined (more unknowns than equations)
|
||||
- Solver computes what it can, leaves rest as unknown
|
||||
- User must provide more constraints
|
||||
|
||||
3. **Overdetermined System**: If too many constraints (conflicting values)
|
||||
- Solver uses first constraint encountered
|
||||
- Other constraints ignored (may warn in future)
|
||||
|
||||
4. **Transcendental Equation Tolerance**: Bisection has numerical error
|
||||
- Default tolerance: 1e-6
|
||||
- Results accurate to ~6 significant figures
|
||||
- Adjust in `numerics.js` if higher precision needed
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Solver not computing?
|
||||
- Ensure at least one constraint is set
|
||||
- Check that equation network connects the variables you're trying to solve
|
||||
- Look for missing material properties (fuel, oxidizer data)
|
||||
|
||||
### Result seems wrong?
|
||||
- Verify input units match variable definition
|
||||
- Check variable category (is it the right variable?)
|
||||
- Try solving step-by-step with intermediate variables
|
||||
|
||||
### Equation not available?
|
||||
- Not all rocketry equations are implemented
|
||||
- Submit a feature request to add more
|
||||
- Or add the equation yourself in `src/engine/equations.js`
|
||||
|
||||
## Development Guide
|
||||
|
||||
### Adding a New Equation
|
||||
|
||||
1. **Define variables** in `variables.js` if needed
|
||||
2. **Implement equation** in `equations.js`:
|
||||
|
||||
```javascript
|
||||
equations.push({
|
||||
id: 'myEquation',
|
||||
name: 'My Equation',
|
||||
variables: ['var1', 'var2', 'var3'],
|
||||
equation: 'var1 = var2 * var3',
|
||||
solvers: {
|
||||
var1: (inputs) => inputs.var2 * inputs.var3,
|
||||
var2: (inputs) => inputs.var1 / inputs.var3,
|
||||
var3: (inputs) => inputs.var1 / inputs.var2
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
3. **Test** by:
|
||||
- Setting constraints manually
|
||||
- Verifying solver produces correct result
|
||||
- Testing with known values
|
||||
|
||||
### Debugging Solver
|
||||
- Add console logs in `solver.js` propagation loop
|
||||
- Log variable state at each iteration
|
||||
- Verify equation network is correct
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-02 | **Status**: Current
|
||||
Reference in New Issue
Block a user