#pragma once
#ifndef ABSTRACTODESOLVER_H
#define ABSTRACTODESOLVER_H
/*1. Write the methods associated with the class AbstractOdeSolver and save these as the file AbstractOdeSolver.cpp.
Note that you do not have to write the pure virtual functions, as the “=0” when they are declared in the file
AbstractOdeSolver.hpp means that these are already written.*/
#include <iostream>
usingnamespace std;
class AbstractOdeSolver
{
protected:
double stepSize = 0.0;
double initialTime = 0.0;
double finalTime = 0.0;
double initialValue = 0.0;
public:
//Constructors to initialise or override values
AbstractOdeSolver();
AbstractOdeSolver(double, double, double, double);
//Methods to set parameters
void SetStepSize(double h);
void SetTimeInterval(double t0, double t1);
void SetInitialValue(double y0);
//Method to view parameters
void viewParameters();
//Virtual member functions to calculate values from ODE and solve equation
//virtual double f(double y, double t) = 0;
virtualvoid SolveEquation() = 0;
virtualdouble dy_dt(double) = 0;
frienddouble f_actual(double);
};
#endif // !ABSTRACTODESOLVER_H
#pragma once
#include "AbstractOdeSolver.h"
AbstractOdeSolver::AbstractOdeSolver()
{
stepSize = 0.0;
initialTime = 0.0;
finalTime = 0.0;
initialValue = 0.0;
}
AbstractOdeSolver::AbstractOdeSolver(double h, double t0, double t1, double y0)
{
stepSize = h;
initialTime = t0;
finalTime = t1;
initialValue = y0;
}
void AbstractOdeSolver::SetStepSize(double h)
{
stepSize = h;
cout << "The step size has been set to " << stepSize << endl;
}
void AbstractOdeSolver::SetTimeInterval(double t0, double t1)
{
initialTime = t0;
finalTime = t1;
cout << "The initial time has been set to " << initialTime << endl;
cout << "The final time has been set to " << finalTime << endl;
}
void AbstractOdeSolver::SetInitialValue(double y0)
{
initialValue = y0;
cout << "The initial time has been set to " << initialValue << endl;
}
void AbstractOdeSolver::viewParameters()
{
cout << "The parameters used to solve the equation are " << endl;
cout << "Step size h = " << stepSize << endl;
cout << "The initial time t0 = " << initialTime << endl;
cout << "The final time t1 = " << finalTime << endl;
cout << "The initial value y0 = " << initialValue << endl;
}
You've defined f_actual at global scope in two source files.
So you end up with two copies of it in the two object files that result from compiling those source files.
Then the linker complains when it tries to combine the object files together into the executable.
You could delete one of them and put a declaration for it in that file (since it needs the declaration to compile). You could also leave both of them but define them as inline:
1 2 3 4 5
inlinedouble f_actual(double t)
{
return (t * t + 2 * t + 4) / 2;
// or return (t * (t + 2) + 4) / 2
}
The #pragma once simply doesn't do anything in a source file, since they are always only processed once. A header may be processed more than once, so it's useful there.
using namespace std in a header file forces the user of the header file to put up with the potential problems of dumping the entire std namespace into the global namespace. In general, you can decide to do that yourself in a source file, but by putting it in a header, you force anyone who uses the header to do it too, which may not be their desire.
Use either #pragma once or the macro-based inclusion guards, but not both; it's redundant.
Some projects avoid #pragma once
a.) because it is nonstandard (which is usually a silly reason: it works effectively everywhere); and more importantly
b.) because its exact semantics differ between systems based on the implementation's notion of file identity (i.e., how the implementation answers the question "are these two files the same").
Defining a notion of "file identity" is far outside the scope of the C++ standard, so #pragma once remains non-standard.
AFAIK this issue with #pragma once generally only occurs in corner cases - for instance, stuff involving filesystem links and exact copies of the same source files.
Trivially, include guards are more error-prone, but with the right static analysis tools, those errors can be avoided.
#ifndef ABSTRACTODESOLVER_H
#define ABSTRACTODESOLVER_H
/*1. Write the methods associated with the class AbstractOdeSolver and save these as the file AbstractOdeSolver.cpp.
Note that you do not have to write the pure virtual functions, as the “=0” when they are declared in the file
AbstractOdeSolver.hpp means that these are already written.*/
#include <iostream>
class AbstractOdeSolver
{
protected:
double stepSize = 0.0;
double initialTime = 0.0;
double finalTime = 0.0;
double initialValue = 0.0;
public:
//Constructors to initialise or override values
AbstractOdeSolver();
AbstractOdeSolver(double, double, double, double);
//Methods to set parameters
void SetStepSize(double h);
void SetTimeInterval(double t0, double t1);
void SetInitialValue(double y0);
//Method to view parameters
void viewParameters();
//Virtual member functions to calculate values from ODE and solve equation
//virtual double f(double y, double t) = 0;
virtualvoid SolveEquation() = 0;
virtualdouble dy_dt(double) = 0;
//friend double f_actual(double);
};
#endif // !ABSTRACTODESOLVER_H
#include "AbstractOdeSolver.h"
AbstractOdeSolver::AbstractOdeSolver()
{
stepSize = 0.0;
initialTime = 0.0;
finalTime = 0.0;
initialValue = 0.0;
}
AbstractOdeSolver::AbstractOdeSolver(double h, double t0, double t1, double y0)
{
stepSize = h;
initialTime = t0;
finalTime = t1;
initialValue = y0;
}
void AbstractOdeSolver::SetStepSize(double h)
{
stepSize = h;
std::cout << "The step size has been set to " << stepSize << std::endl;
}
void AbstractOdeSolver::SetTimeInterval(double t0, double t1)
{
initialTime = t0;
finalTime = t1;
std::cout<< "The initial time has been set to " << initialTime << std::endl;
std::cout<< "The final time has been set to " << finalTime << std::endl;
}
void AbstractOdeSolver::SetInitialValue(double y0)
{
initialValue = y0;
std::cout<< "The initial time has been set to " << initialValue << std::endl;
}
void AbstractOdeSolver::viewParameters()
{
std::cout<< "The parameters used to solve the equation are " << std::endl;
std::cout<< "Step size h = " << stepSize << std::endl;
std::cout<< "The initial time t0 = " << initialTime << std::endl;
std::cout<< "The final time t1 = " << finalTime << std::endl;
std::cout<< "The initial value y0 = " << initialValue << std::endl;
}
I omitted the namespace std where I could and just added the std:: keyword to get around to have a more efficient use of resources. The inline keyword was actually one of the fixes suggested by VS 2019, but I was unsure as to its use. I have since implemented it and it compiles with expected results.
For both methods, RungeKutta and ForwardEuler cpp, I have this issue where I would like to use a variable n, for the limit variable in the for loops, but when doing so, I get an error saying that a non constant int can't be used, despite attempts to use the const keyword in the actual declaration in the body and in the corresponding header files.
I just to use the n for the size of arrays also yes! When I did put it into the array size, then I was hit with the error. I had made it part of a formula and then declare it as an int. This you can see in ForwardEuler h in line 12.
It is the equation using step size and a subtraction
constint n = floor((finalTime - initialTime) / stepSize)
You can't use variables to define the size of an array in C++. You need to use a size that is known at compile-time - i.e. a constexpr.
If you don't know the size at compile-time, you're best off using a std::vector. However, if it's really important to you to use old-fashioned C-style arrays, then you'll need to dynamicallly allocate the memory.
I ideally want the program to respond to used input during run time and not to deal with it during the compile phase.
I could probably write it with array pointers and other related stuff. Maybe I will re-write with vectors as an addition.
If you want to size the array based on user input, you'll need to use one of the techniques I described. Presumably, by "array pointers and other related stuff", you mean dynamically allocating memory, yes?
EDIT: But I strongly advise you to learn how to use vectors for this sort of thing.
Some time ago I had coded a program to calculate the exponent of a complex matrix. It was a total paint to do with the dynamic approach. I will try again with the vectors approach for sure.