├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── PdeFiniteDifferenceSolver └── main.cpp ├── PdeFiniteDifferenceSolverGpuPlot └── main.cu ├── PdeFiniteDifferenceSolverManager ├── AdvectionDiffusionSolver1D.h ├── AdvectionDiffusionSolver1D.tpp ├── AdvectionDiffusionSolver2D.h ├── AdvectionDiffusionSolver2D.tpp ├── FiniteDifferenceManager.cpp ├── FiniteDifferenceManager.h ├── FiniteDifferenceSolver.h ├── FiniteDifferenceSolver.tpp ├── FiniteDifferenceSolver1D.h ├── FiniteDifferenceSolver1D.tpp ├── FiniteDifferenceSolver2D.h ├── FiniteDifferenceSolver2D.tpp ├── IterableEnum.h ├── PdeInputData.h ├── PdeInputData1D.h ├── PdeInputData2D.h ├── WaveEquationSolver1D.h ├── WaveEquationSolver1D.tpp ├── WaveEquationSolver2D.h └── WaveEquationSolver2D.tpp ├── README.md ├── UnitTests ├── AdvectionDiffusion1DTests.cpp ├── AdvectionDiffusion2DTests.cpp ├── WaveEquation1DTests.cpp ├── WaveEquation2DTests.cpp └── main.cpp ├── Utils ├── CommandLineParser.h ├── EnumParser.h └── PdeSetup.h ├── _config.yml ├── diffusion2d_compressed.gif ├── diffusionInstability_compressed.gif ├── instability_compressed.gif ├── multiStep_compressed.gif ├── numericalDiffusion_compressed.gif ├── pdeRunner.py ├── plotter.py ├── rungeKuttaFourthOrderDiffusionInstability_compressed.gif ├── rungeKuttaFourthOrderImplicitDiffusionInstability_compressed.gif ├── rungeKuttaSecondOrderdiffusionInstability_compressed.gif ├── rungeKuttaThirdOrderdiffusionInstability_compressed.gif ├── transport2d_compressed.gif ├── wave1D_compressed.gif ├── wave2d_compressed.gif └── waveInstability1D_compressed.gif /.gitignore: -------------------------------------------------------------------------------- 1 | /home/raiden/programming/PdeFiniteDifferenceSolver/cmake/.gitignore.in -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "cmake"] 2 | path = cmake 3 | url = git@github.com:pmontalb/CmakeUtilities.git 4 | [submodule "forge"] 5 | path = forge 6 | url = git@github.com:arrayfire/forge.git 7 | [submodule "CudaLight"] 8 | path = CudaLight 9 | url = git@github.com:pmontalb/CudaLight.git 10 | [submodule "PdeFiniteDifferenceKernels"] 11 | path = PdeFiniteDifferenceKernels 12 | url = git@github.com:pmontalb/PdeFiniteDifferenceKernels.git 13 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_guard() 2 | 3 | cmake_minimum_required(VERSION 3.14) 4 | project(CudaLight) 5 | 6 | include(${CMAKE_SOURCE_DIR}/cmake/All.cmake) 7 | set(LANGUAGES_USE_CUDA ON CACHE BOOL "" FORCE) 8 | 9 | # forge 10 | #[[ requirements: 11 | sudo apt-get install libboost-all-dev 12 | sudo apt-get install libglfw3 13 | sudo apt-get install libglfw3-dev 14 | sudo apt-get install libfreetype6 15 | sudo apt-get install fontconfig 16 | sudo apt-get install libglm-dev 17 | #]] 18 | set(BUILD_SHARED_LIBS_ORIGINAL ${BUILD_SHARED_LIBS}) 19 | set(BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE) 20 | add_subdirectory(forge ${CMAKE_BINARY_DIR}/forge EXCLUDE_FROM_ALL) 21 | set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_ORIGINAL} CACHE BOOL "" FORCE) 22 | mark_as_advanced(BUILD_SHARED_LIBS_ORIGINAL) 23 | 24 | # CudaLight 25 | add_subdirectory(CudaLight ${CMAKE_BINARY_DIR}/CudaLight EXCLUDE_FROM_ALL) 26 | 27 | # PdeFiniteDifferenceKernels 28 | add_subdirectory(PdeFiniteDifferenceKernels ${CMAKE_BINARY_DIR}/PdeFiniteDifferenceKernels EXCLUDE_FROM_ALL) 29 | 30 | create_library( 31 | NAME 32 | PdeFiniteDifferenceSolverManager 33 | SOURCES 34 | PdeFiniteDifferenceSolverManager/FiniteDifferenceManager.cpp 35 | DEPENDENCIES 36 | CudaLight PdeFiniteDifferenceKernels 37 | PUBLIC_INCLUDE_DIRECTORIES 38 | PdeFiniteDifferenceSolverManager 39 | ) 40 | 41 | create_executable( 42 | NAME 43 | PdeFiniteDifferenceSolver 44 | SOURCES 45 | PdeFiniteDifferenceSolver/main.cpp 46 | DEPENDENCIES 47 | PdeFiniteDifferenceSolverManager 48 | PUBLIC_INCLUDE_DIRECTORIES 49 | . 50 | ) 51 | 52 | create_cuda_executable( 53 | NAME 54 | PdeFiniteDifferenceSolverGpuPlot 55 | SOURCES 56 | PdeFiniteDifferenceSolverGpuPlot/main.cu 57 | DEPENDENCIES 58 | PdeFiniteDifferenceSolverManager 59 | PUBLIC_INCLUDE_DIRECTORIES 60 | . 61 | SYSTEM_DEPENDENCIES 62 | forge 63 | ) 64 | 65 | create_test( 66 | NAME 67 | PdeUnitTests 68 | SOURCES 69 | UnitTests/main.cpp 70 | 71 | UnitTests/AdvectionDiffusion1DTests.cpp 72 | UnitTests/AdvectionDiffusion2DTests.cpp 73 | 74 | UnitTests/WaveEquation1DTests.cpp 75 | UnitTests/WaveEquation2DTests.cpp 76 | PUBLIC_INCLUDE_DIRECTORIES 77 | ${GTEST_INCLUDE_DIR} 78 | DEPENDENCIES 79 | PdeFiniteDifferenceSolverManager 80 | SYSTEM_DEPENDENCIES 81 | gtest pthread 82 | ) 83 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Paolo Montalbano 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 | -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolver/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | void run(solverType& solver, const clp::CommandLineArgumentParser& ap, bool debug) 5 | { 6 | std::chrono::high_resolution_clock::time_point start, end; 7 | 8 | using vType = cl::Vector; 9 | using sType = typename vType::stdType; 10 | std::vector solutionMatrix; 11 | 12 | // steps to advance before outputing the solution 13 | auto n = ap.GetArgumentValue("-n"); 14 | 15 | // total number of steps 16 | auto N = ap.GetArgumentValue("-N"); 17 | 18 | auto outputFileString = ap.GetArgumentValue("-of", "sol.cl"); 19 | 20 | unsigned nSolutions = 0; 21 | DEBUG_PRINT_START(Solving...) 22 | 23 | for (unsigned m = 0; m < N; ++m) 24 | { 25 | solver.Advance(n); 26 | 27 | const auto solution = solver.solution->columns[0]->Get(); 28 | solutionMatrix.insert(solutionMatrix.end(), solution.begin(), solution.end()); 29 | ++nSolutions; 30 | } 31 | 32 | DEBUG_PRINT_END 33 | 34 | DEBUG_PRINT_START(Saving to file...) 35 | cl::MatrixToBinaryFile(solutionMatrix, nSolutions, solver.solution->columns[0]->size(), outputFileString, false); 36 | DEBUG_PRINT_END 37 | } 38 | 39 | int main(int argc, char** argv) 40 | { 41 | clp::CommandLineArgumentParser ap(argc, argv); 42 | 43 | auto mathDomain = ep::ParseMathDomain(ap.GetArgumentValue("-md", "Float")); 44 | auto pdeType = ap.GetArgumentValue("-pde", "AdvectionDiffusion"); 45 | auto dimensionality = ap.GetArgumentValue("-dim", 1); 46 | auto debug = ap.GetFlag("-dbg"); 47 | 48 | if (dimensionality == 1) 49 | { 50 | switch (mathDomain) 51 | { 52 | case MathDomain::Float: 53 | if (pdeType == "AdvectionDiffusion") 54 | { 55 | auto solver = setup1D, MathDomain::Float>(ap, debug); 56 | run(*solver, ap, debug); 57 | } 58 | else if (pdeType == "WaveEquation") 59 | { 60 | auto solver = setup1D, MathDomain::Float>(ap, debug); 61 | run(*solver, ap, debug); 62 | } 63 | else 64 | throw NotImplementedException(); 65 | break; 66 | case MathDomain::Double: 67 | if (pdeType == "AdvectionDiffusion") 68 | { 69 | auto solver = setup1D, MathDomain::Double>(ap, debug); 70 | run(*solver, ap, debug); 71 | } 72 | else if (pdeType == "WaveEquation") 73 | { 74 | auto solver = setup1D, MathDomain::Double>(ap, debug); 75 | run(*solver, ap, debug); 76 | } 77 | else 78 | throw NotImplementedException(); 79 | break; 80 | default: 81 | throw NotImplementedException(); 82 | } 83 | } 84 | else if (dimensionality == 2) 85 | { 86 | switch (mathDomain) 87 | { 88 | case MathDomain::Float: 89 | if (pdeType == "AdvectionDiffusion") 90 | { 91 | auto solver = setup2D, MathDomain::Float>(ap, debug); 92 | run(*solver, ap, debug); 93 | } 94 | else if (pdeType == "WaveEquation") 95 | { 96 | auto solver = setup2D, MathDomain::Float>(ap, debug); 97 | run(*solver, ap, debug); 98 | } 99 | else 100 | throw NotImplementedException(); 101 | break; 102 | case MathDomain::Double: 103 | if (pdeType == "AdvectionDiffusion") 104 | { 105 | auto solver = setup2D, MathDomain::Double>(ap, debug); 106 | run(*solver, ap, debug); 107 | } 108 | else if (pdeType == "WaveEquation") 109 | { 110 | auto solver = setup2D, MathDomain::Double>(ap, debug); 111 | run(*solver, ap, debug); 112 | } 113 | else 114 | throw NotImplementedException(); 115 | break; 116 | default: 117 | throw NotImplementedException(); 118 | } 119 | } 120 | else 121 | { 122 | throw NotImplementedException(); 123 | } 124 | 125 | return 0; 126 | } -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolverGpuPlot/main.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #define USE_FORGE_CUDA_COPY_HELPERS 3 | #include 4 | 5 | #include 6 | 7 | static constexpr size_t plotWidth = { 1024 }; 8 | static constexpr size_t plotHeight = { 768 }; 9 | 10 | template 11 | void run1D(solverType& solver, const clp::CommandLineArgumentParser& ap, bool debug) 12 | { 13 | using vType = cl::Vector; 14 | using sType = typename vType::stdType; 15 | 16 | // steps to advance before outputing the solution 17 | auto n = ap.GetArgumentValue("-n"); 18 | 19 | // total number of steps 20 | auto N = ap.GetArgumentValue("-N"); 21 | 22 | forge::Window wnd(plotWidth, plotHeight, "Solution"); 23 | wnd.makeCurrent(); 24 | 25 | forge::Chart chart(FG_CHART_2D); 26 | auto& grid = solver.inputData.spaceGrid; 27 | auto _grid = solver.inputData.spaceGrid.Get(); 28 | 29 | static constexpr forge::dtype precision = forge::f32; 30 | forge::Plot plt = chart.plot(grid.size(), precision, FG_PLOT_LINE, FG_MARKER_NONE); 31 | plt.setColor(FG_BLUE); 32 | 33 | GfxHandle* handles; 34 | createGLBuffer(&handles, plt.vertices(), FORGE_VERTEX_BUFFER); 35 | 36 | bool toDo = true; 37 | bool setLimits = true; 38 | std::unique_ptr xyPair = nullptr; 39 | do 40 | { 41 | if (toDo) 42 | { 43 | for (unsigned m = 0; m < N; ++m) 44 | { 45 | solver.Advance(n); 46 | if (setLimits) 47 | { 48 | setLimits = false; 49 | auto _initialCondition = solver.solution->columns[0]->Get(); 50 | chart.setAxesLimits(_grid.front(), _grid.back(), *std::min_element(_initialCondition.begin(), _initialCondition.end()), *std::max_element(_initialCondition.begin(), _initialCondition.end())); 51 | } 52 | if (!xyPair) 53 | xyPair = std::make_unique(2 * grid.size()); 54 | vType::MakePair(*xyPair, grid, *solver.solution->columns[0]); 55 | copyToGLBuffer(handles, reinterpret_cast(xyPair->GetBuffer().pointer), plt.verticesSize()); 56 | wnd.draw(chart); 57 | } 58 | } 59 | 60 | wnd.draw(chart); 61 | toDo = false; 62 | } 63 | while (!wnd.close()); 64 | releaseGLBuffer(handles); 65 | } 66 | 67 | 68 | template 69 | void run2D(solverType& solver, const clp::CommandLineArgumentParser& ap, bool debug) 70 | { 71 | using vType = cl::Vector; 72 | using mType = cl::ColumnWiseMatrix; 73 | using sType = typename vType::stdType; 74 | 75 | // steps to advance before outputing the solution 76 | auto n = ap.GetArgumentValue("-n"); 77 | 78 | // total number of steps 79 | auto N = ap.GetArgumentValue("-N"); 80 | 81 | // solution matrix is a collection of flattened solutions over time 82 | forge::Window wnd(plotWidth, plotHeight, "Solution"); 83 | wnd.makeCurrent(); 84 | 85 | forge::Chart chart(FG_CHART_3D); 86 | 87 | auto& xGrid = solver.inputData.xSpaceGrid; 88 | auto& yGrid = solver.inputData.ySpaceGrid; 89 | auto _xGrid = xGrid.Get(); 90 | auto _yGrid = yGrid.Get(); 91 | chart.setAxesTitles("x-axis", "y-axis", "Solution"); 92 | 93 | forge::Surface surf = chart.surface(_xGrid.size(), _yGrid.size(), forge::f32); 94 | surf.setColor(FG_BLUE); 95 | 96 | GfxHandle* handle; 97 | createGLBuffer(&handle, surf.vertices(), FORGE_VERTEX_BUFFER); 98 | 99 | bool toDo = true; 100 | bool setLimits = true; 101 | std::unique_ptr xyzTriple = nullptr; 102 | do 103 | { 104 | if (toDo) 105 | { 106 | for (unsigned m = 0; m < N; ++m) 107 | { 108 | solver.Advance(n); 109 | if (setLimits) 110 | { 111 | setLimits = false; 112 | auto _ic = solver.solution->columns[0]->Get(); 113 | chart.setAxesLimits(_xGrid.front(), _xGrid.back(), _yGrid.front(), _yGrid.back(), *std::min_element(_ic.begin(), _ic.end()), *std::max_element(_ic.begin(), _ic.end())); 114 | } 115 | 116 | if (!xyzTriple) 117 | xyzTriple =std::make_unique(3 * xGrid.size() * yGrid.size()); 118 | 119 | mType::MakeTriple(*xyzTriple, xGrid, yGrid, *solver.solution->columns[0]); 120 | copyToGLBuffer(handle, reinterpret_cast(xyzTriple->GetBuffer().pointer), surf.verticesSize()); 121 | wnd.draw(chart); 122 | } 123 | } 124 | 125 | wnd.draw(chart); 126 | toDo = false; 127 | } 128 | while (!wnd.close()); 129 | releaseGLBuffer(handle); 130 | } 131 | 132 | int main(int argc, char** argv) 133 | { 134 | clp::CommandLineArgumentParser ap(argc, argv); 135 | 136 | auto mathDomain = ep::ParseMathDomain(ap.GetArgumentValue("-md", "Float")); 137 | auto pdeType = ap.GetArgumentValue("-pde", "AdvectionDiffusion"); 138 | auto dimensionality = ap.GetArgumentValue("-dim", 1); 139 | auto debug = ap.GetFlag("-dbg"); 140 | 141 | if (dimensionality == 1) 142 | { 143 | switch (mathDomain) 144 | { 145 | case MathDomain::Float: 146 | if (pdeType == "AdvectionDiffusion") 147 | { 148 | auto solver = setup1D, MathDomain::Float>(ap, debug); 149 | run1D(*solver, ap, debug); 150 | } 151 | else if (pdeType == "WaveEquation") 152 | { 153 | auto solver = setup1D, MathDomain::Float>(ap, debug); 154 | run1D(*solver, ap, debug); 155 | } 156 | else 157 | throw NotImplementedException(); 158 | break; 159 | case MathDomain::Double: 160 | default: 161 | throw NotImplementedException(); 162 | } 163 | } 164 | else if (dimensionality == 2) 165 | { 166 | switch (mathDomain) 167 | { 168 | case MathDomain::Float: 169 | if (pdeType == "AdvectionDiffusion") 170 | { 171 | auto solver = setup2D, MathDomain::Float>(ap, debug); 172 | run2D(*solver, ap, debug); 173 | } 174 | else if (pdeType == "WaveEquation") 175 | { 176 | auto solver = setup2D, MathDomain::Float>(ap, debug); 177 | run2D(*solver, ap, debug); 178 | } 179 | else 180 | throw NotImplementedException(); 181 | break; 182 | case MathDomain::Double: 183 | default: 184 | throw NotImplementedException(); 185 | } 186 | } 187 | else 188 | { 189 | throw NotImplementedException(); 190 | } 191 | 192 | return 0; 193 | } -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolverManager/AdvectionDiffusionSolver1D.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define MAKE_DEFAULT_CONSTRUCTORS(CLASS) \ 6 | ~CLASS() noexcept override = default; \ 7 | CLASS(const CLASS& rhs) noexcept = default; \ 8 | CLASS(CLASS&& rhs) noexcept = default; \ 9 | CLASS& operator=(const CLASS& rhs) noexcept = default; \ 10 | CLASS& operator=(CLASS&& rhs) noexcept = default; 11 | 12 | namespace pde 13 | { 14 | template 15 | class AdvectionDiffusionSolver1D: public FiniteDifferenceSolver1D, memorySpace, mathDomain> 16 | { 17 | public: 18 | // befriend the grandparent CRTP class 19 | friend class FiniteDifferenceSolver, PdeInputData1D, memorySpace, mathDomain>; 20 | // befriend the mother CRTP class 21 | friend class FiniteDifferenceSolver1D, memorySpace, mathDomain>; 22 | 23 | using FiniteDifferenceSolver1D, memorySpace, mathDomain>::FiniteDifferenceSolver1D; 24 | 25 | MAKE_DEFAULT_CONSTRUCTORS(AdvectionDiffusionSolver1D) 26 | 27 | protected: 28 | void MakeTimeDiscretizer(cl::Tensor& timeDiscretizers_, const SolverType solverType); 29 | }; 30 | 31 | #pragma region Type aliases 32 | 33 | typedef AdvectionDiffusionSolver1D GpuSingleAdvectionDiffusionSolver1D; 34 | typedef GpuSingleAdvectionDiffusionSolver1D GpuFloatAdvectionDiffusionSolver1D; 35 | typedef AdvectionDiffusionSolver1D GpuDoubleAdvectionDiffusionSolver1D; 36 | typedef AdvectionDiffusionSolver1D CpuSingleAdvectionDiffusionSolver1D; 37 | typedef CpuSingleAdvectionDiffusionSolver1D CpuFloatAdvectionDiffusionSolver1D; 38 | typedef AdvectionDiffusionSolver1D CpuDoubleAdvectionDiffusionSolver1D; 39 | typedef GpuSingleAdvectionDiffusionSolver1D ad1D; 40 | typedef GpuDoubleAdvectionDiffusionSolver1D dad1D; 41 | 42 | #pragma endregion 43 | } // namespace pde 44 | 45 | #undef MAKE_DEFAULT_CONSTRUCTORS 46 | 47 | #include 48 | -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolverManager/AdvectionDiffusionSolver1D.tpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace pde 6 | { 7 | template 8 | void AdvectionDiffusionSolver1D::MakeTimeDiscretizer(cl::Tensor& timeDiscretizers_, const SolverType solverType) 9 | { 10 | // reset everything to 0 11 | this->spaceDiscretizer = std::make_unique>(this->solution->nRows(), this->solution->nRows(), 0.0); 12 | timeDiscretizers_.Set(0.0); 13 | 14 | FiniteDifferenceInput1D _input(this->inputData.dt, 15 | this->inputData.spaceGrid.GetBuffer(), 16 | this->inputData.velocity.GetBuffer(), 17 | this->inputData.diffusion.GetBuffer(), 18 | solverType, 19 | this->inputData.spaceDiscretizerType, 20 | this->inputData.boundaryConditions); 21 | pde::detail::MakeSpaceDiscretizer1D(this->spaceDiscretizer->GetTile(), _input); 22 | pde::detail::MakeTimeDiscretizerAdvectionDiffusion(timeDiscretizers_.GetCube(), this->spaceDiscretizer->GetTile(), solverType, this->inputData.dt); 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolverManager/AdvectionDiffusionSolver2D.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define MAKE_DEFAULT_CONSTRUCTORS(CLASS) \ 6 | ~CLASS() noexcept override = default; \ 7 | CLASS(const CLASS& rhs) noexcept = default; \ 8 | CLASS(CLASS&& rhs) noexcept = default; \ 9 | CLASS& operator=(const CLASS& rhs) noexcept = default; \ 10 | CLASS& operator=(CLASS&& rhs) noexcept = default; 11 | 12 | namespace pde 13 | { 14 | template 15 | class AdvectionDiffusionSolver2D: public FiniteDifferenceSolver2D, memorySpace, mathDomain> 16 | { 17 | public: 18 | // befriend the grandparent CRTP class 19 | friend class FiniteDifferenceSolver, PdeInputData2D, memorySpace, mathDomain>; 20 | // befriend the mother CRTP class 21 | friend class FiniteDifferenceSolver2D, memorySpace, mathDomain>; 22 | 23 | using FiniteDifferenceSolver2D, memorySpace, mathDomain>::FiniteDifferenceSolver2D; 24 | 25 | MAKE_DEFAULT_CONSTRUCTORS(AdvectionDiffusionSolver2D) 26 | 27 | protected: 28 | void MakeTimeDiscretizer(cl::Tensor& timeDiscretizers_, const SolverType solverType); 29 | }; 30 | 31 | #pragma region Type aliases 32 | 33 | typedef AdvectionDiffusionSolver2D GpuSingleAdvectionDiffusionSolver2D; 34 | typedef GpuSingleAdvectionDiffusionSolver2D GpuFloatAdvectionDiffusionSolver2D; 35 | typedef AdvectionDiffusionSolver2D GpuDoubleAdvectionDiffusionSolver2D; 36 | typedef AdvectionDiffusionSolver2D CpuSingleAdvectionDiffusionSolver2D; 37 | typedef CpuSingleAdvectionDiffusionSolver2D CpuFloatAdvectionDiffusionSolver2D; 38 | typedef AdvectionDiffusionSolver2D CpuDoubleAdvectionDiffusionSolver2D; 39 | typedef GpuSingleAdvectionDiffusionSolver2D ad2D; 40 | typedef GpuDoubleAdvectionDiffusionSolver2D dad2D; 41 | 42 | #pragma endregion 43 | } // namespace pde 44 | 45 | #undef MAKE_DEFAULT_CONSTRUCTORS 46 | 47 | #include 48 | -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolverManager/AdvectionDiffusionSolver2D.tpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace pde 6 | { 7 | template 8 | void AdvectionDiffusionSolver2D::MakeTimeDiscretizer(cl::Tensor& timeDiscretizers_, const SolverType solverType) 9 | { 10 | // reset everything to 0 11 | const unsigned dimension = this->inputData.initialCondition.nRows() * this->inputData.initialCondition.nCols(); 12 | this->spaceDiscretizer = std::make_unique>(dimension, dimension, 0.0); 13 | timeDiscretizers_.Set(0.0); 14 | 15 | FiniteDifferenceInput2D _input(this->inputData.dt, 16 | this->inputData.xSpaceGrid.GetBuffer(), 17 | this->inputData.ySpaceGrid.GetBuffer(), 18 | this->inputData.xVelocity.GetBuffer(), 19 | this->inputData.yVelocity.GetBuffer(), 20 | this->inputData.diffusion.GetBuffer(), 21 | solverType, 22 | this->inputData.spaceDiscretizerType, 23 | this->inputData.boundaryConditions); 24 | pde::detail::MakeSpaceDiscretizer2D(this->spaceDiscretizer->GetTile(), _input); 25 | pde::detail::MakeTimeDiscretizerAdvectionDiffusion(timeDiscretizers_.GetCube(), this->spaceDiscretizer->GetTile(), solverType, this->inputData.dt); 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolverManager/FiniteDifferenceSolver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #define MAKE_DEFAULT_CONSTRUCTORS(CLASS) \ 13 | virtual ~CLASS() noexcept = default; \ 14 | CLASS(const CLASS& rhs) noexcept = delete; \ 15 | CLASS(CLASS&& rhs) noexcept = default; \ 16 | CLASS& operator=(const CLASS& rhs) noexcept = delete; \ 17 | CLASS& operator=(CLASS&& rhs) noexcept = default; 18 | 19 | 20 | namespace pde 21 | { 22 | /** 23 | * CRTP implementation 24 | * Instead of using type traits, I decided to pass another template parameter - pdeInputType - for a less verbose code 25 | */ 26 | template 27 | class FiniteDifferenceSolver 28 | { 29 | public: 30 | FiniteDifferenceSolver(pdeInputType&& inputData); 31 | 32 | MAKE_DEFAULT_CONSTRUCTORS(FiniteDifferenceSolver) 33 | 34 | void Precompute(); 35 | void Advance(const unsigned nSteps = 1); 36 | 37 | std::unique_ptr> solution = nullptr; 38 | pdeInputType inputData; 39 | 40 | protected: 41 | std::unique_ptr> timeDiscretizers = nullptr; 42 | std::unique_ptr> spaceDiscretizer = nullptr; 43 | std::unique_ptr> solutionDerivative = nullptr; 44 | 45 | std::unique_ptr> sparseExplicitTimeDiscretizer = nullptr; // only 1-step explicit solvers are supported in sparse format 46 | 47 | private: 48 | bool hasPrecomputed = false; 49 | }; 50 | } // namespace pde 51 | 52 | #undef MAKE_DEFAULT_CONSTRUCTORS 53 | 54 | #include 55 | -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolverManager/FiniteDifferenceSolver.tpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace pde 6 | { 7 | template 8 | FiniteDifferenceSolver::FiniteDifferenceSolver(pdeInputType&& inputData_) 9 | : inputData(std::move(inputData_)) 10 | { 11 | } 12 | 13 | template 14 | void FiniteDifferenceSolver::Precompute() 15 | { 16 | static_cast(this)->Setup(getNumberOfSteps(inputData.solverType)); 17 | static_cast(this)->MakeTimeDiscretizer(*this->timeDiscretizers, inputData.solverType); 18 | 19 | switch (inputData.solverType) 20 | { 21 | case SolverType::ExplicitEuler: 22 | sparseExplicitTimeDiscretizer = std::make_unique>(*this->timeDiscretizers->matrices[0]); 23 | timeDiscretizers.reset(); 24 | break; 25 | default: 26 | break; 27 | } 28 | } 29 | 30 | template 31 | void FiniteDifferenceSolver::Advance(const unsigned nSteps) 32 | { 33 | if (!hasPrecomputed) 34 | { 35 | hasPrecomputed = true; 36 | Precompute(); 37 | } 38 | 39 | if (timeDiscretizers) 40 | { 41 | assert(sparseExplicitTimeDiscretizer == nullptr); 42 | static_cast(this)->AdvanceImpl(*solution, *timeDiscretizers, inputData.solverType, nSteps); 43 | } 44 | else 45 | { 46 | assert(timeDiscretizers == nullptr); 47 | static_cast(this)->AdvanceImpl(*solution, *sparseExplicitTimeDiscretizer, inputData.solverType, nSteps); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolverManager/FiniteDifferenceSolver1D.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define MAKE_DEFAULT_CONSTRUCTORS(CLASS) \ 14 | ~CLASS() noexcept override = default; \ 15 | CLASS(const CLASS& rhs) noexcept = default; \ 16 | CLASS(CLASS&& rhs) noexcept = default; \ 17 | CLASS& operator=(const CLASS& rhs) noexcept = default; \ 18 | CLASS& operator=(CLASS&& rhs) noexcept = default 19 | 20 | namespace pde 21 | { 22 | /** 23 | * CRTP implementation 24 | */ 25 | template 26 | class FiniteDifferenceSolver1D: public FiniteDifferenceSolver, memorySpace, mathDomain> 27 | { 28 | public: 29 | friend class FiniteDifferenceSolver, memorySpace, mathDomain>; 30 | using FiniteDifferenceSolver, memorySpace, mathDomain>::FiniteDifferenceSolver; 31 | 32 | MAKE_DEFAULT_CONSTRUCTORS(FiniteDifferenceSolver1D); 33 | 34 | protected: 35 | void AdvanceImpl(cl::ColumnWiseMatrix& solution_, const cl::Tensor& timeDiscretizers_, const SolverType solverType, const unsigned nSteps = 1); 36 | void AdvanceImpl(cl::ColumnWiseMatrix& solution_, cl::CompressedSparseRowMatrix& timeDiscretizer_, const SolverType solverType, const unsigned nSteps = 1); 37 | 38 | virtual void Setup(const unsigned solverSteps); 39 | }; 40 | } // namespace pde 41 | 42 | #undef MAKE_DEFAULT_CONSTRUCTORS 43 | 44 | #include 45 | -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolverManager/FiniteDifferenceSolver1D.tpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace pde 6 | { 7 | template 8 | void FiniteDifferenceSolver1D::AdvanceImpl(cl::ColumnWiseMatrix& solution_, 9 | const cl::Tensor& timeDiscretizers_, 10 | const SolverType solverType, 11 | const unsigned nSteps) 12 | { 13 | FiniteDifferenceInput1D _input(this->inputData.dt, 14 | this->inputData.spaceGrid.GetBuffer(), 15 | this->inputData.velocity.GetBuffer(), 16 | this->inputData.diffusion.GetBuffer(), 17 | solverType, 18 | this->inputData.spaceDiscretizerType, 19 | this->inputData.boundaryConditions); 20 | pde::detail::Iterate1D(solution_.GetTile(), timeDiscretizers_.GetCube(), _input, nSteps); 21 | } 22 | 23 | template 24 | void FiniteDifferenceSolver1D::AdvanceImpl(cl::ColumnWiseMatrix& solution_, 25 | cl::CompressedSparseRowMatrix& timeDiscretizer_, 26 | const SolverType solverType, 27 | const unsigned nSteps) 28 | { 29 | assert(solution_.nCols() == 1); 30 | assert(solverType == SolverType::ExplicitEuler); 31 | 32 | FiniteDifferenceInput1D _input(this->inputData.dt, 33 | this->inputData.spaceGrid.GetBuffer(), 34 | this->inputData.velocity.GetBuffer(), 35 | this->inputData.diffusion.GetBuffer(), 36 | solverType, 37 | this->inputData.spaceDiscretizerType, 38 | this->inputData.boundaryConditions); 39 | pde::detail::SparseIterate1D(solution_.GetTile(), timeDiscretizer_.GetCsrBuffer(), _input, nSteps); 40 | } 41 | 42 | template 43 | void FiniteDifferenceSolver1D::Setup(const unsigned solverSteps) 44 | { 45 | this->solution = std::make_unique>(this->inputData.initialCondition.nRows(), solverSteps); 46 | this->solution->Set(*this->inputData.initialCondition.matrices[0]->columns[0], solverSteps - 1); 47 | this->timeDiscretizers = std::make_unique>(this->inputData.initialCondition.nRows(), this->inputData.initialCondition.nRows(), solverSteps); 48 | 49 | // need to calculate solution for all the steps > 1 50 | for (int step = static_cast(solverSteps) - 2; step >= 0; --step) 51 | { 52 | // make a volatile CrankNicolson scheme for filling the required steps in the solution 53 | // WARNING: if the multi-step method is higher than second order, this might reduce the overall accuracy 54 | constexpr SolverType multiStepEvolutionScheme = { SolverType::CrankNicolson }; 55 | 56 | cl::Tensor tmp(this->inputData.initialCondition.nRows(), this->inputData.initialCondition.nRows(), 1); 57 | static_cast(this)->MakeTimeDiscretizer(tmp, multiStepEvolutionScheme); 58 | 59 | // copy the previous step solution 60 | this->solution->Set(*this->solution->columns[static_cast(step) + 1], static_cast(step)); 61 | 62 | // advance with CrankNicolson scheme 63 | const auto& _solution = this->solution->columns[static_cast(step)]; 64 | FiniteDifferenceInput1D _input(this->inputData.dt, 65 | this->inputData.spaceGrid.GetBuffer(), 66 | this->inputData.velocity.GetBuffer(), 67 | this->inputData.diffusion.GetBuffer(), 68 | multiStepEvolutionScheme, 69 | this->inputData.spaceDiscretizerType, 70 | this->inputData.boundaryConditions); 71 | MemoryTile tmpBuffer(_solution->GetBuffer().pointer, _solution->size(), 1, ms, md); 72 | pde::detail::Iterate1D(tmpBuffer, tmp.GetCube(), _input, 1); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolverManager/FiniteDifferenceSolver2D.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define MAKE_DEFAULT_CONSTRUCTORS(CLASS) \ 14 | ~CLASS() noexcept override = default; \ 15 | CLASS(const CLASS& rhs) noexcept = default; \ 16 | CLASS(CLASS&& rhs) noexcept = default; \ 17 | CLASS& operator=(const CLASS& rhs) noexcept = default; \ 18 | CLASS& operator=(CLASS&& rhs) noexcept = default 19 | 20 | namespace pde 21 | { 22 | template 23 | class FiniteDifferenceSolver2D: public FiniteDifferenceSolver, memorySpace, mathDomain> 24 | { 25 | public: 26 | friend class FiniteDifferenceSolver, memorySpace, mathDomain>; 27 | using FiniteDifferenceSolver, memorySpace, mathDomain>::FiniteDifferenceSolver; 28 | 29 | MAKE_DEFAULT_CONSTRUCTORS(FiniteDifferenceSolver2D); 30 | 31 | protected: 32 | void AdvanceImpl(cl::ColumnWiseMatrix& solution_, const cl::Tensor& timeDiscretizers_, const SolverType solverType, const unsigned nSteps = 1); 33 | void AdvanceImpl(cl::ColumnWiseMatrix& solution_, cl::CompressedSparseRowMatrix& timeDiscretizer_, const SolverType solverType, const unsigned nSteps = 1); 34 | 35 | virtual void Setup(const unsigned solverSteps); 36 | }; 37 | } // namespace pde 38 | 39 | #undef MAKE_DEFAULT_CONSTRUCTORS 40 | 41 | #include 42 | -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolverManager/FiniteDifferenceSolver2D.tpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace pde 6 | { 7 | template 8 | void FiniteDifferenceSolver2D::AdvanceImpl(cl::ColumnWiseMatrix& solution_, 9 | const cl::Tensor& timeDiscretizers_, 10 | const SolverType solverType, 11 | const unsigned nSteps) 12 | { 13 | FiniteDifferenceInput2D _input(this->inputData.dt, 14 | this->inputData.xSpaceGrid.GetBuffer(), 15 | this->inputData.ySpaceGrid.GetBuffer(), 16 | this->inputData.xVelocity.GetBuffer(), 17 | this->inputData.yVelocity.GetBuffer(), 18 | this->inputData.diffusion.GetBuffer(), 19 | solverType, 20 | this->inputData.spaceDiscretizerType, 21 | this->inputData.boundaryConditions); 22 | pde::detail::Iterate2D(solution_.GetTile(), timeDiscretizers_.GetCube(), _input, nSteps); 23 | } 24 | template 25 | void FiniteDifferenceSolver2D::AdvanceImpl(cl::ColumnWiseMatrix& solution_, 26 | cl::CompressedSparseRowMatrix& timeDiscretizer_, 27 | const SolverType solverType, 28 | const unsigned nSteps) 29 | { 30 | assert(solution_.nCols() == 1); 31 | assert(solverType == SolverType::ExplicitEuler); 32 | 33 | FiniteDifferenceInput2D _input(this->inputData.dt, 34 | this->inputData.xSpaceGrid.GetBuffer(), 35 | this->inputData.ySpaceGrid.GetBuffer(), 36 | this->inputData.xVelocity.GetBuffer(), 37 | this->inputData.yVelocity.GetBuffer(), 38 | this->inputData.diffusion.GetBuffer(), 39 | solverType, 40 | this->inputData.spaceDiscretizerType, 41 | this->inputData.boundaryConditions); 42 | pde::detail::SparseIterate2D(solution_.GetTile(), timeDiscretizer_.GetCsrBuffer(), _input, nSteps); 43 | } 44 | 45 | template 46 | void FiniteDifferenceSolver2D::Setup(const unsigned solverSteps) 47 | { 48 | const unsigned dimension = this->inputData.initialCondition.nRows() * this->inputData.initialCondition.nCols(); 49 | 50 | this->solution = std::make_unique>(dimension, solverSteps, 0.0); 51 | 52 | // has to linearise the initial condition first 53 | auto flattenInitialCondition = this->inputData.initialCondition.matrices[0]->Flatten(); 54 | this->solution->Set(flattenInitialCondition, solverSteps - 1); 55 | this->timeDiscretizers = std::make_unique>(dimension, dimension, solverSteps); 56 | 57 | // need to calculate solution for all the steps > 1 58 | for (int step = static_cast(solverSteps) - 2; step >= 0; --step) 59 | { 60 | // make a volatile CrankNicolson scheme for filling the required steps in the solution 61 | // WARNING: if the multi-step method is higher than second order, this might reduce the overall accuracy 62 | constexpr SolverType multiStepEvolutionScheme = { SolverType::CrankNicolson }; 63 | 64 | cl::Tensor tmp(dimension, dimension, 1); 65 | static_cast(this)->MakeTimeDiscretizer(tmp, multiStepEvolutionScheme); 66 | 67 | // copy the previous step solution 68 | this->solution->Set(*this->solution->columns[static_cast(step) + 1], static_cast(step)); 69 | 70 | // advance with CrankNicolson scheme 71 | const auto& _solution = this->solution->columns[static_cast(step)]; 72 | FiniteDifferenceInput2D _input(this->inputData.dt, 73 | this->inputData.xSpaceGrid.GetBuffer(), 74 | this->inputData.ySpaceGrid.GetBuffer(), 75 | this->inputData.xVelocity.GetBuffer(), 76 | this->inputData.yVelocity.GetBuffer(), 77 | this->inputData.diffusion.GetBuffer(), 78 | multiStepEvolutionScheme, 79 | this->inputData.spaceDiscretizerType, 80 | this->inputData.boundaryConditions); 81 | MemoryTile tmpBuffer(_solution->GetBuffer().pointer, _solution->size(), 1, ms, md); 82 | pde::detail::Iterate2D(tmpBuffer, tmp.GetCube(), _input, 1); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolverManager/IterableEnum.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace enums 6 | { 7 | template 8 | class IterableEnum 9 | { 10 | public: 11 | class Iterator 12 | { 13 | public: 14 | explicit Iterator(const int iter) : _iter(iter) {} 15 | 16 | ~Iterator() noexcept = default; 17 | Iterator(const Iterator&) noexcept = default; 18 | Iterator(Iterator&&) noexcept = default; 19 | Iterator& operator=(const Iterator&) noexcept = default; 20 | Iterator& operator=(Iterator&&) noexcept = default; 21 | 22 | // needed for the iteration 23 | T operator*() const { return static_cast(_iter); } 24 | 25 | Iterator& operator++() 26 | { 27 | ++_iter; 28 | return *this; 29 | } 30 | 31 | bool operator!=(Iterator rhs) { return _iter != rhs._iter; } 32 | 33 | private: 34 | int _iter; 35 | }; 36 | }; 37 | 38 | // this assumes the elements __BEGIN__ and __END__ must be defined! 39 | template 40 | typename IterableEnum::Iterator begin(IterableEnum) 41 | { 42 | return typename IterableEnum::Iterator(static_cast(T::__BEGIN__)); 43 | } 44 | template 45 | typename IterableEnum::Iterator end(IterableEnum) 46 | { 47 | return typename IterableEnum::Iterator((static_cast(T::__END__))); 48 | } 49 | } // namespace enums 50 | -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolverManager/PdeInputData.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace pde 9 | { 10 | /** 11 | * Supports up to 3D input data. 12 | */ 13 | template 14 | class PdeInputData 15 | { 16 | public: 17 | cl::Tensor initialCondition; 18 | 19 | /** 20 | * Time discretization mesh size 21 | */ 22 | const double dt; 23 | 24 | /** 25 | * Solver Type 26 | */ 27 | const SolverType solverType; 28 | 29 | /** 30 | * Space Discretizer Type 31 | */ 32 | const SpaceDiscretizerType spaceDiscretizerType; 33 | 34 | PdeInputData(const cl::Tensor& initialCondition_, const double dt_, const SolverType solverType_, const SpaceDiscretizerType spaceDiscretizerType_) : initialCondition(initialCondition_), dt(dt_), solverType(solverType_), spaceDiscretizerType(spaceDiscretizerType_) {} 35 | 36 | PdeInputData(const cl::Tensor& initialCondition_, const double dt_, const SolverType solverType_) : initialCondition(initialCondition_), dt(dt_), solverType(solverType_) {} 37 | 38 | virtual ~PdeInputData() noexcept = default; 39 | PdeInputData(const PdeInputData& rhs) = default; 40 | PdeInputData(PdeInputData&& rhs) = default; 41 | PdeInputData& operator=(const PdeInputData& rhs) = default; 42 | PdeInputData& operator=(PdeInputData&& rhs) = default; 43 | }; 44 | } // namespace pde 45 | -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolverManager/PdeInputData1D.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace pde 10 | { 11 | template 12 | class PdeInputData1D: public PdeInputData 13 | { 14 | public: 15 | /** 16 | * Advection coefficient 17 | */ 18 | cl::Vector velocity; 19 | 20 | /** 21 | * Diffusion coefficient 22 | */ 23 | cl::Vector diffusion; 24 | 25 | /** 26 | * Space discretization mesh 27 | */ 28 | cl::Vector spaceGrid; 29 | 30 | const BoundaryCondition1D boundaryConditions = BoundaryCondition1D(); 31 | 32 | PdeInputData1D(const cl::Vector& initialCondition_, const cl::Vector& spaceGrid_, const cl::Vector& velocity_, const cl::Vector& diffusion_, const typename cl::Vector::stdType dt_, const SolverType solverType_, const SpaceDiscretizerType spaceDiscretizerType_, const BoundaryCondition1D& boundaryConditions_ = BoundaryCondition1D()) : PdeInputData(cl::Tensor(initialCondition_), dt_, solverType_, spaceDiscretizerType_), velocity(velocity_), diffusion(diffusion_), spaceGrid(spaceGrid_), boundaryConditions(boundaryConditions_) {} 33 | 34 | PdeInputData1D(const cl::Vector& initialCondition_, const cl::Vector& spaceGrid_, const typename cl::Traits::stdType velocity_, const typename cl::Traits::stdType diffusion_, const typename cl::Vector::stdType dt_, const SolverType solverType_, const SpaceDiscretizerType spaceDiscretizerType_, const BoundaryCondition1D& boundaryConditions_ = BoundaryCondition1D()) : PdeInputData(cl::Tensor(initialCondition_), static_cast(dt_), solverType_, spaceDiscretizerType_), velocity(cl::Vector(initialCondition_.size(), velocity_)), diffusion(cl::Vector(initialCondition_.size(), diffusion_)), spaceGrid(spaceGrid_), boundaryConditions(boundaryConditions_) {} 35 | 36 | PdeInputData1D(PdeInputData1D&& rhs) = default; 37 | }; 38 | 39 | #pragma region Type aliases 40 | 41 | typedef PdeInputData1D GpuSinglePdeInputData1D; 42 | typedef GpuSinglePdeInputData1D GpuFloatPdeInputData1D; 43 | typedef PdeInputData1D GpuDoublePdeInputData1D; 44 | 45 | typedef PdeInputData1D CpuSinglePdeInputData1D; 46 | typedef CpuSinglePdeInputData1D CpuFloatPdeInputData1D; 47 | typedef PdeInputData1D CpuDoublePdeInputData1D; 48 | 49 | #pragma endregion 50 | } // namespace pde 51 | -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolverManager/PdeInputData2D.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace pde 10 | { 11 | template 12 | class PdeInputData2D: public PdeInputData 13 | { 14 | public: 15 | /** 16 | * Space discretization mesh 17 | */ 18 | cl::Vector xSpaceGrid; 19 | cl::Vector ySpaceGrid; 20 | 21 | /** 22 | * Advection coefficient 23 | */ 24 | cl::Vector xVelocity; 25 | cl::Vector yVelocity; 26 | 27 | /** 28 | * Diffusion coefficient: depends on both dimension, and here is flattened out 29 | */ 30 | cl::Vector diffusion; 31 | 32 | const BoundaryCondition2D boundaryConditions = BoundaryCondition2D(); 33 | 34 | PdeInputData2D(const cl::ColumnWiseMatrix& initialCondition_, const cl::Vector& xSpaceGrid_, const cl::Vector& ySpaceGrid_, const cl::ColumnWiseMatrix& xVelocity_, const cl::ColumnWiseMatrix& yVelocity_, const cl::ColumnWiseMatrix& diffusion_, const typename cl::ColumnWiseMatrix::stdType dt_, const SolverType solverType_, const SpaceDiscretizerType spaceDiscretizerType_, const BoundaryCondition2D& boundaryConditions_ = BoundaryCondition2D()) : PdeInputData(cl::Tensor(initialCondition_), dt_, solverType_, spaceDiscretizerType_), xSpaceGrid(xSpaceGrid_), ySpaceGrid(ySpaceGrid_), xVelocity(xVelocity_), yVelocity(yVelocity_), diffusion(diffusion_.Flatten()), boundaryConditions(boundaryConditions_) {} 35 | 36 | PdeInputData2D(const cl::ColumnWiseMatrix& initialCondition_, const cl::Vector& xSpaceGrid_, const cl::Vector& ySpaceGrid_, const typename cl::Traits::stdType xVelocity_, const typename cl::Traits::stdType yVelocity_, const typename cl::Traits::stdType diffusion_, const typename cl::ColumnWiseMatrix::stdType dt_, const SolverType solverType_, const SpaceDiscretizerType spaceDiscretizerType_, const BoundaryCondition2D& boundaryConditions_ = BoundaryCondition2D()) : PdeInputData(cl::Tensor(initialCondition_), static_cast(dt_), solverType_, spaceDiscretizerType_), xSpaceGrid(xSpaceGrid_), ySpaceGrid(ySpaceGrid_), xVelocity(cl::Vector(initialCondition_.nRows(), xVelocity_)), yVelocity(cl::Vector(initialCondition_.nCols(), yVelocity_)), diffusion(cl::Vector(initialCondition_.size(), diffusion_)), boundaryConditions(boundaryConditions_) {} 37 | 38 | PdeInputData2D(PdeInputData2D&& rhs) = default; 39 | }; 40 | 41 | #pragma region Type aliases 42 | 43 | typedef PdeInputData2D GpuSinglePdeInputData2D; 44 | typedef GpuSinglePdeInputData2D GpuFloatPdeInputData2D; 45 | typedef PdeInputData2D GpuDoublePdeInputData2D; 46 | 47 | typedef PdeInputData2D CpuSinglePdeInputData2D; 48 | typedef CpuSinglePdeInputData2D CpuFloatPdeInputData2D; 49 | typedef PdeInputData2D CpuDoublePdeInputData2D; 50 | 51 | #pragma endregion 52 | } // namespace pde 53 | -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolverManager/WaveEquationSolver1D.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define MAKE_DEFAULT_CONSTRUCTORS(CLASS) \ 6 | virtual ~CLASS() noexcept override = default; \ 7 | CLASS(const CLASS& rhs) noexcept = default; \ 8 | CLASS(CLASS&& rhs) noexcept = default; \ 9 | CLASS& operator=(const CLASS& rhs) noexcept = default; \ 10 | CLASS& operator=(CLASS&& rhs) noexcept = default; 11 | 12 | namespace pde 13 | { 14 | template 15 | class WaveEquationSolver1D: public FiniteDifferenceSolver1D, memorySpace, mathDomain> 16 | { 17 | public: 18 | // befriend the grandparent CRTP class 19 | friend class FiniteDifferenceSolver, PdeInputData1D, memorySpace, mathDomain>; 20 | // befriend the mother CRTP class 21 | friend class FiniteDifferenceSolver1D, memorySpace, mathDomain>; 22 | 23 | using FiniteDifferenceSolver1D, memorySpace, mathDomain>::FiniteDifferenceSolver1D; 24 | 25 | MAKE_DEFAULT_CONSTRUCTORS(WaveEquationSolver1D) 26 | 27 | protected: 28 | void AdvanceImpl(cl::ColumnWiseMatrix& solution_, const cl::Tensor& timeDiscretizers_, const SolverType solverType, const unsigned nSteps = 1); 29 | void AdvanceImpl(cl::ColumnWiseMatrix& solution_, cl::CompressedSparseRowMatrix& timeDiscretizers_, const SolverType solverType, const unsigned nSteps = 1); 30 | 31 | void MakeTimeDiscretizer(cl::Tensor& timeDiscretizers_, const SolverType solverType); 32 | 33 | void Setup(const unsigned solverSteps) override; 34 | }; 35 | 36 | #pragma region Type aliases 37 | 38 | typedef WaveEquationSolver1D GpuSingleWaveEquationSolver1D; 39 | typedef GpuSingleWaveEquationSolver1D GpuFloatWaveEquationSolver1D; 40 | typedef WaveEquationSolver1D GpuDoubleWaveEquationSolver1D; 41 | typedef WaveEquationSolver1D CpuSingleWaveEquationSolver1D; 42 | typedef CpuSingleWaveEquationSolver1D CpuFloatWaveEquationSolver1D; 43 | typedef WaveEquationSolver1D CpuDoubleSolver1D; 44 | typedef GpuSingleWaveEquationSolver1D wave1D; 45 | typedef GpuDoubleWaveEquationSolver1D dwave1D; 46 | 47 | #pragma endregion 48 | } // namespace pde 49 | 50 | #undef MAKE_DEFAULT_CONSTRUCTORS 51 | 52 | #include 53 | -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolverManager/WaveEquationSolver1D.tpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace pde 6 | { 7 | template 8 | void WaveEquationSolver1D::AdvanceImpl(cl::ColumnWiseMatrix& solution_, 9 | const cl::Tensor& timeDiscretizers_, 10 | const SolverType solverType, 11 | const unsigned nSteps) 12 | { 13 | // NB: I am not writing support for multi-step algorithm here, so I will write the Iterate code here in place 14 | 15 | FiniteDifferenceInput1D _input(this->inputData.dt, 16 | this->inputData.spaceGrid.GetBuffer(), 17 | this->inputData.velocity.GetBuffer(), 18 | this->inputData.diffusion.GetBuffer(), 19 | solverType, 20 | this->inputData.spaceDiscretizerType, 21 | this->inputData.boundaryConditions); 22 | 23 | cl::ColumnWiseMatrix solutionBuffer(solution_), solutionDerivativeBuffer(*this->solutionDerivative); 24 | cl::ColumnWiseMatrix workBuffer(solution_); 25 | 26 | // for performance reasons, I'm creating two working buffers here, which I will re-use during the main loop 27 | cl::ColumnWiseMatrix *inSol = &solution_, *outSol = &solutionBuffer; 28 | cl::ColumnWiseMatrix *inDer = this->solutionDerivative.get(), *outDer = &solutionDerivativeBuffer; 29 | bool needToCopyBack = false; 30 | 31 | // u'' = L * u 32 | // ==> 33 | // u' = v; v' = L * u 34 | for (unsigned n = 0; n < nSteps; ++n) 35 | { 36 | // u' = v ==> u_{n + 1} = A * (u_n + dt * v_n) 37 | workBuffer.ReadFrom(*inSol); // w = u_n 38 | workBuffer.AddEqualMatrix(*inDer, MatrixOperation::None, MatrixOperation::None, 1.0, this->inputData.dt); // w = u_n + dt * v_n 39 | timeDiscretizers_.matrices[0]->Multiply(*outSol, workBuffer); 40 | pde::detail::SetBoundaryConditions1D(outSol->GetTile(), _input); 41 | // outSol = u_{n + 1} = A * (u_n + dt * v_n) 42 | 43 | // v' = L * u ==> v_{n + 1} = A * (v_n + dt * L * u_n) 44 | this->spaceDiscretizer->Multiply(workBuffer, *inSol, MatrixOperation::None, MatrixOperation::None, this->inputData.dt); // w = L * u_n * dt 45 | workBuffer.AddEqualMatrix(*inDer); // w = v_n + dt * L * u_n 46 | timeDiscretizers_.matrices[0]->Multiply(*outDer, workBuffer); 47 | pde::detail::SetBoundaryConditions1D(outDer->GetTile(), _input); 48 | // outDer = v_{n + 1} = A * (v_n + dt * L * u_n) 49 | 50 | std::swap(inSol, outSol); 51 | std::swap(inDer, outDer); 52 | needToCopyBack = !needToCopyBack; 53 | } 54 | 55 | if (needToCopyBack) 56 | { 57 | solution_.ReadFrom(*inSol); 58 | this->solutionDerivative->ReadFrom(*inDer); 59 | } 60 | } 61 | template 62 | void WaveEquationSolver1D::AdvanceImpl(cl::ColumnWiseMatrix& solution_, 63 | cl::CompressedSparseRowMatrix& timeDiscretizers_, 64 | const SolverType solverType, 65 | const unsigned nSteps) 66 | { 67 | assert(solution_.nCols() == 1); 68 | assert(solverType == SolverType::ExplicitEuler); 69 | 70 | FiniteDifferenceInput1D _input(this->inputData.dt, 71 | this->inputData.spaceGrid.GetBuffer(), 72 | this->inputData.velocity.GetBuffer(), 73 | this->inputData.diffusion.GetBuffer(), 74 | solverType, 75 | this->inputData.spaceDiscretizerType, 76 | this->inputData.boundaryConditions); 77 | 78 | cl::Vector solutionBuffer(*solution_.columns[0]), solutionDerivativeBuffer(*this->solutionDerivative->columns[0]); 79 | cl::Vector workBuffer(*solution_.columns[0]); 80 | 81 | // for performance reasons, I'm creating two working buffers here, which I will re-use during the main loop 82 | cl::Vector *inSol = solution_.columns[0].get(), *outSol = &solutionBuffer; 83 | cl::Vector *inDer = this->solutionDerivative->columns[0].get(), *outDer = &solutionDerivativeBuffer; 84 | bool needToCopyBack = false; 85 | 86 | // u'' = L * u 87 | // ==> 88 | // u' = v; v' = L * u 89 | for (unsigned n = 0; n < nSteps; ++n) 90 | { 91 | // u' = v ==> u_{n + 1} = A * (u_n + dt * v_n) 92 | workBuffer.ReadFrom(*inSol); // w = u_n 93 | workBuffer.AddEqual(*inDer, this->inputData.dt); // w = u_n + dt * v_n 94 | timeDiscretizers_.Dot(*outSol, workBuffer); 95 | 96 | MemoryTile tmp(outSol->GetBuffer()); 97 | pde::detail::SetBoundaryConditions1D(tmp, _input); 98 | // outSol = u_{n + 1} = A * (u_n + dt * v_n) 99 | 100 | // v' = L * u ==> v_{n + 1} = A * (v_n + dt * L * u_n) 101 | this->spaceDiscretizer->Dot(workBuffer, *inSol, MatrixOperation::None, this->inputData.dt); // w = L * u_n * dt 102 | workBuffer.AddEqual(*inDer); // w = v_n + dt * L * u_n 103 | timeDiscretizers_.Dot(*outDer, workBuffer); 104 | 105 | MemoryTile tmp2(outDer->GetBuffer()); 106 | pde::detail::SetBoundaryConditions1D(tmp2, _input); 107 | // outDer = v_{n + 1} = A * (v_n + dt * L * u_n) 108 | 109 | std::swap(inSol, outSol); 110 | std::swap(inDer, outDer); 111 | needToCopyBack = !needToCopyBack; 112 | } 113 | 114 | if (needToCopyBack) 115 | { 116 | solution_.columns[0]->ReadFrom(*inSol); 117 | this->solutionDerivative->columns[0]->ReadFrom(*inDer); 118 | } 119 | } 120 | 121 | template 122 | void WaveEquationSolver1D::MakeTimeDiscretizer(cl::Tensor& timeDiscretizers_, const SolverType solverType) 123 | { 124 | // reset everything to 0 125 | this->spaceDiscretizer = std::make_unique>(this->solution->nRows(), this->solution->nRows(), 0.0); 126 | timeDiscretizers_.Set(0.0); 127 | 128 | // since u_xx is multiplied by velocity^2, there's no actual component for u_x 129 | cl::Vector velocity(this->inputData.velocity.size(), static_cast::stdType>(0.0)); 130 | 131 | // since u_xx is multiplied by velocity^2, the 'diffusion' component is velocity^2 132 | cl::Vector diffusion(this->inputData.velocity); 133 | diffusion.ElementWiseProduct(diffusion); 134 | 135 | FiniteDifferenceInput1D _input(this->inputData.dt, 136 | this->inputData.spaceGrid.GetBuffer(), 137 | velocity.GetBuffer(), 138 | diffusion.GetBuffer(), 139 | solverType, 140 | this->inputData.spaceDiscretizerType, 141 | this->inputData.boundaryConditions); 142 | pde::detail::MakeSpaceDiscretizer1D(this->spaceDiscretizer->GetTile(), _input); 143 | pde::detail::MakeTimeDiscretizerWaveEquation(timeDiscretizers_.GetCube(), this->spaceDiscretizer->GetTile(), solverType, this->inputData.dt); 144 | } 145 | 146 | template 147 | void WaveEquationSolver1D::Setup(const unsigned solverSteps) 148 | { 149 | assert(solverSteps == 1); 150 | 151 | FiniteDifferenceSolver1D, ms, md>::Setup(solverSteps); 152 | 153 | // TODO: read from input instead of setting it to 0 154 | this->solutionDerivative = std::make_unique>(this->inputData.initialCondition.nRows(), solverSteps, static_cast::stdType>(0.0)); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolverManager/WaveEquationSolver2D.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define MAKE_DEFAULT_CONSTRUCTORS(CLASS) \ 6 | virtual ~CLASS() noexcept override = default; \ 7 | CLASS(const CLASS& rhs) noexcept = default; \ 8 | CLASS(CLASS&& rhs) noexcept = default; \ 9 | CLASS& operator=(const CLASS& rhs) noexcept = default; \ 10 | CLASS& operator=(CLASS&& rhs) noexcept = default; 11 | 12 | namespace pde 13 | { 14 | template 15 | class WaveEquationSolver2D: public FiniteDifferenceSolver2D, memorySpace, mathDomain> 16 | { 17 | public: 18 | // befriend the grandparent CRTP class 19 | friend class FiniteDifferenceSolver, PdeInputData2D, memorySpace, mathDomain>; 20 | // befriend the mother CRTP class 21 | friend class FiniteDifferenceSolver2D, memorySpace, mathDomain>; 22 | 23 | using FiniteDifferenceSolver2D, memorySpace, mathDomain>::FiniteDifferenceSolver2D; 24 | 25 | MAKE_DEFAULT_CONSTRUCTORS(WaveEquationSolver2D) 26 | 27 | protected: 28 | void AdvanceImpl(cl::ColumnWiseMatrix& solution_, const cl::Tensor& timeDiscretizers_, const SolverType solverType, const unsigned nSteps = 1); 29 | void AdvanceImpl(cl::ColumnWiseMatrix& solution_, cl::CompressedSparseRowMatrix& timeDiscretizers_, const SolverType solverType, const unsigned nSteps = 1); 30 | 31 | void MakeTimeDiscretizer(cl::Tensor& timeDiscretizers_, const SolverType solverType); 32 | 33 | void Setup(const unsigned solverSteps) override; 34 | }; 35 | 36 | #pragma region Type aliases 37 | 38 | typedef WaveEquationSolver2D GpuSingleWaveEquationSolver2D; 39 | typedef GpuSingleWaveEquationSolver2D GpuFloatWaveEquationSolver2D; 40 | typedef WaveEquationSolver2D GpuDoubleWaveEquationSolver2D; 41 | typedef WaveEquationSolver2D CpuSingleWaveEquationSolver2D; 42 | typedef CpuSingleWaveEquationSolver2D CpuFloatWaveEquationSolver2D; 43 | typedef WaveEquationSolver2D CpuDoubleSolver2D; 44 | typedef GpuSingleWaveEquationSolver2D wave2D; 45 | typedef GpuDoubleWaveEquationSolver2D dwave2D; 46 | 47 | #pragma endregion 48 | } // namespace pde 49 | 50 | #undef MAKE_DEFAULT_CONSTRUCTORS 51 | 52 | #include 53 | -------------------------------------------------------------------------------- /PdeFiniteDifferenceSolverManager/WaveEquationSolver2D.tpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace pde 6 | { 7 | template 8 | void WaveEquationSolver2D::AdvanceImpl(cl::ColumnWiseMatrix& solution_, 9 | const cl::Tensor& timeDiscretizers_, 10 | const SolverType solverType, 11 | const unsigned nSteps) 12 | { 13 | // NB: I am not writing support for multi-step algorithm here, so I will write the Iterate code here in place 14 | 15 | FiniteDifferenceInput2D _input(this->inputData.dt, 16 | this->inputData.xSpaceGrid.GetBuffer(), 17 | this->inputData.ySpaceGrid.GetBuffer(), 18 | this->inputData.diffusion.GetBuffer(), 19 | this->inputData.diffusion.GetBuffer(), 20 | this->inputData.diffusion.GetBuffer(), 21 | solverType, 22 | this->inputData.spaceDiscretizerType, 23 | this->inputData.boundaryConditions); 24 | 25 | cl::ColumnWiseMatrix solutionBuffer(solution_), solutionDerivativeBuffer(*this->solutionDerivative); 26 | cl::ColumnWiseMatrix workBuffer(solution_); 27 | 28 | // for performance reasons, I'm creating two working buffers here, which I will re-use during the main loop 29 | cl::ColumnWiseMatrix *inSol = &solution_, *outSol = &solutionBuffer; 30 | cl::ColumnWiseMatrix *inDer = this->solutionDerivative.get(), *outDer = &solutionDerivativeBuffer; 31 | bool needToCopyBack = false; 32 | 33 | // u'' = L * u 34 | // ==> 35 | // u' = v; v' = L * u 36 | for (unsigned n = 0; n < nSteps; ++n) 37 | { 38 | // u' = v ==> u_{n + 1} = A * (u_n + dt * v_n) 39 | workBuffer.ReadFrom(*inSol); // w = u_n 40 | workBuffer.AddEqualMatrix(*inDer, MatrixOperation::None, MatrixOperation ::None, 1.0, this->inputData.dt); // w = u_n + dt * v_n 41 | timeDiscretizers_.matrices[0]->Multiply(*outSol, workBuffer); 42 | pde::detail::SetBoundaryConditions2D(outSol->GetTile(), _input); 43 | // outSol = u_{n + 1} = A * (u_n + dt * v_n) 44 | 45 | // v' = L * u ==> v_{n + 1} = A * (v_n + dt * L * u_n) 46 | this->spaceDiscretizer->Multiply(workBuffer, *inSol, MatrixOperation::None, MatrixOperation::None, this->inputData.dt); // w = L * u_n * dt 47 | workBuffer.AddEqualMatrix(*inDer); // w = v_n + dt * L * u_n 48 | this->timeDiscretizers->matrices[0]->Multiply(*outDer, workBuffer); 49 | pde::detail::SetBoundaryConditions2D(outDer->GetTile(), _input); 50 | // outDer = v_{n + 1} = A * (v_n + dt * L * u_n) 51 | 52 | std::swap(inSol, outSol); 53 | std::swap(inDer, outDer); 54 | needToCopyBack = !needToCopyBack; 55 | } 56 | 57 | if (needToCopyBack) 58 | { 59 | solution_.ReadFrom(*inSol); 60 | this->solutionDerivative->ReadFrom(*inDer); 61 | } 62 | } 63 | template 64 | void WaveEquationSolver2D::AdvanceImpl(cl::ColumnWiseMatrix& solution_, 65 | cl::CompressedSparseRowMatrix& timeDiscretizers_, 66 | const SolverType solverType, 67 | const unsigned nSteps) 68 | { 69 | assert(solution_.nCols() == 1); 70 | assert(solverType == SolverType::ExplicitEuler); 71 | 72 | FiniteDifferenceInput2D _input(this->inputData.dt, 73 | this->inputData.xSpaceGrid.GetBuffer(), 74 | this->inputData.ySpaceGrid.GetBuffer(), 75 | this->inputData.diffusion.GetBuffer(), 76 | this->inputData.diffusion.GetBuffer(), 77 | this->inputData.diffusion.GetBuffer(), 78 | solverType, 79 | this->inputData.spaceDiscretizerType, 80 | this->inputData.boundaryConditions); 81 | 82 | cl::Vector solutionBuffer(*solution_.columns[0]), solutionDerivativeBuffer(*this->solutionDerivative->columns[0]); 83 | cl::Vector workBuffer(*solution_.columns[0]); 84 | 85 | // for performance reasons, I'm creating two working buffers here, which I will re-use during the main loop 86 | cl::Vector *inSol = solution_.columns[0].get(), *outSol = &solutionBuffer; 87 | cl::Vector *inDer = this->solutionDerivative->columns[0].get(), *outDer = &solutionDerivativeBuffer; 88 | bool needToCopyBack = false; 89 | 90 | // u'' = L * u 91 | // ==> 92 | // u' = v; v' = L * u 93 | for (unsigned n = 0; n < nSteps; ++n) 94 | { 95 | // u' = v ==> u_{n + 1} = A * (u_n + dt * v_n) 96 | workBuffer.ReadFrom(*inSol); // w = u_n 97 | workBuffer.AddEqual(*inDer, this->inputData.dt); // w = u_n + dt * v_n 98 | timeDiscretizers_.Dot(*outSol, workBuffer); 99 | 100 | MemoryTile tmp(outSol->GetBuffer()); 101 | pde::detail::SetBoundaryConditions2D(tmp, _input); 102 | // outSol = u_{n + 1} = A * (u_n + dt * v_n) 103 | 104 | // v' = L * u ==> v_{n + 1} = A * (v_n + dt * L * u_n) 105 | this->spaceDiscretizer->Dot(workBuffer, *inSol, MatrixOperation::None, this->inputData.dt); // w = L * u_n * dt 106 | workBuffer.AddEqual(*inDer); // w = v_n + dt * L * u_n 107 | timeDiscretizers_.Dot(*outDer, workBuffer); 108 | 109 | MemoryTile tmp2(outDer->GetBuffer()); 110 | pde::detail::SetBoundaryConditions2D(tmp2, _input); 111 | // outDer = v_{n + 1} = A * (v_n + dt * L * u_n) 112 | 113 | std::swap(inSol, outSol); 114 | std::swap(inDer, outDer); 115 | needToCopyBack = !needToCopyBack; 116 | } 117 | 118 | if (needToCopyBack) 119 | { 120 | solution_.columns[0]->ReadFrom(*inSol); 121 | this->solutionDerivative->columns[0]->ReadFrom(*inDer); 122 | } 123 | } 124 | 125 | template 126 | void WaveEquationSolver2D::MakeTimeDiscretizer(cl::Tensor& timeDiscretizers_, const SolverType solverType) 127 | { 128 | // reset everything to 0 129 | const unsigned dimension = this->inputData.initialCondition.nRows() * this->inputData.initialCondition.nCols(); 130 | this->spaceDiscretizer = std::make_unique>(dimension, dimension, 0.0); 131 | timeDiscretizers_.Set(0.0); 132 | 133 | // since u_xx is multiplied by velocity^2, there's no actual component for u_x 134 | cl::Vector velocity(this->inputData.xVelocity.size(), static_cast::stdType>(0.0)); 135 | 136 | // since u_xx is multiplied by velocity^2, the 'diffusion' component is velocity^2 137 | // not really ideal, but I'm reading it from xVelocity, pretty much arbitrarily 138 | auto _v = this->inputData.xVelocity.Get(); // FIXME: temp hack! 139 | cl::Vector diffusion(dimension, _v[0]); 140 | diffusion.ElementWiseProduct(diffusion); 141 | 142 | FiniteDifferenceInput2D _input(this->inputData.dt, 143 | this->inputData.xSpaceGrid.GetBuffer(), 144 | this->inputData.ySpaceGrid.GetBuffer(), 145 | velocity.GetBuffer(), 146 | velocity.GetBuffer(), 147 | diffusion.GetBuffer(), 148 | solverType, 149 | this->inputData.spaceDiscretizerType, 150 | this->inputData.boundaryConditions); 151 | pde::detail::MakeSpaceDiscretizer2D(this->spaceDiscretizer->GetTile(), _input); 152 | pde::detail::MakeTimeDiscretizerWaveEquation(timeDiscretizers_.GetCube(), this->spaceDiscretizer->GetTile(), solverType, this->inputData.dt); 153 | } 154 | 155 | template 156 | void WaveEquationSolver2D::Setup(const unsigned solverSteps) 157 | { 158 | assert(solverSteps == 1); 159 | 160 | FiniteDifferenceSolver2D, ms, md>::Setup(solverSteps); 161 | 162 | // TODO: read from input instead of setting it to 0 163 | const unsigned dimension = this->inputData.initialCondition.nRows() * this->inputData.initialCondition.nCols(); 164 | this->solutionDerivative = std::make_unique>(dimension, solverSteps, static_cast::stdType>(0.0)); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PdeFiniteDifferenceSolver 2 | C++ manager class for PdeFiniteDifferenceKernels API. The low level calls are managed in the namespace pde::detail DeviceManager, whereas the high level infrastructure is delegated to the particular solver type. 3 | 4 | Only linear hyperbolic and parabolic PDEs are supported (up to 3D). The exposed implementation is through: 5 | - AdvectionDiffusionSolver1D, AdvectionDiffusionSolver2D 6 | - WaveEquationSolver1D, WaveEquationSolver2D 7 | 8 | These solvers are implemented with the Curiously Recurring Template Pattern (CRTP), useful for delegating the members data type at compile time. 9 | 10 | For convenience's sake the following typedefs have been defined: 11 | 12 | - Advection-Diffusion: 13 | ```c++ 14 | typedef AdvectionDiffusionSolver1D GpuSingleAdvectionDiffusionSolver1D; 15 | typedef GpuSingleAdvectionDiffusionSolver1D GpuFloatAdvectionDiffusionSolver1D; 16 | typedef AdvectionDiffusionSolver1D GpuDoubleAdvectionDiffusionSolver1D; 17 | typedef AdvectionDiffusionSolver1D CpuSingleAdvectionDiffusionSolver1D; 18 | typedef CpuSingleAdvectionDiffusionSolver1D CpuFloatAdvectionDiffusionSolver1D; 19 | typedef AdvectionDiffusionSolver1D CpuDoubleAdvectionDiffusionSolver1D; 20 | typedef GpuSingleAdvectionDiffusionSolver1D ad1D; 21 | typedef GpuDoubleAdvectionDiffusionSolver1D dad1D; 22 | 23 | typedef AdvectionDiffusionSolver2D GpuSingleAdvectionDiffusionSolver2D; 24 | typedef GpuSingleAdvectionDiffusionSolver2D GpuFloatAdvectionDiffusionSolver2D; 25 | typedef AdvectionDiffusionSolver2D GpuDoubleAdvectionDiffusionSolver2D; 26 | typedef AdvectionDiffusionSolver2D CpuSingleAdvectionDiffusionSolver2D; 27 | typedef CpuSingleAdvectionDiffusionSolver2D CpuFloatAdvectionDiffusionSolver2D; 28 | typedef AdvectionDiffusionSolver2D CpuDoubleAdvectionDiffusionSolver2D; 29 | typedef GpuSingleAdvectionDiffusionSolver2D ad2D; 30 | typedef GpuDoubleAdvectionDiffusionSolver2D dad2D; 31 | ``` 32 | 33 | - Wave Equation: 34 | ```c++ 35 | typedef WaveEquationSolver1D GpuSingleWaveEquationSolver1D; 36 | typedef GpuSingleWaveEquationSolver1D GpuFloatWaveEquationSolver1D; 37 | typedef WaveEquationSolver1D GpuDoubleWaveEquationSolver1D; 38 | typedef WaveEquationSolver1D CpuSingleWaveEquationSolver1D; 39 | typedef CpuSingleWaveEquationSolver1D CpuFloatWaveEquationSolver1D; 40 | typedef WaveEquationSolver1D CpuDoubleSolver1D; 41 | typedef GpuSingleWaveEquationSolver1D wave1D; 42 | typedef GpuDoubleWaveEquationSolver1D dwave1D; 43 | 44 | typedef WaveEquationSolver2D GpuSingleWaveEquationSolver2D; 45 | typedef GpuSingleWaveEquationSolver2D GpuFloatWaveEquationSolver2D; 46 | typedef WaveEquationSolver2D GpuDoubleWaveEquationSolver2D; 47 | typedef WaveEquationSolver2D CpuSingleWaveEquationSolver2D; 48 | typedef CpuSingleWaveEquationSolver2D CpuFloatWaveEquationSolver2D; 49 | typedef WaveEquationSolver2D CpuDoubleSolver2D; 50 | typedef GpuSingleWaveEquationSolver2D wave2D; 51 | typedef GpuDoubleWaveEquationSolver2D dwave2D; 52 | ``` 53 | 54 | ## Sample usage - 1D 55 | ### Advection-Diffusion 56 | ```c++ 57 | cl::vec grid = cl::LinSpace(0.0f, 1.0f, 128); 58 | auto _grid = grid.Get(); 59 | 60 | std::vector _initialCondition(grid.size()); 61 | for (unsigned i = 0; i < _initialCondition.size(); ++i) 62 | _initialCondition[i] = sin(_grid[i]); 63 | 64 | cl::vec initialCondition(_initialCondition); 65 | 66 | unsigned steps = 100; 67 | double dt = 1e-4; 68 | float velocity = .05f; 69 | float diffusion = 0.1f; 70 | 71 | BoundaryCondition leftBoundaryCondition(BoundaryConditionType::Neumann, 0.0); 72 | BoundaryCondition rightBoundaryCondition(BoundaryConditionType::Neumann, 0.0); 73 | BoundaryCondition1D boundaryConditions(leftBoundaryCondition, rightBoundaryCondition); 74 | 75 | pde::GpuSinglePdeInputData1D data(initialCondition, grid, velocity, diffusion, dt, solverType, SpaceDiscretizerType::Centered, boundaryConditions); 76 | pde::ad1D solver(data); 77 | 78 | solver.Advance(steps); 79 | const auto solution = solver.solution->columns[0]->Get(); 80 | ``` 81 | 82 | ### Wave Equation 83 | ```c++ 84 | cl::dvec grid = cl::LinSpace(0.0, 1.0, 128); 85 | auto _grid = grid.Get(); 86 | 87 | std::vector _initialCondition(grid.size()); 88 | for (unsigned i = 0; i < _initialCondition.size(); ++i) 89 | _initialCondition[i] = sin(_grid[i]); 90 | 91 | cl::dvec initialCondition(_initialCondition); 92 | 93 | unsigned steps = 100; 94 | double dt = 1e-4; 95 | double velocity = .05; 96 | 97 | BoundaryCondition leftBoundaryCondition(BoundaryConditionType::Neumann, 0.0); 98 | BoundaryCondition rightBoundaryCondition(BoundaryConditionType::Neumann, 0.0); 99 | BoundaryCondition1D boundaryConditions(leftBoundaryCondition, rightBoundaryCondition); 100 | 101 | pde::GpuDoublePdeInputData1D data(initialCondition, grid, velocity, diffusion, dt, solverType, SpaceDiscretizerType::Centered, boundaryConditions); 102 | pde::dwave1D solver(data); 103 | 104 | solver.Advance(steps); 105 | const auto solution = solver.solution->columns[0]->Get(); 106 | ``` 107 | 108 | ## Sample results - 1D 109 | I wrote a simple python script for plotting the results: 110 | 111 | ### Hyperbolic - first order: transport equation 112 | - Numerical instability of the centered difference scheme (regardless of the time solver)

113 | - Numerical diffusion induced by the Upwind scheme and solved by the Lax-Wendroff scheme

114 | 115 | ### Parabolic: heat equation, advection-diffusion equation 116 | - Numerical instability of Explicit Euler scheme

117 | - Numerical instability of Gauss-Legendre (4th order diagonally implicit Runge-Kutta)

118 | 119 | ### Hyperbolic - second order: wave equation 120 | - Numerical instability of Implicit/Explicit Euler scheme

121 | 122 | ## Sample usage - 2D 123 | ### Advection-Diffusion 124 | ```c++ 125 | cl::dvec xGrid = cl::LinSpace(0.0f, 1.0f, 32u); 126 | cl::dvec yGrid = cl::LinSpace(0.0f, 1.0f, 32u); 127 | double dt = 1e-5; 128 | double xVelocity = .02; 129 | double yVelocity = .05; 130 | double diffusion = 1.0; 131 | 132 | auto _xGrid = xGrid.Get(); 133 | auto _yGrid = yGrid.Get(); 134 | std::vector _initialCondition(xGrid.size() * yGrid.size()); 135 | for (unsigned j = 0; j < _yGrid.size(); ++j) 136 | for (unsigned i = 0; i < _xGrid.size(); ++i) 137 | _initialCondition[i + _xGrid.size() * j] = exp(-_xGrid[i] * _xGrid[i] - _yGrid[i] * _yGrid[i]); 138 | 139 | cl::dmat initialCondition(_initialCondition, xGrid.size(), yGrid.size()); 140 | 141 | pde::GpuDoublePdeInputData2D data(initialCondition, xGrid, yGrid, xVelocity, yVelocity, diffusion, dt, solverType, SpaceDiscretizerType::Centered, boundaryConditions); 142 | pde::dad2D solver(data); 143 | const auto solution = solver.solution->columns[0]->Get(); 144 | ``` 145 | 146 | ### Wave Equation 147 | ```c++ 148 | cl::dvec xGrid = cl::LinSpace(0.0f, 1.0f, 32u); 149 | cl::dvec yGrid = cl::LinSpace(0.0f, 1.0f, 32u); 150 | double dt = 1e-5; 151 | double xVelocity = .02; 152 | 153 | auto _xGrid = xGrid.Get(); 154 | auto _yGrid = yGrid.Get(); 155 | std::vector _initialCondition(xGrid.size() * yGrid.size()); 156 | for (unsigned j = 0; j < _yGrid.size(); ++j) 157 | for (unsigned i = 0; i < _xGrid.size(); ++i) 158 | _initialCondition[i + _xGrid.size() * j] = exp(-_xGrid[i] * _xGrid[i] - _yGrid[i] * _yGrid[i]); 159 | 160 | cl::dmat initialCondition(_initialCondition, xGrid.size(), yGrid.size()); 161 | 162 | pde::GpuDoublePdeInputData2D data(initialCondition, xGrid, yGrid, xVelocity, 0.0, 0.0, dt, solverType, SpaceDiscretizerType::Centered, boundaryConditions); 163 | pde::dwave2D solver(data); 164 | const auto solution = solver.solution->columns[0]->Get(); 165 | ``` 166 | 167 | ## Sample results - 2D 168 | ### Hyperbolic - first order: transport equation 169 | - Lax-Wendroff

170 | 171 | ### Parabolic: heat equation, advection-diffusion equation 172 | - Crank-Nicolson

173 | 174 | ### Hyperbolic - second order: wave equation 175 | - Explicit Euler

176 | -------------------------------------------------------------------------------- /UnitTests/AdvectionDiffusion1DTests.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace pdet 11 | { 12 | class AdvectionDiffusion1DTests: public ::testing::Test 13 | { 14 | }; 15 | 16 | TEST_F(AdvectionDiffusion1DTests, ConstantSolutionNoTransportNoDiffusion) 17 | { 18 | cl::vec initialCondition(10, 1.0f); 19 | cl::vec grid = cl::vec::LinSpace(0.0f, 1.0f, initialCondition.size()); 20 | float dt = 1e-4f; 21 | float velocity = 0.0f; 22 | float diffusion = 0.0f; 23 | 24 | for (const SolverType solverType : enums::IterableEnum()) 25 | { 26 | if (solverType != SolverType::AdamsBashforth2) 27 | continue; 28 | pde::GpuSinglePdeInputData1D data(initialCondition, grid, velocity, diffusion, dt, solverType, SpaceDiscretizerType::Centered); 29 | pde::ad1D solver(std::move(data)); 30 | 31 | const auto _initialCondition = solver.inputData.initialCondition.Get(); 32 | for (unsigned n = 1; n < 10; ++n) 33 | { 34 | solver.Advance(n); 35 | const auto solution = solver.solution->columns[0]->Get(); 36 | 37 | for (size_t i = 0; i < solution.size(); ++i) 38 | EXPECT_TRUE(std::fabs(solution[i] - _initialCondition[i]) <= 1e-7f) << static_cast(solverType); 39 | } 40 | } 41 | } 42 | 43 | TEST_F(AdvectionDiffusion1DTests, ConstantSolutionNoDiffusion) 44 | { 45 | cl::vec initialCondition(10, 1.0f); 46 | cl::vec grid = cl::vec::LinSpace(0.0f, 1.0f, initialCondition.size()); 47 | float dt = 1e-4f; 48 | float velocity = 1.0f; 49 | float diffusion = 0.0f; 50 | 51 | for (const SolverType solverType : enums::IterableEnum()) 52 | { 53 | pde::GpuSinglePdeInputData1D data(initialCondition, grid, velocity, diffusion, dt, solverType, SpaceDiscretizerType::Centered); 54 | pde::ad1D solver(std::move(data)); 55 | 56 | const auto _initialCondition = solver.inputData.initialCondition.Get(); 57 | for (unsigned n = 0; n < 10; ++n) 58 | { 59 | solver.Advance(n); 60 | const auto solution = solver.solution->columns[0]->Get(); 61 | 62 | for (size_t i = 0; i < solution.size(); ++i) 63 | { 64 | if (solverType != SolverType::RichardsonExtrapolation2 && solverType != SolverType::RichardsonExtrapolation3) 65 | ASSERT_TRUE(std::fabs(solution[i] - _initialCondition[i]) <= 2.8e-6f); 66 | else 67 | ASSERT_TRUE(std::fabs(solution[i] - _initialCondition[i]) <= 3e-5f); 68 | } 69 | } 70 | } 71 | } 72 | 73 | TEST_F(AdvectionDiffusion1DTests, ConstantSolution) 74 | { 75 | cl::vec initialCondition(10, 1.0f); 76 | cl::vec grid = cl::vec::LinSpace(0.0f, 1.0f, initialCondition.size()); 77 | float dt = 1e-4f; 78 | float velocity = 1.0f; 79 | float diffusion = 2.0f; 80 | 81 | for (const SolverType solverType : enums::IterableEnum()) 82 | { 83 | pde::GpuSinglePdeInputData1D data(initialCondition, grid, velocity, diffusion, dt, solverType, SpaceDiscretizerType::Centered); 84 | pde::ad1D solver(std::move(data)); 85 | 86 | const auto _initialCondition = solver.inputData.initialCondition.Get(); 87 | for (unsigned n = 1; n < 10; ++n) 88 | { 89 | solver.Advance(n); 90 | const auto solution = solver.solution->columns[0]->Get(); 91 | 92 | for (size_t i = 0; i < solution.size(); ++i) 93 | { 94 | if (solverType != SolverType::RichardsonExtrapolation2 && solverType != SolverType::RichardsonExtrapolation3) 95 | EXPECT_TRUE(std::fabs(solution[i] - _initialCondition[i]) <= 5.5e-6f) << static_cast(solverType) << "|" << std::fabs(solution[i] - _initialCondition[i]); 96 | else 97 | ASSERT_TRUE(std::fabs(solution[i] - _initialCondition[i]) <= 3e-5f); 98 | } 99 | } 100 | } 101 | } 102 | 103 | TEST_F(AdvectionDiffusion1DTests, LinearSolutionNoTransport) 104 | { 105 | cl::vec initialCondition = cl::vec::LinSpace(0.0f, 10.0f, 10); 106 | cl::vec grid = cl::vec::LinSpace(0.0f, 1.0f, initialCondition.size()); 107 | float dt = 1e-4f; 108 | float velocity = 0.0f; 109 | float diffusion = 2.0f; 110 | 111 | for (const SolverType solverType : enums::IterableEnum()) 112 | { 113 | // need to setup the correct boundary condition with the slope of the line 114 | BoundaryCondition leftBoundaryCondition(BoundaryConditionType::Neumann, 10.0); 115 | BoundaryCondition rightBoundaryCondition(BoundaryConditionType::Neumann, -10.0); 116 | BoundaryCondition1D boundaryConditions(leftBoundaryCondition, rightBoundaryCondition); 117 | 118 | pde::GpuSinglePdeInputData1D data(initialCondition, grid, velocity, diffusion, dt, solverType, SpaceDiscretizerType::Centered, boundaryConditions); 119 | pde::ad1D solver(std::move(data)); 120 | 121 | const auto _initialCondition = solver.inputData.initialCondition.Get(); 122 | for (unsigned n = 1; n < 10; ++n) 123 | { 124 | solver.Advance(n); 125 | const auto solution = solver.solution->columns[0]->Get(); 126 | 127 | for (size_t i = 0; i < solution.size(); ++i) 128 | { 129 | if (solverType != SolverType::RichardsonExtrapolation2 && solverType != SolverType::RichardsonExtrapolation3) 130 | ASSERT_TRUE(std::fabs(solution[i] - _initialCondition[i]) <= 2.7e-5f); 131 | else 132 | ASSERT_TRUE(std::fabs(solution[i] - _initialCondition[i]) <= 5e-4f); 133 | } 134 | } 135 | } 136 | } 137 | 138 | TEST_F(AdvectionDiffusion1DTests, SineSolutionNoDiffusion) 139 | { 140 | cl::vec grid = cl::vec::LinSpace(0.0f, 1.0f, 10); 141 | auto _grid = grid.Get(); 142 | 143 | std::vector _initialCondition(10); 144 | for (unsigned i = 0; i < _initialCondition.size(); ++i) 145 | _initialCondition[i] = std::sin(_grid[i]); 146 | 147 | cl::vec initialCondition(_initialCondition); 148 | 149 | unsigned steps = 10; 150 | auto dt = 1e-4f; 151 | auto velocity = .05f; 152 | auto diffusion = 0.0f; 153 | 154 | auto finalTime = static_cast(steps) * dt; 155 | std::vector _exactSolution(10); 156 | for (unsigned i = 0; i < _initialCondition.size(); ++i) 157 | _exactSolution[i] = std::sin(_grid[i] - velocity * finalTime); 158 | 159 | for (const SolverType solverType : enums::IterableEnum()) 160 | { 161 | pde::GpuSinglePdeInputData1D data(initialCondition, grid, velocity, diffusion, dt, solverType, SpaceDiscretizerType::Centered); 162 | pde::ad1D solver(std::move(data)); 163 | 164 | solver.Advance(steps); 165 | const auto solution = solver.solution->columns[0]->Get(); 166 | 167 | for (size_t i = 1; i < solution.size() - 1; ++i) // excluding BC 168 | ASSERT_TRUE(std::fabs(solution[i] - _exactSolution[i]) <= 1.1e-4f); 169 | } 170 | } 171 | } // namespace pdet 172 | -------------------------------------------------------------------------------- /UnitTests/AdvectionDiffusion2DTests.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace pdet 11 | { 12 | class AdvectionDiffusion2DTests: public ::testing::Test 13 | { 14 | }; 15 | 16 | TEST_F(AdvectionDiffusion2DTests, ConstantSolutionNoTransportNoDiffusion) 17 | { 18 | // I chose double precision as implicit methods have a numerical error ~5e-5 19 | cl::dmat initialCondition(10, 8, 1.0); 20 | cl::dvec xGrid = cl::dvec::LinSpace(0.0, 1.0, initialCondition.nRows()); 21 | cl::dvec yGrid = cl::dvec::LinSpace(0.0, 1.0, initialCondition.nCols()); 22 | double dt = 1e-5; 23 | double velocity = 0.0; 24 | double diffusion = 0.0; 25 | 26 | for (const SolverType solverType : enums::IterableEnum()) 27 | { 28 | pde::GpuDoublePdeInputData2D data(initialCondition, xGrid, yGrid, velocity, velocity, diffusion, dt, solverType, SpaceDiscretizerType::Centered); 29 | pde::dad2D solver(std::move(data)); 30 | 31 | const auto _initialCondition = solver.inputData.initialCondition.Get(); 32 | for (unsigned n = 1; n < 10; ++n) 33 | { 34 | solver.Advance(10 * n); 35 | const auto solution = solver.solution->columns[0]->Get(); 36 | 37 | for (size_t i = 0; i < solution.size(); ++i) 38 | ASSERT_TRUE(fabs(solution[i] - _initialCondition[i]) <= 1e-12); 39 | } 40 | } 41 | } 42 | 43 | TEST_F(AdvectionDiffusion2DTests, ConstantSolutionNoDiffusion) 44 | { 45 | // I chose double precision as implicit methods have a numerical error ~5e-5 46 | cl::dmat initialCondition(10, 8, 1.0); 47 | cl::dvec xGrid = cl::dvec::LinSpace(0.0, 1.0, initialCondition.nRows()); 48 | cl::dvec yGrid = cl::dvec::LinSpace(0.0, 1.0, initialCondition.nCols()); 49 | double dt = 1e-5; 50 | double xVelocity = .5; 51 | double yVelocity = .7; 52 | double diffusion = 0.0; 53 | 54 | for (const SolverType solverType : enums::IterableEnum()) 55 | { 56 | pde::GpuDoublePdeInputData2D data(initialCondition, xGrid, yGrid, xVelocity, yVelocity, diffusion, dt, solverType, SpaceDiscretizerType::Centered); 57 | pde::dad2D solver(std::move(data)); 58 | 59 | const auto _initialCondition = solver.inputData.initialCondition.Get(); 60 | for (unsigned n = 1; n < 10; ++n) 61 | { 62 | solver.Advance(10 * n); 63 | const auto solution = solver.solution->columns[0]->Get(); 64 | 65 | for (size_t i = 0; i < solution.size(); ++i) 66 | ASSERT_TRUE(fabs(solution[i] - _initialCondition[i]) <= 1e-12); 67 | } 68 | } 69 | } 70 | 71 | TEST_F(AdvectionDiffusion2DTests, ConstantSolution) 72 | { 73 | // I chose double precision as implicit methods have a numerical error ~5e-5 74 | cl::dmat initialCondition(10, 8, 1.0); 75 | cl::dvec xGrid = cl::dvec::LinSpace(0.0, 1.0, initialCondition.nRows()); 76 | cl::dvec yGrid = cl::dvec::LinSpace(0.0, 1.0, initialCondition.nCols()); 77 | double dt = 1e-5; 78 | double xVelocity = .5; 79 | double yVelocity = .7; 80 | double diffusion = 1.0; 81 | 82 | for (const SolverType solverType : enums::IterableEnum()) 83 | { 84 | pde::GpuDoublePdeInputData2D data(initialCondition, xGrid, yGrid, xVelocity, yVelocity, diffusion, dt, solverType, SpaceDiscretizerType::Centered); 85 | pde::dad2D solver(std::move(data)); 86 | 87 | const auto _initialCondition = solver.inputData.initialCondition.Get(); 88 | for (unsigned n = 1; n < 10; ++n) 89 | { 90 | solver.Advance(n); 91 | const auto solution = solver.solution->columns[0]->Get(); 92 | 93 | for (size_t i = 0; i < solution.size(); ++i) 94 | { 95 | if (solverType != SolverType::RichardsonExtrapolation2 && solverType != SolverType::RichardsonExtrapolation3) 96 | ASSERT_TRUE(fabs(solution[i] - _initialCondition[i]) <= 5.5e-6); 97 | else 98 | ASSERT_TRUE(fabs(solution[i] - _initialCondition[i]) <= 3e-5); 99 | } 100 | } 101 | } 102 | } 103 | 104 | TEST_F(AdvectionDiffusion2DTests, LinearSolutionNoTransport) 105 | { 106 | // I chose double precision as implicit methods have a numerical error ~5e-5 107 | cl::dvec xGrid = cl::dvec::LinSpace(0.0, 1.0, 10u); 108 | cl::dvec yGrid = cl::dvec::LinSpace(0.0, 1.0, 8u); 109 | double dt = 1e-5; 110 | double xVelocity = .0; 111 | double yVelocity = .0; 112 | double diffusion = 1.0; 113 | 114 | auto _xGrid = xGrid.Get(); 115 | auto _yGrid = yGrid.Get(); 116 | std::vector _initialCondition(xGrid.size() * yGrid.size()); 117 | for (unsigned j = 0; j < _yGrid.size(); ++j) 118 | for (unsigned i = 0; i < _xGrid.size(); ++i) 119 | _initialCondition[i + _xGrid.size() * j] = 2.0 * _xGrid[i] + 3.0 * _yGrid[j]; 120 | 121 | cl::dmat initialCondition(_initialCondition, xGrid.size(), yGrid.size()); 122 | 123 | for (const SolverType solverType : enums::IterableEnum()) 124 | { 125 | // need to setup the correct boundary condition with the slope of the planes 126 | BoundaryCondition leftBoundaryCondition(BoundaryConditionType::Neumann, 3.0); 127 | BoundaryCondition rightBoundaryCondition(BoundaryConditionType::Neumann, -3.0); 128 | BoundaryCondition downBoundaryCondition(BoundaryConditionType::Neumann, -2.0); 129 | BoundaryCondition upBoundaryCondition(BoundaryConditionType::Neumann, 2.0); 130 | BoundaryCondition2D boundaryConditions(leftBoundaryCondition, rightBoundaryCondition, downBoundaryCondition, upBoundaryCondition); 131 | 132 | pde::GpuDoublePdeInputData2D data(initialCondition, xGrid, yGrid, xVelocity, yVelocity, diffusion, dt, solverType, SpaceDiscretizerType::Centered, boundaryConditions); 133 | pde::dad2D solver(std::move(data)); 134 | 135 | const auto _ic = solver.inputData.initialCondition.Get(); 136 | for (unsigned n = 1; n < 10; ++n) 137 | { 138 | solver.Advance(n); 139 | const auto solution = solver.solution->columns[0]->Get(); 140 | 141 | // excluding corners 142 | for (unsigned j = 1; j < _yGrid.size() - 1; ++j) 143 | { 144 | for (unsigned i = 1; i < _xGrid.size() - 1; ++i) 145 | { 146 | const unsigned idx = i + static_cast(_xGrid.size()) * j; 147 | ASSERT_LE(fabs(solution[idx] - _ic[idx]), 5e-12); 148 | } 149 | } 150 | } 151 | } 152 | } 153 | } // namespace pdet 154 | -------------------------------------------------------------------------------- /UnitTests/WaveEquation1DTests.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | namespace pdet 12 | { 13 | class WaveEquation1DTests: public ::testing::Test 14 | { 15 | }; 16 | 17 | TEST_F(WaveEquation1DTests, ConstantSolutionNoTransport) 18 | { 19 | cl::vec initialCondition(10, 1.0f); 20 | cl::vec grid = cl::vec::LinSpace(0.0f, 1.0f, initialCondition.size()); 21 | float dt = 1e-4f; 22 | float velocity = 0.0f; 23 | float diffusion = 0.0f; 24 | 25 | for (const SolverType solverType : enums::IterableEnum()) 26 | { 27 | if (solverType != SolverType::ExplicitEuler && solverType != SolverType::ImplicitEuler) 28 | continue; 29 | 30 | pde::GpuSinglePdeInputData1D data(initialCondition, grid, velocity, diffusion, dt, solverType, SpaceDiscretizerType::Centered); 31 | pde::wave1D solver(std::move(data)); 32 | 33 | const auto _initialCondition = solver.inputData.initialCondition.Get(); 34 | for (unsigned n = 1; n < 10; ++n) 35 | { 36 | solver.Advance(n); 37 | const auto solution = solver.solution->columns[0]->Get(); 38 | 39 | for (size_t i = 0; i < solution.size(); ++i) 40 | ASSERT_TRUE(std::fabs(solution[i] - _initialCondition[i]) <= 1e-15f); 41 | } 42 | } 43 | } 44 | 45 | TEST_F(WaveEquation1DTests, ConstantSolution) 46 | { 47 | cl::vec initialCondition(10, 1.0f); 48 | cl::vec grid = cl::vec::LinSpace(0.0f, 1.0f, initialCondition.size()); 49 | float dt = 1e-4f; 50 | float velocity = 1.0f; 51 | float diffusion = 2.0f; 52 | 53 | for (const SolverType solverType : enums::IterableEnum()) 54 | { 55 | if (solverType != SolverType::ExplicitEuler && solverType != SolverType::ImplicitEuler) 56 | continue; 57 | 58 | pde::GpuSinglePdeInputData1D data(initialCondition, grid, velocity, diffusion, dt, solverType, SpaceDiscretizerType::Centered); 59 | pde::wave1D solver(std::move(data)); 60 | 61 | const auto _initialCondition = solver.inputData.initialCondition.Get(); 62 | for (unsigned n = 1; n < 10; ++n) 63 | { 64 | solver.Advance(n); 65 | const auto solution = solver.solution->columns[0]->Get(); 66 | 67 | for (size_t i = 0; i < solution.size(); ++i) 68 | ASSERT_TRUE(std::fabs(solution[i] - _initialCondition[i]) <= 1e-15f); 69 | } 70 | } 71 | } 72 | 73 | TEST_F(WaveEquation1DTests, LinearSolution) 74 | { 75 | cl::dvec grid = cl::dvec::LinSpace(0.0, 1.0, 10); 76 | auto _grid = grid.Get(); 77 | 78 | const auto f = [](const double x) { return 2.0 * x + 1; }; 79 | 80 | std::vector _initialCondition(10); 81 | for (unsigned i = 0; i < _initialCondition.size(); ++i) 82 | _initialCondition[i] = f(_grid[i]); 83 | 84 | cl::dvec initialCondition(_initialCondition); 85 | 86 | unsigned steps = 1000; 87 | double dt = 1e-4; 88 | double velocity = .5; 89 | double diffusion = 0.0; 90 | 91 | double finalTime = steps * dt; 92 | std::vector _exactSolution(10); 93 | for (unsigned i = 0; i < _initialCondition.size(); ++i) 94 | _exactSolution[i] = .5 * (f(_grid[i] - velocity * finalTime) + f(_grid[i] + velocity * finalTime)); 95 | 96 | // need to setup the correct boundary condition with the slope of the line 97 | BoundaryCondition leftBoundaryCondition(BoundaryConditionType::Neumann, 2.0); 98 | BoundaryCondition rightBoundaryCondition(BoundaryConditionType::Neumann, -2.0); 99 | BoundaryCondition1D boundaryConditions(leftBoundaryCondition, rightBoundaryCondition); 100 | for (const SolverType solverType : enums::IterableEnum()) 101 | { 102 | if (solverType != SolverType::ExplicitEuler && solverType != SolverType::ImplicitEuler) 103 | continue; 104 | 105 | pde::GpuDoublePdeInputData1D data(initialCondition, grid, velocity, diffusion, dt, solverType, SpaceDiscretizerType::Centered, boundaryConditions); 106 | pde::dwave1D solver(std::move(data)); 107 | 108 | solver.Advance(steps); 109 | const auto solution = solver.solution->columns[0]->Get(); 110 | 111 | for (size_t i = 0; i < solution.size(); ++i) 112 | ASSERT_TRUE(fabs(solution[i] - _exactSolution[i]) <= 5e-6) << "err=" << fabs(solution[i] - _exactSolution[i]); 113 | } 114 | } 115 | } // namespace pdet 116 | -------------------------------------------------------------------------------- /UnitTests/WaveEquation2DTests.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace pdet 11 | { 12 | class WaveEquation2DTests: public ::testing::Test 13 | { 14 | }; 15 | 16 | TEST_F(WaveEquation2DTests, ConstantSolutionNoTransport) 17 | { 18 | // I chose double precision as implicit methods have a numerical error ~5e-5 19 | cl::dmat initialCondition(10, 8, 1.0); 20 | cl::dvec xGrid = cl::dvec::LinSpace(0.0, 1.0, initialCondition.nRows()); 21 | cl::dvec yGrid = cl::dvec::LinSpace(0.0, 1.0, initialCondition.nCols()); 22 | double dt = 1e-5; 23 | double velocity = 0.0; 24 | double diffusion = 0.0; 25 | 26 | for (const SolverType solverType : enums::IterableEnum()) 27 | { 28 | if (solverType != SolverType::ExplicitEuler && solverType != SolverType::ImplicitEuler) 29 | continue; 30 | 31 | pde::GpuDoublePdeInputData2D data(initialCondition, xGrid, yGrid, velocity, velocity, diffusion, dt, solverType, SpaceDiscretizerType::Centered); 32 | pde::dwave2D solver(std::move(data)); 33 | 34 | const auto _initialCondition = solver.inputData.initialCondition.Get(); 35 | for (unsigned n = 1; n < 10; ++n) 36 | { 37 | solver.Advance(10 * n); 38 | const auto solution = solver.solution->columns[0]->Get(); 39 | 40 | for (size_t i = 0; i < solution.size(); ++i) 41 | ASSERT_TRUE(fabs(solution[i] - _initialCondition[i]) <= 1e-12); 42 | } 43 | } 44 | } 45 | 46 | TEST_F(WaveEquation2DTests, ConstantSolution) 47 | { 48 | // I chose double precision as implicit methods have a numerical error ~5e-5 49 | cl::dmat initialCondition(10, 8, 1.0); 50 | cl::dvec xGrid = cl::dvec::LinSpace(0.0, 1.0, initialCondition.nRows()); 51 | cl::dvec yGrid = cl::dvec::LinSpace(0.0, 1.0, initialCondition.nCols()); 52 | double dt = 1e-5; 53 | double xVelocity = .5; 54 | double diffusion = 0.0; 55 | 56 | for (const SolverType solverType : enums::IterableEnum()) 57 | { 58 | if (solverType != SolverType::ExplicitEuler && solverType != SolverType::ImplicitEuler) 59 | continue; 60 | 61 | pde::GpuDoublePdeInputData2D data(initialCondition, xGrid, yGrid, xVelocity, 0.0, diffusion, dt, solverType, SpaceDiscretizerType::Centered); 62 | pde::dwave2D solver(std::move(data)); 63 | 64 | const auto _initialCondition = solver.inputData.initialCondition.Get(); 65 | for (unsigned n = 1; n < 10; ++n) 66 | { 67 | solver.Advance(10 * n); 68 | const auto solution = solver.solution->columns[0]->Get(); 69 | 70 | for (size_t i = 0; i < solution.size(); ++i) 71 | ASSERT_TRUE(fabs(solution[i] - _initialCondition[i]) <= 1e-12); 72 | } 73 | } 74 | } 75 | 76 | TEST_F(WaveEquation2DTests, LinearSolution) 77 | { 78 | // I chose double precision as implicit methods have a numerical error ~5e-5 79 | cl::dvec xGrid = cl::dvec::LinSpace(0.0, 1.0, 10u); 80 | cl::dvec yGrid = cl::dvec::LinSpace(0.0, 1.0, 8u); 81 | double dt = 1e-5; 82 | double xVelocity = .1; 83 | 84 | auto _xGrid = xGrid.Get(); 85 | auto _yGrid = yGrid.Get(); 86 | std::vector _initialCondition(xGrid.size() * yGrid.size()); 87 | for (unsigned j = 0; j < _yGrid.size(); ++j) 88 | for (unsigned i = 0; i < _xGrid.size(); ++i) 89 | _initialCondition[i + _xGrid.size() * j] = 2.0 * _xGrid[i] + 3.0 * _yGrid[j]; 90 | 91 | cl::dmat initialCondition(_initialCondition, xGrid.size(), yGrid.size()); 92 | 93 | for (const SolverType solverType : enums::IterableEnum()) 94 | { 95 | if (solverType != SolverType::ExplicitEuler && solverType != SolverType::ImplicitEuler) 96 | continue; 97 | 98 | // need to setup the correct boundary condition with the slope of the planes 99 | BoundaryCondition leftBoundaryCondition(BoundaryConditionType::Neumann, 3.0); 100 | BoundaryCondition rightBoundaryCondition(BoundaryConditionType::Neumann, -3.0); 101 | BoundaryCondition downBoundaryCondition(BoundaryConditionType::Neumann, -2.0); 102 | BoundaryCondition upBoundaryCondition(BoundaryConditionType::Neumann, 2.0); 103 | BoundaryCondition2D boundaryConditions(leftBoundaryCondition, rightBoundaryCondition, downBoundaryCondition, upBoundaryCondition); 104 | 105 | pde::GpuDoublePdeInputData2D data(initialCondition, xGrid, yGrid, xVelocity, 0.0, 0.0, dt, solverType, SpaceDiscretizerType::Centered, boundaryConditions); 106 | pde::dwave2D solver(std::move(data)); 107 | 108 | const auto _ic = solver.inputData.initialCondition.Get(); 109 | for (unsigned n = 1; n < 10; ++n) 110 | { 111 | solver.Advance(n); 112 | const auto solution = solver.solution->columns[0]->Get(); 113 | 114 | // excluding corners 115 | for (unsigned j = 1; j < _yGrid.size() - 1; ++j) 116 | { 117 | for (unsigned i = 1; i < _xGrid.size() - 1; ++i) 118 | { 119 | const auto idx = i + _xGrid.size() * j; 120 | ASSERT_LE(fabs(solution[idx] - _ic[idx]), 5e-12); 121 | } 122 | } 123 | } 124 | } 125 | } 126 | } // namespace pdet 127 | -------------------------------------------------------------------------------- /UnitTests/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | int main(int ac, char* av[]) 5 | { 6 | testing::InitGoogleTest(&ac, av); 7 | return RUN_ALL_TESTS(); 8 | } 9 | -------------------------------------------------------------------------------- /Utils/CommandLineParser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace clp 4 | { 5 | class IllegalArgumentException: public Exception 6 | { 7 | public: 8 | IllegalArgumentException(const std::string& message = "") : Exception("IllegalArgumentException: " + message) {} 9 | }; 10 | 11 | class ArgumentNotFoundException: public Exception 12 | { 13 | public: 14 | ArgumentNotFoundException(const std::string& message = "") : Exception("ArgumentNotFoundException: " + message) {} 15 | }; 16 | 17 | class CommandLineArgumentParser 18 | { 19 | public: 20 | CommandLineArgumentParser(int argc, char** argv) : args(argv, argv + argc) {} 21 | 22 | template 23 | T GetArgumentValue(const std::string& option) const; 24 | 25 | template 26 | T GetArgumentValue(const std::string& option, const T& defaultValue) const noexcept 27 | { 28 | T ret; 29 | try 30 | { 31 | ret = GetArgumentValue(option); 32 | } 33 | catch (ArgumentNotFoundException&) 34 | { 35 | ret = defaultValue; 36 | } 37 | 38 | return ret; 39 | } 40 | 41 | bool GetFlag(const std::string& option) const { return std::find(args.begin(), args.end(), option) != args.end(); } 42 | 43 | private: 44 | std::vector args; 45 | }; 46 | 47 | template<> 48 | std::string CommandLineArgumentParser::GetArgumentValue(const std::string& option) const 49 | { 50 | auto itr = std::find(args.begin(), args.end(), option); 51 | if (itr != args.end()) 52 | { 53 | if (++itr == args.end()) 54 | throw IllegalArgumentException(option); 55 | return *itr; 56 | } 57 | 58 | throw ArgumentNotFoundException(option); 59 | } 60 | 61 | template<> 62 | int CommandLineArgumentParser::GetArgumentValue(const std::string& option) const 63 | { 64 | return std::atoi(GetArgumentValue(option).c_str()); 65 | } 66 | 67 | template<> 68 | short CommandLineArgumentParser::GetArgumentValue(const std::string& option) const 69 | { 70 | return static_cast(std::atoi(GetArgumentValue(option).c_str())); 71 | } 72 | 73 | template<> 74 | unsigned CommandLineArgumentParser::GetArgumentValue(const std::string& option) const 75 | { 76 | return static_cast(std::atoi(GetArgumentValue(option).c_str())); 77 | } 78 | 79 | template<> 80 | long CommandLineArgumentParser::GetArgumentValue(const std::string& option) const 81 | { 82 | return static_cast(std::atoi(GetArgumentValue(option).c_str())); 83 | } 84 | 85 | template<> 86 | long long CommandLineArgumentParser::GetArgumentValue(const std::string& option) const 87 | { 88 | return static_cast(std::atoi(GetArgumentValue(option).c_str())); 89 | } 90 | 91 | template<> 92 | double CommandLineArgumentParser::GetArgumentValue(const std::string& option) const 93 | { 94 | return std::atof(GetArgumentValue(option).c_str()); 95 | } 96 | 97 | template<> 98 | float CommandLineArgumentParser::GetArgumentValue(const std::string& option) const 99 | { 100 | return static_cast(std::atof(GetArgumentValue(option).c_str())); 101 | } 102 | } // namespace clp 103 | -------------------------------------------------------------------------------- /Utils/EnumParser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ep 4 | { 5 | #define PARSE(E, X) \ 6 | if (!strcmp(text.c_str(), #X)) \ 7 | return E::X; 8 | 9 | static inline SolverType ParseSolverType(const std::string& text) noexcept 10 | { 11 | #define PARSE_WORKER(X) PARSE(SolverType, X) 12 | 13 | PARSE_WORKER(ExplicitEuler) 14 | PARSE_WORKER(ImplicitEuler) 15 | PARSE_WORKER(CrankNicolson) 16 | PARSE_WORKER(RungeKuttaRalston) 17 | PARSE_WORKER(RungeKutta3) 18 | PARSE_WORKER(RungeKutta4) 19 | PARSE_WORKER(RungeKuttaThreeEight) 20 | PARSE_WORKER(RungeKuttaGaussLegendre4) 21 | PARSE_WORKER(RichardsonExtrapolation2) 22 | PARSE_WORKER(RichardsonExtrapolation3) 23 | PARSE_WORKER(AdamsBashforth2) 24 | PARSE_WORKER(AdamsMouldon2) 25 | 26 | #undef PARSE_WORKER 27 | 28 | return SolverType::Null; 29 | } 30 | 31 | static inline SpaceDiscretizerType ParseSpaceDiscretizer(const std::string& text) noexcept 32 | { 33 | #define PARSE_WORKER(X) PARSE(SpaceDiscretizerType, X) 34 | 35 | PARSE_WORKER(Centered) 36 | PARSE_WORKER(Upwind) 37 | PARSE_WORKER(LaxWendroff) 38 | 39 | #undef PARSE_WORKER 40 | return SpaceDiscretizerType::Null; 41 | } 42 | 43 | static inline BoundaryConditionType ParseBoundaryConditionType(const std::string& text) noexcept 44 | { 45 | #define PARSE_WORKER(X) PARSE(BoundaryConditionType, X); 46 | 47 | PARSE_WORKER(Dirichlet) 48 | PARSE_WORKER(Neumann) 49 | PARSE_WORKER(Periodic) 50 | 51 | #undef PARSE_WORKER 52 | return BoundaryConditionType::Null; 53 | } 54 | 55 | static inline MathDomain ParseMathDomain(const std::string& text) noexcept 56 | { 57 | #define PARSE_WORKER(X) PARSE(MathDomain, X) 58 | 59 | PARSE_WORKER(Double) 60 | PARSE_WORKER(Float) 61 | 62 | #undef PARSE_WORKER 63 | return MathDomain::Null; 64 | } 65 | 66 | #undef PARSE 67 | 68 | } // namespace ep 69 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-midnight -------------------------------------------------------------------------------- /diffusion2d_compressed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmontalb/PdeFiniteDifferenceSolver/aefb57b7a8e38c10bb677bdd40fffd141eed4134/diffusion2d_compressed.gif -------------------------------------------------------------------------------- /diffusionInstability_compressed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmontalb/PdeFiniteDifferenceSolver/aefb57b7a8e38c10bb677bdd40fffd141eed4134/diffusionInstability_compressed.gif -------------------------------------------------------------------------------- /instability_compressed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmontalb/PdeFiniteDifferenceSolver/aefb57b7a8e38c10bb677bdd40fffd141eed4134/instability_compressed.gif -------------------------------------------------------------------------------- /multiStep_compressed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmontalb/PdeFiniteDifferenceSolver/aefb57b7a8e38c10bb677bdd40fffd141eed4134/multiStep_compressed.gif -------------------------------------------------------------------------------- /numericalDiffusion_compressed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmontalb/PdeFiniteDifferenceSolver/aefb57b7a8e38c10bb677bdd40fffd141eed4134/numericalDiffusion_compressed.gif -------------------------------------------------------------------------------- /pdeRunner.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from subprocess import Popen 3 | import os 4 | from plotter import * 5 | 6 | CWD = os.getcwd() 7 | if os.name == 'nt': 8 | debugBin = "{}\\x64\\Debug\\PdeFiniteDifferenceSolver.exe".format(CWD) 9 | releaseBin = "{}\\x64\\Release\\PdeFiniteDifferenceSolver.exe".format(CWD) 10 | 11 | GRID_FILE = "{}\\grid.npy".format(CWD) 12 | INITIAL_CONDITION_FILE = "{}\\ic.npy".format(CWD) 13 | 14 | X_GRID_FILE = "{}\\x_grid.npy".format(CWD) 15 | Y_GRID_FILE = "{}\\y_grid.npy".format(CWD) 16 | else: 17 | debugBin = "{}/cmake-build-gcc-debug/PdeFiniteDifferenceSolver".format(CWD) 18 | releaseBin = "{}/cmake-build-gcc-release/PdeFiniteDifferenceSolver".format(CWD) 19 | 20 | GRID_FILE = "{}/grid.npy".format(CWD) 21 | INITIAL_CONDITION_FILE = "{}/ic.npy".format(CWD) 22 | 23 | X_GRID_FILE = "{}/x_grid.npy".format(CWD) 24 | Y_GRID_FILE = "{}/y_grid.npy".format(CWD) 25 | 26 | chosenBin = debugBin 27 | 28 | def read_solution_2D(file_name, N, N_x, N_y): 29 | _tensor = np.load(file_name).flatten() 30 | tensor = [] 31 | for n in range(N): 32 | m = np.zeros((N_x, N_y)) 33 | for i in range(N_x): 34 | for j in range(N_y): 35 | m[j, i] = _tensor[i + j * N_x + n * N_x * N_y] 36 | tensor.append(m) 37 | return np.array(tensor) 38 | 39 | 40 | def read_solution_1D(file_name, N, N_x): 41 | _tensor = np.load(file_name).flatten() 42 | tensor = [] 43 | for n in range(N): 44 | m = np.zeros(N_x) 45 | for i in range(N_x): 46 | m[i] = _tensor[i + n * N_x] 47 | tensor.append(m) 48 | return np.array(tensor).transpose() 49 | 50 | 51 | def run_transport_1D(space_discretizer="Upwind", 52 | solver_type='ExplicitEuler', 53 | output_file="transport.cl", 54 | name="transport.gif", 55 | run=True, show=True, show_grid=False, save=False, 56 | run_animation=True): 57 | 58 | try: 59 | os.remove(GRID_FILE) 60 | os.remove(INITIAL_CONDITION_FILE) 61 | except FileNotFoundError: 62 | pass 63 | 64 | grid = np.linspace(-np.pi, np.pi, 128) 65 | ic = np.sin(grid) 66 | N = 500 67 | if run: 68 | np.save(GRID_FILE, grid) 69 | np.save(INITIAL_CONDITION_FILE, ic) 70 | 71 | p = Popen([chosenBin] + 72 | ["-ic", INITIAL_CONDITION_FILE] + 73 | ["-g", GRID_FILE] + 74 | ["-of", output_file] + 75 | ["-md", "Double"] + 76 | ["-lbct", "Periodic"] + 77 | ["-lbc", "0.0"] + 78 | ["-rbct", "Periodic"] + 79 | ["-rbc", "0.0"] + 80 | ["-st", solver_type] + 81 | ["-sdt", space_discretizer] + 82 | ["-d", "0"] + 83 | ["-v", ".05"] + 84 | ["-dt", "0.1"] + 85 | ["-n", "50"] + 86 | ["-N", str(N)]) 87 | p.communicate() 88 | 89 | solution = read_solution_1D(output_file, N, len(grid)) 90 | if run_animation: 91 | animate(solution, grid, show=show, save=save, grid=show_grid, name=name) 92 | 93 | return grid, solution 94 | 95 | 96 | def run_diffusion_1D(solver_type="CrankNicolson", output_file="diffusion.cl", name="diffusion.gif", 97 | run=True, show=True, show_grid=False, save=False, 98 | run_animation=True): 99 | 100 | try: 101 | os.remove(GRID_FILE) 102 | os.remove(INITIAL_CONDITION_FILE) 103 | except FileNotFoundError: 104 | pass 105 | 106 | grid = np.linspace(-np.pi, np.pi, 128) 107 | ic = np.exp(-.5 * grid * grid) 108 | N = 500 109 | if run: 110 | np.save(GRID_FILE, grid) 111 | np.save(INITIAL_CONDITION_FILE, ic) 112 | 113 | p = Popen([chosenBin] + 114 | ["-ic", INITIAL_CONDITION_FILE] + 115 | ["-g", GRID_FILE] + 116 | ["-of", output_file] + 117 | ["-md", "Double"] + 118 | ["-lbct", "Neumann"] + 119 | ["-lbc", "0.0"] + 120 | ["-rbct", "Neumann"] + 121 | ["-st", solver_type] + 122 | ["-d", "0.5"] + 123 | ["-v", "0"] + 124 | ["-dt", "0.00246"] + 125 | ["-n", "50"] + 126 | ["-N", str(N)]) 127 | p.communicate() 128 | 129 | solution = read_solution_1D(output_file, N, len(grid)) 130 | if run_animation: 131 | animate(solution, grid, show=show, save=save, grid=show_grid, name=name) 132 | 133 | return grid, solution 134 | 135 | 136 | def run_wave_1D(solver_type="ExplicitEuler", 137 | output_file="wave.cl", name="wave.gif", run=True, show=True, show_grid=False, save=False, 138 | run_animation=True): 139 | 140 | try: 141 | os.remove(GRID_FILE) 142 | os.remove(INITIAL_CONDITION_FILE) 143 | except FileNotFoundError: 144 | pass 145 | 146 | grid = np.linspace(-np.pi, np.pi, 128) 147 | ic = np.exp(-grid * grid) 148 | N = 500 149 | if run: 150 | np.save(GRID_FILE, grid) 151 | np.save(INITIAL_CONDITION_FILE, ic) 152 | 153 | p = Popen([chosenBin] + 154 | ["-ic", INITIAL_CONDITION_FILE] + 155 | ["-g", GRID_FILE] + 156 | ["-of", output_file] + 157 | ["-md", "Double"] + 158 | ["-lbct", "Dirichlet"] + 159 | ["-lbc", "0.0"] + 160 | ["-rbct", "Dirichlet"] + 161 | ["-st", solver_type] + 162 | ["-pde", "WaveEquation"] + 163 | ["-d", "0"] + 164 | ["-v", ".05"] + 165 | ["-dt", "0.035"] + 166 | ["-n", "100"] + 167 | ["-N", str(N)] + 168 | ["-dbg"]) 169 | p.communicate() 170 | 171 | solution = read_solution_1D(output_file, N, len(grid)) 172 | 173 | if run_animation: 174 | animate(solution, grid, show=show, grid=show_grid, save=save, name=name) 175 | 176 | return grid, solution 177 | 178 | 179 | def __compare_solver_worker(worker, solver_list, is_space_solver=False, 180 | run=True, show=True, show_grid=False, save=False, name="comparison.gif", 181 | y_lim=None): 182 | out = [] 183 | for solver in solver_list: 184 | print("Running {}".format(solver)) 185 | if not is_space_solver: 186 | out.append(worker(solver_type=solver, output_file="{}.cl".format(solver), run=run, show=False, 187 | show_grid=show_grid, save=False, run_animation=False)) 188 | else: 189 | out.append(worker(space_discretizer=solver, output_file="{}.cl".format(solver), run=run, show=False, 190 | show_grid=show_grid, save=False, run_animation=False)) 191 | 192 | animate_multicurve([x[1] for x in out], [x[0] for x in out], grid=show_grid, show=show, 193 | labels=solver_list, save=save, name=name, y_lim=y_lim) 194 | 195 | 196 | def compare_solvers_transport_1D(solver_list, run=True, show=True, show_grid=False, save=False, name="comparison.gif"): 197 | __compare_solver_worker(run_transport_1D, solver_list, is_space_solver=True, 198 | run=run, show=show, show_grid=show_grid, save=save, name=name) 199 | 200 | 201 | def compare_solvers_diffusion_1D(solver_list, run=True, show=True, show_grid=False, save=False, name="comparison.gif"): 202 | __compare_solver_worker(run_diffusion_1D, solver_list, 203 | run=run, show=show, show_grid=show_grid, save=save, name=name) 204 | 205 | 206 | def compare_solvers_wave_1D(solver_list, run=True, show=True, show_grid=False, save=False, name="comparison.gif"): 207 | __compare_solver_worker(run_wave_1D, solver_list, 208 | run=run, show=show, show_grid=show_grid, save=save, name=name, y_lim=(-1.1, 1.1)) 209 | 210 | 211 | def run_transport_2D(space_discretizer="Upwind", 212 | output_file="transport2d.cl", 213 | name="transport2d.gif", 214 | run=True, show=True, save=False, 215 | run_animation=True): 216 | 217 | try: 218 | os.remove(X_GRID_FILE) 219 | os.remove(Y_GRID_FILE) 220 | os.remove(INITIAL_CONDITION_FILE) 221 | except FileNotFoundError: 222 | pass 223 | 224 | x_grid = np.linspace(-np.pi, np.pi, 128) 225 | y_grid = np.linspace(-np.pi, np.pi, 128) 226 | X, Y = np.meshgrid(x_grid, y_grid) 227 | ic = np.exp(-X * X - Y * Y) 228 | N = 50 229 | if run: 230 | np.save(X_GRID_FILE, x_grid) 231 | np.save(Y_GRID_FILE, y_grid) 232 | np.save(INITIAL_CONDITION_FILE, ic) 233 | 234 | p = Popen([chosenBin] + 235 | ["-dbg"] + 236 | ["-dim", "2"] + 237 | ["-ic", INITIAL_CONDITION_FILE] + 238 | ["-gx", X_GRID_FILE] + 239 | ["-gy", Y_GRID_FILE] + 240 | ["-of", output_file] + 241 | ["-md", "Double"] + 242 | ["-ubct", "Periodic"] + 243 | ["-ubc", "0.0"] + 244 | ["-dbct", "Periodic"] + 245 | ["-dbc", "0.0"] + 246 | ["-lbct", "Periodic"] + 247 | ["-lbc", "0.0"] + 248 | ["-rbct", "Periodic"] + 249 | ["-st", "ExplicitEuler"] + 250 | ["-sdt", space_discretizer] + 251 | ["-d", "0"] + 252 | ["-vx", "0.5"] + 253 | ["-vy", "0.5"] + 254 | ["-dt", "0.05"] + 255 | ["-n", "10"] + 256 | ["-N", str(N)]) 257 | p.communicate() 258 | 259 | solution = read_solution_2D(output_file, N, len(x_grid), len(y_grid)) 260 | if run_animation: 261 | animate_3D(solution, x_grid, y_grid, show=show, save=save, name=name, rstride=4, cstride=4) 262 | 263 | return x_grid, y_grid, solution 264 | 265 | 266 | def run_diffusion_2D(solver_type="ImplicitEuler", output_file="diffusion2d.cl", 267 | name="diffusion2d.gif", 268 | run=True, show=True, save=False, 269 | run_animation=True): 270 | 271 | try: 272 | os.remove(X_GRID_FILE) 273 | os.remove(Y_GRID_FILE) 274 | os.remove(INITIAL_CONDITION_FILE) 275 | except FileNotFoundError: 276 | pass 277 | 278 | x_grid = np.linspace(-np.pi, np.pi, 64) 279 | y_grid = np.linspace(-np.pi, np.pi, 64) 280 | X, Y = np.meshgrid(x_grid, y_grid) 281 | ic = np.exp(-X ** 2 - Y ** 2) 282 | N = 50 283 | if run: 284 | np.save(X_GRID_FILE, x_grid) 285 | np.save(Y_GRID_FILE, y_grid) 286 | np.save(INITIAL_CONDITION_FILE, ic) 287 | 288 | p = Popen([chosenBin] + 289 | ["-dbg"] + 290 | ["-dim", "2"] + 291 | ["-ic", INITIAL_CONDITION_FILE] + 292 | ["-gx", X_GRID_FILE] + 293 | ["-gy", Y_GRID_FILE] + 294 | ["-of", output_file] + 295 | ["-md", "Double"] + 296 | ["-lbct", "Neumann"] + 297 | ["-lbc", "0.0"] + 298 | ["-rbct", "Neumann"] + 299 | ["-st", solver_type] + 300 | ["-sdt", "Centered"] + 301 | ["-d", "1"] + 302 | ["-vx", "0"] + 303 | ["-vy", "0"] + 304 | ["-dt", "0.005"] + 305 | ["-n", "10"] + 306 | ["-N", str(N)]) 307 | p.communicate() 308 | 309 | _solution = np.load(output_file).flatten() 310 | solution = [] 311 | for n in range(N): 312 | m = np.zeros((len(x_grid), len(y_grid))) 313 | for i in range(len(x_grid)): 314 | for j in range(len(y_grid)): 315 | m[i, j] = _solution[i + j * len(x_grid) + n * len(x_grid) * len(y_grid)] 316 | solution.append(m) 317 | solution = np.array(solution) 318 | if run_animation: 319 | animate_3D(solution, x_grid, y_grid, show=show, save=save, name=name, rstride=2, cstride=2, fixed_view=True) 320 | 321 | return x_grid, y_grid, solution 322 | 323 | 324 | def run_wave_2D(solver_type="ImplicitEuler", output_file="wave2d.cl", 325 | name="wave2d.gif", 326 | run=True, show=True, save=False, 327 | run_animation=True): 328 | 329 | try: 330 | os.remove(X_GRID_FILE) 331 | os.remove(Y_GRID_FILE) 332 | os.remove(INITIAL_CONDITION_FILE) 333 | except FileNotFoundError: 334 | pass 335 | 336 | x_grid = np.linspace(-np.pi, np.pi, 64) 337 | y_grid = np.linspace(-np.pi, np.pi, 64) 338 | X, Y = np.meshgrid(x_grid, y_grid) 339 | ic = np.exp(-X ** 2 - Y ** 2) 340 | 341 | np.save(X_GRID_FILE, x_grid) 342 | np.save(Y_GRID_FILE, x_grid) 343 | np.save(INITIAL_CONDITION_FILE, ic) 344 | N = 50 345 | if run: 346 | p = Popen([debugBin] + 347 | ["-dbg"] + 348 | ["-pde", "WaveEquation"] + 349 | ["-dim", "2"] + 350 | ["-ic", INITIAL_CONDITION_FILE] + 351 | ["-gx", X_GRID_FILE] + 352 | ["-gy", Y_GRID_FILE] + 353 | ["-of", output_file] + 354 | ["-md", "Double"] + 355 | ["-lbct", "Neumann"] + 356 | ["-lbc", "0.0"] + 357 | ["-rbct", "Neumann"] + 358 | ["-st", solver_type] + 359 | ["-sdt", "Centered"] + 360 | ["-d", "0"] + 361 | ["-vx", "0.05"] + 362 | ["-vy", "0.0"] + 363 | ["-dt", "0.1"] + 364 | ["-n", "20"] + 365 | ["-N", str(N)]) 366 | p.communicate() 367 | 368 | _solution = np.load(output_file).flatten() 369 | solution = [] 370 | for n in range(N): 371 | m = np.zeros((len(x_grid), len(y_grid))) 372 | for i in range(len(x_grid)): 373 | for j in range(len(y_grid)): 374 | m[i, j] = _solution[i + j * len(x_grid) + n * len(x_grid) * len(y_grid)] 375 | solution.append(m) 376 | solution = np.array(solution) 377 | if run_animation: 378 | animate_3D(solution, x_grid, y_grid, show=show, save=save, name=name, rstride=2, cstride=2, fixed_view=True) 379 | 380 | return x_grid, y_grid, solution 381 | 382 | 383 | if __name__ == "__main__": 384 | 385 | # run_transport_1D() 386 | # compare_solvers_transport_1D(["LaxWendroff", "Upwind"], run=True, show=True, show_grid=True, 387 | # save=False, name="numericalDiffusion.gif") 388 | 389 | # run_diffusion_1D() 390 | # compare_solvers_diffusion_1D(["ExplicitEuler", "ImplicitEuler", "CrankNicolson"], 391 | # run=True, show=True, show_grid=True, 392 | # save=False, name="multiStep.gif") 393 | # compare_solvers_diffusion_1D(["AdamsBashforth2", "AdamsMouldon2", "CrankNicolson"], 394 | # run=True, show=True, show_grid=True, 395 | # save=True, name="multiStep.gif") 396 | 397 | # run_wave_1D("ImplicitEuler") 398 | # compare_solvers_wave_1D(["ExplicitEuler", "ImplicitEuler"], 399 | # run=True, show=True, show_grid=True, 400 | # save=False, name="waveInstability1D.gif") 401 | 402 | # run_transport_2D(run=True, save=False, show=True) 403 | # run_diffusion_2D(run=True, save=False, show=True) 404 | run_wave_2D(run=True, save=False, show=True) 405 | -------------------------------------------------------------------------------- /plotter.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from mpl_toolkits.mplot3d import Axes3D 3 | from matplotlib import cm 4 | import matplotlib.pyplot as plt 5 | import matplotlib.animation as animation 6 | 7 | 8 | def __ax_formatter(ax, grid=False, title="", x_label="", y_label="", show_legend=False): 9 | ax.set_title(title) 10 | ax.set_xlabel(x_label) 11 | ax.set_ylabel(y_label) 12 | 13 | if grid: 14 | ax.grid() 15 | 16 | if show_legend: 17 | ax.legend(loc='best') 18 | 19 | 20 | def surf(z, x, y, title="", x_label="", y_label="", show_legend=False, show=False, rstride=1, cstride=1, cmap=cm.coolwarm): 21 | fig = plt.figure() 22 | ax = fig.gca(projection='3d') 23 | 24 | # Create X and Y data 25 | x_grid, y_grid = np.meshgrid(x, y) 26 | 27 | ax.plot_surface(x_grid, y_grid, z, cmap=cmap, rstride=rstride, cstride=cstride, antialiased=True) 28 | 29 | __ax_formatter(ax, False, title, x_label, y_label, show_legend) 30 | 31 | if show: 32 | plt.show() 33 | 34 | 35 | def plot(z, x, grid=False, title="", x_label="", y_label="", show_legend=False, show=False): 36 | fig = plt.figure() 37 | ax = fig.add_subplot(111) 38 | 39 | for i in range(len(x)): 40 | ax.plot(z[:, i]) 41 | 42 | __ax_formatter(ax, grid, title, x_label, y_label, show_legend) 43 | 44 | if show: 45 | plt.show() 46 | 47 | 48 | def animate(z, x, grid=False, show=False, save=False, label="", name="", y_lim=None): 49 | fig, ax = plt.subplots() 50 | 51 | line, = ax.plot(x, z[:, 0], label=label) 52 | if grid: 53 | ax.grid() 54 | 55 | def update_line(i): 56 | if i >= z.shape[1]: 57 | return line, 58 | line.set_ydata(z[:, i]) # update the data 59 | return line, 60 | 61 | # Init only required for blitting to give a clean slate. 62 | def init(): 63 | line.set_ydata(z[:, 0]) 64 | return line, 65 | 66 | ani = animation.FuncAnimation(fig, update_line, np.arange(1, 200), init_func=init, interval=25, blit=True) 67 | 68 | if show: 69 | plt.show() 70 | 71 | if y_lim is not None: 72 | ax.set_ylim(y_lim) 73 | 74 | if save: 75 | ani.save(name, fps=60, writer="imagemagick") 76 | 77 | 78 | def animate_multicurve(z_vector, x_vector, grid=False, show=False, save=False, labels=None, name="", 79 | y_lim=None): 80 | assert(len(z_vector) == len(x_vector)) 81 | 82 | fig, ax = plt.subplots() 83 | 84 | lines = [] 85 | 86 | # Init only required for blitting to give a clean slate. 87 | def init(): 88 | for line in lines: 89 | line.set_ydata(z[:, 0]) 90 | return tuple(lines) 91 | 92 | def update_line(i): 93 | if i >= z.shape[1]: 94 | return tuple(lines) 95 | for j, line in enumerate(lines): 96 | line.set_ydata(z_vector[j][:, i]) # update the data 97 | return tuple(lines) 98 | 99 | show_legend = True 100 | if labels is None: 101 | show_legend = False 102 | labels = [""] * len(x_vector) 103 | for x, z, l in zip(x_vector, z_vector, labels): 104 | line, = ax.plot(x, z[:, 0], label=l) 105 | lines.append(line) 106 | 107 | if grid: 108 | ax.grid() 109 | 110 | if show_legend: 111 | ax.legend(loc='best') 112 | 113 | if y_lim is not None: 114 | ax.set_ylim(y_lim) 115 | 116 | ani = animation.FuncAnimation(fig, update_line, np.arange(1, 200), init_func=init, interval=25, blit=True) 117 | 118 | if save: 119 | ani.save(name, fps=60, writer="imagemagick") 120 | 121 | if show: 122 | plt.show() 123 | 124 | 125 | def animate_3D(z, x, y, rstride=1, cstride=1, cmap=cm.coolwarm, show=False, save=False, name="", fixed_view=False): 126 | fig = plt.figure() 127 | ax = fig.gca(projection='3d') 128 | 129 | # Create X and Y data 130 | x_grid, y_grid = np.meshgrid(x, y) 131 | 132 | line = ax.plot_surface(x_grid, y_grid, z[0], cmap=cmap, rstride=rstride, cstride=cstride, antialiased=True) 133 | z_lim = ax.get_zlim() 134 | 135 | def update_line(i): 136 | if i >= z.shape[0]: 137 | return line, 138 | ax.clear() 139 | l = ax.plot_surface(x_grid, y_grid, z[i], cmap=cm.coolwarm, rstride=rstride, cstride=cstride, antialiased=True) 140 | if fixed_view: 141 | ax.set_zlim(z_lim) 142 | return l, 143 | 144 | ani = animation.FuncAnimation(fig, update_line, np.arange(1, 200), interval=25, blit=False) 145 | 146 | if show: 147 | plt.show() 148 | 149 | if save: 150 | ani.save(name, writer='imagemagick', fps=60) 151 | 152 | 153 | def animate_colormap(z, x, y, cmap='PuBu_r', shading='gouraud', show=False, save=False, name=""): 154 | fig, ax = plt.subplots() 155 | x, y = np.meshgrid(x, y) 156 | ax.pcolormesh(x, y, z[0], shading=shading, cmap=cmap) 157 | 158 | def update_line(i): 159 | if i >= z.shape[0]: 160 | return None, 161 | ax.clear() 162 | ax.pcolormesh(x, y, z[i], shading=shading, cmap=cmap) 163 | return None, 164 | 165 | ani = animation.FuncAnimation(fig, update_line, np.arange(1, 200), interval=25, blit=False) 166 | 167 | if show: 168 | plt.show() 169 | 170 | if save: 171 | ani.save(name, writer='imagemagick', fps=60) -------------------------------------------------------------------------------- /rungeKuttaFourthOrderDiffusionInstability_compressed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmontalb/PdeFiniteDifferenceSolver/aefb57b7a8e38c10bb677bdd40fffd141eed4134/rungeKuttaFourthOrderDiffusionInstability_compressed.gif -------------------------------------------------------------------------------- /rungeKuttaFourthOrderImplicitDiffusionInstability_compressed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmontalb/PdeFiniteDifferenceSolver/aefb57b7a8e38c10bb677bdd40fffd141eed4134/rungeKuttaFourthOrderImplicitDiffusionInstability_compressed.gif -------------------------------------------------------------------------------- /rungeKuttaSecondOrderdiffusionInstability_compressed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmontalb/PdeFiniteDifferenceSolver/aefb57b7a8e38c10bb677bdd40fffd141eed4134/rungeKuttaSecondOrderdiffusionInstability_compressed.gif -------------------------------------------------------------------------------- /rungeKuttaThirdOrderdiffusionInstability_compressed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmontalb/PdeFiniteDifferenceSolver/aefb57b7a8e38c10bb677bdd40fffd141eed4134/rungeKuttaThirdOrderdiffusionInstability_compressed.gif -------------------------------------------------------------------------------- /transport2d_compressed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmontalb/PdeFiniteDifferenceSolver/aefb57b7a8e38c10bb677bdd40fffd141eed4134/transport2d_compressed.gif -------------------------------------------------------------------------------- /wave1D_compressed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmontalb/PdeFiniteDifferenceSolver/aefb57b7a8e38c10bb677bdd40fffd141eed4134/wave1D_compressed.gif -------------------------------------------------------------------------------- /wave2d_compressed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmontalb/PdeFiniteDifferenceSolver/aefb57b7a8e38c10bb677bdd40fffd141eed4134/wave2d_compressed.gif -------------------------------------------------------------------------------- /waveInstability1D_compressed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmontalb/PdeFiniteDifferenceSolver/aefb57b7a8e38c10bb677bdd40fffd141eed4134/waveInstability1D_compressed.gif --------------------------------------------------------------------------------