Solution Manager class
The Solution Manager class can serve as a solution observer and/or as an event function that stops computation when a certain event occurs. Solution Manager functor will be called every time step, providing the time t
and the corresponding solution vector x
for the user for further post-processing. If the functor returns an integer which is not equal to 0
(i.e., true
), the computation will immediately stop.
The solver provides a derived from the Solution Manager class called daecpp::Solution
, which writes the solution vector x
and time t
every time step or every specific times defined by the user into the Solution Holder object. The user can access this object for further post-processing, printing the solution on the screen or writing it to a file.
By default, the DAE solver does nothing with the solution, i.e., it does not print it on the screen or save it anywhere. It is the user’s responsibility to define a Solution Manager object that processes the solution (saves it to a vector, to a file, etc.). The solver, however, provides a basic Solution Manager called daecpp::Solution
.
Solution Manager definition
The Solution Manager class daecpp::SolutionManager
is a virtual class that provides an interface to define the Solution Manager. In order to make use of this class, the user should inherit it and overload the ()
operator:
class UserDefinedSolutionManager : public daecpp::SolutionManager
{
public:
int operator()(const daecpp::state_vector &x, const double t)
{
// Solution Manager definition
return 0; // Returns 0 by default or an integer != 0 to stop the computation
}
};
The operator ()
will be called every time step, providing the user with the current state x
and the corresponding time t
. If the functor returns non-zero integer, the computation will stop. The user is free to define the solution container and save the solution within the Solution Manager class. The solver provides SolutionHolder
helper class that stores solution vectors x
and the corresponding times t
.
The type of vector x
in the class definition above is daecpp::state_vector
, which is an alias of std::vector<float_type>
type. See dae-cpp
types section for more information.
Example
In the following example, we define a custom Solution Manager that performs the following 3 tasks:
- writes the element
x[0]
of the solutionx
and timet
intostd::vector
every time step, - prints time
t
andx[0]
on the screen every time step, - stops computation if
x[0]
becomes negative.
class UserDefinedSolutionManager : public daecpp::SolutionManager
{
daecpp::state_vector &m_x_sol; // Vector of solutions x[0]
std::vector<double> &m_t_sol; // Vector of the corresponding times t
public:
UserDefinedSolutionManager(daecpp::state_vector &x_sol, std::vector<double> &t_sol)
: m_x_sol(x_sol), m_t_sol(t_sol) {}
int operator()(const daecpp::state_vector &x, const double t)
{
const auto x_0 = x[0]; // Solution element of interest
m_x_sol.push_back(x_0); // Writes x[0] every time step
m_t_sol.push_back(t); // Writes time t every time step
std::cout << t << '\t' << x_0 << '\n'; // Prints t and x[0] on screen
if (x_0 < 0.0)
{
return -1; // Stops computation if x[0] is negative
}
return 0;
}
};
Vectors x_sol
and t_sol
should be defined before calling the UserDefinedSolutionManager
constructor and must live until the end of the computation. They shouldn’t be passed to the constructor as temporary objects and must not go out of scope before finishing the system solving (otherwise, this will lead to dangling references &m_x_sol
and &m_t_sol
in the class definition above).
Similar to the mass matrix, vector function and Jacobian matrix definitions, inhereting the daecpp::SolutionManager
class is a good practice (it serves as a blueprint), but it is not necessary. The user is allowed to define custom Solution Managers without inhereting daecpp::SolutionManager
.
Solution Holder class
The Solution Holder class daecpp::SolutionHolder
is a helper class that stores solution vectors x
and the corresponding times t
. In addition, it provides a utility method that prints the entire solution or selected elements of the solution versus time on the screen. The objects of the Solution Holder class can be used with the user-defined Solution Managers or together with the Solution class.
Solution Holder class public data
Variable | Type | Description |
---|---|---|
x | std::vector<daecpp::state_vector> | Vector of solution vectors |
t | std::vector<double> | Vector of the corresponding solution times |
Thus, for example, the solution time t_end
(time at the final time step) and the corresponding solution x_end
can be accessed by
daecpp::SolutionHolder sol;
double t_end = sol.t.back(); // Time `t_end` at the last time step
daecpp::state_vector x_end = sol.x.back(); // Solution vector at time `t_end`
Solution Holder class methods
void print(const std::vector<std::size_t> &ind = {}) const
A helper function to print x
and t
on screen. By default, prints the entire vector x
and the corresponding time t
.
Parameter:
- (optional)
ind
- vector of solution indices (out of range indices will be ignored) for printing (std::vector<std::size_t>
)
Example:
daecpp::SolutionHolder sol;
// Prints only 0th, 1st, and 42nd elements of the solution vector
sol.print({0, 1, 42});
Solution class
Solution class daecpp::Solution
is derived from daecpp::SolutionManager
and serves as a basic solution observer that writes solution vectors x
and the corresponding times t
every time step or every specific time from t_output
vector (that can be optionally provided in the constructor) into daecpp::SolutionHolder
class object. The Solution class is also used in the daecpp::System
class as a default Solution Manager.
Solution class constructor
Solution(SolutionHolder &sol, const std::vector<double> &t_output)
Constructs Solution class object.
Parameters:
sol
-daecpp::SolutionHolder
object that will store the solution- (optional)
t_output
- a vector of output times for writing (std::vector<double>
)
Solution Holder sol
should be defined before calling the Solution class constructor and must live until the end of the computation. It shouldn’t be passed to the constructor as a temporary and must not go out of scope before finishing the system solving (the solver writes data to sol
).
Example:
daecpp::SolutionHolder sol; // Solution Holder
daecpp::Solution mgr(sol); // Solution Manager
Alternatively, in order to write the solution at time t_output
, which is equal to, for example, 1.0
, 2.0
, and 3.0
only:
daecpp::Solution mgr(sol, {1.0, 2.0, 3.0}); // Solution Manager that filters time `t`
After solving the system, the solution will be stored in the sol
object (Solution Holder).