In this example we will connect multiple modules to create a system, rather than solving a system in a single module.
This manner of solving differential equations is what makes Ascent so powerful.
By designing our modules in this manner, we will be able to connect any number of springs, masses, and dampers without having to build a single state space matrix.
Our system will have two bodies, one a fixed wall and the other a free mass that is attached to the wall by a spring and a damper.
Body.h
#pragma once #include "ascent/Link.h" class Body : public asc::Module { public: double s{}; // position double v{}; // velocity double a{}; // acceleration double m{}; // mass double f{}; // force Body(size_t sim); void update(); void reset(); };
Body.cpp
#include "Body.h" using namespace asc; Body::Body(size_t sim) : Module(sim) { #define ascNS Body ascModule(Body) ascVar(s) ascVar(v) ascVar(a) ascVar(m) ascVar(f) addIntegrator(s, v); addIntegrator(v, a); } void Body::update() { if (m != 0.0) a = f / m; } void Body::reset() { f = 0.0; }
Damper.h
#pragma once #include "Body.h" #include "ascent/Link.h" class Damper : public asc::Module { public: asc::Link<Body> b0; asc::Link<Body> b1; double c{}; // damping coefficient double f{}; // force Damper(size_t sim) : Damper(sim, b0, b1) {} Damper(size_t sim, const asc::Link<Body>& b0, const asc::Link<Body>& b1); void init(); void update(); };
Link<>
container comes into use whenever modules are to be interconnected. It enforces proper ordering and handles shared memory.
Damper.cpp
#include "Damper.h" Damper::Damper(size_t sim, const asc::Link<Body>& b0, const asc::Link<Body>& b1) : asc::Module(sim), b0(b0), b1(b1) { #define ascNS Damper ascModule(Damper) ascVar(c) ascLink(b0) ascLink(b1) } void Damper::init() { runBefore(b0, b1); } void Damper::update() { double dv = b0->v - b1->v; f = c*dv; b0->f -= f; b1->f += f; }
Spring.h
#pragma once #include "Body.h" #include "ascent/Link.h" class Spring : public asc::Module { public: asc::Link<Body> b0; asc::Link<Body> b1; double l0{}; // initial spring length (distance between masses) double k{}; // spring coefficient double f{}; // force Spring(size_t sim) : Spring(sim, b0, b1) {} Spring(size_t sim, const asc::Link<Body>& b0, const asc::Link<Body>& b1); void init(); void update(); };
Spring.cpp
#include "Spring.h" Spring::Spring(size_t sim, const asc::Link<Body>& b0, const asc::Link<Body>& b1) : asc::Module(sim), b0(b0), b1(b1) { #define ascNS Spring ascModule(Spring) ascVar(k) ascLink(b0) ascLink(b1) } void Spring::init() { runBefore(b0, b1); l0 = b1->s - b0->s; } void Spring::update() { double ds = l0 + b0->s - b1->s; f = k*ds; b0->f -= f; b1->f += f; }
Main.cpp
#include "Damper.h" #include "Spring.h" int main() { const size_t sim = 0; asc::Link<Body> body0(sim); asc::Link<Body> body1(sim); body0.name("body0"); body1.name("body1"); asc::Link<Spring> spring(sim, body0, body1); spring.name("spring"); asc::Link<Damper> damper(sim, body0, body1); damper.name("damper"); body1->s = 1.0; // initial position body1->v = 40.0; // initial velocity body1->m = 1.0; // mass spring->k = 2000.0; // spring constant damper->c = 5.0; // damping constant asc::Module tracker(0); // create another Module to track variables tracker.name<asc::Module>("b0vsb1"); tracker.track("t"); tracker.track(body0, "s"); tracker.track(body1, "s"); spring->run(0.01, 1.5); tracker.outputTrack(); return 0; }