C++ numerical integrators to solve systems of ode's - c++

I recently started using C++ and I just created a class that allows the integration of a user-defined system of ode's. It uses two different integrators in order to compare its performance. Here is the general layout of the code:
class integrators {
private:
double ti; // initial time
double *xi; // initial solution
double tf; // end time
double dt; // time step
int n; // number of ode's
public:
// Function prototypes
double f(double, double *, double *); // function to integrate
double rk4(int, double, double, double, double *, double *);
double dp8(int, double, double, double, double *, double *);
};
// 4th Order Runge-Kutta function
double integrators::rk4(int n, double ti, double tf, double dt, double *xi, double *xf) {
// Function statements
}
// 8th Order Dormand-Prince function
double integrators::dp8(int n, double ti, double tf, double dt, double *xi, double *xf) {
// Function statements
}
// System of first order differential equations
double integrators::f(double t, double *x, double *dx) {
// Function statements
}
int main() {
// Initial conditions and time related parameters
const int n = 4;
double t0, tmax, dt;
double x0[n], xf[n];
x0[0] = 0.0;
x0[1] = 0.0;
x0[2] = 1.0;
x0[3] = 2.0;
// Calling class integrators
integrators example01;
integrators example02;
// First integrator
example02.dp8(n, t0, tmax, dt, x0, xf);
// Second integrator
example01.rk4(n, t0, tmax, dt, x0, xf);
}
The problem is that the array containing the initial conditions x0 in main, changes after executing the first integrator and I cannot use the same initial conditions for the second integrator, unless I define another array with the same initial conditions (x0_rk4 and x0_dp8). Is there a more professional way to keep this array constant in order to use it in both integrators?

The easiest way is to make a local copy of array inside integrating functions.
Change the way you are passing 'n' to function to 'const int n', so you can make something like double currentSolution[n]; inside and copy elements from initial array to new one. This approach will save your initial array intact, unless you will "accidently" modify it somewhere.
To prevent this probability of accident modification we need to go deeper and use one of stl containers. I think you will be fine with std::valarray<T>.
Change the way you are passing it to const std::valarray<double>& and again make non-const local copy.

No, not really. But there exist a more elegant solution:
std::array<double, n> x0_rk4 = { 0.0, 0.0, 1.0, 2.0 };
auto x0_dp8 = x0_rk4; // copy!
You will have to use x0_rk4.data() to access the underlying array. Note that it would be better if you used std::array and other modern C++ features instead of raw pointers and the like.

Related

Intuitive GSL stiff EDO

I need to solve an ODE that has stiff behaviour. Said ODE would be given by
int odefunc (double x, const double y[], double f[], void *params)
{
double k=1e12; //At k=1e7 it works (doesn't show stiffness)
double y_eq=-1.931+1.5*log(x)-x;
f[0] = k*(exp(2*y_eq-y[0])-exp(y[0]));
return GSL_SUCCESS;
}
I'm not so used to C (I am to C++, though) and the documentation in the GSL page for Differential Equations doesn't have that many comments for me to understand it. I've used this post to understand it (also because I only have 1 ode, not a system of them), and to some extent it has been useful, but it's still a bit confusing to me. For my tests I've used my function instead of the one given, and I've gone with it although I don't understand the code. But now it was key for me to understand, because...
I would need to modify the examples they give me, I need to use an algorithm that solves stiff equations, preferably gsl_odeiv2_step_msbdf with an adaptative step-size or something equivalent to that (the BDF method seems to be used profusely in the academia). That needs the jacobian, and that part is really intricate if you're not used to GSL or C.
To manually implement an algorithmic differentiation it helps to include explicit intermediary steps
int odefunc (double x, const double y[], double f[], void *params)
{
double k=1e12; //At k=1e7 it works (doesn't show stiffness)
double y_eq=-1.931+1.5*log(x)-x;
double v1 = exp(2*y_eq-y[0]);
double v2 = exp(y[0]);
double z = k*(v1-v2);
f[0] = z;
return GSL_SUCCESS;
}
Then you can enhance each step with its derivative propagation
int jac (double x, const double y[], double *dfdy,
double dfdx[], void *params)
{
double k=1e12; //At k=1e7 it works (doesn't show stiffness)
// Dx_dx=Dy_dy=1, Dx_dy=Dy_dx=0 are used without naming them
double y_eq = -1.931+1.5*log(x)-x,
Dy_eq_dx=1.5/x-1,
Dy_eq_dy=0;
double v1 = exp(2*y_eq-y[0]),
Dv1_dx=v1*(2*Dy_eq_dx-0),
Dv1_dy=v1*(2*Dy_eq_dy-1);
double v2 = exp(y[0]),
Dv2_dx=v2*(0),
Dv2_dy=v2*(1);
double z = k*(v1-v2),
Dz_dx=k*(Dv1_dx-Dv2_dx),
Dz_dy=k*(Dv1_dy-Dv2_dy);
dfdx[0] = Dz_dx;
dfdy[0] = Dz_dy;
return GSL_SUCCESS;
}
For larger code use a code-rewriting tool that does these steps automatically. auto-diff.org should have a list of appropriate ones.

Dynamic parameterization of Armadillo matrix dimensions in C++

The title summarizes the goal that is more exactly to dynamically retrieve the number of dimensions of MATLAB arrays passed to armadillo matrices.
I would like to change the second and third arguments of mY() and mD() to parametric ones below.
// mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false)
arma::mat mY(&dY[0], 2, 168, false);
arma::mat mD(&dD[0], 2, 168, false);
This must be definitely a common use case, but I still could not find a nice way of achieving it for the general case when the number of dimensions of the arrays feeding from MATLAB could be arbitrary (n > 2).
For the matrix (two dimensional) case, I could possibly hack my way around but I feel like that is not elegant enough (probably not efficient either).
IMHO, the way to go must be:
matlab::data::TypedArray<double> has getDimensions() member function which retrieves matlab::data::ArrayDimensions that is fundamentally a std::vector<size_t>.
Indexing the first and second element of the vector retrieved by getDimensions() one can retrieve the number of rows and columns, for instance like below.
unsigned int mYrows = matrixY.getDimensions()[0];
unsigned int mYcols = matrixY.getDimensions()[1];
However, with my current setup, I cannot get to call getDimensions() through pointers/references in the foo() function of sub.cpp. If it is feasible, I would neither like to create additional temporary objects nor passing other arguments to foo(). How it possible that way?
Intuition keeps telling me that there must be an elegant solution that way too. Maybe using multiple indirection?
I would highly appreciate any help, hints or constructive comments from more knowledgeable SO members. Thank you in advance.
Setup:
Two C++ source files and a header file:
main.cpp
contains the general IO interface between MATLAB and C++
feeds two double arrays and two double const doubles into C++
it does some Armadillo based looping (this part is not that important therefore omitted) by calling foo()
returns outp which is a “just a plain” scalar double
Nothing fancy or complicated.
sub.cpp
This is only for the foo() looping part.
sub.hpp
Just a simple header file.
// main.cpp
// MATLAB API Header Files
#include "mex.hpp"
#include "mexAdapter.hpp"
// Custom header
#include "sub.hpp"
// Overloading the function call operator, thus class acts as a functor
class MexFunction : public matlab::mex::Function {
public:
void operator()(matlab::mex::ArgumentList outputs,
matlab::mex::ArgumentList inputs){
matlab::data::ArrayFactory factory;
// Validate arguments
checkArguments(outputs, inputs);
matlab::data::TypedArray<double> matrixY = std::move(inputs[0]);
matlab::data::TypedArray<double> matrixD = std::move(inputs[1]);
const double csT = inputs[2][0];
const double csKy = inputs[3][0];
buffer_ptr_t<double> mY = matrixY.release();
buffer_ptr_t<double> mD = matrixD.release();
double* darrY = mY.get();
double* darrD = mD.get();
// data type of outp is "just" a plain double, NOT a double array
double outp = foo(darrY, darrD, csT, csKy);
outputs[0] = factory.createScalar(outp);
void checkArguments(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs){
// Create pointer to MATLAB engine
std::shared_ptr<matlab::engine::MATLABEngine> matlabPtr = getEngine();
// Create array factory, allows us to create MATLAB arrays in C++
matlab::data::ArrayFactory factory;
// Check input size and types
if (inputs[0].getType() != ArrayType::DOUBLE ||
inputs[0].getType() == ArrayType::COMPLEX_DOUBLE)
{
// Throw error directly into MATLAB if type does not match
matlabPtr->feval(u"error", 0,
std::vector<Array>({ factory.createScalar("Input must be double array.") }));
}
// Check output size
if (outputs.size() > 1) {
matlabPtr->feval(u"error", 0,
std::vector<Array>({ factory.createScalar("Only one output is returned.") }));
}
}
};
// sub.cpp
#include "sub.hpp"
#include "armadillo"
double foo(double* dY, double* dD, const double T, const double Ky) {
double sum = 0;
// Conversion of input parameters to Armadillo types
// mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false)
arma::mat mY(&dY[0], 2, 168, false);
arma::mat mD(&dD[0], 2, 168, false);
// Armadillo calculations
for(int t=0; t<int(T); t++){
// some armadillo based calculation
// each for cycle increments sum by its return value
}
return sum;
}
// sub.hpp
#ifndef SUB_H_INCLUDED
#define SUB_H_INCLUDED
double foo(double* dY, double* dD, const double T, const double Ky);
#endif // SUB_H_INCLUDED
One way is to convert it to arma matrix using a function
template<class T>
arma::Mat<T> getMat( matlab::data::TypedArray<T> A)
{
matlab::data::TypedIterator<T> it = A.begin();
matlab::data::ArrayDimensions nDim = A.getDimensions();
return arma::Mat<T>(it.operator->(), nDim[0], nDim[1]);
}
and call it by
arma::mat Y = getMat<double>(inputs[0]);
arma::mat D = getMat<double>(inputs[1]);
...
double outp = foo(Y,D, csT, csKy);
and change foo() to
double foo( arma::mat& dY, arma::mat& dD, const double T, const double Ky)

Setting Struct Bounding Box Values

I'm trying to create some bounding boxes around some objects that I have.
I create them like this:
struct boundingBox{
float top;
float bottom;
float left;
float right;
};
I'm having issues setting their values. I have this function which I was hoping would set their values:
void makeBoundingBox(boundingBox bound, float t, float b, float l, float r){
bound.top =t;
bound.bottom =b;
bound.left=l;
bound.right=r;
}
and I am trying to set them like this:
makeBoundingBox(car,b_Size,b_Size,b_Size,b_Size); //where car is a boundingBox object and b_Size is a calculated box size
When I'm using the above line, the values aren't set; I know this because I print to the console to check the values and they always come back 0. However, if within the same function I use makeBoundingBox, I set car.top = 500 manually instead, and print, the car.top is set successfully to 500 and works...
Does the makeBoundingBox method not work correctly by passing a struct object how I am expecting it too?
You're passing the box by value. For the routine to change the actual argument, pass it by reference. I.e.
void makeBoundingBox(boundingBox& bound, float t, float b, float l, float r){
bound.top =t;
bound.bottom =b;
bound.left=l;
bound.right=r;
}
However, instead of that, in C++11 just write
boundingBox{ 1, 1, 10, 10 }
when you need a boundingBox with those values.
In C++03 you can instead define a constructor for the boundingBox class.
Read up on that in your favorite C++ text book.
Here is an example class definition:
struct boundingBox{
float top;
float bottom;
float left;
float right;
boundingBox( float a_top, float a_bottom, float a_left, float a_right )
: top( a_top ), bottom( a_bottom )
, left( a_left ), right( a_right )
{}
};
and you can then create an instance like
boundingBox( 1, 1, 10, 10 )
As a general observation, using float instead of double can create some problems (especially with C++11 curly braces initialization), it has less precision than double, and it has no special advantages except where you need zillions of values, like a few billion.
So, use double.
That's the default floating point type in C++, e.g. it's the type of 3.14.
You're passing the boundingBox by value, not by reference. This means the makeBoundingBox function is operating on a local copy and changes do not propagate back to the original object.
Change your function definition as follows:
void makeBoundingBox(boundingBox& bound, float t, float b, float l, float r)
You're passing the struct to the function by VALUE, you need to pass by REFERENCE so:
void makeBoundingBox(boundingBox &bound, float t, float b, float l, float r){
bound.top =t;
bound.bottom =b;
bound.left=l;
bound.right=r;
}
Notice the & to pass by reference.
In your function call, do the same & in front of the variable name.

Creating Array of Classes

I got a class such as:
class me362
{
public:
void geometry(long double xLength);
void mesh(int xNode);
void properties(long double H, long double D, long double K,long double Q, long double DT,long double PHO,long double CP, long double TINF);
void drichlet(long double TLeft,long double TRight);
void neumann(bool Tlinks, bool Trechts);
void updateDiscretization(long double**** A,long double* b, long double* Tp);
void printVectorToFile(long double *x);
private:
int xdim;
long double xlength;
long double tleft;
long double tright;
long double h;
long double d;
long double k;
long double q;
long double dt;
long double cp;
long double rho;
long double Tinf;
bool tlinks;
bool trechts;
};
And I initialize it using
me362 domain1;
me362 domain2;
me362 domain3;
But I want to determine the number of domains that I want to initialize. So I need a dynamic array of me362 structures. How can I do that? Can it be done?
Thank you all,
Emre.
Yes, it can be done. Use std::vector instead which increases it's size dynamically on every push_back operation.
std::vector<me362> obj ;
for( int i = 0; i < numberOfInstancesRequired; ++i )
{
obj.push_back( me362() ) ;
}
Use std::vector, which handles dynamic memory for you:
#include <vector>
// ...
std::vector<me362> domains;
std::vector also has a lot of nice features and guarantees, like being layout-compatible with C, having locality of reference, zero overhead per element, and so on.
Also note that std::vector has a constructor that takes an integral argument, and creates that many elements:
// Will create a vector with 42 default-constructed me362 elements in it
std::vector<me362> domains(42);
See any standard library reference (like cppreference.com or cplusplus.com) for details about using std::vector.)
For starters, welcome to the world of STL(standard template library)!
In your case, you can use std::vector, as it can hold variable number of elements for you.
#include<vector>
using namespace std;
//Create a std::vector object with zero size
vector<me362> myVector;
//Insert new items
myVector.push_back(domain1);
myVector.push_back(domain2);
myVector.push_back(domain3);
//Get the size of the vector, i.e., number of elements in vector
myVector.size();
Besides, you can create a vector object like this.
//nMe362: number of elements in vector, me362Obj: init value of each me362 object
vector<me362> myVector(nMe362, me362Obj);

How to differentiate two constructors with the same parameters?

Suppose we want two constructors for a class representing complex numbers:
Complex (double re, double img) // construct from cartesian coordinates
Complex (double A, double w) // construct from polar coordinates
but the parameters (number and type) are the same: what is the more elegant way
to identify what is intended? Adding a third parameter to one of the constructors?
It is better to add static methods with appropriate names and let them to create the objects.
static Complex createFromCartesian(double re, double img);
static Complex createFromPolar(double A, double w);
You can't have two constructors (or any functions) with the same signatures. The best solution is probably to create classes for your coordinate types and overload on those. For example:
struct CartCoord {
CartCoord( double re, double img ) : mRe(re), mImg(img) {}
double mRe, mImg;
};
struct PolarCoord {
PolarCoord( double a, double v ) : mA(a), mV(v) {}
double mA, mV;
};
Then your constructors become:
Complex( const CartCoord & c );
Complex( const PolarCoord & c);
In use:
Complex c( CartCoord( 1, 2 ) );
You can also use them with overloaded operators of the Complex class. For example, assuming you have a binary + operator for the class define as:
Complex operator+( const Complex &, const Complex & );
then:
Complex a( CartCoord( 0, 0 ) );
Complex b = a + PolarCoord( 1, 1 );
As we have a conversion constructor from PolarCoord to Complex, this will be used in the + expression. This is more natural (IMHO) than calling static functions to create the temporary..
This is an example of Koenig's dictum (or at least a version of it) - whenever faced with a difficult problem, introduce a new level of classes to solve it.
Use the named constructor idiom described here at the Parashift C++ FAQ.
You can't - if the method signatures are the same, you're stuffed.
Of course, you could always create a subclass for each type of coordinate system, but that's far from ideal - especially if you later find a good reason to want to subclass Complex for other reasons...
Instead of having two constructors, you need to have only one, but add a third parameter, for example:
Complex (double re, double img, enum type) {
if(type == polar) {
..treat as polar coordinates..
} else {
..treat as cartesian coordinates..
}
}
'enum type' can have a default type as well so you don't have to supply what type of coordinates you intend to use every time, the default should off course be what's most used.
Complex (double re, double img, enum type = polar);
Since no one mentioned it, you can use tags:
class Complex{
struct CartCoord{};
struct PolarCoord{};
Complex( CartCoord, double re, double img);
Complex( PolarCoord, double a, double v);
};
int main(){
auto c1 = Complex(Complex::CartCoord{}, 5, 6);
auto c2 = Complex(Complex::PolarCoord{}, 5, 6);
}
I don't know if it is good practice in C++ but I would name these two methods differently, at least for a maintainability point of view.
I would probably create more classes, but then I am an adept of Boost Strong Typedef library.
For me it does not make sense to use double to represent both coordinates, module and angles.
So I would have:
class CoordinateX {};
class CoordinateY {};
class Modulus {};
class Angle {};
class Complex
{
public:
Complex(CoordinateX, CoordinateY);
Complex(Modulus, Angle);
};
And there it's perfectly clear, and there is no ambiguity. Also you add some compile-time check of "units" in a loose sense. It rarely makes sense (if ever) to add an X-coordinate and an Y-coordinate, or worse a distance and an angle.
For me the more elegant way would be to create a class representing cartesian coordinates and another representing polar coordinates, then give object of the relevant class to the constructors.
That way you would have two constructor using different parameters.
I use this simple alternative way while practicing C++, I don't know if it is what you want.
class Vector{
public:
float x, y;
Vector();
Vector(float , float, bool);
float get_distance(Vector v); // find distance from another vector
float get_magnitude(); // find distance from origin point
Vector add(Vector v);
Vector subtract(Vector v);
Vector multiply(Vector v); // multiply by vector
Vector multiply(float scalar); // multiply by scalar
float get_angle();
};
Vector::Vector(float a, float b, bool is_cartesian = true){
if(is_cartesian){
x = a;
y = b;
}
else{
x = a * cos( b );
y = a * sin( b );
}
}