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;
}