├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── banner.png ├── build └── Readme.md ├── docs ├── engines.drawio ├── engines.png ├── mesh.drawio └── mesh.png ├── examples └── 1dHeatRod │ └── 1dHeatRod.cpp └── src ├── Engine.cpp ├── Engine.hpp ├── Engine1D.cpp ├── Engine1D.hpp ├── Engine2D.cpp ├── Engine2D.hpp ├── FDMEngine.hpp ├── Mesh.hpp ├── Mesh1D.hpp └── Mesh2D.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/* 2 | *.pch 3 | *.log 4 | *.tlog 5 | *.obj 6 | *.idb 7 | *.pdb 8 | *.ipch 9 | *.db 10 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(QODES) 3 | set(CMAKE_CXX_STANDARD 20) 4 | include_directories(src/) 5 | set(SOURCES examples/1dHeatRod/1dHeatRod.cpp src/Engine1D.cpp src/Engine.cpp) 6 | add_executable(FDM ${SOURCES}) 7 | 8 | if(MSVC) 9 | add_compile_options(/W4 /WX) 10 | else() 11 | add_compile_options(-Wall -Wextra -pedantic -Werror) 12 | endif() 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Thomas Thelen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![alt text](https://github.com/ThomasThelen/Finite-Difference-Method/raw/master/banner.png) 2 | 3 | [![The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) [![license](https://img.shields.io/github/license/mashape/apistatus.svg)]() 4 | [![CodeFactor](https://www.codefactor.io/repository/github/thomasthelen/finite-difference-method/badge)](https://www.codefactor.io/repository/github/thomasthelen/finite-difference-method) 5 | 6 | 7 | # FDM 8 | 9 | FDM is a C++ source library that exposes an engine for running the method of Finite Elements across one dimension. This is mostly a personal exploration of The Finite Difference Method, CUDA, and Python Bindings. 10 | 11 | ## Using 12 | 13 | To use the engine, git clone the repository and include the required headers in your source file. Examples are given in the [examples](examples/) directory. 14 | 15 | ## Building 16 | 17 | To build, run `cmake ..` and `cmake --build .` from the `build/` directory. 18 | 19 | ## Source 20 | The project has a few directories to separate various parts of the project, detailed below 21 | 22 | - `build/`: Contains the build output 23 | - `examples`: Examples of how the library can be used 24 | - `src`: The source code for the engine 25 | 26 | ### Overview 27 | 28 | The `Mesh` struct manages information about the object being simulated over. There's an associated _Mesh_ class for each dimension. For example, one dimensional simulations should use `1DMesh`. 29 | 30 | ![Mesh](docs/mesh.png) 31 | 32 | The `Engine` class parses information from associated mesh and is responsible for encapsulating the routines for the Finite Difference Method. 33 | 34 | ![Engine](docs/engines.png) 35 | 36 | ## Creating Simulations 37 | 38 | 39 | A successful simulation will have both a Mesh and an Engine class instantiated. 40 | 41 | The mesh must have the following defined, 42 | ``` 43 | mesh.spatial_length = 10; 44 | mesh.spatial_step_size = 1; 45 | mesh.thermal_conductivity = 0.01; 46 | mesh.DirchletBoundaryEquation = BC; 47 | mesh.InitialDistribution = InitialRodDistribution; 48 | ``` 49 | 50 | The boundary condition must be a function with the signature 51 | 52 | `double BoundaryCondition(double x, int time)` 53 | 54 | For example, 55 | ``` 56 | double BoundaryCondition(double x, int time) 57 | { 58 | return 15*x+t/2; 59 | } 60 | ``` 61 | 62 | The initial distribution method provides an interface to defining the temperature along the object and must have a signature of 63 | 64 | `double TempDistribution(double position)` 65 | 66 | For example, a function that defines the temperature as a constant `50` between the boundary points... 67 | 68 | ``` 69 | double TempDistribution(double position) { 70 | return 10; 71 | } 72 | ``` 73 | 74 | To run, call `StartSimulation` on the engine object and pass it the mesh object. 75 | ``` 76 | Engine engine; 77 | engine.StartSimulation(int time_length, int time_step, Mesh1D& mesh); 78 | ``` -------------------------------------------------------------------------------- /banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThomasThelen/Finite-Difference-Method/35c377038d0bc7cc311f31213bb0841681d9c411/banner.png -------------------------------------------------------------------------------- /build/Readme.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThomasThelen/Finite-Difference-Method/35c377038d0bc7cc311f31213bb0841681d9c411/build/Readme.md -------------------------------------------------------------------------------- /docs/engines.drawio: -------------------------------------------------------------------------------- 1 | 7VbBbptAEP0ajqmAtZF7re2kqlrlYLV1jxsYw7YLQ5bBgL++iz0YkIWVSFGiSDmZefOGnXlvNdgRy7S+MzJPfmAE2vHdqHbEyvF9z10E9qdFmhOycBmIjYqY1AMbdYCuktFSRVCMiISoSeVjMMQsg5BGmDQGqzFth3p8ai5juAA2odSX6G8VUcJTzN0e/woqTrqTPZczqezIDBSJjLAaQGLtiKVBpNNTWi9Bt+J1upzqbiey58YMZPSUgttt5X17kI936a9DVfzcFof7m5s590ZNNzBEdn4O0VCCMWZSr3v0i8Eyi6B9q2ujnvMdMbegZ8G/QNSwmbIktFBCqeYs1Iq2bfmnOUd/BplVzW8+Bg0Hpz7b5ibHZ6jA0oRwZWafr5E0MdAVnjibZG83YApkGltnQEtS+3Efkq9ZfOb1TtgHNuMZxgQfxkzyZm9pDDe5l7rkk9ZZrDK48Kt3oxWwShTBJpdHASq7K8fK75TWS9RojrUikrDYhRYvyOA/GGSCcAEPu7PuezAE9XXlL5XiAvGZVxPvZi/guOo3ndetr2Sw5Trei4srJsT1Vu9RXn8sr/32vbG8swl5/fco7yx4PXlt2H+oj7nB3x2x/g8= -------------------------------------------------------------------------------- /docs/engines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThomasThelen/Finite-Difference-Method/35c377038d0bc7cc311f31213bb0841681d9c411/docs/engines.png -------------------------------------------------------------------------------- /docs/mesh.drawio: -------------------------------------------------------------------------------- 1 | 7VZRb5swEP41PG4CHFD6WpJ2D+1eMmnboxdfwYvhmDkC7NfPJEcApYk6aWpVaU/4vruz777POuOJJG/vrSyzR1RgvNBXrSdWXhgG/jJ2nx7pjsjSZyC1WnHQCGz0bxgyGa21gmoWSIiGdDkHt1gUsKUZJq3FZh72hGZ+ailTOAM2W2nO0a9aUcZdRP6IfwKdZsPJgc+eXA7BDFSZVNhMILH2RGIR6bjK2wRMT97AyzHv7oL3VJiFgl6S0NDnjbhN6wS/PNyL3S9R3uw+8C4VdUPDoFz/bKKlDFMspFmP6K3FulDQ7+o7a4x5QCwdGDjwJxB1LKasCR2UUW7YC62mb336x4it7xPPquWdD0bHxrHOvriL7Q+9YG23cKVnwddI2hToStziJJK73YA5kO1cngUjSe/ndUi+ZukpblTCLViMvxAm/C/MxbjoLYXhIvfS1HzSI1TZmVqjFj19TaYJNqU8tN+4STnn/Ukbk6BBe8gVKoKlWji8Ios7mHiW4Q8RxyfW92AJ2uu8n/PECeKGBxNP5iBmuxnnXDAMr2wy44a4f07t4llqg9V7JDeck+vevTcmN3qW3PA9kruIX49cZ45P9ME3+dER6z8= -------------------------------------------------------------------------------- /docs/mesh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThomasThelen/Finite-Difference-Method/35c377038d0bc7cc311f31213bb0841681d9c411/docs/mesh.png -------------------------------------------------------------------------------- /examples/1dHeatRod/1dHeatRod.cpp: -------------------------------------------------------------------------------- 1 | // 1dHeatRod.cpp : Simulation of heat flowing in a one dimensional rod with a time & position dependant boundary condition. 2 | 3 | #include "../../src/Engine1D.hpp" 4 | #include "../../src/Mesh1D.hpp" 5 | 6 | // A boundary condition that varies in space and time 7 | double BC(double x, int time) 8 | { 9 | return .2+x * (time +.1); 10 | } 11 | 12 | // A function that defines the initial temperature at some x position. Used to 13 | // populate the initial conditions on the rod (everything inbetween the boundaries) 14 | double TempDistribution(double position) { 15 | return position + 1; 16 | } 17 | 18 | int main() 19 | { 20 | // Create a 1 dimensional mesh with 21 | // Length: 10 meters 22 | // Step size: 1 meter 23 | // Thermal Conductivity: 0.01 24 | Mesh1D mesh; 25 | mesh.x_length = 10; 26 | mesh.spatial_step_size = 1; 27 | mesh.thermal_conductivity = 0.01; 28 | mesh.DirchletBoundaryEquation = BC; 29 | mesh.InitialDistribution = TempDistribution; 30 | // Create the engine 31 | Engine1D engine; 32 | 33 | // Start the simulation using the mesh above 34 | engine.StartSimulation(10, 1, mesh); 35 | return 0; 36 | } -------------------------------------------------------------------------------- /src/Engine.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Engine.hpp" 3 | 4 | void Engine::CreateTime() 5 | { 6 | cout << "Creating time domain..." << endl; 7 | times.push_back(0); 8 | while (times.back() < time_length) 9 | { 10 | times.push_back(times.back() + time_step); 11 | } 12 | cout << "Time Created" << endl; 13 | } 14 | 15 | tuple Engine::GetRecordb(int state_position, int record_position) const 16 | { 17 | vector> state; 18 | tuple record; 19 | state = results.at(state_position); 20 | record = state.at(record_position); 21 | return record; 22 | } -------------------------------------------------------------------------------- /src/Engine.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Mesh.hpp" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using std::vector; 10 | using std::tuple; 11 | using std::cout; 12 | using std::endl; 13 | using std::get; 14 | 15 | /*! \class Engine 16 | * \A base class for solution engines 17 | * Contains basic information that's used across n-dimensional solvers. 18 | */ 19 | 20 | class Engine 21 | { 22 | public: 23 | size_t number_of_states; 24 | 25 | double time_length; 26 | 27 | double time_step; 28 | 29 | double initial_temperature = 0; 30 | 31 | double last_temperature = 0; 32 | 33 | // CHANGE NAME 34 | vector times; 35 | 36 | //Contains the position and Temperature data at a specific time. 37 | vector> current_configuration; 38 | 39 | // Contains all of the configurations over time. 40 | vector< vector>> results; 41 | 42 | // !Initiates the simulation 43 | 44 | // !Fills the first element of results with the initial state. 45 | void CreateInitialState(Mesh&); 46 | // !Create a csv file with the simulation results. 47 | // !This is the FTCS numerical approximation. 48 | // !Retrieve a temperature at a given state and node from the results vector. 49 | //virtual void CreateMesh(Mesh&); 50 | // !Retrieves a record from the results vector 51 | tuple GetRecordb(int, int) const; 52 | // !Retrieves a temperature in the forward or backward position. 53 | void CreateTime(); 54 | }; -------------------------------------------------------------------------------- /src/Engine1D.cpp: -------------------------------------------------------------------------------- 1 | #include "Engine1D.hpp" 2 | 3 | double Engine1D::GetPreviousTemperature(int current_state, int node_location, char direction) 4 | { 5 | // Check if we're checking the value at the next node location 6 | if (direction == 'f') 7 | { 8 | return RetrieveTemperature(current_state - 1, node_location + 1); 9 | } 10 | // Check if we're going back one node length 11 | if (direction == 'b') 12 | { 13 | return RetrieveTemperature(current_state - 1, node_location - 1); 14 | } 15 | // Check if we're at the current node 16 | if (direction == 'n') 17 | { 18 | return RetrieveTemperature(current_state - 1, node_location); 19 | } 20 | else 21 | { 22 | cout << "Error Obtaining Temperature" << endl; 23 | return 0; 24 | } 25 | } 26 | 27 | void Engine1D::StartSimulation(int time_length, int time_step, Mesh1D& mesh) 28 | { 29 | this->time_length = time_length; 30 | this->time_step = time_step; 31 | number_of_states = static_cast(time_length / time_step); 32 | CreateMesh(mesh); 33 | CreateTime(); 34 | CreateInitialState(mesh); 35 | Solve(mesh); 36 | } 37 | 38 | void Engine1D::CreateInitialState(Mesh1D& mesh) 39 | { 40 | // !Place first initial condition 41 | current_configuration.push_back(std::make_tuple(mesh.all_node_locations.front(), mesh.DirchletBoundaryEquation(0, 1))); 42 | // !Iterate over the nodes which are not boundary conditions. (starts at node 1 and stops at # of nodes-1 43 | for (auto j = 1; j <= mesh.number_of_nodes - 1; ++j) 44 | { 45 | current_configuration.push_back(std::make_tuple(mesh.all_node_locations.at(j), mesh.InitialDistribution(0))); 46 | } 47 | current_configuration.push_back(std::make_tuple(mesh.all_node_locations.back(), mesh.DirchletBoundaryEquation(0, 2))); // Once filled, set the last B.C. 48 | results.push_back(current_configuration); // Add to the vector of states 49 | current_configuration.clear(); // Clear the current state 50 | } 51 | void Engine1D::CreateMesh(Mesh1D& mesh) 52 | { 53 | mesh.number_of_nodes = static_cast((mesh.x_length / mesh.spatial_step_size)); 54 | cout << "Computing the localtions for all of the nodes (points where calculations are made)" << endl; 55 | mesh.all_node_locations.push_back(0); 56 | 57 | //Store the location of each node 58 | for (int i = 0; i < mesh.number_of_nodes; i++) 59 | { 60 | mesh.all_node_locations.push_back(mesh.all_node_locations.back() + mesh.spatial_step_size); 61 | } 62 | cout << "Mesh Created" << endl; 63 | } 64 | void Engine1D::Solve(Mesh1D& mesh) 65 | { 66 | if ((mesh.thermal_conductivity * time_step) / (mesh.spatial_step_size * mesh.spatial_step_size) > 0.5) 67 | { 68 | cout << endl << endl; 69 | cout << "Under the current configuration, unstale results may occur." << endl; 70 | cout << "To Ensure convergence, please satisfy the following equation." << endl; 71 | cout << "0.5<(thermal_conductivity*time_step)/(spatial_step_size^2)" << endl; 72 | cout << endl; 73 | cout << "This can be achieved by modifying any of the following:" << endl; 74 | cout << " -thermal_conductivity" << endl; 75 | cout << " -time_step" << endl; 76 | cout << " -spatial_step_size" << endl; 77 | cout << endl; 78 | cout << " It is currently at " << (mesh.thermal_conductivity * time_step) / (mesh.spatial_step_size * mesh.spatial_step_size) << endl; 79 | } 80 | 81 | vector temperature; 82 | temperature.reserve(number_of_states); 83 | double constants = (mesh.thermal_conductivity * time_step) / (mesh.spatial_step_size * mesh.spatial_step_size); 84 | int current_node = 1; // Always start at 1; 0 is set by the boundary condition 85 | 86 | int state_location = 1; // Always start at state 1; 0 is set by the intitial condition 87 | 88 | for (double i = 0; i <= number_of_states; i++) // Iterate over time. Start at 0 because the time step may be < or > 1 89 | { 90 | cout << i << endl; 91 | temperature.push_back(mesh.DirchletBoundaryEquation(i, 1)); //Set the boundary condition 92 | current_configuration.push_back(std::make_tuple(mesh.all_node_locations.at(0), temperature.front())); // Set the boundary condition 93 | for (int j = 1; j < mesh.number_of_nodes; ++j) // Start at the 2nd node (first is set above by the bountary condition) 94 | { 95 | cout << GetPreviousTemperature(state_location, current_node, 'f') << " + " << (1 - 2 * constants) * GetPreviousTemperature(state_location, current_node, 'n') << " +" << GetPreviousTemperature(state_location, current_node, 'b') * constants << endl; 96 | double new_temp = constants * GetPreviousTemperature(state_location, current_node, 'f') + (1 - 2 * constants) * GetPreviousTemperature(state_location, current_node, 'n') + GetPreviousTemperature(state_location, current_node, 'b') * constants; // Get the temperature at the current node #; passes the current state. 97 | temperature.push_back(new_temp); 98 | current_configuration.push_back(std::make_tuple(mesh.all_node_locations.at(current_node), temperature.at(current_node))); // Use current_node to access the approriate physical node location. 99 | ++current_node; 100 | } 101 | ++state_location; 102 | temperature.push_back(mesh.DirchletBoundaryEquation(i, 2)); 103 | current_configuration.push_back(std::make_tuple(mesh.all_node_locations.at(current_node), 0)); 104 | cout << endl << endl << endl << endl << endl; 105 | current_node = 1; 106 | results.push_back(current_configuration); // Push the current state configuration vector into the results vector. 107 | current_configuration.clear(); // Prepare for the next configuration by clearing the current one 108 | temperature.clear(); // Clear the temperature vector 109 | } 110 | CreateCSV(mesh); 111 | } 112 | void Engine1D::CreateCSV(Mesh1D mesh) 113 | { 114 | std::ofstream dataFile; 115 | dataFile.open("Results.csv"); 116 | tuple record; 117 | //Iterate records with GetRecord 118 | 119 | for (int i = 0; i < number_of_states; i++) 120 | { 121 | for (int j = 0; j < mesh.all_node_locations.size(); j++) 122 | { 123 | record = GetRecordb(i, j); 124 | dataFile << get<0>(record) << "," << get<1>(record) << endl; 125 | } 126 | } 127 | dataFile.close(); 128 | } 129 | double Engine1D::RetrieveTemperature(int state_position, int record_position) 130 | { 131 | vector> state; 132 | tuple record; 133 | state = results.at(state_position); 134 | record = state.at(record_position); 135 | return get<1>(record); 136 | } -------------------------------------------------------------------------------- /src/Engine1D.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Engine.hpp" 3 | #include "Mesh1D.hpp" 4 | 5 | class Engine1D : public Engine 6 | { 7 | public: 8 | /*! Starts the simulation. 9 | @arg Mesh1D The one-dimensional mesh that will be used during the simulation 10 | */ 11 | void StartSimulation(int time_length, int time_step, Mesh1D& mesh); 12 | 13 | 14 | /*! Fills in important properties for the mesh. 15 | @arg Mesh1D An empty mesh object whose are going to be set by CreateMesh 16 | 17 | */ 18 | void CreateMesh(Mesh1D&); 19 | 20 | 21 | /*! Creates the initial state of the experiment 22 | @arg Mesh1D 23 | 24 | */ 25 | void CreateInitialState(Mesh1D&); 26 | 27 | void CreateCSV(Mesh1D); 28 | 29 | void Solve(Mesh1D&); 30 | 31 | double RetrieveTemperature(int, int); 32 | 33 | double GetPreviousTemperature(int, int, char); 34 | }; 35 | -------------------------------------------------------------------------------- /src/Engine2D.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Engine2D.hpp" 3 | 4 | void Engine2D::CreateInitialState(Mesh2D &mesh) 5 | { 6 | vector>> instance_container; 7 | for (int i = 0; i < (mesh.c_length / mesh.spatial_step_size); i++) 8 | { 9 | current_configuration.push_back(std::make_tuple(mesh.all_node_locations.front(), mesh.left_side_boundary_conditions.at(0))); //Put the first I.C in. 10 | for (float j = 1; j <= mesh.number_of_nodes - 1; ++j) // Iterate over the nodes which are not boundary conditions. (starts at node 1 and stops at # of nodes-1 11 | { 12 | current_configuration.push_back(std::make_tuple(mesh.all_node_locations.at(j), mesh.InitialDistribution(0))); 13 | } 14 | current_configuration.push_back(std::make_tuple(mesh.all_node_locations.back(), mesh.right_side_boundary_conditions.at(0))); // Once filled, set the last B.C. 15 | Instance.push_back(current_configuration); 16 | current_configuration.clear(); // Clear the current state 17 | } 18 | 19 | Results.push_back(Instance); // Add to the vector of states 20 | Instance.clear(); 21 | } 22 | void Engine2D::CreateMesh(Mesh2D& mesh) 23 | { 24 | // Fill side1 length BC -> Left side 25 | // Fill side2 length BC -> Right side 26 | // Fill width1 BC ->Top 27 | // Fill width2 BC ->bottom 28 | mesh.number_of_nodes = (mesh.c_length / mesh.spatial_step_size); 29 | mesh.all_node_locations.push_back(0); 30 | for (int i = 0; i < mesh.number_of_nodes; i++) 31 | { 32 | mesh.all_node_locations.push_back(mesh.all_node_locations.back() + mesh.spatial_step_size); 33 | } 34 | // !Create a boundary condition for each state 35 | for (int state = 0; state <= number_of_states; state++) 36 | { 37 | mesh.left_side_boundary_conditions.push_back(mesh.SideBoundary()); 38 | mesh.right_side_boundary_conditions.push_back(mesh.SideBoundary()); 39 | // !First and last states 40 | if (state== 0 || state==number_of_states) 41 | { 42 | mesh.top_width_boundary_conditions.push_back(mesh.WidthBoundary()); 43 | mesh.bottom_width_boundary_conditions.push_back(mesh.WidthBoundary()); 44 | } 45 | } 46 | } 47 | bool Engine2D::CheckStability(Mesh2D mesh) 48 | { 49 | double check = 2 * (mesh.thermal_conductivity*time_step) / (mesh.spatial_step_size*mesh.spatial_step_size); 50 | if (check <= 0.5) 51 | { 52 | cout << endl << endl; 53 | cout << "Under the current configuration, unstale results may occur." << endl; 54 | cout << "To Ensure convergence, please satisfy the following equation." << endl; 55 | cout << "2*(mesh.thermal_conductivity*time_step)/(mesh.spatial_step_size*mesh.spatial_step_size) <=0.5" << endl; 56 | cout << endl; 57 | cout << "This can be achieved by modifying any of the following:" << endl; 58 | cout << " -thermal_conductivity" << endl; 59 | cout << " -time_step" << endl; 60 | cout << " -spatial_step_size" << endl; 61 | cout << endl; 62 | cout << " It is currently at " << (mesh.thermal_conductivity*time_step) / (mesh.spatial_step_size*mesh.spatial_step_size) << endl; 63 | return false; 64 | } 65 | return true; 66 | } 67 | void Engine2D::Solve(Mesh2D &mesh) 68 | { 69 | CheckStability(mesh); 70 | 71 | double alpha = (mesh.thermal_conductivity*time_step) / (mesh.spatial_step_size); 72 | double beta=alpha; 73 | vector Temperature; 74 | // !Iterate over the states 75 | for (int i = 1; i <= number_of_states; i++) 76 | { 77 | // !Iterate through each row on the panel 78 | for (int j = 1; j < (mesh.x_length / mesh.spatial_step_size); j++) 79 | { 80 | // SET THE FIRST NODE FROM BC 81 | Temperature.push_back(5); 82 | current_configuration.push_back(std::make_tuple(mesh.all_node_locations.at(0), Temperature.at(0))); 83 | // !Iterate over the node 84 | for (int k = 1; k < mesh.number_of_nodes; k++) 85 | { 86 | Temperature.push_back(alpha*(GetPreviousTemperature(i, j, k, 'f') + GetPreviousTemperature(i, j, k, 'b')) +(beta*(GetPreviousTemperature(i, j,k, 'f') + GetPreviousTemperature(i, j, k, 'b'))) + ((1 - 2 * alpha - 2 * beta)*GetPreviousTemperature(i, j, k, 'n'))); 87 | current_configuration.push_back(std::make_tuple(mesh.all_node_locations.at(k), Temperature.at(k))); 88 | } 89 | Temperature.push_back(5); 90 | current_configuration.push_back(std::make_tuple(mesh.all_node_locations.at(mesh.all_node_locations.size()-1), Temperature.at(mesh.number_of_nodes))); 91 | Instance.push_back(current_configuration); 92 | current_configuration.clear(); 93 | } 94 | Results.push_back(Instance); 95 | Instance.clear(); 96 | } 97 | CreateCSV(mesh); 98 | } 99 | 100 | double Engine2D::RetrieveTemperature(int current_state, int y_position, int node_position) 101 | { 102 | vector>> instance_container; 103 | vector> temperature_dist; 104 | tuple record; 105 | instance_container = Results.at(current_state); 106 | temperature_dist =instance_container.at(y_position); 107 | record = temperature_dist.at(node_position); 108 | return get<1>(record); 109 | } 110 | 111 | double Engine2D::GetPreviousTemperature(int current_state, int y_position, double node_location, char direction) 112 | { 113 | if (direction == 'f') 114 | { 115 | return RetrieveTemperature(current_state-1, y_position - 1, node_location + 1); 116 | } 117 | if (direction == 'b') 118 | { 119 | return RetrieveTemperature(current_state-1, y_position - 1, node_location - 1); 120 | } 121 | if (direction == 'n') 122 | { 123 | return RetrieveTemperature(current_state-1, y_position - 1, node_location); 124 | } 125 | else 126 | { 127 | cout << "Error Obtaining Temperature" << endl; 128 | return 0; 129 | } 130 | } 131 | 132 | void Engine2D::CreateCSV(Mesh2D mesh) 133 | { 134 | std::ofstream dataFile; 135 | dataFile.open("Results.csv"); 136 | tuple record; 137 | //Iterate records with GetRecord 138 | 139 | for (int i = 0; i < number_of_states; i++) 140 | { 141 | for (int j = 0; j < mesh.all_node_locations.size(); j++) 142 | { 143 | record = GetRecordb(i, j); 144 | dataFile << get<0>(record) << "," << get<1>(record) << endl; // CORRECT THIS 145 | } 146 | } 147 | dataFile.close(); 148 | } 149 | -------------------------------------------------------------------------------- /src/Engine2D.hpp: -------------------------------------------------------------------------------- 1 | #include "Engine.hpp" 2 | 3 | class Engine2D : public Engine 4 | { 5 | public: 6 | vector>> Instance; 7 | 8 | // !This is ugly 9 | vector>>> Results; 10 | 11 | void CreateMesh(Mesh2D&); 12 | 13 | void CreateInitialState(Mesh2D&); 14 | 15 | void CreateCSV(Mesh2D); 16 | 17 | void Solve(Mesh2D&); 18 | 19 | bool CheckStability(Mesh2D); 20 | 21 | double RetrieveTemperature(int, int, int); 22 | 23 | double GetPreviousTemperature(int, int, double, char); 24 | }; -------------------------------------------------------------------------------- /src/FDMEngine.hpp: -------------------------------------------------------------------------------- 1 | /*! \file FDMEngine.hpp 2 | * \brief Contains the methods for the simulation. 3 | * 4 | * The two classes are Mesh and Engine 5 | */ 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/Mesh.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | struct Mesh 5 | { 6 | //! Length of the object. 7 | double x_length; 8 | 9 | //! The step size between nodes. 10 | double spatial_step_size; 11 | 12 | //! The thermal conductivity of the mesh. 13 | double thermal_conductivity; 14 | 15 | //! Number of nodes on the mesh -> POSSIBLY DELETE. 16 | int number_of_nodes; 17 | 18 | //! A pointer to the user defined boundary condition function. 19 | std::function DirchletBoundaryEquation; 20 | std::function InitialDistribution; 21 | }; -------------------------------------------------------------------------------- /src/Mesh1D.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Mesh.hpp" 3 | #include 4 | struct Mesh1D : public Mesh 5 | { 6 | public: 7 | double spatial_width; 8 | std::vector all_node_locations; 9 | }; 10 | -------------------------------------------------------------------------------- /src/Mesh2D.hpp: -------------------------------------------------------------------------------- 1 | #include "Mesh.hpp" 2 | 3 | struct Mesh2D : public Mesh 4 | { 5 | double spacial_width; 6 | 7 | vector left_side_boundary_conditions; 8 | 9 | vector right_side_boundary_conditions; 10 | 11 | vector top_width_boundary_conditions; 12 | 13 | vector bottom_width_boundary_conditions; 14 | 15 | vector all_node_locations; 16 | 17 | std::function SideBoundary; 18 | 19 | std::function WidthBoundary; 20 | }; --------------------------------------------------------------------------------