Skip to content

Commit ae15144

Browse files
Phase 6.2 complete: added mass_spring_damper model with curated mechanical units, compiler signatures, tests, example, docs updates, and fixed a unit-contract bug in Example 2
1 parent cb42a2b commit ae15144

21 files changed

Lines changed: 885 additions & 85 deletions

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ set(FLUXGRAPH_SOURCES
4848
src/model/thermal_rc2.cpp
4949
src/model/first_order_process.cpp
5050
src/model/second_order_process.cpp
51+
src/model/mass_spring_damper.cpp
5152
src/graph/compiler.cpp
5253
src/engine.cpp
5354
)

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ int main() {
129129
- [`05_thermal_rc2`](./examples/05_thermal_rc2/) - Two-node thermal RC model example
130130
- [`06_first_order_process`](./examples/06_first_order_process/) - First-order process primitive (PT1)
131131
- [`07_second_order_process`](./examples/07_second_order_process/) - Second-order process primitive (PT2)
132+
- [`08_mass_spring_damper`](./examples/08_mass_spring_damper/) - Translational mass-spring-damper model example
132133

133134
## Project Structure
134135

docs/api-reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ spec.models.push_back(model);
251251
- `thermal_rc2` - Two-node thermal RC network (coupled temperatures)
252252
- `first_order_process` - First-order process primitive (PT1)
253253
- `second_order_process` - Second-order process primitive (PT2)
254+
- `mass_spring_damper` - Translational mass-spring-damper (mechanical)
254255

255256
#### RuleSpec
256257

docs/schema-json.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,44 @@ Second-order process primitive:
204204
}
205205
```
206206

207+
### MassSpringDamper Model
208+
209+
Translational single-degree-of-freedom mass-spring-damper:
210+
`m*x'' + c*x' + k*x = F`
211+
212+
**Parameters:**
213+
| Parameter | Type | Units | Description |
214+
|-----------|------|-------|-------------|
215+
| `position_signal` | string | m | Output position signal path |
216+
| `velocity_signal` | string | m/s | Output velocity signal path |
217+
| `force_signal` | string | N | Input force signal path |
218+
| `mass` | number | kg | Mass (must be > 0) |
219+
| `damping_coeff` | number | N*s/m | Damping coefficient (must be >= 0) |
220+
| `spring_constant` | number | N/m | Spring constant (must be >= 0) |
221+
| `initial_position` | number | m | Initial position |
222+
| `initial_velocity` | number | m/s | Initial velocity |
223+
| `integration_method` | string | - | Optional: `"forward_euler"` (default) or `"rk4"` |
224+
225+
**Example:**
226+
227+
```json
228+
{
229+
"id": "msd",
230+
"type": "mass_spring_damper",
231+
"params": {
232+
"position_signal": "msd.x",
233+
"velocity_signal": "msd.v",
234+
"force_signal": "msd.F",
235+
"mass": 1.0,
236+
"damping_coeff": 2.0,
237+
"spring_constant": 20.0,
238+
"initial_position": 0.0,
239+
"initial_velocity": 0.0,
240+
"integration_method": "rk4"
241+
}
242+
}
243+
```
244+
207245
## Edges
208246

209247
Edges connect signals through transforms, defining the dataflow graph.

docs/schema-yaml.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,42 @@ models:
198198
integration_method: rk4
199199
```
200200

201+
### MassSpringDamper Model
202+
203+
Translational single-degree-of-freedom mass-spring-damper:
204+
`m*x'' + c*x' + k*x = F`
205+
206+
**Parameters:**
207+
| Parameter | Type | Units | Description |
208+
|-----------|------|-------|-------------|
209+
| `position_signal` | string | m | Output position signal path |
210+
| `velocity_signal` | string | m/s | Output velocity signal path |
211+
| `force_signal` | string | N | Input force signal path |
212+
| `mass` | number | kg | Mass (must be > 0) |
213+
| `damping_coeff` | number | N*s/m | Damping coefficient (must be >= 0) |
214+
| `spring_constant` | number | N/m | Spring constant (must be >= 0) |
215+
| `initial_position` | number | m | Initial position |
216+
| `initial_velocity` | number | m/s | Initial velocity |
217+
| `integration_method` | string | - | Optional: `forward_euler` (default) or `rk4` |
218+
219+
**Example:**
220+
221+
```yaml
222+
models:
223+
- id: msd
224+
type: mass_spring_damper
225+
params:
226+
position_signal: msd.x
227+
velocity_signal: msd.v
228+
force_signal: msd.F
229+
mass: 1.0
230+
damping_coeff: 2.0
231+
spring_constant: 20.0
232+
initial_position: 0.0
233+
initial_velocity: 0.0
234+
integration_method: rk4
235+
```
236+
201237
## Edges
202238

203239
Edges connect signals through transforms, defining the dataflow graph.

examples/02_thermal_mass/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ int main() {
4949
auto temp_noisy_sig = sig_ns.resolve("chamber.temperature_noisy");
5050

5151
// 5. Initialize ambient temperature
52-
store.write(ambient_sig, 20.0, "celsius");
52+
store.write(ambient_sig, 20.0, "degC");
5353

5454
// 6. Simulation: heat chamber for 5 seconds, then turn off
5555
std::cout << "Thermal Mass Simulation\n";
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
add_executable(example_mass_spring_damper main.cpp)
2+
target_link_libraries(example_mass_spring_damper PRIVATE fluxgraph)
3+
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#include <fluxgraph/core/namespace.hpp>
2+
#include <fluxgraph/core/signal_store.hpp>
3+
#include <fluxgraph/engine.hpp>
4+
#include <fluxgraph/graph/compiler.hpp>
5+
#include <iomanip>
6+
#include <iostream>
7+
8+
int main() {
9+
fluxgraph::SignalNamespace sig_ns;
10+
fluxgraph::FunctionNamespace func_ns;
11+
fluxgraph::SignalStore store;
12+
13+
fluxgraph::GraphSpec spec;
14+
15+
fluxgraph::ModelSpec model;
16+
model.id = "msd";
17+
model.type = "mass_spring_damper";
18+
model.params["position_signal"] = std::string("msd.position_m");
19+
model.params["velocity_signal"] = std::string("msd.velocity_m_s");
20+
model.params["force_signal"] = std::string("msd.force_N");
21+
model.params["mass"] = 1.0; // kg
22+
model.params["damping_coeff"] = 2.0; // N*s/m
23+
model.params["spring_constant"] = 20.0; // N/m
24+
model.params["initial_position"] = 0.0; // m
25+
model.params["initial_velocity"] = 0.0; // m/s
26+
model.params["integration_method"] = std::string("rk4");
27+
spec.models.push_back(model);
28+
29+
fluxgraph::GraphCompiler compiler;
30+
auto program = compiler.compile(spec, sig_ns, func_ns);
31+
32+
fluxgraph::Engine engine;
33+
engine.load(std::move(program));
34+
35+
const auto force_id = sig_ns.resolve("msd.force_N");
36+
const auto position_id = sig_ns.resolve("msd.position_m");
37+
const auto velocity_id = sig_ns.resolve("msd.velocity_m_s");
38+
39+
std::cout << "Mass-Spring-Damper Step Response\n";
40+
std::cout << "================================\n";
41+
std::cout << std::fixed << std::setprecision(4);
42+
43+
constexpr double dt = 0.01;
44+
for (double t = 0.0; t <= 5.0; t += dt) {
45+
const double force = (t < 2.5) ? 1.0 : 0.0;
46+
store.write(force_id, force, "N");
47+
48+
engine.tick(dt, store);
49+
50+
if (static_cast<int>(t / dt) % 50 == 0) {
51+
const double x = store.read_value(position_id);
52+
const double v = store.read_value(velocity_id);
53+
std::cout << "t=" << std::setw(5) << t << " s "
54+
<< "F=" << std::setw(6) << force << " N "
55+
<< "x=" << std::setw(8) << x << " m "
56+
<< "v=" << std::setw(8) << v << " m/s\n";
57+
}
58+
}
59+
60+
return 0;
61+
}
62+

examples/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# Example 5: Thermal RC2 - Coupled two-node thermal model
88
# Example 6: First-order process (dimensionless PT1)
99
# Example 7: Second-order process (dimensionless PT2)
10+
# Example 8: Mass-spring-damper (translational mechanical model)
1011

1112
add_subdirectory(01_basic_transform)
1213
add_subdirectory(02_thermal_mass)
@@ -15,3 +16,4 @@ add_subdirectory(04_yaml_graph)
1516
add_subdirectory(05_thermal_rc2)
1617
add_subdirectory(06_first_order_process)
1718
add_subdirectory(07_second_order_process)
19+
add_subdirectory(08_mass_spring_damper)

examples/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,22 @@ Demonstrates a canonical second-order process primitive:
213213
./build/examples/07_second_order_process/Debug/example_second_order_process.exe
214214
```
215215

216+
## Example 8: Mass-Spring-Damper (Mechanical)
217+
218+
**Location:** `08_mass_spring_damper/`
219+
220+
Demonstrates a simple translational mechanical model:
221+
222+
- `mass_spring_damper` model with physical parameters (`mass`, `damping_coeff`, `spring_constant`)
223+
- Force input contract (`N`) and position/velocity outputs (`m`, `m/s`)
224+
- Step force response
225+
226+
**Run:**
227+
228+
```bash
229+
./build/examples/08_mass_spring_damper/Debug/example_mass_spring_damper.exe
230+
```
231+
216232
## When to Use Each Approach
217233

218234
### Manual GraphSpec (Examples 1 & 2)

0 commit comments

Comments
 (0)