Skip to content
Snippets Groups Projects
Commit 2b8ea86c authored by sucharush's avatar sucharush
Browse files

update output and readme

parent 62ad7dc6
No related branches found
No related tags found
No related merge requests found
......@@ -20,15 +20,32 @@ i.e. $y^{'}(t)=f(t,y(t))$. The solvers currently support the following methods:
- **Backward Euler (BE)**: Implicit single-step solver with optional tolerance and iteration settings.
The default implementation solves the model problem $y^{'} = -0.3y$ using a 4th-order Runge-Kutta method.
The project is modular, allowing customization of solvers, models, and configurations.
The project is modular, allowing customization of solvers, functions, and configurations.
### Right-Hand Side (RHS) Functions
To represent the right-hand side function $f(t, y)$ in the ODE, the project provides the following built-in options:
1. **Model Problem (`model`)**:
- Describes a simple exponential decay problem $f(t, y) = -k \cdot y$.
- Requires a positive decay constant `decay = k`.
- Given the initial condition $y(t) = y_0$, the analytical solution is $y(t) = y_0 e^{-k(t-t_0)}.$
2. **Polynomial Function (`poly`)**:
- Describes a polynomial of the form $f(t, y) = a_0 + a_1 y + a_2 y^2 + \dots + a_n y^n$.
- Requires a list of coefficients `[a_0, a_1, ..., a_n]`.
These RHS functions can be selected and configured in the configuration file (`config/config.json`),
providing flexibility for different types of ODEs.
You can also implement custom RHS functions by modifying
the `ExampleRHS` classes.
## Structure
The main structure of the project is as follows:
- `/config`: configuration settings to run the main.
- `/include`: contains all header files.
- `/src`: contains all implementation files.
- `/test`: contains test files, supported by googletest.
The detailed structure of the project is shown below.
[//]: # (The detailed structure of the project is shown below.)
```
pcsc-project/
├── config/
......@@ -47,7 +64,9 @@ The detailed structure of the project is shown below.
│ ├── ImplicitSolver.h // Base class for implicit solvers
│ ├── json.hpp
│ ├── KnownDerivativeRHS.h // RHS with analytically known derivative
│ ├── ModelProblemRHS.h
│ ├── ODERightHandSide.h // Base class for RHS functions
│ ├── PolynomialRHS.h
│ ├── RungeKuttaSolver.h
│ ├── SolverFactory.h
│ └── UnknownDerivativeRHS.h // RHS with unknown derivative
......@@ -57,11 +76,14 @@ The detailed structure of the project is shown below.
│ └── [Test files using googletest]
├── .gitignore
├── .gitmodules
├── main.cc
└── CMakeLists.txt
├── main.cc
├── CMakeLists.txt
└── README.md
```
Here is a brief sketch of the class hierarchy.
<img src="hierarchy.png" alt="UML Diagram" width="700">
```
## How to
### Quick Start
First, you need to clone the project repository on your local machine.
......@@ -76,47 +98,62 @@ The project relies on *googletest* and *Eigen*, initialize them as submodules to
git submodule update --init
```
You can build the project using your preferred IDE (e.g., CLion) or the terminal:
You can build and run the project using your preferred IDE (e.g., CLion) or the terminal. Below are the steps for the terminal:
```
mkdir build
cd build
cmake ..
make
```
After these lines, executable files are produced. Simply run 'main' on your IDE,
or run the following lines on your terminal:
```
cd .. // or simply open your terminal in the root project folder
./main
```
[//]: # (Instructions should appear to guide you. )
[//]: # (After these lines, executable files are produced. Simply run 'main' on your IDE, )
[//]: # (or run the following lines on your terminal:)
[//]: # ()
[//]: # (```)
[//]: # (cd .. // or simply open your terminal in the root project folder)
[//]: # (./main)
[//]: # (```)
[//]: # (TODO complete instructions if needed, typical execution)
### Update the Configuration
Modify the `config/config.json` file to set global parameters and solver-specific options.
```
{
"global": {
"stepSize": The time step for the solver,
"t0": Start time of the simulation,
"t1": End time of the simulation,
"initialValue": Initial guess y_0,
"stepSize": The time step for the solver, e.g., 0.01,
"t0": Start time of the simulation, e.g., 0.0,
"t1": End time of the simulation, e.g., 1.0,
"initialValue": Initial guess y_0, e.g., 1.0
},
"solver": {
"method": Solver type, choosing from {"RK", "AB", "BE", "FE"},
"method": Solver type, choose from {"RK", "AB", "BE", "FE"},
"parameters": {
"order": For Runge Kutta, choosing from {1, 2, 4},
"steps": For AdamsBashforth, choosing from {1, 2, 3, 4},
"initMethod": For AdamsBashforth, choosing from {"RK4", "FE"},
"tolerance": For Newton method in backward Euler, defaulted as 1e-6,
"maxIter": For Newton method in backward Euler, defaulted as 50
"order": For Runge Kutta, choose from {1, 2, 4},
"steps": For AdamsBashforth, choose from {1, 2, 3, 4},
"initMethod": For AdamsBashforth, choose from {"RK4", "FE"},
"tolerance": For Newton method in Backward Euler, default 1e-6,
"maxIter": For Newton method in Backward Euler, default 50
}
},
"rhs": {
"type": RHS type, choose from {"model", "poly"},
"parameters": {
"decay": Decay constant for model problem, e.g., 0.1,
"coefficients": For polynomials, a list of coefficients [a_0, a_1, ..., a_n]
}
}
}
```
**Note:** It will automatically build the forward Euler method when choosing `order = 1` for Runge Kutta
or `steps = 1` for AdamsBashforth, as they are the same solvers.
### Modify the RHS Function
[//]: # (TODO logic)
......@@ -150,7 +187,7 @@ The user would also need to specify if they want to use the known derivative for
### Dimension of the ODE
The main limitation of our project is that it focuses strictly on ODEs that are from $\mathbb{R} \times \mathbb{R}$ to $\mathbb{R}$, i.e. $y,t$ are real and so is $f(t,y)$.
Possible extensions could be
- allowing $y$ to be a vector, i.e. in $\mathbb{R}^n$
- allowing $y$ to be a vector, i.e. $y \in \mathbb{R}^n$
- allowing $f$ to be a vector-valued function, i.e. solve a system of ODEs
- allowing $y$ to be a complex number, or a complex-valued vector, etc.
......
hierarchy.png

75.9 KiB

#include <iostream>
#include <filesystem>
#include <fstream>
#include <memory>
#include <cmath>
#include "include/ForwardEulerSolver.h"
......@@ -16,9 +17,9 @@
#include "ExampleRHS.h"
#include "SolverFactory.h"
double computeAnalyticalSolution(double t, double y0, double k) {
return y0 * std::exp(-k * t); // define analytical solution if you know
}
// double computeAnalyticalSolution(double t, double y0, double k) {
// return y0 * std::exp(-k * t); // define analytical solution if you know
// }
// double computeAnalyticalSolution(double t, double y0) {
// double C = y0 + 0.5; // Calculate the integration constant C
// return C * exp(2 * t) - 0.5; // y = Ce^(2t) - 0.5
......@@ -33,26 +34,22 @@ int main() {
SolverConfig config = ConfigParser::parseConfig(configPath.string());
auto solver = SolverFactory::createSolver(config);
// set right hand side function
double k = 0.3; // Decay constant
// std::unique_ptr<ODERightHandSide> rhs = std::make_unique<ExampleRHS>(k);
// solver->SetRightHandSide(std::move(rhs));
// solve equation
solver->SolveEquation(std::cout);
// print result
double t = config.globalParams["t0"];
double stepSize = config.globalParams["stepSize"];
int numSteps = static_cast<int>((config.globalParams["t1"] - t) / stepSize) + 1;
double initialValue = config.globalParams["initialValue"];
std::cout << "\nComparison with Analytical Solution:\n";
std::cout << "t\tNumerical\tAnalytical\tError\n";
for (int i = 0; i < numSteps; ++i, t += stepSize) {
double numerical = solver->results[i];
double analytical = computeAnalyticalSolution(t, initialValue, k);
double error = std::abs(numerical - analytical);
std::cout << t << "\t" << numerical << "\t" << analytical << "\t" << error << "\n";
// generate output file from result
Eigen::VectorXd results = solver -> results;
std::filesystem::path outputPath = projectRoot / "output.txt";
std::ofstream outputFile(outputPath);
outputFile << std::fixed << std::setprecision(4);
if (!outputFile.is_open()) {
std::cerr << "Could not open output file " << outputPath.string() << std::endl;
return 1;
}
for (int i = 0; i < results.size(); i++) {
outputFile << results[i] << std::endl;
}
outputFile.close();
} catch (const std::exception& ex) {
std::cerr << "Error: " << ex.what() << std::endl;
return 1;
......
1.0000
0.9970
0.9940
0.9910
0.9881
0.9851
0.9822
0.9792
0.9763
0.9734
0.9704
0.9675
0.9646
0.9618
0.9589
0.9560
0.9531
0.9503
0.9474
0.9446
0.9418
0.9389
0.9361
0.9333
0.9305
0.9277
0.9250
0.9222
0.9194
0.9167
0.9139
0.9112
0.9085
0.9057
0.9030
0.9003
0.8976
0.8949
0.8923
0.8896
0.8869
0.8843
0.8816
0.8790
0.8763
0.8737
0.8711
0.8685
0.8659
0.8633
0.8607
0.8581
0.8556
0.8530
0.8504
0.8479
0.8454
0.8428
0.8403
0.8378
0.8353
0.8328
0.8303
0.8278
0.8253
0.8228
0.8204
0.8179
0.8155
0.8130
0.8106
0.8082
0.8057
0.8033
0.8009
0.7985
0.7961
0.7937
0.7914
0.7890
0.7866
0.7843
0.7819
0.7796
0.7772
0.7749
0.7726
0.7703
0.7680
0.7657
0.7634
0.7611
0.7588
0.7565
0.7543
0.7520
0.7498
0.7475
0.7453
0.7430
0.7408
uml.pdf 0 → 100644
File added
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment