Add new stand alone numerical integration library SAIntegrator #1056 #936

This commit is contained in:
Penn, John M 047828115 2020-09-27 18:36:49 -05:00
parent b2ec2313e8
commit cb3869a36c
22 changed files with 1464 additions and 0 deletions

View File

@ -0,0 +1,255 @@
# Stand-Alone Integration Library
## Contents
* [Introduction](#Introduction)
* [class Integrator](#class-Integrator)
* [typedef derivsFunc](#typedef-derivsFunc)
* [class FirstOrderODEIntegrator](#class-FirstOrderODEIntegrator)
* [class FirstOrderODEVariableStepIntegrator](#class-FirstOrderODEVariableStepIntegrator)
* [class EulerIntegrator](#class-EulerIntegrator)
* [class HeunsMethod](#class-HeunsMethod)
* [class RK2Integrator](#class-RK2Integrator)
* [class RK4Integrator](#class-RK4Integrator)
* [class RK3_8Integrator](#class-RK3_8Integrator)
* [class EulerCromerIntegrator](#class-EulerCromerIntegrator)
* [class ABM2Integrator](#class-ABM2Integrator)
* [class ABM4Integrator](#class-ABM4Integrator)
<a id=Introduction></a>
## Introduction
The Stand-Alone Integration Library can be used within a Trick simulation, or independent of it.
Some examples of using these integrators can be found in the **examples/** directory.
* [CannonBall](examples/CannonBall/README.md) uses the RK2Integrator.
* [MassSpringDamper](examples/MassSpringDamper/README.md) uses the EulerCromerIntegrator.
* [Orbit](examples/Orbit/README.md) uses the EulerCromerIntegrator.
<a id=class-Integrator></a>
## class Integrator
### Description
This class represents an **integrator**.
### Constructor
#### ```Integrator(double dt, void* user_data);```
|Parameter|Type |Description|
|---------|------------|--------------------|
|dt |```double```|Default time step value|
|user_data|```void*```| A pointer to user defined data that will be passed to a derivsFunc when called by the Integrator. |
### Member Functions
#### ```virtual void step();```
Increment time by the default time-step.
#### ```virtual void load() = 0;```
Load the input state.
#### ```virtual void unload() = 0;```
Load the output state.
#### ```double getTime();```
Return the integrator time.
<a id=typedef-derivsFunc></a>
## typedef derivsFunc
```
typedef void (*derivsFunc)( double t, double state[], double derivs[], void* user_data);
```
where:
|Parameter|Type|Description|
|---|---|---|
|t|```double```| time|
|state|```double*```| (input) an array of state variable values |
|derivs|```double*```| (output) an array into which derivatives are to be returned|
|user_data|```void*```| a pointer to user_data |
#### Example
```
void my_derivs( double t, double state[], double deriv[], void* udata) { ... }
```
<a id=class-FirstOrderODEIntegrator></a>
## class FirstOrderODEIntegrator
Derived from [Integrator](#class-Integrator).
### Description
This class represents an integrator for a first order [Ordinary Differential Equation (ODE)]([https://en.wikipedia.org/wiki/Ordinary_differential_equation).
### Constructor
```
FirstOrderODEIntegrator( double dt,
int N,
double* in_vars[],
double* out_vars[],
derivsFunc func,
void* user_data);
```
where:
<a id=FOODEConstructorParameters></a>
|Parameter|Type|Description|
|---|---|---|
|dt|```double```|Default time step value|
|N|```int```|Number of state variables to be integrated|
|in_vars|```double*```|Array of pointers to the state variables from which we ```load()``` the integrator state (```in_vars``` and ```out_vars``` will generally point to the same array of pointers.)|
|out_vars|```double*```|Array of pointers to the state variables to which we ```unload()``` the integrator state (```in_vars``` and ```out_vars``` will generally point to the same array of pointers.)|
| derivs_func |[derivsFunc](#typedef-derivsFunc)| A function that returns |
|user_data|```void*```| A pointer to user defined data that will be passed to a derivsFunc when called by the Integrator. |
### Member Functions
#### void step( double dt )
Integrate over time step specified by **dt**.
|Parameter|Type|Description|
|---|---|---|
|dt | double| A variable time-step |
#### virtual void undo_step()
If the integrator has not already been reset then :
1. Decrement time by the last time-step dt.
2. Copy the input state back to the origin variables.
3. Set the integrator's 'reset' mode to true.
Calling load() sets the 'reset' mode to false.
#### void step()
Integrate over the default time-step.
#### void load()
Load the integrator's initial state from the variables specified by **in_vars**.
#### void unload();
Unload the integrator's result state to the variables specified by **out_vars**.
<a id=class-FirstOrderODEVariableStepIntegrator></a>
## class FirstOrderODEVariableStepIntegrator
### Description
This class represents an integrator for a first order ODE, that be
### Constructor
### Member Functions
#### virtual void step( double dt );
<a id=class-EulerIntegrator></a>
## class EulerIntegrator
Derived from [FirstOrderODEIntegrator](#class-FirstOrderODEIntegrator).
### Description
The Euler method is a first order numerical integration method. It is the simplest, explicit RungeKutta method.
### Constructor
```
EulerIntegrator(double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func, void* user_data)
```
[Constructor Parameters](#FOODEConstructorParameters) are those [FirstOrderODEIntegrator](#class-FirstOrderODEIntegrator).
<a id=class-HeunsMethod></a>
## class HeunsMethod
Derived from [FirstOrderODEIntegrator](#class-FirstOrderODEIntegrator).
### Description
This integrator implements
[Heun's Method](https://en.wikipedia.org/wiki/Heun%27s_method).
### Constructor
```
HeunsMethod( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func, void* user_data)
```
[Constructor Parameters](#FOODEConstructorParameters) are those [FirstOrderODEIntegrator](#class-FirstOrderODEIntegrator).
<a id=class-RK2Integrator></a>
## class RK2Integrator
Derived from [FirstOrderODEIntegrator](#class-FirstOrderODEIntegrator).
### Description
The Runga-Kutta-2 method is a second order, explicit, numerical integration method.
### Constructor
```
RK2Integrator( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func, void* user_data)
```
[Constructor Parameters](#FOODEConstructorParameters) are those [FirstOrderODEIntegrator](#class-FirstOrderODEIntegrator).
<a id=class-RK4Integrator></a>
## class RK4Integrator
Derived from [FirstOrderODEIntegrator](#class-FirstOrderODEIntegrator).
### Description
The Runga-Kutta-4 method is a fourth order, explicit, numerical integration method.
### Constructor
```
RK4Integrator( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func, void* user_data)
```
[Constructor Parameters](#FOODEConstructorParameters) are those [FirstOrderODEIntegrator](#class-FirstOrderODEIntegrator).
<a id=class-RK3_8Integrator></a>
## class RK3_8Integrator
Derived from [FirstOrderODEIntegrator](#class-FirstOrderODEIntegrator).
### Description
The Runga-Kutta-3/8 method is a fourth order, explicit, numerical integration method.
### Constructor
```
RK3_8Integrator( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func, void* user_data)
```
[Constructor Parameters](#FOODEConstructorParameters) are those [FirstOrderODEIntegrator](#class-FirstOrderODEIntegrator).
<a id=class-ABM2Integrator></a>
## class ABM2Integrator
Derived from [FirstOrderODEIntegrator](#class-FirstOrderODEIntegrator).
### Description
The ABM2Integrator implements the second-order Adams-Bashforth-Moulton predictor/corrector method. Adams methods maintain a history of derivatives rather than calculating intermediate values like Runga-Kutta methods.
### Constructor
```
ABM2Integrator ( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func, void* user_data)
```
[Constructor Parameters](#FOODEConstructorParameters) are those [FirstOrderODEIntegrator](#class-FirstOrderODEIntegrator).
<a id=class-ABM4Integrator></a>
## class ABM4Integrator
Derived from [FirstOrderODEIntegrator](#class-FirstOrderODEIntegrator).
### Description
The ABM2Integrator implements the second-order Adams-Bashforth-Moulton predictor/corrector method. Adams methods maintain a history of derivatives rather than calculating intermediate values like Runga-Kutta methods.
### Constructor
```
ABM4Integrator ( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func, void* user_data)
```
[Constructor Parameters](#FOODEConstructorParameters) are those [FirstOrderODEIntegrator](#class-FirstOrderODEIntegrator).
<a id=class-EulerCromerIntegrator></a>
## EulerCromerIntegrator
Derived from [class-Integrator](#class-Integrator).
### Description
EulerCromer is integration method that conserves energy in oscillatory systems better than Runge-Kutta. So, it's good for mass-spring-damper systems, and orbital systems.
### Constructor
```
SemiImplicitEuler(double dt, int N, double* xp[], double* vp[], derivsFunc gfunc, derivsFunc ffunc, void* user_data)
```
|Parameter|Type |Description|
|-----------|-------------|-----------------------|
| dt |```double``` |Default time step value|
| N |```int``` |Number of state variables to be integrated|
| xp |```double*```|Array of pointers to the variables from which we ```load()``` and to which we ```unload()``` the integrator's position values .|
| vp |```double*```|Array of pointers to the variables from which we ```load()``` and to which we ```unload()``` the integrator's velocity values .|
| gfunc |[derivsFunc](#typedef-derivsFunc)| A function that returns acceleration |
| ffunc |[derivsFunc](#typedef-derivsFunc)| A function that returns velocity |
|user_data |```void*```| A pointer to user defined data that will be passed to a derivsFunc when called by the Integrator. |

View File

@ -0,0 +1,45 @@
#include <math.h>
#include <stdio.h>
#include "SAIntegrator.hh"
struct Cannon {
double pos[2];
double vel[2];
};
void init_state ( Cannon& cannon ) {
cannon.pos[0] = 0.0;
cannon.pos[1] = 0.0;
cannon.vel[0] = 50.0 * cos(M_PI/6.0);
cannon.vel[1] = 50.0 * sin(M_PI/6.0);
}
void deriv( double t, double state[], double derivs[], void* udata) {
derivs[0] = state[2]; // vel_x
derivs[1] = state[3]; // vel_y
derivs[2] = 0.0; // acc_x
derivs[3] = -9.81; // acc_y
}
void print_header() {
printf ("t, cannon.pos[0], cannon.pos[1], cannon.vel[0], cannon.vel[1]\n");
}
void print_state( double t, Cannon& cannon) {
printf ("%5.3f, %10.10f, %10.10f, %10.10f, %10.10f\n", t, cannon.pos[0], cannon.pos[1], cannon.vel[0], cannon.vel[1]);
}
int main ( int argc, char* argv[]) {
Cannon cannon;
double* state_var_p[4] = { &(cannon.pos[0]), &(cannon.pos[1]), &(cannon.vel[0]), &(cannon.vel[1]) };
unsigned count = 0;
double dt = 0.01;
double t = count * dt;
SA::RK2Integrator integ(dt, 4, state_var_p, state_var_p, deriv, NULL);
init_state(cannon);
print_header();
print_state(t, cannon );
while (t < 5.1) {
integ.load();
integ.step();
integ.unload();
count ++;
t = count * dt;
print_state(t, cannon );
}
}

View File

@ -0,0 +1,11 @@
# CannonBall
This is an example of using the RK2Integrator to create a simple cannon ball simulation.
'''
$ make
$ ./CannonBall > cannon.csv
$ python plot_trajectory.py
'''
![Cannon](images/Cannon.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

View File

@ -0,0 +1,20 @@
RM = rm -rf
CC = cc
CPP = c++
CXXFLAGS = -g -Wall
INCLUDE_DIRS = -I../../include
LIBDIR = ../../lib
all: CannonBall
CannonBall:
$(CPP) $(CXXFLAGS) CannonBall.cpp ${INCLUDE_DIRS} -L${LIBDIR} -lSAInteg -o CannonBall
clean:
${RM} CannonBall.dSYM
spotless: clean
${RM} CannonBall
${RM} cannon.csv

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python
import matplotlib.pyplot as plt
import numpy as np
data = np.genfromtxt('cannon.csv',
delimiter=',',
skip_header=1,
skip_footer=1,
names=['t', 'px', 'py', 'vx', 'vx'],
dtype=(float, float, float, float, float)
)
curve1 = plt.plot(data['px'], data['py'], 'C1-')
plt.title('Cannonball Trajectory')
plt.xlabel('pos-x')
plt.ylabel('pos-y')
plt.grid(True)
plt.show()

View File

@ -0,0 +1,52 @@
#include <math.h>
#include <stdio.h>
#include "SAIntegrator.hh"
#define N_DIMENSIONS 1
struct MassSpringDamper {
double pos;
double vel;
double k; // Spring constant
double c; // Damping constant
double mass;
};
void init_state ( MassSpringDamper& msd ) {
msd.pos = 2.0;
msd.vel = 0.0;
msd.k = 1.0;
msd.c = 0.0;
msd.mass = 5.0;
}
void print_header() {
printf ("time, msd.pos, msd.vel\n");
}
void print_state( double t, MassSpringDamper& msd ) {
printf ("%10.10f, %10.10f, %10.10f\n", t, msd.pos, msd.vel);
}
void G( double t, double x[], double g_out[], void* udata) {
MassSpringDamper* msd = (MassSpringDamper*)udata;
g_out[0] = -(msd->k/msd->mass) * x[0];
}
void F( double t, double v[], double f_out[], void* udata) {
f_out[0] = v[0];
f_out[1] = v[1];
}
int main ( int argc, char* argv[]) {
MassSpringDamper msd;
double* x_p[N_DIMENSIONS] = { &msd.pos };
double* v_p[N_DIMENSIONS] = { &msd.vel };
unsigned count = 0;
double dt = 0.001;
double time = count * dt;
init_state(msd);
print_header();
print_state(time, msd);
SA::EulerCromerIntegrator integ(dt, N_DIMENSIONS, x_p, v_p, G, F, &msd);
while (time < 50) {
integ.load();
integ.step();
integ.unload();
count ++;
time = count * dt;
print_state(time, msd);
}
}

View File

@ -0,0 +1,22 @@
# MassSpringDamper
This program uses the SemiImplicitEuler integrator to simulate a simple mass-spring-damper.
Generate the results as follows:
'''
$ make
$ ./MassSpringDamper > msd.csv
'''
Plot the results as follows:
'''
$ python plot_position.py
'''
and
'''
$ python plot_velocity.py
'''
![MSD](images/MSD.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 KiB

View File

@ -0,0 +1,20 @@
RM = rm -rf
CC = cc
CPP = c++
CXXFLAGS = -g -Wall
INCLUDE_DIRS = -I../../include
LIBDIR = ../../lib
all: MassSpringDamper
MassSpringDamper: MassSpringDamper.cpp
$(CPP) $(CXXFLAGS) MassSpringDamper.cpp ${INCLUDE_DIRS} -L${LIBDIR} -lSAInteg -o MassSpringDamper
clean:
${RM} MassSpringDamper.dSYM
spotless: clean
${RM} MassSpringDamper
${RM} msd.csv

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python
import matplotlib.pyplot as plt
import numpy as np
data = np.genfromtxt('msd.csv',
delimiter=',',
skip_header=1,
skip_footer=1,
names=['t', 'pos', 'vel'],
dtype=(float, float, float)
)
curve1 = plt.plot(data['t'], data['pos'], 'C1-')
plt.title('MSD Position')
plt.xlabel('position')
plt.ylabel('time')
plt.grid(True)
plt.show()

View File

@ -0,0 +1,57 @@
#include <math.h>
#include <stdio.h>
#include "SAIntegrator.hh"
#define GRAVITATIONAL_CONSTANT 6.674e-11
#define EARTH_MASS 5.9723e24
#define EARTH_RADIUS 6367500.0
struct Orbit {
double pos[2];
double vel[2];
double planet_mass;
};
void init_state ( Orbit& orbit ) {
orbit.pos[0] = 0.0;
orbit.pos[1] = 6578000.0;
orbit.vel[0] = 7905.0;
orbit.vel[1] = 0.0;
orbit.planet_mass = EARTH_MASS;
}
void print_header() {
printf ("time, orbit.pos[0], orbit.pos[1], orbit.vel[0], orbit.vel[1]\n");
}
void print_state( double t, Orbit& orbit ) {
printf ("%10.10f, %10.10f, %10.10f, %10.10f, %10.10f\n",
t, orbit.pos[0], orbit.pos[1], orbit.vel[0], orbit.vel[1]);
}
void G( double t, double x[], double g_out[], void* udata) {
Orbit* orbit = (Orbit*)udata;
double d = sqrt( x[0]*x[0] + x[1]*x[1]);
g_out[0] = -x[0] * GRAVITATIONAL_CONSTANT * orbit->planet_mass / (d*d*d);
g_out[1] = -x[1] * GRAVITATIONAL_CONSTANT * orbit->planet_mass / (d*d*d);
}
void F( double t, double v[], double f_out[], void* udata) {
f_out[0] = v[0];
f_out[1] = v[1];
}
int main ( int argc, char* argv[]) {
Orbit orbit;
double* x_p[2] = { &orbit.pos[0], &orbit.pos[1] };
double* v_p[2] = { &orbit.vel[0], &orbit.vel[1] };
unsigned count = 0;
double dt = 0.1;
double time = count * dt;
init_state(orbit);
print_header();
print_state(time, orbit);
SA::EulerCromerIntegrator integ(dt, 2, x_p, v_p, G, F, &orbit);
while (time < 6000) {
integ.load();
integ.step();
integ.unload();
count ++;
time = count * dt;
print_state(time, orbit);
}
}

View File

@ -0,0 +1,17 @@
# Orbit
This program uses the *SemiImplicitEuler* integrator to simulate the orbit of an Earth satellite.
Generate the results as follows:
'''
$ make
$ ./Orbit > orbit.csv
'''
Plot the results as follows:
'''
$ python plot_position.py
'''
![Orbit](images/Orbit.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 KiB

View File

@ -0,0 +1,20 @@
RM = rm -rf
CC = cc
CPP = c++
CXXFLAGS = -g -Wall
INCLUDE_DIRS = -I../../include
LIBDIR = ../../lib
all: Orbit
Orbit: Orbit.cpp
$(CPP) $(CXXFLAGS) Orbit.cpp ${INCLUDE_DIRS} -L${LIBDIR} -lSAInteg -o Orbit
clean:
${RM} Orbit.dSYM
spotless: clean
${RM} Orbit
${RM} orbit.csv

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python
import matplotlib.pyplot as plt
import numpy as np
data = np.genfromtxt('orbit.csv',
delimiter=',',
skip_header=1,
skip_footer=1,
names=['t', 'posx','posy','velx','vely'],
dtype=(float, float, float, float, float)
)
curve1 = plt.plot(data['posx'], data['posy'], 'C1-')
plt.title('Orbit')
plt.xlabel('position')
plt.ylabel('position')
plt.grid(True)
plt.show()

View File

@ -0,0 +1,15 @@
examples:
@make -C CannonBall
@make -C MassSpringDamper
@make -C Orbit
clean:
@make -C CannonBall clean
@make -C MassSpringDamper clean
@make -C Orbit clean
spotless:
@make -C CannonBall spotless
@make -C MassSpringDamper spotless
@make -C Orbit spotless

View File

@ -0,0 +1,177 @@
namespace SA {
class Integrator {
protected:
double time;
double default_dt;
void * udata;
public:
Integrator(double dt, void* user_data): time(0.0), default_dt(dt), udata(user_data) {};
virtual ~Integrator() {}
virtual void step() { time += default_dt; }; // Integrate over implicit (default) time step (default: default_dt)
virtual void load() = 0;
virtual void unload() = 0;
double getTime() {return time;}
};
typedef void (*derivsFunc)( double t, double state[], double derivs[], void* user_data);
class FirstOrderODEIntegrator : public Integrator {
protected:
unsigned int state_size; // size of the state vector.
double* istate;
double* ostate;
double** orig_vars;
double** dest_vars;
derivsFunc derivs_func;
bool reset;
double last_dt; // What was the last step-size? For undo_step().
public:
FirstOrderODEIntegrator( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func, void* user_data);
~FirstOrderODEIntegrator();
// void init(double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func);
virtual void undo_step();
void load();
void unload();
};
class FirstOrderODEVariableStepIntegrator : public FirstOrderODEIntegrator {
public:
FirstOrderODEVariableStepIntegrator( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func, void* user_data);
using Integrator::step;
virtual void step( double dt );
};
class EulerIntegrator : public FirstOrderODEVariableStepIntegrator {
public:
double *derivs;
EulerIntegrator( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc derivs_func, void* user_data);
~EulerIntegrator();
void step( double dt);
void step();
};
class HeunsMethod : public FirstOrderODEVariableStepIntegrator {
public:
double *wstate;
double *derivs[2];
HeunsMethod( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc derivs_func, void* user_data);
~HeunsMethod();
void step( double dt);
void step();
};
class RK2Integrator : public FirstOrderODEVariableStepIntegrator {
public:
double *wstate;
double *derivs[2];
RK2Integrator( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc derivs_func, void* user_data);
~RK2Integrator();
void step( double dt);
void step();
};
class RK4Integrator : public FirstOrderODEVariableStepIntegrator {
public:
double *wstate[3];
double *derivs[4];
RK4Integrator( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc derivs_func, void* user_data);
~RK4Integrator();
void step( double dt);
void step();
};
class RK3_8Integrator : public FirstOrderODEVariableStepIntegrator {
public:
double *wstate[4];
double *derivs[4];
RK3_8Integrator( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc derivs_func, void* user_data);
~RK3_8Integrator();
void step( double dt);
void step();
};
class EulerCromerIntegrator : public Integrator {
protected:
unsigned int state_size;
double** x_p;
double** v_p;
double* x_in;
double* v_in;
double* x_out;
double* v_out;
double* g_out;
double* f_out;
derivsFunc gderivs;
derivsFunc fderivs;
public:
EulerCromerIntegrator(double dt, int N, double* xp[], double* vp[], derivsFunc g, derivsFunc f , void* user_data);
~EulerCromerIntegrator();
void step( double dt);
void step();
void load();
void unload();
};
// AdamsBashforthMoulton 4
// class ABMIntegrator : public FirstOrderODEIntegrator {
// protected:
// double ** deriv_history;
// double * composite_deriv;
// unsigned int bufferSize;
// unsigned int algorithmHistorySize;
// unsigned int hix;
// unsigned int currentHistorySize;
//
// public:
// ABMIntegrator(int history_size, double dt, int N, double* in_vars[], double* out_vars[], derivsFunc derivs_func, void* user_data);
// ~ABMIntegrator();
// void step();
// void undo_step();
// };
// AdamsBashforthMoulton 2
class ABM2Integrator : public FirstOrderODEIntegrator {
protected:
double ** deriv_history;
double * composite_deriv;
unsigned int bufferSize;
unsigned int hix;
unsigned int currentHistorySize;
FirstOrderODEVariableStepIntegrator* priming_integrator;
public:
ABM2Integrator( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc derivs_func, void* user_data);
~ABM2Integrator();
void step();
void undo_step();
};
// AdamsBashforthMoulton 4
class ABM4Integrator : public FirstOrderODEIntegrator {
protected:
double ** deriv_history;
double * composite_deriv;
unsigned int bufferSize;
unsigned int hix;
unsigned int currentHistorySize;
FirstOrderODEVariableStepIntegrator* priming_integrator;
public:
ABM4Integrator( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc derivs_func, void* user_data);
~ABM4Integrator();
void step();
void undo_step();
};
}

View File

@ -0,0 +1,44 @@
RM = rm -rf
CC = cc
CPP = c++
CFLAGS = -g -Wall
INCLUDE_DIRS = -Iinclude
OBJDIR = obj
LIBDIR = lib
LIBNAME = libSAInteg.a
LIBOBJS = ${OBJDIR}/SAIntegrator.o
all: test examples
test: ${LIBDIR}/${LIBNAME}
${MAKE} -C test
examples: ${LIBDIR}/${LIBNAME}
${MAKE} -C examples
$(LIBOBJS): $(OBJDIR)/%.o : src/%.cpp | $(OBJDIR)
$(CPP) $(CFLAGS) ${INCLUDE_DIRS} -c $< -o $@
${LIBDIR}/${LIBNAME}: ${LIBOBJS} | ${LIBDIR}
ar crs $@ ${LIBOBJS}
${OBJDIR}:
mkdir -p ${OBJDIR}
${LIBDIR}:
mkdir -p ${LIBDIR}
clean:
${RM} *~
${RM} ${OBJDIR}
${MAKE} -C test clean
${MAKE} -C examples clean
spotless:
${RM} *~
${RM} ${OBJDIR}
${RM} ${LIBDIR}
${MAKE} -C test spotless
${MAKE} -C examples spotless

View File

@ -0,0 +1,511 @@
#include <stdlib.h>
#include <iostream>
#include "SAIntegrator.hh"
// ------------------------------------------------------------
SA::FirstOrderODEIntegrator::FirstOrderODEIntegrator(double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func, void* user_data)
: Integrator(dt, user_data) {
state_size = N;
orig_vars = in_vars;
dest_vars = out_vars;
istate = new double[state_size];
ostate = new double[state_size];
derivs_func = func;
reset=false;
}
SA::FirstOrderODEIntegrator::~FirstOrderODEIntegrator() {
delete istate;
delete ostate;
}
void SA::FirstOrderODEIntegrator::load() {
reset = false;
for (int i=0 ; i<state_size; i++ ) {
istate[i] = *(orig_vars[i]);
}
}
void SA::FirstOrderODEIntegrator::unload() {
for (int i=0 ; i<state_size; i++ ) {
*(dest_vars[i]) = ostate[i];
}
}
void SA::FirstOrderODEIntegrator::undo_step() {
if (!reset) { // If we've not already reset the integrator, then reset it.
for (int i=0 ; i<state_size; i++ ) {
*(orig_vars[i]) = istate[i]; // Copy istate values back to the state variables from whence they came.
}
time -= last_dt; // Undo the last time-step.
}
reset = true;
}
// ------------------------------------------------------------
SA::FirstOrderODEVariableStepIntegrator::FirstOrderODEVariableStepIntegrator( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func, void* user_data)
: FirstOrderODEIntegrator(dt, N, in_vars, out_vars, func, user_data) {}
void SA::FirstOrderODEVariableStepIntegrator::step( double dt ) {
last_dt = dt; time += dt;
}
// ------------------------------------------------------------
SA::EulerIntegrator::EulerIntegrator(double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func, void* user_data)
: FirstOrderODEVariableStepIntegrator(dt, N, in_vars, out_vars, func, user_data)
{
derivs = new double[N];
}
SA::EulerIntegrator::~EulerIntegrator() {
delete derivs;
}
void SA::EulerIntegrator::step( double dt) {
(*derivs_func)( time, istate, derivs, udata);
for (unsigned int i=0; i<state_size; i++) {
ostate[i] = istate[i] + derivs[i] * dt;
}
SA::FirstOrderODEVariableStepIntegrator::step(dt);
}
void SA::EulerIntegrator::step() {
step(default_dt);
}
// ------------------------------------------------------------
SA::HeunsMethod::HeunsMethod( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func, void* user_data)
: FirstOrderODEVariableStepIntegrator(dt, N, in_vars, out_vars, func, user_data)
{
wstate = new double[N];
for (unsigned int i = 0; i < 2 ; i++) {
derivs[i] = new double[N];
}
}
SA::HeunsMethod::~HeunsMethod() {
delete wstate;
for (unsigned int i = 0; i < 2 ; i++) {
delete derivs[i];
}
}
void SA::HeunsMethod::step( double dt) {
(*derivs_func)( time, istate, derivs[0], udata);
for (unsigned int i=0; i<state_size; i++) {
wstate[i] = istate[i] + dt * derivs[0][i];
}
(*derivs_func)( time, wstate, derivs[1], udata);
for (unsigned int i=0; i<state_size; i++) {
ostate[i] = istate[i] + (dt/2) * ( derivs[0][i] + derivs[1][i] );
}
SA::FirstOrderODEVariableStepIntegrator::step(dt);
}
void SA::HeunsMethod::step() {
step(default_dt);
}
// ------------------------------------------------------------
SA::RK2Integrator::RK2Integrator( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func, void* user_data)
: FirstOrderODEVariableStepIntegrator(dt, N, in_vars, out_vars, func, user_data)
{
wstate = new double[N];
for (unsigned int i = 0; i < 2 ; i++) {
derivs[i] = new double[N];
}
}
SA::RK2Integrator::~RK2Integrator() {
delete (wstate);
for (unsigned int i = 0; i < 2 ; i++) {
delete derivs[i];
}
}
void SA::RK2Integrator::step( double dt) {
(*derivs_func)( time, istate, derivs[0], udata);
for (unsigned int i=0; i<state_size; i++) {
wstate[i] = istate[i] + 0.5 * dt * derivs[0][i];
}
(*derivs_func)( time + 0.5 * dt , wstate, derivs[1], udata);
for (unsigned int i=0; i<state_size; i++) {
ostate[i] = istate[i] + dt * derivs[1][i];
}
SA::FirstOrderODEVariableStepIntegrator::step(dt);
}
void SA::RK2Integrator::step() {
step(default_dt);
}
// ------------------------------------------------------------
SA::RK4Integrator::RK4Integrator( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func, void* user_data)
: FirstOrderODEVariableStepIntegrator(dt, N, in_vars, out_vars, func, user_data)
{
for (unsigned int i = 0; i < 3 ; i++) {
wstate[i] = new double[N];
}
for (unsigned int i = 0; i < 4 ; i++) {
derivs[i] = new double[N];
}
}
SA::RK4Integrator::~RK4Integrator() {
for (unsigned int i = 0; i < 3 ; i++) {
delete (wstate[i]);
}
for (unsigned int i = 0; i < 4 ; i++) {
delete (derivs[i]);
}
}
void SA::RK4Integrator::step( double dt) {
(*derivs_func)( time, istate, derivs[0], udata);
for (unsigned int i=0; i<state_size; i++) {
wstate[0][i] = istate[i] + 0.5 * derivs[0][i] * dt;
}
(*derivs_func)( time + 0.5 * dt , wstate[0], derivs[1], udata);
for (unsigned int i=0; i<state_size; i++) {
wstate[1][i] = istate[i] + 0.5 * derivs[1][i] * dt;
}
(*derivs_func)( time + 0.5 * dt , wstate[1], derivs[2], udata);
for (unsigned int i=0; i<state_size; i++) {
wstate[2][i] = istate[i] + derivs[2][i] * dt;
}
(*derivs_func)( time + dt , wstate[2], derivs[3], udata);
for (unsigned int i=0; i<state_size; i++) {
ostate[i] = istate[i] + ((1/6.0)* derivs[0][i] +
(1/3.0)* derivs[1][i] +
(1/3.0)* derivs[2][i] +
(1/6.0)* derivs[3][i]) * dt;
}
SA::FirstOrderODEVariableStepIntegrator::step(dt);
}
void SA::RK4Integrator::step() {
step(default_dt);
}
// ------------------------------------------------------------
SA::RK3_8Integrator::RK3_8Integrator( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func, void* user_data)
: FirstOrderODEVariableStepIntegrator(dt, N, in_vars, out_vars ,func, user_data)
{
for (unsigned int i = 0; i < 3 ; i++) {
wstate[i] = new double[N];
}
for (unsigned int i = 0; i < 4 ; i++) {
derivs[i] = new double[N];
}
}
SA::RK3_8Integrator::~RK3_8Integrator() {
for (unsigned int i = 0; i < 3 ; i++) {
delete (wstate[i]);
}
for (unsigned int i = 0; i < 4 ; i++) {
delete (derivs[i]);
}
}
void SA::RK3_8Integrator::step( double dt) {
(*derivs_func)( time, istate, derivs[0], udata);
for (unsigned int i=0; i<state_size; i++) {
wstate[0][i] = istate[i] + dt * (1/3.0) * derivs[0][i];
}
(*derivs_func)( time + (1/3.0) * dt , wstate[0], derivs[1], udata);
for (unsigned int i=0; i<state_size; i++) {
wstate[1][i] = istate[i] + dt * ((-1/3.0) * derivs[0][i] + derivs[1][i]);
}
(*derivs_func)( time + (2/3.0) * dt , wstate[1], derivs[2], udata);
for (unsigned int i=0; i<state_size; i++) {
wstate[2][i] = istate[i] + dt * (derivs[0][i] - derivs[1][i] + derivs[2][i]);
}
(*derivs_func)( time + dt , wstate[2], derivs[3], udata);
for (unsigned int i=0; i<state_size; i++) {
ostate[i] = istate[i] + ((1/8.0)* derivs[0][i] +
(3/8.0)* derivs[1][i] +
(3/8.0)* derivs[2][i] +
(1/8.0)* derivs[3][i]) * dt;
}
SA::FirstOrderODEVariableStepIntegrator::step(dt);
}
void SA::RK3_8Integrator::step() {
step(default_dt);
}
// ------------------------------------------------------------
SA::EulerCromerIntegrator::EulerCromerIntegrator(double dt, int N, double* xp[], double* vp[], derivsFunc gfunc, derivsFunc ffunc, void* user_data)
: Integrator(dt, user_data) {
state_size = N;
x_p = new double*[N];
v_p = new double*[N];
x_in = new double[N];
v_in = new double[N];
g_out = new double[N];
f_out = new double[N];
x_out = new double[N];
v_out = new double[N];
for (int i=0 ; i<N; i++ ) {
x_p[i] = xp[i];
v_p[i] = vp[i];
}
gderivs = gfunc;
fderivs = ffunc;
}
SA::EulerCromerIntegrator::~EulerCromerIntegrator() {
delete x_p;
delete v_p;
delete x_in;
delete v_in;
delete g_out;
delete f_out;
delete x_out;
delete v_out;
}
void SA::EulerCromerIntegrator::step( double dt) {
(*gderivs)( time, x_in, g_out, udata);
for (int i=0 ; i<state_size; i++ ) {
v_out[i] = v_in[i] + g_out[i] * dt;
}
(*fderivs)( time, v_in, f_out, udata);
for (int i=0 ; i<state_size; i++ ) {
x_out[i] = x_in[i] + f_out[i] * dt;
}
time += dt;
}
void SA::EulerCromerIntegrator::step() {
step(default_dt);
}
void SA::EulerCromerIntegrator::load() {
for (int i=0 ; i<state_size; i++ ) {
x_in[i] = *(x_p[i]);
v_in[i] = *(v_p[i]);
}
}
void SA::EulerCromerIntegrator::unload(){
for (int i=0 ; i<state_size; i++ ) {
*(x_p[i]) = x_out[i];
*(v_p[i]) = v_out[i];
}
}
// // Adams-Bashforth Coefficients
// static const double ABCoeffs[5][5] = {
// {1.0, 0.0, 0.0, 0.0, 0.0},
// {(3/2.0), (-1/2.0), 0.0, 0.0, 0.0},
// {(23/12.0), (-16/12.0), (5/12.0), 0.0, 0.0},
// {(55/24.0), (-59/24.0), (37/24.0), (-9/24.0), 0.0},
// {(1901/720.0), (-2774/720.0), (2616/720.0), (-1274/720.0), (251/720.0)}
// };
//
// // Adams-Moulton Coefficients
// static const double AMCoeffs[5][5] = {
// {1.0, 0.0, 0.0, 0.0, 0.0},
// {(1/2.0), (1/2.0), 0.0, 0.0, 0.0},
// {(5/12.0), (8/12.0), (-1/12.0), 0.0, 0.0},
// {(9/24.0), (19/24.0), (-5/24.0), (1/24.0), 0.0},
// {(251/720.0), (646/720.0), (-264/720.0), (106/720.0), (-19/720.0)}
// };
// SA::ABMIntegrator::ABMIntegrator ( int history_size, double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func, void* user_data)
// : FirstOrderODEIntegrator(dt, N, in_vars, out_vars ,func, user_data) {
//
// algorithmHistorySize=history_size; // The amount of history that we intend to use in this ABM integrator.
// bufferSize=algorithmHistorySize+1; // The size of the buffer needs to be one more than the history so that an integration step can be reset.
// hix=0;
// currentHistorySize=0; // How much derivative history is currently stored int the history buffer. Initially there will be none until we've primed the integrator.
// deriv_history = new double*[bufferSize];
// for (unsigned int i=0; i<bufferSize ; i++) {
// deriv_history[i] = new double[state_size];
// }
// composite_deriv = new double[state_size];
// }
// SA::ABMIntegrator::~ABMIntegrator() {
// for (int i=0; i<bufferSize ; i++) {
// delete (deriv_history[i]);
// }
// delete(deriv_history);
// delete(composite_deriv);
// }
// void SA::ABMIntegrator::step() {
//
// hix = (hix+1)%bufferSize; // Move history index forward
// (*derivs_func)( time, istate, deriv_history[hix], udata); // Calculated and store the deriv for current, corrected state.
// // Increase the size of the stored history, up to the limit specified by the user.
// currentHistorySize = (currentHistorySize < algorithmHistorySize) ? currentHistorySize+1 : algorithmHistorySize;
// // (Predictor) Predict the next state using the Adams-Bashforth explicit method.
// for (int i=0; i<state_size; i++) {
// composite_deriv[i] = 0.0;
// for (int n=0,j=hix; n<currentHistorySize ; n++,j=(j+bufferSize-1)%bufferSize) {
// composite_deriv[i] += ABCoeffs[currentHistorySize-1][n] * deriv_history[j][i];
// }
// ostate[i] = istate[i] + default_dt * composite_deriv[i];
// }
// // Move history index forward, so we can temporarily store the derivative of the predicted next state.
// // We do not increase the currentHistorySize.
// hix = (hix+1)%bufferSize;
// (*derivs_func)( time, ostate, deriv_history[hix], udata); // Calc deriv for predicted next state.
// // (Corrector) Refine the next state using the Adams-Moulton implicit method. This is the corrected next state.
// for (int i=0; i<state_size; i++) {
// composite_deriv[i] = 0.0;
// for (int n=0,j=hix; n<currentHistorySize ; n++,j=(j+bufferSize-1)%bufferSize) {
// composite_deriv[i] += AMCoeffs[currentHistorySize-1][n] * deriv_history[j][i];
// }
// ostate[i] = istate[i] + default_dt * composite_deriv[i];
// }
// // Move history index backward, so we over-write the predicted state with the corrected state on our next step().
// hix = (hix+bufferSize-1)%bufferSize;
// SA::Integrator::step();
// }
//
// void SA::ABMIntegrator::undo_step() {
// hix = (hix+bufferSize-1)%bufferSize;
// FirstOrderODEIntegrator::undo_step();
// }
// =======================================================================
static const double AB2Coeffs[2] = {(3/2.0), (-1/2.0)};
static const double AM2Coeffs[2] = {(1/2.0), (1/2.0)};
SA::ABM2Integrator::ABM2Integrator ( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func, void* user_data)
: FirstOrderODEIntegrator(dt, N, in_vars, out_vars ,func, user_data) {
// The amount of history that we intend to use in this ABM integrator.
bufferSize=2+1; // The size of the buffer needs to be one more than the history so that an integration step can be reset.
hix=0;
currentHistorySize=0; // How much derivative history is currently stored int the history buffer. Initially there will be none until we've primed the integrator.
deriv_history = new double*[bufferSize];
for (unsigned int i=0; i<bufferSize ; i++) {
deriv_history[i] = new double[state_size];
}
composite_deriv = new double[state_size];
// NEW: Create a priming integrator.
double** priming_integrator_in_vars = new double*[state_size];
for (unsigned int i=0; i<N ; i++) {
priming_integrator_in_vars[i] = &(istate[i]);
}
double** priming_integrator_out_vars = new double*[state_size];
for (unsigned int i=0; i<N ; i++) {
priming_integrator_out_vars[i] = &(ostate[i]);
}
priming_integrator = new SA::RK2Integrator(dt, N, priming_integrator_in_vars, priming_integrator_out_vars, func, user_data);
}
SA::ABM2Integrator::~ABM2Integrator() {
for (int i=0; i<bufferSize ; i++) {
delete (deriv_history[i]);
}
delete(deriv_history);
delete(composite_deriv);
delete priming_integrator;
}
void SA::ABM2Integrator::step() {
hix = (hix+1)%bufferSize; // Move history index forward
(*derivs_func)( time, istate, deriv_history[hix], udata); // Calculated and store the deriv for current, corrected state.
// Increase the size of the stored history, up to the limit specified by the user.
currentHistorySize = (currentHistorySize < 2) ? currentHistorySize+1 : 2;
if ( currentHistorySize < 2 ) {
priming_integrator->load();
priming_integrator->step();
priming_integrator->unload();
} else {
// (Predictor) Predict the next state using the Adams-Bashforth explicit method.
for (int i=0; i<state_size; i++) {
composite_deriv[i] = 0.0;
for (int n=0,j=hix; n<2 ; n++,j=(j+bufferSize-1)%bufferSize) {
composite_deriv[i] += AB2Coeffs[n] * deriv_history[j][i];
}
ostate[i] = istate[i] + default_dt * composite_deriv[i];
}
// Move history index forward, so we can temporarily store the derivative of the predicted next state.
// We do not increase the currentHistorySize.
hix = (hix+1)%bufferSize;
(*derivs_func)( time, ostate, deriv_history[hix], udata); // Calc deriv for predicted next state.
// (Corrector) Refine the next state using the Adams-Moulton implicit method. This is the corrected next state.
for (int i=0; i<state_size; i++) {
composite_deriv[i] = 0.0;
for (int n=0,j=hix; n<2 ; n++,j=(j+bufferSize-1)%bufferSize) {
composite_deriv[i] += AM2Coeffs[n] * deriv_history[j][i];
}
ostate[i] = istate[i] + default_dt * composite_deriv[i];
}
// Move history index backward, so we over-write the predicted state with the corrected state on our next step().
hix = (hix+bufferSize-1)%bufferSize;
}
SA::Integrator::step();
}
void SA::ABM2Integrator::undo_step() {
hix = (hix+bufferSize-1)%bufferSize;
FirstOrderODEIntegrator::undo_step();
}
// =======================================================================
static const double AB4Coeffs[4] = {(55/24.0), (-59/24.0), (37/24.0), (-9/24.0)};
static const double AM4Coeffs[4] = {(9/24.0), (19/24.0), (-5/24.0), (1/24.0)};
SA::ABM4Integrator::ABM4Integrator ( double dt, int N, double* in_vars[], double* out_vars[], derivsFunc func, void* user_data)
: FirstOrderODEIntegrator(dt, N, in_vars, out_vars ,func, user_data) {
// The amount of history that we intend to use in this ABM integrator.
bufferSize=4+1; // The size of the buffer needs to be one more than the history so that an integration step can be reset.
hix=0;
currentHistorySize=0; // How much derivative history is currently stored int the history buffer. Initially there will be none until we've primed the integrator.
deriv_history = new double*[bufferSize];
for (unsigned int i=0; i<bufferSize ; i++) {
deriv_history[i] = new double[state_size];
}
composite_deriv = new double[state_size];
// NEW: Create a priming integrator.
double** priming_integrator_in_vars = new double*[state_size];
for (unsigned int i=0; i<N ; i++) {
priming_integrator_in_vars[i] = &(istate[i]);
}
double** priming_integrator_out_vars = new double*[state_size];
for (unsigned int i=0; i<N ; i++) {
priming_integrator_out_vars[i] = &(ostate[i]);
}
priming_integrator = new SA::RK4Integrator(dt, N, priming_integrator_in_vars, priming_integrator_out_vars, func, user_data);
}
SA::ABM4Integrator::~ABM4Integrator() {
for (int i=0; i<bufferSize ; i++) {
delete (deriv_history[i]);
}
delete(deriv_history);
delete(composite_deriv);
delete priming_integrator;
}
void SA::ABM4Integrator::step() {
hix = (hix+1)%bufferSize; // Move history index forward
(*derivs_func)( time, istate, deriv_history[hix], udata); // Calculated and store the deriv for current, corrected state.
// Increase the size of the stored history, up to the limit specified by the user.
currentHistorySize = (currentHistorySize < 4) ? currentHistorySize+1 : 4;
if ( currentHistorySize < 4 ) {
priming_integrator->load();
priming_integrator->step();
priming_integrator->unload();
} else {
// (Predictor) Predict the next state using the Adams-Bashforth explicit method.
for (int i=0; i<state_size; i++) {
composite_deriv[i] = 0.0;
for (int n=0,j=hix; n<4 ; n++,j=(j+bufferSize-1)%bufferSize) {
composite_deriv[i] += AB4Coeffs[n] * deriv_history[j][i];
}
ostate[i] = istate[i] + default_dt * composite_deriv[i];
}
// Move history index forward, so we can temporarily store the derivative of the predicted next state.
// We do not increase the currentHistorySize.
hix = (hix+1)%bufferSize;
(*derivs_func)( time, ostate, deriv_history[hix], udata); // Calc deriv for predicted next state.
// (Corrector) Refine the next state using the Adams-Moulton implicit method. This is the corrected next state.
for (int i=0; i<state_size; i++) {
composite_deriv[i] = 0.0;
for (int n=0,j=hix; n<4 ; n++,j=(j+bufferSize-1)%bufferSize) {
composite_deriv[i] += AM4Coeffs[n] * deriv_history[j][i];
}
ostate[i] = istate[i] + default_dt * composite_deriv[i];
}
// Move history index backward, so we over-write the predicted state with the corrected state on our next step().
hix = (hix+bufferSize-1)%bufferSize;
}
SA::Integrator::step();
}
void SA::ABM4Integrator::undo_step() {
hix = (hix+bufferSize-1)%bufferSize;
FirstOrderODEIntegrator::undo_step();
}

View File

@ -0,0 +1,116 @@
#include <gtest/gtest.h>
#include <iostream>
#include "SAIntegrator.hh"
#include <math.h>
void deriv4( double t, double state[], double derivs[], void* udata) {
double t2 = t*t;
double t3 = t2*t;
derivs[0] = 3.0*t3 - 3.0*t2 - 3.0*t + 4.0;
derivs[1] = 2.0*t3 + 1.0*t2 - 5.0*t + 7.0;
derivs[2] = t3 + t2 + 5.0*t + 4.0;
derivs[3] = t3 - 6.0*t2 + 4.0*t + 12.0;
}
void deriv2( double t, double state[], double derivs[], void* udata) {
derivs[0] = -2.0*t + 3.0;
derivs[1] = 5.0*t + 4.0;
derivs[2] = 3.0*t - 3.0;
derivs[3] = 5.0*t + 4.0;
}
void deriv1( double t, double state[], double derivs[], void* udata) {
derivs[0] = 4.0;
derivs[1] = -4.0;
derivs[2] = M_PI;
derivs[3] = -M_PI;
}
#define EXCEPTABLE_ERROR 0.00000000001
TEST(Integ_unittest, EulerIntegrator_1) {
double state[4] = {0.0, 0.0, 0.0, 0.0};
double* state_var_p[4] = { &(state[0]), &(state[1]), &(state[2]), &(state[3])};
double dt = 0.01;
unsigned int count = 0;
double t = count * dt;
SA::EulerIntegrator integ(dt, 4, state_var_p, state_var_p, deriv1, NULL);
while (t < 2.0) {
integ.load();
integ.step();
integ.unload();
count ++;
t = count * dt;
}
EXPECT_NEAR(state[0], 8.0, EXCEPTABLE_ERROR);
EXPECT_NEAR(state[1], -8.0, EXCEPTABLE_ERROR);
EXPECT_NEAR(state[2], (2*M_PI), EXCEPTABLE_ERROR);
EXPECT_NEAR(state[3], (-2*M_PI), EXCEPTABLE_ERROR);
}
TEST(Integ_unittest, RungeKutta2_1) {
double state[4] = {0.0, 0.0, 0.0, 0.0};
double* state_var_p[4] = { &(state[0]), &(state[1]), &(state[2]), &(state[3]) };
double dt = 0.01;
unsigned int count = 0;
double t = count * dt;
SA::RK2Integrator integ(dt, 4, state_var_p, state_var_p, deriv2, NULL);
while (t < 2.0) {
integ.load();
integ.step();
integ.unload();
count ++;
t = count * dt;
}
EXPECT_NEAR(state[0], 2.0, EXCEPTABLE_ERROR);
EXPECT_NEAR(state[1], 18.0, EXCEPTABLE_ERROR);
EXPECT_NEAR(state[2], 0.0, EXCEPTABLE_ERROR);
EXPECT_NEAR(state[3], 18.0, EXCEPTABLE_ERROR);
}
TEST(Integ_unittest, RungeKutta4_1) {
double state[4] = {0.0, 0.0, 0.0, 0.0};
double* state_var_p[4] = { &(state[0]), &(state[1]), &(state[2]), &(state[3]) };
double dt = 0.01;
unsigned int count = 0;
double t = count * dt;
SA::RK4Integrator integ(dt, 4, state_var_p, state_var_p, deriv4, NULL);
while (t < 2.0) {
integ.load();
integ.step();
integ.unload();
count ++;
t = count * dt;
}
EXPECT_NEAR(state[0], 6.0, EXCEPTABLE_ERROR);
EXPECT_NEAR(state[1], (14.0+2/3.0), EXCEPTABLE_ERROR);
EXPECT_NEAR(state[2], (24+2/3.0), EXCEPTABLE_ERROR);
EXPECT_NEAR(state[3], 20.0, EXCEPTABLE_ERROR);
}
TEST(Integ_unittest, RungeKutta38_1) {
double state[4] = {0.0, 0.0, 0.0, 0.0};
double* state_var_p[4] = { &(state[0]), &(state[1]), &(state[2]), &(state[3]) };
double dt = 0.01;
unsigned int count = 0;
double t = count * dt;
SA::RK3_8Integrator integ(dt, 4, state_var_p, state_var_p, deriv4, NULL);
while (t < 2.0) {
integ.load();
integ.step();
integ.unload();
count ++;
t = count * dt;
}
EXPECT_NEAR(state[0], 6.0, EXCEPTABLE_ERROR);
EXPECT_NEAR(state[1], (14.0+2/3.0), EXCEPTABLE_ERROR);
EXPECT_NEAR(state[2], (24+2/3.0), EXCEPTABLE_ERROR);
EXPECT_NEAR(state[3], 20.0, EXCEPTABLE_ERROR);
}

View File

@ -0,0 +1,28 @@
RM = rm -rf
CC = cc
CPP = c++
CFLAGS = -g -Wall
INCLUDE_DIRS = -I../include
OBJDIR = obj
LIBDIR = ../lib
LIBNAME = libSAInteg.a
LIBOBJS = ${OBJDIR}/Integrator.o
all: Integ_unittest
Integ_unittest.o : Integ_unittest.cc
$(CPP) $(CFLAGS) $(INCLUDE_DIRS) -c $<
Integ_unittest : ${LIBDIR}/${LIBNAME} Integ_unittest.o
$(CPP) $(CFLAGS) -o $@ $^ -lgtest -lgtest_main -lpthread
${LIBDIR}/${LIBNAME} :
$(MAKE) -C ..
clean:
${RM} *.o
spotless: clean
${RM} Integ_unittest