Background
I'm writing some C++ code for a project that involves the simulation of a lot of State Space mathematical models. Because there are several different types of models that exist with varying degrees of complexity (ie non-linear/linear & time varying/non-varying) I created a polymorphic class structure that would be able to represent various features of each type while still keeping the common properties of the matrices bundled together nicely. This way I can generalize easily to various model types for simulation inside of OdeInt.
The current version of the simple base class looks like this:
class SS_ModelBase
{
public:
virtual Eigen::MatrixXd getA() = 0;
virtual Eigen::MatrixXd getB() = 0;
virtual Eigen::MatrixXd getC() = 0;
virtual Eigen::MatrixXd getD() = 0;
virtual Eigen::MatrixXd getX0() = 0;
virtual int getNumInputs() = 0;
virtual int getNumOutputs() = 0;
virtual int getNumStates() = 0;
private:
};
typedef boost::shared_ptr<SS_ModelBase> SS_ModelBase_sPtr;
Each derived class for the mentioned types then have their own variables for actually storing specific representations of the matrices A, B, C, D, & X0. The inputs, outputs, states are used for some dimensional information that might be needed for later manipulation. This has been working extremely well for me in practice.
An example of how I would use the model in the simulator is as shown:
Eigen::MatrixXd StateSpaceSimulator::stepResponse(const double start, const double stop, const double dt,
const SS_ModelBase_sPtr& model, const PID_Values pid)
{
/* Due to how Odeint works internally, it's not feasible to pass in a full state space model object
and run the algorithm directly on that. (Odeint REALLY dislikes pointers) Instead, a local copy of the
model will be made. For now, assume that only NLTIV models are handled. */
SS_NLTIVModel localModel(model);
/* Copy the relevant data from the model for cleaner code in main sim*/
Eigen::MatrixXd x = localModel.getX0();
Eigen::MatrixXd A = localModel.getA();
Eigen::MatrixXd B = localModel.getB();
Eigen::MatrixXd C = localModel.getC();
Eigen::MatrixXd D = localModel.getD();
//Other irrelevant code that actually performs numerical simulations
}
The custom copy constructor in the above code looks like this:
/* Copy constructor */
SS_NLTIVModel(const SS_ModelBase_sPtr& base)
{
inputs = base->getNumInputs();
outputs = base->getNumOutputs();
states = base->getNumStates();
A.resize(states, states); A.setZero(states, states);
B.resize(states, inputs); B.setZero(states, inputs);
C.resize(outputs, states); C.setZero(outputs, states);
D.resize(outputs, inputs); D.setZero(outputs, inputs);
X0.resize(states, 1); X0.setZero(states, 1);
U.resize(inputs, 1); U.setZero(inputs, 1);
}
Question
I noticed in some of my simulations that I would get memory access violation errors in the copy constructor when trying to resize the various matrices. std::malloc() gets called deep in the Eigen framework and was throwing errors. After many hours pounding my head against the wall trying to figure out why the Eigen software was suddenly not working, I stumbled upon a solution, but I don't know why it works or if there is a better way.
My old broken function signature looked like this:
Eigen::MatrixXd StateSpaceSimulator::stepResponse(const double start, const double stop, const double dt,
SS_ModelBase_sPtr model, PID_Values pid)
The new working function signature looks like this:
Eigen::MatrixXd StateSpaceSimulator::stepResponse(const double start, const double stop, const double dt,
const SS_ModelBase_sPtr& model, const PID_Values pid)
What's the run time difference between these two that could be causing my error? I know the first will increment the reference counter for the shared_ptr and the second will not, but other than that, I'm not sure what would change the behavior enough to cause my problem...thoughts?