├── .gitignore ├── README.md ├── Tempest.cbp ├── Tempest.pro ├── bin ├── input_box ├── input_sphere └── sphere_hex.geo ├── include ├── error.hpp ├── flux.hpp ├── geo.hpp ├── global.hpp ├── input.hpp ├── matrix.hpp ├── output.hpp ├── solver.hpp └── tempest.hpp ├── makefile └── src ├── flux.cpp ├── geo.cpp ├── global.cpp ├── input.cpp ├── matrix.cpp ├── output.cpp ├── solver.cpp ├── solver_bounds.cpp └── tempest.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tempest 2 | 3D Overset Finite Volume CFD Code 3 | -------------------------------------------------------------------------------- /Tempest.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 128 | 129 | -------------------------------------------------------------------------------- /Tempest.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console debug 3 | CONFIG -= qt 4 | 5 | QMAKE_CXXFLAGS += -std=c++11 6 | 7 | INCLUDEPATH += $$PWD/include \ 8 | lib/tioga/src 9 | 10 | SOURCES += src/global.cpp \ 11 | src/matrix.cpp \ 12 | src/input.cpp \ 13 | src/ele.cpp \ 14 | src/operators.cpp \ 15 | src/geo.cpp \ 16 | src/output.cpp \ 17 | src/face.cpp \ 18 | src/flux.cpp \ 19 | src/solver.cpp \ 20 | lib/tioga/src/ADT.C \ 21 | lib/tioga/src/MeshBlock.C \ 22 | lib/tioga/src/parallelComm.C \ 23 | lib/tioga/src/tioga.C \ 24 | lib/tioga/src/tiogaInterface.C \ 25 | lib/tioga/src/math.c \ 26 | lib/tioga/src/utils.c \ 27 | lib/tioga/src/cellVolume.f90 \ 28 | lib/tioga/src/computeCellVolume.f90 \ 29 | lib/tioga/src/kaiser.f \ 30 | lib/tioga/src/median.F90 \ 31 | src/solver_bounds.cpp \ 32 | src/tempest.cpp 33 | 34 | HEADERS += include/global.hpp \ 35 | include/matrix.hpp \ 36 | include/input.hpp \ 37 | include/geo.hpp \ 38 | include/output.hpp \ 39 | include/face.hpp \ 40 | include/flux.hpp \ 41 | include/solver.hpp \ 42 | include/error.hpp \ 43 | lib/tioga/src/ADT.h \ 44 | lib/tioga/src/codetypes.h \ 45 | lib/tioga/src/globals.h \ 46 | lib/tioga/src/MeshBlock.h \ 47 | lib/tioga/src/parallelComm.h \ 48 | lib/tioga/src/tioga.h \ 49 | lib/tioga/src/tiogaInterface.h \ 50 | lib/tioga/src/utils.h \ 51 | include/tempest.hpp 52 | 53 | DISTFILES += \ 54 | README.md \ 55 | 56 | OTHER_FILES += makefile \ 57 | lib/tioga/src/makefile 58 | -------------------------------------------------------------------------------- /bin/input_box: -------------------------------------------------------------------------------- 1 | # ============================================================= 2 | # Basic Options 3 | # ============================================================= 4 | equation 1 (0: Advection-Diffusion; 1: Euler/Navier-Stokes) 5 | order 1 (Polynomial order to use) 6 | timeType 4 (0: Forward Euler, 4: RK44) 7 | dtType 0 (0: Fixed, 1: CFL-based) 8 | CFL .4 9 | dt .001 10 | iterMax 100 11 | 12 | viscous 0 (0: Inviscid, 1: Viscous) 13 | motion 0 (0: Static, 1: Perturbation test case) 14 | riemannType 0 (Advection: use 0 | N-S: 0: Rusanov, 1: Roe) 15 | testCase 0 16 | 17 | # ============================================================= 18 | # Physics Parameters 19 | # ============================================================= 20 | # Advection-Diffusion Equation Parameters 21 | advectVx 1 (Wave speed, x-direction) 22 | advectVy 1 (Wave speed, y-direction) 23 | advectVz -1 (Wave speed, z-direction) 24 | lambda 1 (Upwinding Parameter - 0: Central, 1: Upwind) 25 | diffD .1 (Diffusion Coefficient) 26 | 27 | # ============================================================= 28 | # Initial Condition 29 | # ============================================================= 30 | # Advection: 0-Gaussian, 1-u=x+y+z test case, 2-u=cos(x)*cos(y)*cos(z) test case 31 | # N-S: 0-Uniform flow, 1-Uniform+Vortex 32 | icType 2 33 | 34 | # ============================================================= 35 | # Plotting/Output Options 36 | # ============================================================= 37 | plotFreq 10 (Frequency to write plot files) 38 | monitorResFreq 1 (Frequency to print residual to terminal) 39 | resType 2 (1: 1-norm, 2: 2-norm, 3: Inf-norm) 40 | dataFileName Box (Filename prefix for output files) 41 | entropySensor 0 (Calculate & plot entropy-error sensor) 42 | 43 | # ============================================================= 44 | # Mesh Options 45 | # ============================================================= 46 | meshType 0 (0: Read mesh, 1: Create mesh, 2: Overset Mesh) 47 | meshFileName hexbox.msh 48 | # The following parameters are only needed when creating a mesh: 49 | # nDims, nx, ny, nz, xmin, xmax, etc. 50 | nDims 3 51 | nx 20 52 | ny 2 53 | nz 20 54 | xmin 0 55 | xmax 5 56 | ymin 0 57 | ymax 1 58 | zmin 0 59 | zmax 5 60 | 61 | # ============================================================= 62 | # Boundary Conditions 63 | # ============================================================= 64 | # For creating a cartesian mesh, boundary condition to apply to each face 65 | # (default is periodic) 66 | #create_bcTop char 67 | #create_bcBottom slip_wall ... etc. 68 | 69 | create_bcTop sup_in 70 | create_bcBottom slip_wall 71 | create_bcFront slip_wall 72 | create_bcBack slip_wall 73 | create_bcLeft sup_in 74 | create_bcRight slip_wall 75 | 76 | # Gmsh Boundary Conditions 77 | # List each Gmsh boundary: 'mesh_bound ' 78 | # i.e. mesh_bound airfoil slip_wall 79 | #mesh_bound sphere slip_wall 80 | #mesh_bound overset sup_in 81 | mesh_bound fluid fluid 82 | 83 | mesh_bound top sup_out 84 | mesh_bound bottom sup_out 85 | mesh_bound left sup_out 86 | mesh_bound right sup_out 87 | mesh_bound front sup_out 88 | mesh_bound back sup_out 89 | 90 | #mesh_bound top sup_in 91 | #mesh_bound bottom slip_wall 92 | #mesh_bound left sup_in 93 | #mesh_bound right sup_in 94 | #mesh_bound front slip_wall 95 | #mesh_bound back slip_wall 96 | 97 | # ============================================================= 98 | # Freestream Boundary Conditions [for all freestream/inlet-type boundaries] 99 | # ============================================================= 100 | # Inviscid Flows 101 | rhoBound 1 102 | uBound 2. 103 | vBound 0. 104 | wBound -0.2 105 | pBound .7142857143 106 | 107 | # Viscous Flows 108 | MachBound .2 109 | Re 100 110 | Lref 1.0 111 | TBound 300 112 | nxBound 1 113 | nyBound 0 114 | nzBound 0 115 | 116 | # ============================================================= 117 | # Numerics Options 118 | # ============================================================= 119 | # Other FR-method parameters 120 | spts_type_quad Legendre 121 | 122 | # Shock Capturing Parameters 123 | shockCapture 0 124 | threshold .1 125 | -------------------------------------------------------------------------------- /bin/input_sphere: -------------------------------------------------------------------------------- 1 | # ============================================================= 2 | # Basic Options 3 | # ============================================================= 4 | equation 1 (0: Advection-Diffusion; 1: Euler/Navier-Stokes) 5 | order 1 (Polynomial order to use) 6 | timeType 0 (0: Forward Euler, 4: RK44) 7 | dtType 0 (0: Fixed, 1: CFL-based) 8 | CFL .6 9 | dt .002 10 | iterMax 10000 11 | 12 | viscous 0 (0: Inviscid, 1: Viscous) 13 | motion 0 (0: Static, 1: Perturbation test case) 14 | riemannType 0 (Advection: use 0 | N-S: 0: Rusanov, 1: Roe) 15 | testCase 0 16 | 17 | # ============================================================= 18 | # Physics Parameters 19 | # ============================================================= 20 | # Advection-Diffusion Equation Parameters 21 | advectVx 1 (Wave speed, x-direction) 22 | advectVy 1 (Wave speed, y-direction) 23 | advectVz -1 (Wave speed, z-direction) 24 | lambda 1 (Upwinding Parameter - 0: Central, 1: Upwind) 25 | diffD .1 (Diffusion Coefficient) 26 | 27 | # ============================================================= 28 | # Initial Condition 29 | # ============================================================= 30 | # Advection: 0-Gaussian, 1-u=x+y+z test case, 2-u=cos(x)*cos(y)*cos(z) test case 31 | # N-S: 0-Uniform flow, 1-Uniform+Vortex 32 | icType 0 33 | 34 | # ============================================================= 35 | # Plotting/Output Options 36 | # ============================================================= 37 | plotFreq 1000 (Frequency to write plot files) 38 | monitorResFreq 200 (Frequency to print residual to terminal) 39 | resType 2 (1: 1-norm, 2: 2-norm, 3: Inf-norm) 40 | dataFileName Sphere (Filename prefix for output files) 41 | entropySensor 0 (Calculate & plot entropy-error sensor) 42 | 43 | # ============================================================= 44 | # Mesh Options 45 | # ============================================================= 46 | meshType 0 (0: Read mesh, 1: Create mesh, 2: Overset Mesh) 47 | meshFileName sphere_hex.msh 48 | oversetGrids 2 sphere_hex.msh HexBox.msh 49 | # The following parameters are only needed when creating a mesh: 50 | # nDims, nx, ny, nz, xmin, xmax, etc. 51 | nDims 3 52 | nx 10 53 | ny 10 54 | nz 10 55 | xmin 0 56 | xmax 3 57 | ymin 0 58 | ymax 3 59 | zmin 0 60 | zmax 3 61 | 62 | # ============================================================= 63 | # Boundary Conditions 64 | # ============================================================= 65 | # For creating a cartesian mesh, boundary condition to apply to each face 66 | # (default is periodic) 67 | #create_bcTop char 68 | #create_bcBottom slip_wall ... etc. 69 | create_bcTop sup_in 70 | create_bcBottom slip_wall 71 | create_bcFront sup_in 72 | create_bcBack sup_in 73 | create_bcLeft sup_in 74 | create_bcRight sup_in 75 | 76 | # Gmsh Boundary Conditions 77 | # List each Gmsh boundary: 'mesh_bound ' 78 | # i.e. mesh_bound airfoil slip_wall 79 | mesh_bound sphere slip_wall 80 | mesh_bound overset sup_in 81 | mesh_bound fluid fluid 82 | 83 | # ============================================================= 84 | # Freestream Boundary Conditions [for all freestream/inlet-type boundaries] 85 | # ============================================================= 86 | # Inviscid Flows 87 | rhoBound 1 88 | uBound 0.2 89 | vBound 0. 90 | wBound 0. 91 | pBound .7142857143 92 | 93 | # Viscous Flows 94 | MachBound .2 95 | Re 100 96 | Lref 1.0 97 | TBound 300 98 | nxBound 1 99 | nyBound 0 100 | nzBound 0 101 | 102 | # ============================================================= 103 | # Numerics Options 104 | # ============================================================= 105 | # Other FR-method parameters 106 | spts_type_quad Legendre 107 | 108 | # Shock Capturing Parameters 109 | shockCapture 0 110 | threshold .1 111 | -------------------------------------------------------------------------------- /bin/sphere_hex.geo: -------------------------------------------------------------------------------- 1 | // Gmsh project 2 | size1 = .3; // .19 3 | size2 = .7; // .55 4 | R1 = .5; // Radius of inner sphere 5 | R2 = 8; // Radius of outer sphere 6 | NN = 7; // Number of points in each direction on each spherical surface 7 | NL = 17; // Number of layers between surfaces 8 | prog1 = 1.15; // Geometric progression factor for layer width 9 | 10 | R1 = Sqrt(R1*R1/3); 11 | R2 = Sqrt(R2*R2/3); 12 | 13 | /* ---- Inner Sphere Surface ---- */ 14 | 15 | Point(1) = {0.0,0.0,0.0,size1}; 16 | 17 | Point(2) = {-R1, -R1, -R1, size1}; 18 | Point(3) = { R1, -R1, -R1, size1}; 19 | Point(4) = { R1, R1, -R1, size1}; 20 | Point(5) = {-R1, R1, -R1, size1}; 21 | 22 | Point(6) = {-R1, -R1, R1, size1}; 23 | Point(7) = { R1, -R1, R1, size1}; 24 | Point(8) = { R1, R1, R1, size1}; 25 | Point(9) = {-R1, R1, R1, size1}; 26 | 27 | // 'Bottom' circles 28 | Circle(1) = {2,1,3}; 29 | Circle(2) = {3,1,4}; 30 | Circle(3) = {4,1,5}; 31 | Circle(4) = {5,1,2}; 32 | 33 | // 'Top' circles 34 | Circle(5) = {6,1,7}; 35 | Circle(6) = {7,1,8}; 36 | Circle(7) = {8,1,9}; 37 | Circle(8) = {9,1,6}; 38 | 39 | // 'Vertical' circles 40 | Circle(9) = {2,1,6}; 41 | Circle(10) = {3,1,7}; 42 | Circle(11) = {4,1,8}; 43 | Circle(12) = {5,1,9}; 44 | 45 | Transfinite Line {1:12} = NN Using Progression 1.0; 46 | 47 | Line Loop (1) = {1,2,3,4}; 48 | Line Loop (2) = {5,6,7,8}; 49 | Line Loop (3) = {4,9,-8,-12}; 50 | Line Loop (4) = {2,11,-6,-10}; 51 | Line Loop (5) = {1,10,-5,-9}; 52 | Line Loop (6) = {3,12,-7,-11}; 53 | 54 | Ruled Surface (1) = {1}; 55 | Ruled Surface (2) = {2}; 56 | Ruled Surface (3) = {3}; 57 | Ruled Surface (4) = {4}; 58 | Ruled Surface (5) = {5}; 59 | Ruled Surface (6) = {6}; 60 | 61 | Transfinite Surface {1:6}; 62 | Recombine Surface {1:6}; 63 | 64 | 65 | /* ---- Outer Sphere Surface ---- */ 66 | 67 | Point(11) = {0.0,0.0,0.0,size1}; 68 | 69 | Point(12) = {-R2, -R2, -R2, size1}; 70 | Point(13) = { R2, -R2, -R2, size1}; 71 | Point(14) = { R2, R2, -R2, size1}; 72 | Point(15) = {-R2, R2, -R2, size1}; 73 | 74 | Point(16) = {-R2, -R2, R2, size1}; 75 | Point(17) = { R2, -R2, R2, size1}; 76 | Point(18) = { R2, R2, R2, size1}; 77 | Point(19) = {-R2, R2, R2, size1}; 78 | 79 | // 'Bottom' circles 80 | Circle(21) = {12,11,13}; 81 | Circle(22) = {13,11,14}; 82 | Circle(23) = {14,11,15}; 83 | Circle(24) = {15,11,12}; 84 | 85 | // 'Top' circles 86 | Circle(25) = {16,11,17}; 87 | Circle(26) = {17,11,18}; 88 | Circle(27) = {18,11,19}; 89 | Circle(28) = {19,11,16}; 90 | 91 | // 'Vertical' circles 92 | Circle(29) = {12,11,16}; 93 | Circle(30) = {13,11,17}; 94 | Circle(31) = {14,11,18}; 95 | Circle(32) = {15,11,19}; 96 | 97 | Transfinite Line {21:32} = NN Using Progression 1.0; 98 | 99 | Line Loop (11) = {21,22,23,24}; 100 | Line Loop (12) = {25,26,27,28}; 101 | Line Loop (13) = {24,29,-28,-32}; 102 | Line Loop (14) = {22,31,-26,-30}; 103 | Line Loop (15) = {21,30,-25,-29}; 104 | Line Loop (16) = {23,32,-27,-31}; 105 | 106 | Ruled Surface (11) = {11}; 107 | Ruled Surface (12) = {12}; 108 | Ruled Surface (13) = {13}; 109 | Ruled Surface (14) = {14}; 110 | Ruled Surface (15) = {15}; 111 | Ruled Surface (16) = {16}; 112 | 113 | Transfinite Surface {11:16}; 114 | Recombine Surface {11:16}; 115 | 116 | 117 | /* ---- Connecting Lines ---- */ 118 | 119 | Line(41) = {2,12}; 120 | Line(42) = {3,13}; 121 | Line(43) = {4,14}; 122 | Line(44) = {5,15}; 123 | Line(45) = {6,16}; 124 | Line(46) = {7,17}; 125 | Line(47) = {8,18}; 126 | Line(48) = {9,19}; 127 | 128 | Transfinite Line {41:48} = NL Using Progression prog1; 129 | 130 | /* ---- Connecting Planes ---- */ 131 | 132 | Line Loop (21) = {-1,41,21,-42}; 133 | Line Loop (22) = {-2,42,22,-43}; 134 | Line Loop (23) = {-3,43,23,-44}; 135 | Line Loop (24) = {-4,44,24,-41}; 136 | 137 | Line Loop (25) = {5,46,-25,-45}; 138 | Line Loop (26) = {6,47,-26,-46}; 139 | Line Loop (27) = {7,48,-27,-47}; 140 | Line Loop (28) = {8,45,-28,-48}; 141 | 142 | Line Loop (29) = {-9,41,29,-45}; 143 | Line Loop (30) = {-10,42,30,-46}; 144 | Line Loop (31) = {-11,43,31,-47}; 145 | Line Loop (32) = {-12,44,32,-48}; 146 | 147 | Ruled Surface (31) = {21}; 148 | Ruled Surface (32) = {22}; 149 | Ruled Surface (33) = {23}; 150 | Ruled Surface (34) = {24}; 151 | Ruled Surface (35) = {25}; 152 | Ruled Surface (36) = {26}; 153 | Ruled Surface (37) = {27}; 154 | Ruled Surface (38) = {28}; 155 | Ruled Surface (39) = {29}; 156 | Ruled Surface (40) = {30}; 157 | Ruled Surface (41) = {31}; 158 | Ruled Surface (42) = {32}; 159 | 160 | Transfinite Surface {31:42}; 161 | Recombine Surface {31:42}; 162 | 163 | /* ---- Construct All Volumes ---- */ 164 | 165 | Surface Loop (1) = {1,11,31,32,33,34}; 166 | Surface Loop (2) = {2,12,35,36,37,38}; 167 | Surface Loop (3) = {5,15,31,35,39,40}; 168 | Surface Loop (4) = {4,14,32,36,40,41}; 169 | Surface Loop (5) = {6,16,33,37,41,42}; 170 | Surface Loop (6) = {3,13,34,38,42,39}; 171 | 172 | Volume (1) = {1}; 173 | Volume (2) = {2}; 174 | Volume (3) = {3}; 175 | Volume (4) = {4}; 176 | Volume (5) = {5}; 177 | Volume (6) = {6}; 178 | Transfinite Volume {1:6}; 179 | 180 | /* ---- Physical Names (Boundary Names) ---- */ 181 | 182 | Physical Volume ("FLUID") = {1:6}; 183 | Physical Surface ("OVERSET") = {11:16}; 184 | Physical Surface ("SPHERE") = {1:6}; 185 | -------------------------------------------------------------------------------- /include/error.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | //! Prints the error message, the stack trace, and exits 8 | #define FatalError(s) { \ 9 | printf("Fatal error '%s' at %s:%d\n",s,__FILE__,__LINE__); \ 10 | exit(1); } 11 | -------------------------------------------------------------------------------- /include/flux.hpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file flux.hpp 3 | * \brief Header file flux-calculation functions 4 | * 5 | * \author - Jacob Crabill 6 | * Aerospace Computing Laboratory (ACL) 7 | * Aero/Astro Department. Stanford University 8 | * 9 | * \version 0.0.1 10 | * 11 | * Flux Reconstruction in C++ (Flurry++) Code 12 | * Copyright (C) 2014 Jacob Crabill. 13 | * 14 | */ 15 | #pragma once 16 | 17 | #include "global.hpp" 18 | 19 | #include "input.hpp" 20 | #include "matrix.hpp" 21 | 22 | /*! Calculate the inviscid portion of the Euler or Navier-Stokes flux vector at a point */ 23 | void inviscidFlux(double* U, matrix &F, input *params); 24 | 25 | /*! Calculate the viscous portion of the Navier-Stokes flux vector at a point */ 26 | void viscousFlux(double *U, matrix &gradU, matrix &Fvis, input *params); 27 | 28 | /*! Calculate the viscous flux for Advection-Diffusion */ 29 | void viscousFluxAD(matrix &gradU, matrix &Fvis, input *params); 30 | 31 | /*! Calculate the common inviscid flux at a point using Roe's method */ 32 | void roeFlux(double* uL, double* uR, double *norm, double *Fn, input *params); 33 | 34 | /*! Calculate the common inviscid flux at a point using the Rusanov scalar-diffusion method */ 35 | void rusanovFlux(double* UL, double* UR, matrix &FL, matrix &FR, double *norm, double *Fn, double *waveSp, input *params); 36 | 37 | /*! Simple central-difference flux (For advection problems) */ 38 | void centralFlux(double* uL, double* uR, double *norm, double *Fn, input *params); 39 | 40 | /*! Simple central-difference flux (For Navier-Stokes problems) */ 41 | void centralFlux(matrix &FL, matrix &FR, double* norm, double* Fn, input *params); 42 | 43 | /*! Simple upwinded flux (primarily for advection problems) */ 44 | void upwindFlux(double* uL, double* uR, double *norm, double *Fn, input *params); 45 | 46 | /*! Lax-Friedrichs flux (advection-diffusion) */ 47 | void laxFriedrichsFlux(double* uL, double* uR, double *norm, double *Fn, input *params); 48 | 49 | /*! Calculate the common viscous flux at a point using the LDG penalty method */ 50 | void ldgFlux(double* uL, double* uR, matrix &gradU_L, matrix &gradU_R, double *Fn, input *params); 51 | -------------------------------------------------------------------------------- /include/geo.hpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file geo.hpp 3 | * \brief Header file for geometry class 4 | * 5 | * \author - Jacob Crabill 6 | * Aerospace Computing Laboratory (ACL) 7 | * Aero/Astro Department. Stanford University 8 | * 9 | * \version 0.0.1 10 | * 11 | * Flux Reconstruction in C++ (Flurry++) Code 12 | * Copyright (C) 2014 Jacob Crabill. 13 | * 14 | */ 15 | #pragma once 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "global.hpp" 22 | 23 | //class tioga; 24 | 25 | #include "input.hpp" 26 | #include "solver.hpp" 27 | //#include "tioga.h" 28 | 29 | class geo 30 | { 31 | public: 32 | geo(); 33 | 34 | ~geo(); 35 | 36 | /* === Primay setup routines === */ 37 | 38 | //! Setup the geomery using input parameters 39 | void setup(input* params); 40 | 41 | //! Take the basic connectivity data and generate the rest 42 | void processConnectivity(); 43 | 44 | /* === Helper Routines === */ 45 | 46 | //! Read essential connectivity from a Gmsh mesh file 47 | void readGmsh(string fileName); 48 | 49 | //! Create a simple Cartesian mesh from input parameters 50 | void createMesh(); 51 | 52 | //! Get the reference-domain location of the solution points for the given element & polynomial order 53 | vector getLocSpts(int eType, int order); 54 | 55 | //! Get the reference-domain location of the flux points for the given element & polynomial order 56 | vector getLocFpts(int eType, int order); 57 | 58 | //! Get the point locations of the requested type (i.e. Gauss, Lobatto) for the given order 59 | vector getPts1D(string ptsType, int order); 60 | 61 | //! Get the Gauss quadrature weights for the Gauss points of the given order [2D] 62 | vector getQptWeights(int order); 63 | 64 | //! Get the Gauss quadrature weights for the Gauss points of the given order [1D] 65 | vector getQptWeights1D(int order); 66 | 67 | //! Update connectivity / node-blanking for overset grids 68 | void registerGridDataTIOGA(); 69 | 70 | /*! 71 | * \brief Call TIOGA to re-process overset connectivity 72 | * 73 | * Called once during pre-processing by default; re-call each iteration 74 | * for moving-mesh cases 75 | */ 76 | void updateOversetConnectivity(); 77 | 78 | //! Have TIOGA output the mesh along with nodal IBLANK values 79 | void writeOversetConnectivity(); 80 | 81 | int nDims, nFields; 82 | int nEles, nVerts, nEdges, nFaces, nIntFaces, nBndFaces, nMpiFaces; 83 | int nBounds; //! Number of boundaries 84 | int meshType; 85 | 86 | // Basic [essential] Connectivity Data 87 | matrix c2v; 88 | matrix xv; 89 | 90 | // Additional Connectivity Data 91 | matrix c2e, c2b, e2c, e2v, v2e, v2v, v2c; 92 | matrix c2f, f2v, f2c, c2c; 93 | vector v2nv, v2nc, c2nv, c2nf, c2ne, c2nc, e2nc, f2nv, ctype; 94 | vector v2b; //! Does vertex lie on a boundary? (0 or 1) 95 | vector e2A; //! Dual-mesh face area for each edge 96 | vector v2vol; //! Dual-mesh element volumes for each vertex 97 | vector intFaces, bndFaces, mpiFaces, mpiCells; 98 | vector nBcFaces; //! Number of faces on each boundary 99 | vector> bcFaceList; //! Global face IDs of faces on each boundary 100 | Array bndNorm; //! Outward unit normal for each boundary point 101 | matrix bndArea; //! Face area for each boundary point 102 | vector c2xc; //! Centroid of each cell in mesh (for creation of dual mesh) 103 | vector bcList; //! List of boundary conditions for each boundary 104 | vector bcTypeF; //! Boundary condition for each boundary face 105 | vector bcTypeE; //! Boundary condition for each boundary edge 106 | matrix bndPts; //! List of node IDs on each boundary 107 | vector nBndPts; //! Number of points on each boudary 108 | vector > bcFaces; //! List of nodes on each face (edge) for each boundary condition 109 | vector nFacesPerBnd; //! List of # of faces on each boundary 110 | vector procR; //! What processor lies to the 'right' of this face 111 | vector locF_R; //! The local mpiFace ID of each mpiFace on the opposite processor 112 | vector gIC_R; //! The global cell ID of the right cell on the opposite processor 113 | vector mpiLocF; //! Element-local face ID of MPI Face in left cell 114 | vector mpiLocF_R; //! Element-local face ID of MPI Face in right cell 115 | vector isBnd; // might want to change this to "int" and have it store WHICH boundary the face is on (-1 for internal) 116 | vector isBndEdge; //! For each edge, flag for "normal" or "boundary" 117 | 118 | /* --- Overset-Related Variables --- */ 119 | int nprocPerGrid; //! Number of MPI processes assigned to each (overset) grid block 120 | int gridID; //! Which (overset) grid block is this process handling 121 | int gridRank; //! MPI rank of process *within* the grid block [0 to nprocPerGrid-1] 122 | vector iblank; //! Output of TIOGA: flag for whether vertex is normal, blanked, or receptor 123 | vector iblankCell; //! Output? of TIOGA: flag for whether cell is normal, blanked, or receptor 124 | vector iwall; //! List of nodes on wall boundaries 125 | vector iover; //! List of nodes on overset boundaries 126 | 127 | //tioga* tg; //! Pointer to Tioga object for processing overset grids 128 | int* nodesPerCell; //! Pointer for Tioga to know # of nodes for each element type 129 | array conn; //! Pointer to c2v for each element type [but only 1, so will be size(1)] 130 | 131 | Vec3 getFaceNormalTri(int faceID); 132 | Vec3 getFaceNormalQuad(int faceID); 133 | private: 134 | 135 | input *params; 136 | 137 | /* --- MPI-Related Varialbes (global vs. local data) --- */ 138 | matrix c2v_g; //! Global element connectivity 139 | matrix xv_g; //! Global mesh node locations 140 | vector ic2icg; //! Local cell to global cell index 141 | vector iv2ivg; //! Local vertex to global vertex index 142 | vector ctype_g, c2ne_g, c2nv_g; //! Global element info 143 | matrix bndPts_g; //! Global lists of points on boundaries 144 | vector nBndPts_g; //! Global number of points on each boundary 145 | map bcIdMap; //! Map from Gmsh boundary ID to Flurry BC ID 146 | int nEles_g, nVerts_g; 147 | 148 | #ifndef _NO_MPI 149 | MPI_Comm gridComm; 150 | #endif 151 | 152 | void processConnEdges(void); 153 | void processConnFaces(void); 154 | void processConnDual(void); 155 | 156 | //! Match up pairs of periodic boundary faces 157 | void processPeriodicBoundaries(void); 158 | 159 | //! Check if two given periodic edges match up 160 | bool checkPeriodicFaces(int *edge1, int *edge2); 161 | bool checkPeriodicFaces3D(vector &face1, vector &face2); 162 | 163 | //! Compare the orientation (rotation in ref. space) betwen the local faces of 2 elements 164 | int compareOrientation(int ic1, int ic2, int f1, int f2); 165 | 166 | //! Compare the orientation (rotation in ref. space) betwen the local faces of 2 elements across MPI boundary 167 | int compareOrientationMPI(int ic1, int ic2, int f1, int f2); 168 | 169 | //! For MPI runs, partition the mesh across all processors 170 | void partitionMesh(void); 171 | 172 | //! For MPI runs, match internal faces across MPI boundaries 173 | void matchMPIFaces(); 174 | 175 | //! Compare two faces [lists of nodes] to see if they match [used for MPI] 176 | bool compareFaces(vector &face1, vector &face2); 177 | }; 178 | -------------------------------------------------------------------------------- /include/global.hpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file global.hpp 3 | * \brief Header file for global constants, objects, and variables 4 | * 5 | * \author - Jacob Crabill 6 | * Aerospace Computing Laboratory (ACL) 7 | * Aero/Astro Department. Stanford University 8 | * 9 | * \version 0.0.1 10 | * 11 | * Flux Reconstruction in C++ (Flurry++) Code 12 | * Copyright (C) 2014 Jacob Crabill. 13 | * 14 | */ 15 | #pragma once 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include // std::size_t 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | #include "error.hpp" 34 | 35 | template class matrix; 36 | 37 | #include "matrix.hpp" 38 | 39 | // Forward declarations of basic Tempest classes 40 | class geo; 41 | class solver; 42 | 43 | using namespace std; 44 | 45 | typedef unsigned int uint; 46 | 47 | /* --- Misc. Common Constants / Globally-Useful Variables --- */ 48 | extern double pi; 49 | 50 | extern map bcStr2Num; //! Maps a boundary-condition string to its integer enum 51 | 52 | /*! enumeration for element type */ 53 | enum ETYPE { 54 | TRI = 0, 55 | QUAD = 1, 56 | TET = 2, 57 | PRISM = 3, 58 | HEX = 4, 59 | PYRAMID = 5 60 | }; 61 | 62 | /*! Enumeration for mesh (either create cartesian mesh or read from file) */ 63 | enum meshType { 64 | READ_MESH = 0, 65 | CREATE_MESH = 1, 66 | OVERSET_MESH = 2 67 | }; 68 | 69 | enum EQUATION { 70 | ADVECTION_DIFFUSION = 0, 71 | NAVIER_STOKES = 1 72 | }; 73 | 74 | /*! Enumeration for all available boundary conditions */ 75 | enum BC_TYPE { 76 | NONE = -1, 77 | PERIODIC = 0, 78 | CHAR = 1, 79 | SUP_IN = 2, 80 | SUP_OUT = 3, 81 | SUB_IN = 4, 82 | SUB_OUT = 5, 83 | SUB_IN_CHAR = 6, 84 | SUB_OUT_CHAR = 7, 85 | SLIP_WALL = 8, 86 | ISOTHERMAL_NOSLIP = 9, 87 | ADIABATIC_NOSLIP = 10, 88 | OVERSET = 11, 89 | SYMMETRY = 12 90 | }; 91 | 92 | /*! Enumeration for VCJH scheme (correction function) to use */ 93 | enum VCJH_SCHEME { 94 | DG = 0, 95 | SD = 1, 96 | HU = 2, 97 | CPLUS = 3 98 | }; 99 | 100 | /*! For convinience with geometry, a simple struct to hold an x,y,z coordinate */ 101 | struct point 102 | { 103 | double x, y, z; 104 | 105 | point() { 106 | x = 0; 107 | y = 0; 108 | z = 0; 109 | } 110 | 111 | point(double* pt) { 112 | x = pt[0]; 113 | y = pt[1]; 114 | z = pt[2]; 115 | } 116 | 117 | void zero() { 118 | x = 0; 119 | y = 0; 120 | z = 0; 121 | } 122 | 123 | double& operator[](int ind) { 124 | switch(ind) { 125 | case 0: 126 | return x; 127 | case 1: 128 | return y; 129 | case 2: 130 | return z; 131 | default: 132 | FatalError("Invalid index for point struct."); 133 | } 134 | } 135 | 136 | point operator=(double* a) { 137 | struct point pt; 138 | pt.x = a[0]; 139 | pt.y = a[1]; 140 | pt.z = a[2]; 141 | return pt; 142 | } 143 | 144 | point operator-(point b) { 145 | struct point c; 146 | c.x = x - b.x; 147 | c.y = y - b.y; 148 | c.z = z - b.z; 149 | return c; 150 | } 151 | 152 | point operator+(point b) { 153 | struct point c; 154 | c.x = x + b.x; 155 | c.y = y + b.y; 156 | c.z = z + b.z; 157 | return c; 158 | } 159 | 160 | point operator/(point b) { 161 | struct point c; 162 | c.x = x / b.x; 163 | c.y = y / b.y; 164 | c.z = z / b.z; 165 | return c; 166 | } 167 | 168 | point& operator+=(point b) { 169 | x += b.x; 170 | y += b.y; 171 | z += b.z; 172 | return *this; 173 | } 174 | 175 | point& operator-=(point b) { 176 | x -= b.x; 177 | y -= b.y; 178 | z -= b.z; 179 | return *this; 180 | } 181 | 182 | point& operator+=(double* b) { 183 | x += b[0]; 184 | y += b[1]; 185 | z += b[2]; 186 | return *this; 187 | } 188 | 189 | point& operator-=(double* b) { 190 | x -= b[0]; 191 | y -= b[1]; 192 | z -= b[2]; 193 | return *this; 194 | } 195 | 196 | point& operator/=(double a) { 197 | x /= a; 198 | y /= a; 199 | z /= a; 200 | return *this; 201 | } 202 | 203 | point& operator*=(double a) { 204 | x *= a; 205 | y *= a; 206 | z *= a; 207 | return *this; 208 | } 209 | 210 | double operator*(point b) { 211 | return x*b.x + y*b.y + z*b.z; 212 | } 213 | 214 | void abs(void) { 215 | x = std::abs(x); 216 | y = std::abs(y); 217 | z = std::abs(z); 218 | } 219 | 220 | double norm(void) { 221 | return std::sqrt(x*x+y*y+z*z); 222 | } 223 | 224 | point cross(point b) { 225 | point v; 226 | v.z = x*b.y - y*b.x; 227 | v.y = x*b.z - z*b.x; 228 | v.x = y*b.z - z*b.y; 229 | return v; 230 | } 231 | 232 | }; 233 | 234 | point operator/(point a, double b); 235 | point operator*(point a, double b); 236 | 237 | //! For clearer notation when a vector is implied, rather than a point 238 | typedef struct point Vec3; 239 | 240 | int factorial(int n); 241 | 242 | void setGlobalVariables(void); 243 | 244 | bool checkNaN(vector &vec); 245 | 246 | bool checkNaN(double* vec, int size); 247 | 248 | //double randRange(double xMin, double xMax); 249 | 250 | /*! Find indices of all values in vec equal to val */ 251 | template 252 | vector findEq(const vector &vec, T val) 253 | { 254 | vector out; 255 | 256 | for (uint i=0; i 265 | int findFirst(vector &vec, T val) 266 | { 267 | if (vec.size()==0) return -1; 268 | 269 | for (int i=0; i<(int)vec.size(); i++) { 270 | if (vec[i]==val) return i; 271 | } 272 | 273 | // If not found... 274 | return -1; 275 | } 276 | 277 | template 278 | int findFirst(T* vec, T val, uint length) 279 | { 280 | if (length==0) return -1; 281 | 282 | for (int i=0; i<(int)length; i++) { 283 | if (vec[i]==val) return i; 284 | } 285 | 286 | // If not found... 287 | return -1; 288 | } 289 | 290 | /*! Assign a value to vector vec at indices indicated in ind */ 291 | template 292 | void vecAssign(vector &vec, vector &ind, T val) 293 | { 294 | for (auto& i:ind) vec[i] = val; 295 | } 296 | 297 | // Good for numeric types 298 | template 299 | T getMax(vector &vec) 300 | { 301 | T max = 0; 302 | for (auto& i:vec) { 303 | if (i>max) max = i; 304 | } 305 | 306 | return max; 307 | } 308 | 309 | template 310 | void addVectors(vector &vec1, vector &vec2) 311 | { 312 | if (vec1.size() != vec2.size()) FatalError("Vectors not of same size."); 313 | 314 | for (unsigned int i=0; i 318 | vector & operator+=(vector& lhs, vector& rhs) 319 | { 320 | if (lhs.size() != rhs.size()) FatalError("Vectors not of same size."); 321 | 322 | for (unsigned int i=0; i 328 | vector operator+(const vector& lhs, vector& rhs) 329 | { 330 | if (lhs.size() != rhs.size()) FatalError("Vectors not of same size."); 331 | 332 | vector out(lhs.size()); 333 | for (unsigned int i=0; i 339 | vector operator*(const vector& lhs, double rhs) 340 | { 341 | vector out(lhs.size()); 342 | for (unsigned int i=0; i& mat, Vec3 &vec); 348 | 349 | //----------Performance boost mod---------------------- 350 | /*template 351 | void std::vector::operator*()*/ 352 | //----------Performance boost mod---------------------- 353 | 354 | template 355 | vector operator/(const vector& lhs, double rhs) 356 | { 357 | vector out(lhs.size()); 358 | for (unsigned int i=0; i 17 | #include 18 | #include 19 | 20 | #include "global.hpp" 21 | 22 | class fileReader 23 | { 24 | 25 | public: 26 | /*! Default constructor */ 27 | fileReader(); 28 | 29 | fileReader(string fileName); 30 | 31 | /*! Default destructor */ 32 | ~fileReader(); 33 | 34 | /*! Set the file to be read from */ 35 | void setFile(string fileName); 36 | 37 | /*! Open the file to prepare for reading simulation parameters */ 38 | void openFile(void); 39 | 40 | /*! Close the file & clean up */ 41 | void closeFile(void); 42 | 43 | /* === Functions to read paramters from input file === */ 44 | 45 | /*! Read a single value from the input file; if not found, apply a default value */ 46 | template 47 | void getScalarValue(string optName, T &opt, T defaultVal); 48 | 49 | /*! Read a single value from the input file; if not found, throw an error and exit */ 50 | template 51 | void getScalarValue(string optName, T &opt); 52 | 53 | /*! Read a vector of values from the input file; if not found, apply the default value to all elements */ 54 | template 55 | void getVectorValue(string optName, vector &opt, T defaultVal); 56 | 57 | /*! Read a vector of values from the input file; if not found, throw an error and exit */ 58 | template 59 | void getVectorValue(string optName, vector &opt); 60 | 61 | /*! Read in a map of type from input file; each entry prefaced by optName */ 62 | template 63 | void getMap(string optName, map &opt); 64 | 65 | private: 66 | ifstream optFile; 67 | string fileName; 68 | 69 | }; 70 | 71 | class input 72 | { 73 | public: 74 | /*! Default constructor */ 75 | input(); 76 | 77 | void readInputFile(char *filename); 78 | 79 | void nonDimensionalize(void); 80 | 81 | /* --- Basic Problem Variables --- */ 82 | int equation; //! {0 | Advection/diffusion} {1 | Euler/Navier-Stokes} 83 | int viscous; //! {0 | No diffusion/Viscosity} {1 | Diffusion/Viscosity} 84 | int order; 85 | int icType; 86 | int motion; 87 | int test_case; 88 | int riemannType; 89 | 90 | 91 | /* --- Viscous Solver Parameters --- */ 92 | double penFact; //! Penalty factor for the LDG viscous flux 93 | double tau; //! Bias parameter for the LDG viscous flux 94 | double Re; //! Reynolds number 95 | double Lref; //! Reference length for Reynlds number 96 | 97 | // For Sutherland's Law 98 | double muGas; 99 | double TGas; 100 | double SGas; 101 | double rt_inf; //! For Sutherland's Law 102 | double mu_inf; //! For Sutherland's Law 103 | double fix_vis; //! Use Sutherland's Law or fixed (constant) viscosity? 104 | double c_sth; //! For Sutherland's Law 105 | 106 | /* --- Simulation Run Parameters --- */ 107 | int nFields; 108 | int nDims; 109 | double dt; 110 | double CFL; 111 | int dtType; 112 | int timeType; 113 | double rkTime; 114 | double time; 115 | int iterMax; 116 | int initIter; 117 | int restartIter; 118 | int restart; 119 | int restart_freq; 120 | 121 | int iter; 122 | 123 | /* --- Output Parameters --- */ 124 | 125 | string dataFileName; 126 | int resType; 127 | int monitorResFreq; 128 | int plotFreq; 129 | int plotType; 130 | 131 | bool calcEntropySensor; 132 | 133 | /* --- Boundary & Initial Condition Parameters --- */ 134 | double Uinf; 135 | double Vinf; 136 | double rhoinf; 137 | double Pinf; 138 | 139 | double rhoIC; 140 | double vxIC, vyIC, vzIC; 141 | double pIC; 142 | 143 | double rhoBound; 144 | double uBound, vBound, wBound; 145 | double pBound; 146 | 147 | double TWall; 148 | 149 | // Viscous Boundary Conditions / Initial Conditions 150 | double nxBound, nyBound, nzBound; 151 | double MachBound; 152 | double TBound; 153 | double TtBound; 154 | double PtBound; 155 | double muBound; 156 | 157 | double TIC; 158 | double muIC; 159 | 160 | bool slipPenalty; //! Use "penalty method" on slip-wall boundary 161 | 162 | /* --- Misc. Physical/Equation Parameters --- */ 163 | double gamma; 164 | double prandtl; 165 | double mu; 166 | double RGas; 167 | 168 | double advectVx; //! Advection speed, x-direction 169 | double advectVy; //! Advection speed, y-direction 170 | double advectVz; //! Advection speed, z-direction 171 | double diffD; //! Diffusion constant 172 | double lambda; //! Lax-Friedrichs upwind coefficient (0: Central, 1: Upwind) 173 | 174 | /* --- Mesh Parameters --- */ 175 | string meshFileName; 176 | vector oversetGrids; 177 | int meshType; 178 | int nx, ny, nz; 179 | int nGrids; 180 | double xmin, xmax, ymin, ymax, zmin, zmax; 181 | double periodicTol, periodicDX, periodicDY, periodicDZ; 182 | string create_bcTop, create_bcBottom, create_bcLeft; 183 | string create_bcRight, create_bcFront, create_bcBack; //! BC's to apply to Flurry-created mesh 184 | map meshBounds; 185 | 186 | /* --- FR Parameters --- */ 187 | string sptsTypeTri; //! Legendre, Lobatto, ... 188 | string sptsTypeQuad; 189 | int vcjhSchemeTri; 190 | int vcjhSchemeQuad; 191 | 192 | /* --- Shock Capturing Parameters --- */ 193 | int scFlag; // Shock Capturing Flag 194 | double threshold; // Threshold for considering as shock -Set to 1.0 by default 195 | 196 | /* --- PID Boundary Conditions --- */ 197 | double Kp; 198 | double Kd; 199 | double Ki; 200 | 201 | /* --- Other --- */ 202 | int rank; 203 | int nproc; 204 | 205 | private: 206 | fileReader opts; 207 | }; 208 | -------------------------------------------------------------------------------- /include/matrix.hpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file matrix.hpp 3 | * \brief Header file for matrix class 4 | * 5 | * The matrix class is really just a handy wrapper for a vector>, with 6 | * some functions for setting up and multiplying 7 | * 8 | * Yes, I'm well aware that I really should just be using boost::multi_array 9 | * instead for loads of reasons, but this was more fun. Don't judge me. 10 | * 11 | * \author - Jacob Crabill 12 | * Aerospace Computing Laboratory (ACL) 13 | * Aero/Astro Department. Stanford University 14 | * 15 | * \version 0.0.1 16 | * 17 | * Flux Reconstruction in C++ (Flurry++) Code 18 | * Copyright (C) 2014 Jacob Crabill. 19 | * 20 | */ 21 | #pragma once 22 | 23 | #include 24 | #include // for setw, setprecision 25 | #include 26 | #include 27 | 28 | #include "error.hpp" 29 | 30 | struct point; 31 | 32 | #include "global.hpp" 33 | 34 | #define INSERT_AT_END -1 35 | 36 | using namespace std; 37 | 38 | typedef unsigned int uint; 39 | 40 | template 41 | class matrixBase 42 | { 43 | public: 44 | /* --- Constructors, destructors --- */ 45 | //! Default Constructor 46 | matrixBase(); 47 | 48 | //! Secondary Constructor with Size Allocation 49 | matrixBase(uint inDim0, uint inDim1=1, uint inDim2=1, uint inDim3=1); 50 | 51 | //! Copy Constructor 52 | matrixBase(const matrixBase& inMatrix); 53 | 54 | //! Assignment 55 | matrixBase operator=(const matrixBase& inMatrix); 56 | 57 | /*! Get dim0 [number of rows] */ 58 | uint getDim0(void) {return this->dims[0];} 59 | 60 | /*! Get dim1 [number of columns] */ 61 | uint getDim1(void) {return this->dims[1];} 62 | 63 | /*! Get the size of the underlying data array (total number of matrix elements) */ 64 | uint getSize(void) {return data.size();} 65 | 66 | /* --- Member Functions --- */ 67 | 68 | void setup(uint inDim0, uint inDim1=1, uint inDim2=1, uint inDim3=1); 69 | 70 | void insertRow(const vector &vec, int rowNum); 71 | 72 | /*! Prints the contents of the matrix to the console */ 73 | void print(void); 74 | 75 | /* --- Data-Access Operators --- */ 76 | 77 | /*! Returns a pointer to the first element of row inDim0 */ 78 | T* operator[](int inDim0); 79 | 80 | /*! Standard (i,j) access operator */ 81 | T &operator()(int i, int j=0, int k=0, int l=0); 82 | 83 | /*! Returns the .data() pointer of the underlying vector data */ 84 | T* getData(); 85 | 86 | /* --- Search Operations --- */ 87 | 88 | /* --- Member Variables --- */ 89 | //uint dim0, dim1; 90 | uint nDims; 91 | array dims; //! Dimensions of the matrix 92 | 93 | vector data; 94 | }; 95 | 96 | template 97 | class Array : public matrixBase { }; 98 | 99 | template 100 | class matrix : public matrixBase { 101 | public: 102 | 103 | matrix(); 104 | 105 | matrix(uint inDim0, uint inDim1); 106 | 107 | /*! Standard (i,j) access operator */ 108 | T &operator()(int i, int j); 109 | 110 | void initializeToZero(void); 111 | 112 | void initializeToValue(T val); 113 | 114 | //! Insert a column at the end of the matrix 115 | void addCol(void); 116 | 117 | //! Insert a column at the end of the matrix 118 | void addCols(int nCols); 119 | 120 | /*! Insert a row into the matrix at location rowNum [zero-indexed], with the default being at the end */ 121 | void insertRow(const vector &vec, int rowNum = -1); 122 | 123 | void insertRowUnsized(const vector &vec); 124 | 125 | void insertRow(T* vec, uint rowNum, uint length); 126 | 127 | void insertRowUnsized(T* vec, int length); 128 | 129 | //! Remove columns from the end of the matrix 130 | void removeCols(int nCols = 1); 131 | 132 | vector getRow(uint row); 133 | 134 | //! Return a sub-matrix view of the matrix using the rows in ind 135 | matrix getRows(vector ind); 136 | 137 | vector getCol(int col); 138 | 139 | /*! Find all unique 'rows' in a matrix */ 140 | void unique(matrix &out, vector &iRow); 141 | }; 142 | -------------------------------------------------------------------------------- /include/output.hpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file output.hpp 3 | * \brief Header file for restart & visualization data output functions 4 | * 5 | * \author - Jacob Crabill 6 | * Aerospace Computing Laboratory (ACL) 7 | * Aero/Astro Department. Stanford University 8 | * 9 | * \version 0.0.1 10 | * 11 | * Flux Reconstruction in C++ (Flurry++) Code 12 | * Copyright (C) 2014 Jacob Crabill. 13 | * 14 | */ 15 | #pragma once 16 | 17 | #include "global.hpp" 18 | #include "solver.hpp" 19 | #include "geo.hpp" 20 | 21 | /*! Write solution to file (of type params->plotType) */ 22 | void writeData(solver *Solver, input *params); 23 | 24 | /*! Write solution data to a CSV file. */ 25 | void writeCSV(solver *Solver, input *params); 26 | 27 | /*! Write solution data to a Paraview .vtu file. */ 28 | void writeParaview(solver *Solver, geo* Geo, input *params); 29 | 30 | /*! Compute the residual and print to the screen. */ 31 | void writeResidual(solver *Solver, input *params); 32 | 33 | /*! Write a Tecplot mesh file compatible with TIOGA's testTioga FORTRAN interface */ 34 | void writeMeshTecplot(solver* Solver, input* params); 35 | -------------------------------------------------------------------------------- /include/solver.hpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file solver.cpp 3 | * \brief Class to store all solution data & apply FR operators 4 | * 5 | * \author - Jacob Crabill 6 | * Aerospace Computing Laboratory (ACL) 7 | * Aero/Astro Department. Stanford University 8 | * 9 | * \version 0.0.1 10 | * 11 | * Flux Reconstruction in C++ (Flurry++) Code 12 | * Copyright (C) 2014 Jacob Crabill. 13 | * 14 | */ 15 | #pragma once 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | class oper; 23 | 24 | #include "global.hpp" 25 | 26 | #include "geo.hpp" 27 | #include "input.hpp" 28 | 29 | //class tioga; 30 | 31 | //#include "tioga.h" 32 | 33 | class solver 34 | { 35 | friend class geo; // Probably only needed if I make eles, opers private? 36 | 37 | public: 38 | /* === Member Variables === */ 39 | //! Pointer to geometry object for mesh-related operations 40 | geo *Geo; 41 | 42 | //! Pointer to Tioga object for processing overset grids 43 | //tioga* tg; 44 | 45 | /* === Setup Functions === */ 46 | 47 | solver(); 48 | 49 | ~solver(); 50 | 51 | //! Setup the solver with the given simulation parameters & geometry 52 | void setup(input *params, geo *Geo); 53 | 54 | //! Setup the FR operators for all ele types and polynomial orders which will be used in computation 55 | void setupOperators(); 56 | 57 | //! Finish setting up all dual-mesh data structures 58 | void setupDualMesh(); 59 | 60 | //! If restarting from data file, read data and setup eles & faces accordingly 61 | void readRestartFile(); 62 | 63 | //! Finish setting up the MPI faces 64 | void finishMpiSetup(void); 65 | 66 | /* === Functions Related to Basic FR Process === */ 67 | 68 | //! Apply the initial condition to all elements 69 | void initializeSolution(); 70 | 71 | void update(void); 72 | 73 | //! Perform one full step of computation 74 | void calcResidual(int step); 75 | 76 | //! Calculate the stable time step limit based upon given CFL 77 | void calcDt(void); 78 | 79 | //! Advance solution in time - Generate intermediate RK stage 80 | void timeStepA(int step); 81 | 82 | //! Advance solution in time - Final RK stage [assemble intermediate stages] 83 | void timeStepB(int step); 84 | 85 | //! For RK time-stepping - store solution at time 'n' 86 | void copyU_U0(void); 87 | 88 | //! For RK time-stepping - recover solution at time 'n' before advancing to 'n+1' 89 | void copyU0_U(void); 90 | 91 | //! Calculate the inviscid flux at all edges 92 | void calcInviscidFlux(void); 93 | 94 | //! Apply boundary conditions to all boundary nodes 95 | void applyBoundaryConditions(void); 96 | 97 | //! Calculate the inviscid interface flux at all MPI edges 98 | void calcInviscidFlux_mpi(void); 99 | 100 | //! Have all MPI faces begin their communication 101 | void doCommunication(void); 102 | 103 | //! Calculate the gradient of the solution at the solution points 104 | void calcGradU(void); 105 | 106 | //! Calculate the viscous flux at all edges 107 | void calcViscousFlux(void); 108 | 109 | //! Calculate the viscous interface flux at all MPI edges 110 | void calcViscousFlux_mpi(void); 111 | 112 | //! Wrapper to calc divergence of flux (using one of two possible methods) 113 | void calcFluxDivergence(int step); 114 | 115 | //! Apply mesh motion 116 | void moveMesh(int step); 117 | 118 | 119 | /* === Functions for Shock Capturing & Filtering=== */ 120 | 121 | //! Use concentration sensor + exponential modal filter to capture discontinuities 122 | void shockCapture(void); 123 | 124 | /* === Functions Related to Adaptation === */ 125 | 126 | //! Calculate an entropy-adjoint-based error indicator 127 | void calcEntropyErr_spts(); 128 | 129 | /* === Functions Related to Overset Grids === */ 130 | 131 | //! Do initial preprocessing for overset grids 132 | void setupOverset(); 133 | 134 | /*! 135 | * \brief Initialize overset-related data storage 136 | * 137 | * Allocates global storage for overset data. Re-call if adding elements, 138 | * changing polynomial orders, etc. 139 | */ 140 | void setupOversetData(); 141 | 142 | //! Perform the overset data interpolation using TIOGA high-order 143 | void callDataUpdateTIOGA(); 144 | 145 | //private: 146 | //! Pointer to the parameters object for the current solution 147 | input *params; 148 | 149 | //! Set of all element types present in current simulation 150 | set eTypes; 151 | 152 | //! Lists of cells to apply various adaptation methods to 153 | vector r_adapt_cells, h_adapt_cells, p_adapt_cells; 154 | 155 | int nRKSteps; 156 | 157 | int gridID, gridRank, nprocPerGrid; 158 | 159 | vector RKa, RKb; 160 | 161 | /* --- Solution Variables --- */ 162 | 163 | int nDims; 164 | int nFields; 165 | int nVerts; 166 | int nEdges; 167 | 168 | matrix U; //! Global solution array for solver (at vertices) 169 | matrix U0; //! Global solution array at beginning of RK step 170 | matrix A; //! Area of dual-mesh faces around each vertex 171 | vector vol; //! Volume of dual-mesh elements 172 | Array F; //! Global flux array for solver (at edges) 173 | matrix Fn; //! Global normal flux array for solver (at edges) 174 | Array divF; //! Divergence of flux (at vertices) (for each RK step) 175 | Array gradU; //! Gradient of solution (at vertices) 176 | vector waveSp; //! Wave speed at each edge 177 | 178 | matrix tempFL, tempFR; 179 | 180 | matrix xv; //! Coordinates of each vertex 181 | 182 | matrix normDir; //! Direction of normal flux for each each around each vertex (+1 or -1) 183 | vector Ae; //! Face area for all dual-mesh faces 184 | vector v2ne; //! Number of edges (or dual-mesh faces) for each vertex 185 | matrix v2e; //! Vertex to edge connectivity 186 | matrix v2v; //! Vertex to vertex connectivity 187 | 188 | int nBounds; 189 | vector bcList; //! Boundary condition for each boundary 190 | matrix bndPts; //! List of boundary nodes on each boundary 191 | vector nBndPts; //! List of boundary nodes on each boundary 192 | //vector bcType; //! Boundary condition for each boundary node 193 | Array bndNorm; //! Unit normal at all boundary nodes 194 | matrix bndArea; //! Face area at all boundary nodes 195 | 196 | /* ---- Overset Grid Variables / Functions ---- */ 197 | 198 | 199 | }; 200 | -------------------------------------------------------------------------------- /include/tempest.hpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file flurry.hpp 3 | * \brief Header file for functions to run a whole simulation 4 | * 5 | * \author - Jacob Crabill 6 | * Aerospace Computing Laboratory (ACL) 7 | * Aero/Astro Department. Stanford University 8 | * 9 | * \version 0.0.1 10 | * 11 | * Flux Reconstruction in C++ (Flurry++) Code 12 | * Copyright (C) 2014 Jacob Crabill. 13 | * 14 | */ 15 | #pragma once 16 | 17 | #include "global.hpp" 18 | #include "input.hpp" 19 | #include "geo.hpp" 20 | #include "solver.hpp" 21 | #include "output.hpp" 22 | 23 | int main(int argc, char *argv[]); 24 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | ############################################################################# 2 | # Makefile for building Flurry 3 | # Command: make debug 4 | # make release 5 | # make openmp 6 | ############################################################################# 7 | 8 | ####### Compiler, tools and options 9 | 10 | CXX = g++ 11 | F90 = mpif90 12 | LINK = g++ 13 | MPICXX = mpicxx 14 | MPILD = mpicxx 15 | LIBS = $(SUBLIBS) 16 | 17 | TARGET = Tempest 18 | 19 | # Location of libmetis.a, metis.h 20 | METIS_LIB_DIR = /usr/local/lib/ 21 | METIS_INC_DIR = /usr/local/include/ 22 | 23 | # Location of mpi.h 24 | MPI_INC_DIR = /usr/lib/openmpi/include 25 | 26 | # Location of libtioga.a 27 | TIOGA_INC = ./lib/tioga/src 28 | TIOGA_LIB = #./lib/tioga/src/libtioga.a 29 | 30 | CXX_BASE = -pipe -Wunused-parameter -Wuninitialized -std=c++11 -I./include -I$(TIOGA_INC) $(DEFINES) 31 | CXX_STD = -g -02 32 | CXX_DEBUG = -g -pg -O0 -rdynamic #-fsanitize=address -fno-omit-frame-pointer 33 | CXX_RELEASE = -O3 34 | 35 | CXXFLAGS_RELEASE = $(CXX_BASE) $(CXX_RELEASE) -Wno-unknown-pragmas -D_NO_MPI $(DEFINES) 36 | CXXFLAGS_DEBUG = $(CXX_BASE) $(CXX_DEBUG) -Wno-unknown-pragmas -D_NO_MPI $(DEFINES) 37 | CXXFLAGS_OPENMP = $(CXX_BASE) $(CXX_RELEASE) -fopenmp -D_NO_MPI $(DEFINES) 38 | CXXFLAGS_MPI = $(CXX_BASE) $(DEFINES) -Wno-literal-suffix 39 | CXXFLAGS_MPI += -I$(METIS_INC_DIR) -I$(MPI_INC_DIR) 40 | CXXFLAGS_MPI += -L$(METIS_LIB_DIR) 41 | 42 | ####### Output directory - these do nothing currently 43 | 44 | OBJECTS_DIR = ./obj 45 | DESTDIR = ./bin 46 | 47 | ####### Files 48 | 49 | SOURCES = src/global.cpp \ 50 | src/matrix.cpp \ 51 | src/input.cpp \ 52 | src/geo.cpp \ 53 | src/output.cpp \ 54 | src/solver_bounds.cpp \ 55 | src/flux.cpp \ 56 | rc/tempest.cpp \ 57 | src/solver.cpp 58 | 59 | TIOGA_SRC = lib/tioga/src/ADT.C \ 60 | lib/tioga/src/MeshBlock.C \ 61 | lib/tioga/src/parallelComm.C \ 62 | lib/tioga/src/tioga.C \ 63 | lib/tioga/src/utils.c \ 64 | lib/tioga/src/kaiser.f \ 65 | lib/tioga/src/cellVolume.f90 \ 66 | lib/tioga/src/median.F90 67 | 68 | OBJECTS = obj/global.o \ 69 | obj/matrix.o \ 70 | obj/input.o \ 71 | obj/geo.o \ 72 | obj/output.o \ 73 | obj/flux.o \ 74 | obj/tempest.o \ 75 | obj/solver.o \ 76 | obj/solver_bounds.o 77 | 78 | TIOGA_OBJ = obj/ADT.o \ 79 | obj/MeshBlock.o \ 80 | obj/parallelComm.o \ 81 | obj/utils.o \ 82 | obj/tioga.o \ 83 | obj/kaiser.o \ 84 | obj/median.o \ 85 | obj/cellVolume.o 86 | 87 | ####### Implicit rules 88 | 89 | .cpp.o: 90 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" 91 | 92 | .f90.o: 93 | $(F90) -c $(FFLAGS) $(INCPATH) -o "$@" "$<" 94 | 95 | .F90.o: 96 | $(F90) -c $(FFLAGS) $(INCPATH) -o "$@" "$<" 97 | 98 | ####### Build rules 99 | 100 | $(TARGET): $(OBJECTS) 101 | $(LINK) $(LFLAGS) -o $(DESTDIR)/$(TARGET) $(OBJECTS) $(OBJCOMP) $(LIBS) $(DBG) 102 | 103 | clean: 104 | cd obj && rm -f *.o && cd .. && rm -f bin/Tempest 105 | 106 | .PHONY: debug 107 | debug: DBG=-pg 108 | debug: CXXFLAGS=$(CXXFLAGS_DEBUG) 109 | debug: LIBS+= #-lasan 110 | debug: $(TARGET) 111 | 112 | .PHONY: release 113 | release: CXXFLAGS=$(CXXFLAGS_RELEASE) 114 | release: $(TARGET) 115 | 116 | .PHONY: openmp 117 | openmp: CXXFLAGS=$(CXXFLAGS_OPENMP) 118 | openmp: LIBS+= -fopenmp -lgomp 119 | openmp: $(TARGET) 120 | 121 | .PHONY: mpi 122 | mpi: CXX=$(MPICXX) 123 | mpi: LINK=$(MPILD) 124 | mpi: CXXFLAGS=$(CXXFLAGS_MPI) $(CXX_RELEASE) 125 | mpi: FFLAGS=-O3 126 | mpi: LIBS+= -lmetis $(TIOGA_LIB) 127 | mpi: $(TARGET) 128 | 129 | .PHONY: mpidebug 130 | mpidebug: CXX=$(MPICXX) 131 | mpidebug: LINK=$(MPILD) 132 | mpidebug: CXXFLAGS=$(CXXFLAGS_MPI) $(CXX_DEBUG) 133 | mpidebug: FFLAGS=-g -O1 -rdynamic #-fsanitize=address -fno-omit-frame-pointer 134 | mpidebug: LIBS+= -lmetis $(TIOGA_LIB) #-lasan 135 | mpidebug: $(TARGET) 136 | 137 | ####### Compile 138 | 139 | obj/global.o: src/global.cpp include/global.hpp \ 140 | include/error.hpp \ 141 | include/matrix.hpp 142 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o obj/global.o src/global.cpp 143 | 144 | obj/funcs.o: src/funcs.cpp include/funcs.hpp include/global.hpp 145 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o obj/funcs.o src/funcs.cpp 146 | 147 | obj/matrix.o: src/matrix.cpp include/matrix.hpp \ 148 | include/error.hpp 149 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o obj/matrix.o src/matrix.cpp 150 | 151 | obj/input.o: src/input.cpp include/input.hpp \ 152 | include/global.hpp \ 153 | include/error.hpp \ 154 | include/matrix.hpp 155 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o obj/input.o src/input.cpp 156 | 157 | obj/geo.o: src/geo.cpp include/geo.hpp \ 158 | include/global.hpp \ 159 | include/error.hpp \ 160 | include/matrix.hpp \ 161 | include/input.hpp \ 162 | include/solver.hpp 163 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o obj/geo.o src/geo.cpp 164 | 165 | obj/output.o: src/output.cpp include/output.hpp \ 166 | include/global.hpp \ 167 | include/error.hpp \ 168 | include/matrix.hpp \ 169 | include/solver.hpp \ 170 | include/geo.hpp \ 171 | include/input.hpp 172 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o obj/output.o src/output.cpp 173 | 174 | obj/solver_bounds.o: src/solver_bounds.cpp include/solver.hpp 175 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o obj/solver_bounds.o src/solver_bounds.cpp 176 | 177 | obj/flux.o: src/flux.cpp include/flux.hpp \ 178 | include/global.hpp \ 179 | include/error.hpp \ 180 | include/matrix.hpp \ 181 | include/input.hpp 182 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o obj/flux.o src/flux.cpp 183 | 184 | obj/tempest.o: src/tempest.cpp include/tempest.hpp \ 185 | include/global.hpp \ 186 | include/error.hpp \ 187 | include/matrix.hpp \ 188 | include/input.hpp \ 189 | include/geo.hpp \ 190 | include/solver.hpp \ 191 | include/output.hpp 192 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o obj/tempest.o src/tempest.cpp 193 | 194 | obj/solver.o: src/solver.cpp include/solver.hpp \ 195 | include/global.hpp \ 196 | include/error.hpp \ 197 | include/matrix.hpp \ 198 | include/geo.hpp \ 199 | include/input.hpp 200 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o obj/solver.o src/solver.cpp 201 | 202 | obj/ADT.o: lib/tioga/src/ADT.C lib/tioga/src/ADT.h \ 203 | lib/tioga/src/codetypes.h \ 204 | lib/tioga/src/utils.h 205 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o obj/ADT.o lib/tioga/src/ADT.C 206 | 207 | obj/utils.o: lib/tioga/src/utils.c lib/tioga/src/utils.h 208 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o obj/utils.o lib/tioga/src/utils.c 209 | 210 | obj/parallelComm.o: lib/tioga/src/parallelComm.C lib/tioga/src/parallelComm.h \ 211 | lib/tioga/src/codetypes.h 212 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o obj/parallelComm.o lib/tioga/src/parallelComm.C 213 | 214 | obj/MeshBlock.o: lib/tioga/src/MeshBlock.C lib/tioga/src/MeshBlock.h \ 215 | lib/tioga/src/codetypes.h \ 216 | lib/tioga/src/utils.h \ 217 | lib/tioga/src/ADT.h \ 218 | lib/tioga/src/parallelComm.h \ 219 | include/solver.hpp 220 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o obj/MeshBlock.o lib/tioga/src/MeshBlock.C 221 | 222 | obj/tioga.o: lib/tioga/src/tioga.C lib/tioga/src/tioga.h \ 223 | lib/tioga/src/codetypes.h \ 224 | lib/tioga/src/utils.h \ 225 | lib/tioga/src/parallelComm.h \ 226 | lib/tioga/src/MeshBlock.h 227 | $(CXX) -c $(CXXFLAGS) $(INCPATH) -o obj/tioga.o lib/tioga/src/tioga.C 228 | 229 | obj/kaiser.o: lib/tioga/src/kaiser.f 230 | $(F90) -c $(FFLAGS) $(INCPATH) -o obj/kaiser.o lib/tioga/src/kaiser.f 231 | 232 | obj/cellVolume.o: lib/tioga/src/cellVolume.f90 233 | $(F90) -c $(FFLAGS) $(INCPATH) -o obj/cellVolume.o lib/tioga/src/cellVolume.f90 234 | 235 | obj/median.o: lib/tioga/src/median.F90 236 | $(F90) -c $(FFLAGS) $(INCPATH) -o obj/median.o lib/tioga/src/median.F90 237 | -------------------------------------------------------------------------------- /src/flux.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file flux.cpp 3 | * \brief Functions for calculation of fluxes 4 | * 5 | * Includes functions for Euler and Navier-Stokes fluxes, as well as common flux 6 | * / Riemann solve routines 7 | * 8 | * \author - Jacob Crabill 9 | * Aerospace Computing Laboratory (ACL) 10 | * Aero/Astro Department. Stanford University 11 | * 12 | * \version 0.0.1 13 | * 14 | * Flux Reconstruction in C++ (Flurry++) Code 15 | * Copyright (C) 2014 Jacob Crabill. 16 | * 17 | */ 18 | 19 | #include "../include/flux.hpp" 20 | 21 | #include 22 | #include 23 | 24 | void inviscidFlux(double* U, matrix &F, input *params) 25 | { 26 | /* --- Note: Flux matrix expected to be --- */ 27 | if (params->equation == ADVECTION_DIFFUSION) { 28 | F(0,0) = params->advectVx*U[0]; 29 | F(1,0) = params->advectVy*U[0]; 30 | if (params->nDims == 3) 31 | F(2,0) = params->advectVz*U[0]; 32 | } 33 | else if (params->equation == NAVIER_STOKES) { 34 | if (params->nDims == 2) { 35 | double rho = U[0]; 36 | double u = U[1]/rho; 37 | double v = U[2]/rho; 38 | double p = (params->gamma-1.0)*(U[3]-(0.5*rho*((u*u)+(v*v)))); 39 | 40 | /* --- Assuming F has already been sized properly... --- */ 41 | F(0,0) = U[1]; F(1,0) = U[2]; 42 | F(0,1) = U[1]*u+p; F(1,1) = U[1]*v; 43 | F(0,2) = U[2]*u; F(1,2) = U[2]*v+p; 44 | F(0,3) = (U[3]+p)*u; F(1,3) = (U[3]+p)*v; 45 | } 46 | else if (params->nDims == 3) { 47 | double rho = U[0]; 48 | double u = U[1]/rho; 49 | double v = U[2]/rho; 50 | double w = U[3]/rho; 51 | double p = (params->gamma-1.0)*(U[4]-(0.5*rho*((u*u)+(v*v)+(w*w)))); 52 | 53 | /* --- Assuming F has already been sized properly... --- */ 54 | F(0,0) = U[1]; F(1,0) = U[2]; F(2,0) = U[3]; 55 | F(0,1) = U[1]*u+p; F(1,1) = U[1]*v; F(2,1) = U[1]*w; 56 | F(0,2) = U[2]*u; F(1,2) = U[2]*v+p; F(2,2) = U[2]*w; 57 | F(0,3) = U[3]*u; F(1,3) = U[3]*v; F(2,3) = U[3]*w+p; 58 | F(0,4) = (U[4]+p)*u; F(1,4) = (U[4]+p)*v; F(2,4) = (U[4]+p)*w; 59 | } 60 | } 61 | } 62 | 63 | 64 | void viscousFlux(double* U, matrix &gradU, matrix &Fvis, input *params) 65 | { 66 | int nDims = params->nDims; 67 | 68 | /* --- Calculate Primitives --- */ 69 | double rho = U[0]; 70 | double u = U[1]/rho; 71 | double v = U[2]/rho; 72 | double e = U[nDims+1]/rho - 0.5*(u*u+v*v); 73 | 74 | double w; 75 | if (nDims == 3) { 76 | w = U[3]/rho; 77 | e -= 0.5*(w*w); 78 | } 79 | else { 80 | w = 0.; 81 | } 82 | 83 | /* --- Get Gradients --- */ 84 | double dRho_dx = gradU(0,0); 85 | double dRhoU_dx = gradU(0,1); 86 | double dRhoV_dx = gradU(0,2); 87 | double dE_dx = gradU(0,nDims+1); 88 | 89 | double dRho_dy = gradU(1,0); 90 | double dRhoU_dy = gradU(1,1); 91 | double dRhoV_dy = gradU(1,2); 92 | double dE_dy = gradU(1,nDims+1); 93 | 94 | // 3D Derivatives 95 | double dRho_dz = 0; 96 | double dRhoU_dz = 0; 97 | double dRhoV_dz = 0; 98 | double dRhoW_dx = 0; 99 | double dRhoW_dy = 0; 100 | double dRhoW_dz = 0; 101 | double dE_dz = 0; 102 | if (nDims == 3) { 103 | dRho_dz = gradU(2,0); 104 | dRhoU_dz = gradU(2,1); 105 | dRhoV_dz = gradU(2,2); 106 | dRhoW_dx = gradU(0,3); 107 | dRhoW_dy = gradU(1,3); 108 | dRhoW_dz = gradU(2,3); 109 | dE_dz = gradU(2,4); 110 | } 111 | 112 | /* --- Calculate Viscosity --- */ 113 | double rt_ratio = (params->gamma-1.0)*e/(params->rt_inf); 114 | double mu = (params->mu_inf)*pow(rt_ratio,1.5)*(1.+(params->c_sth))/(rt_ratio+(params->c_sth)); 115 | mu+= params->fix_vis*(params->mu_inf - mu); 116 | 117 | double mu_t = 0.; 118 | 119 | /* --- Calculate Gradients --- */ 120 | double du_dx = (dRhoU_dx-dRho_dx*u)/rho; 121 | double du_dy = (dRhoU_dy-dRho_dy*u)/rho; 122 | 123 | double dv_dx = (dRhoV_dx-dRho_dx*v)/rho; 124 | double dv_dy = (dRhoV_dy-dRho_dy*v)/rho; 125 | 126 | // 3D Derivatives 127 | double du_dz=0, dv_dz=0; 128 | double dw_dx=0, dw_dy=0; 129 | double dw_dz = 0; 130 | if (nDims == 3) { 131 | du_dz = (dRhoU_dz-dRho_dz*u)/rho; 132 | dv_dz = (dRhoV_dz-dRho_dz*v)/rho; 133 | 134 | dw_dx = (dRhoW_dx-dRho_dx*w)/rho; 135 | dw_dy = (dRhoW_dy-dRho_dy*w)/rho; 136 | dw_dz = (dRhoW_dz-dRho_dz*w)/rho; 137 | } 138 | 139 | double dK_dx, dK_dy, dK_dz; 140 | if (nDims == 2) { 141 | dK_dx = 0.5*(u*u+v*v)*dRho_dx+rho*(u*du_dx+v*dv_dx); 142 | dK_dy = 0.5*(u*u+v*v)*dRho_dy+rho*(u*du_dy+v*dv_dy); 143 | dK_dz = 0; 144 | } 145 | else { 146 | dK_dx = 0.5*(u*u+v*v+w*w)*dRho_dx+rho*(u*du_dx+v*dv_dx+w*dw_dx); 147 | dK_dy = 0.5*(u*u+v*v+w*w)*dRho_dy+rho*(u*du_dy+v*dv_dy+w*dw_dy); 148 | dK_dz = 0.5*(u*u+v*v+w*w)*dRho_dz+rho*(u*du_dz+v*dv_dz+w*dw_dz); 149 | } 150 | 151 | double de_dx = (dE_dx-dK_dx-dRho_dx*e)/rho; 152 | double de_dy = (dE_dy-dK_dy-dRho_dy*e)/rho; 153 | double de_dz = 0; 154 | if (nDims == 3) 155 | de_dz = (dE_dz-dK_dz-dRho_dz*e)/rho; 156 | 157 | double diag = (du_dx + dv_dy + dw_dz)/3.0; 158 | 159 | double tauxx = 2.0*(mu+mu_t)*(du_dx-diag); 160 | double tauyy = 2.0*(mu+mu_t)*(dv_dy-diag); 161 | 162 | double tauxy = (mu+mu_t)*(du_dy + dv_dx); 163 | 164 | double tauxz = 0; 165 | double tauyz = 0; 166 | double tauzz = 0; 167 | if (nDims == 3) { 168 | tauxz = (mu+mu_t)*(du_dz + dv_dx); 169 | tauyz = (mu+mu_t)*(du_dz + dv_dy); 170 | tauzz = 2.0*(mu+mu_t)*(dw_dz-diag); 171 | } 172 | 173 | /* --- Calculate Viscous Flux --- */ 174 | Fvis(0,0) = 0.0; 175 | Fvis(0,1) = -tauxx; 176 | Fvis(0,2) = -tauxy; 177 | Fvis(0,nDims+1) = -(u*tauxx+v*tauxy+w*tauxz+(mu/params->prandtl)*(params->gamma)*de_dx); 178 | 179 | Fvis(1,0) = 0.0; 180 | Fvis(1,1) = -tauxy; 181 | Fvis(1,2) = -tauyy; 182 | Fvis(1,nDims+1) = -(u*tauxy+v*tauyy+w*tauyz+(mu/params->prandtl)*(params->gamma)*de_dy); 183 | 184 | if (nDims == 3) { 185 | Fvis(0,3) = -tauzz; 186 | Fvis(1,3) = -tauyz; 187 | 188 | Fvis(2,0) = 0.0; 189 | Fvis(2,1) = -tauxz; 190 | Fvis(2,2) = -tauyz; 191 | Fvis(2,3) = -tauzz; 192 | Fvis(2,4) = -(u*tauxz+v*tauyz+w*tauzz+(mu/params->prandtl)*(params->gamma)*de_dz); 193 | } 194 | } 195 | 196 | void viscousFluxAD(matrix &gradU, matrix &Fvis, input *params) 197 | { 198 | Fvis(0,0) = -params->diffD * gradU(0,0); 199 | Fvis(1,0) = -params->diffD * gradU(1,0); 200 | if (params->nDims == 3) 201 | Fvis(2,0) = -params->diffD * gradU(2,0); 202 | } 203 | 204 | void centralFlux(double* uL, double* uR, double* norm, double* Fn, input *params) 205 | { 206 | if (params->equation == ADVECTION_DIFFUSION) { 207 | Fn[0] = params->advectVx*0.5*norm[0]*(uL[0]+uR[0]) 208 | + params->advectVy*0.5*norm[1]*(uL[0]+uR[0]); 209 | } 210 | else if (params->equation == NAVIER_STOKES) { 211 | FatalError("centralFlux not supported for Navier-Stokes simulations."); 212 | } 213 | } 214 | 215 | void centralFlux(matrix &FL, matrix &FR, double* norm, double* Fn, input *params) 216 | { 217 | for (int i=0; inFields; i++) { 218 | Fn[i] = 0; 219 | for (int j=0; jnDims; j++) { 220 | Fn[i] += 0.5*(FL[j][i]+FR[j][i])*norm[j]; 221 | } 222 | } 223 | } 224 | 225 | void upwindFlux(double* uL, double* uR, double* norm, double* Fn, input *params) 226 | { 227 | if (params->equation == ADVECTION_DIFFUSION) { 228 | double FL, FR, alpha; 229 | alpha = params->advectVx*norm[0]+params->advectVy*norm[1]; 230 | FL = alpha*uL[0]; 231 | FR = alpha*uR[0]; 232 | Fn[0] = 0.5*( FR+FL - alpha*(FR-FL) ); 233 | } 234 | } 235 | 236 | 237 | void ldgFlux(double* , double* , matrix &, matrix &, double* , input *) 238 | { 239 | FatalError("LDG flux not implemented just yet. Go to flux.cpp and do it!!"); 240 | } 241 | -------------------------------------------------------------------------------- /src/geo.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file geo.cpp 3 | * \brief Class for handling geometry setup & modification 4 | * 5 | * \author - Jacob Crabill 6 | * Aerospace Computing Laboratory (ACL) 7 | * Aero/Astro Department. Stanford University 8 | * 9 | * \version 0.0.1 10 | * 11 | * Flux Reconstruction in C++ (Flurry++) Code 12 | * Copyright (C) 2014 Jacob Crabill. 13 | * 14 | */ 15 | 16 | #include "../include/geo.hpp" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #ifndef _NO_MPI 28 | #include "mpi.h" 29 | #include "metis.h" 30 | #endif 31 | 32 | geo::geo() 33 | { 34 | nodesPerCell = NULL; 35 | } 36 | 37 | geo::~geo() 38 | { 39 | #ifndef _NO_MPI 40 | MPI_Comm_free(&gridComm); 41 | #endif 42 | 43 | if (nodesPerCell != NULL) 44 | delete nodesPerCell; 45 | } 46 | 47 | void geo::setup(input* params) 48 | { 49 | this->params = params; 50 | 51 | meshType = params->meshType; 52 | gridID = 0; 53 | gridRank = params->rank; 54 | nprocPerGrid = params->nproc; 55 | 56 | switch(meshType) { 57 | case READ_MESH: 58 | readGmsh(params->meshFileName); 59 | break; 60 | 61 | case CREATE_MESH: 62 | createMesh(); 63 | break; 64 | 65 | case OVERSET_MESH: 66 | // Find out which grid this process will be handling 67 | nprocPerGrid = params->nproc/params->nGrids; 68 | gridID = params->rank / nprocPerGrid; 69 | gridRank = params->rank % nprocPerGrid; 70 | readGmsh(params->oversetGrids[gridID]); 71 | break; 72 | 73 | default: 74 | FatalError("Mesh type not recognized."); 75 | } 76 | 77 | #ifndef _NO_MPI 78 | partitionMesh(); 79 | #endif 80 | 81 | processConnectivity(); 82 | 83 | processPeriodicBoundaries(); 84 | 85 | // if (meshType == OVERSET_MESH) { 86 | // registerGridDataTIOGA(); 87 | // } 88 | } 89 | 90 | 91 | void geo::processConnectivity() 92 | { 93 | if (params->rank==0) cout << "Geo: Processing element connectivity" << endl; 94 | 95 | processConnEdges(); 96 | processConnFaces(); 97 | processConnDual(); 98 | } 99 | 100 | void geo::processConnEdges(void) 101 | { 102 | /* --- Store Cell-Centers --- */ 103 | 104 | c2xc.resize(nEles); 105 | for (int e=0; e e2v1; 118 | vector edge(2); 119 | 120 | // Populate global list of edges in mesh 121 | for (int e=0; e iE; 184 | // First, organize all edges such that the lower vertex ID comes first 185 | for (int ie=0; ie e2v1(ie,1)) { 187 | int tmp = e2v1(ie,1); 188 | e2v1(ie,1) = e2v1(ie,0); 189 | e2v1(ie,0) = tmp; 190 | } 191 | } 192 | e2v1.unique(e2v,iE); 193 | nEdges = e2v.getDim0(); 194 | 195 | /* --- Generate Vertex-To-Edge/Vertex Connectivity --- */ 196 | 197 | vector> v2v_tmp(nVerts); 198 | vector> v2e_tmp(nVerts); 199 | for (int ie=0; ie f2v1; 364 | vector f2nv1; 365 | 366 | // Handy map to store local face-vertex lists for each ele type 367 | map> ct2fv; 368 | map> ct2fnv; 369 | // --- FIX ORDERING FOR FUTURE USE --- 370 | ct2fv[HEX].insertRow(vector{0,1,2,3}); // Bottom 371 | ct2fv[HEX].insertRow(vector{4,5,6,7}); // Top 372 | ct2fv[HEX].insertRow(vector{3,0,4,7}); // Left 373 | ct2fv[HEX].insertRow(vector{2,1,5,6}); // Right 374 | ct2fv[HEX].insertRow(vector{1,0,4,5}); // Front 375 | ct2fv[HEX].insertRow(vector{3,2,6,7}); // Back 376 | ct2fnv[HEX] = {4,4,4,4,4,4}; 377 | 378 | ct2fv[PRISM].insertRowUnsized(vector{0,1,2}); // Bottom 379 | ct2fv[PRISM].insertRowUnsized(vector{3,4,5}); // Top 380 | ct2fv[PRISM].insertRowUnsized(vector{0,1,4,3}); 381 | ct2fv[PRISM].insertRowUnsized(vector{0,3,5,2}); 382 | ct2fv[PRISM].insertRowUnsized(vector{1,2,5,4}); 383 | ct2fnv[PRISM] = {3,3,4,4,4}; 384 | 385 | ct2fv[PYRAMID].insertRowUnsized(vector{0,1,2,3}); // Bottom 386 | ct2fv[PYRAMID].insertRowUnsized(vector{0,1,4}); // Top 387 | ct2fv[PYRAMID].insertRowUnsized(vector{1,2,4}); 388 | ct2fv[PYRAMID].insertRowUnsized(vector{2,3,4}); 389 | ct2fv[PYRAMID].insertRowUnsized(vector{3,0,4}); 390 | ct2fnv[PYRAMID] = {4,3,3,3,3}; 391 | 392 | ct2fv[TET].insertRowUnsized(vector{0,1,2}); 393 | ct2fv[TET].insertRowUnsized(vector{1,0,3}); 394 | ct2fv[TET].insertRowUnsized(vector{2,1,3}); 395 | ct2fv[TET].insertRowUnsized(vector{0,2,3}); 396 | ct2fnv[TET] = {3,3,3,3}; 397 | 398 | for (int e=0; e facev(ct2fnv[ctype[e]][f]); 405 | for (int i=0; i0 && facev[i] == facev[i-1]) facev[i] = -1; 408 | } 409 | 410 | // Sort the vertices for easier comparison later 411 | std::sort(facev.begin(),facev.end()); 412 | f2v1.insertRowUnsized(facev); 413 | f2nv1.push_back(ct2fnv[ctype[e]][f]); 414 | } 415 | } 416 | 417 | /* --- Get a unique list of faces --- */ 418 | 419 | // NOTE: Could setup f2c here, but I already have another algorithm implemented later 420 | 421 | // iE is of length [original f2v1] with range [final f2v] 422 | // The number of times a face appears in iF is equal to 423 | // the number of cells that face touches 424 | vector iF; 425 | f2v1.unique(f2v,iF); 426 | nFaces = f2v.getDim0(); 427 | 428 | f2nv.resize(nFaces); 429 | for (uint i=0; i2) { 447 | stringstream ss; ss << i; 448 | string errMsg = "More than 2 cells for face " + ss.str(); 449 | FatalError(errMsg.c_str()); 450 | } 451 | else if (ff.size()==2) { 452 | // Internal Edge which has not yet been added 453 | intFaces.push_back(iF[i]); 454 | nIntFaces++; 455 | } 456 | else if (ff.size()==1) { 457 | // Boundary or MPI Edge 458 | bndFaces.push_back(iF[i]); 459 | isBnd[iF[i]] = true; 460 | nBndFaces++; 461 | } 462 | 463 | // Mark edges as completed 464 | vecAssign(iF,ff,-1); 465 | } 466 | } 467 | 468 | /* --- Match Boundary Faces to Boundary Conditions --- */ 469 | 470 | // For each vertex on each boundary, the surrounding boundary-face ID's 471 | matrixBase,2> bcV2F(nBounds,getMax(nBndPts)); 472 | matrix bcv2nf(nBounds,getMax(nBndPts)); 473 | bcv2nf.initializeToZero(); 474 | 475 | v2b.assign(nVerts,0); 476 | bcFaces.resize(nBounds); 477 | bcFaceList.resize(nBounds); 478 | nBcFaces.assign(nBounds,0); 479 | bcTypeF.assign(nBndFaces,-1); 480 | for (int i=0; i bfpts; 484 | for (int j=0; j facev(fnv); 529 | for (int i=0; i= 0) { 570 | if (f2c(ff,0) != ic) 571 | c2c(ic,j) = f2c(ff,0); 572 | else 573 | c2c(ic,j) = f2c(ff,1); 574 | c2nc[ic]++; 575 | } 576 | } 577 | } 578 | 579 | /* --- Compute (Outward) Face Normals at All Boundary Faces --- */ 580 | 581 | // Outward face normal for each boundary face 582 | matrixBase bcFaceNorm(nBounds,getMax(nBcFaces)); 583 | 584 | for (int bnd=0; bnd 0) { 634 | // Face normal is pointing into cell; flip 635 | norm *= -1; 636 | } 637 | else { 638 | // Face normal is pointing out of cell; keep direction 639 | //norm /= norm.norm(); 640 | } 641 | 642 | return norm; 643 | } 644 | 645 | Vec3 geo::getFaceNormalQuad(int faceID) 646 | { 647 | // Get the (approximate) face normal of an arbitrary 3D quadrilateral 648 | // by splitting into 2 triangles and averaging 649 | 650 | // Triangle #1 651 | point pt0 = point(xv[f2v(faceID,0)]); 652 | point pt1 = point(xv[f2v(faceID,1)]); 653 | point pt2 = point(xv[f2v(faceID,2)]); 654 | Vec3 a = pt1 - pt0; 655 | Vec3 b = pt2 - pt0; 656 | Vec3 norm1 = a.cross(b); // Face normal vector 657 | Vec3 dxc = c2xc[f2c(faceID,0)] - (pt0+pt1+pt2)/3.; // Face centroid to cell centroid 658 | if (norm1*dxc > 0) { 659 | // Face normal is pointing into cell; flip 660 | norm1 *= -1; 661 | } 662 | 663 | // Triangle #2 664 | pt0 = point(xv[f2v(faceID,3)]); 665 | a = pt1 - pt0; 666 | b = pt2 - pt0; 667 | Vec3 norm2 = a.cross(b); 668 | if (norm2*dxc > 0) { 669 | // Face normal is pointing into cell; flip 670 | norm2 *= -1; 671 | } 672 | 673 | // Average the two triangle's face outward normals 674 | Vec3 norm = (norm1+norm2)/2.; 675 | 676 | return norm; 677 | } 678 | 679 | void geo::processConnDual(void) 680 | { 681 | /* --- Setup Dual Mesh Faces --- */ 682 | 683 | e2A.resize(nEdges); 684 | v2vol.assign(nVerts,0); 685 | for (int i=0; i facePts; 689 | 690 | // Get the 'edges' of the dual-mesh face 691 | // Use similar concept to getting e2v: get unique 'edges' by putting lower 692 | // cell ID first 693 | // if (isBndEdge[i]) { 694 | // Have to do some special logic to get face-centers of boundary faces 695 | // int nmatched = 0; 696 | for (int j=0; jic && findFirst(e2c[i],ic2,e2nc[i]) != -1) { 722 | facePts.insertRow({c2xc[ic],c2xc[ic2]},INSERT_AT_END); 723 | } 724 | } 725 | } 726 | } 727 | // } 728 | // else { 729 | // // each 'edge' of the dual face (neighboring cell centers) 730 | // facePts.setup(e2nc[i],2); 731 | // int nmatched = 0; 732 | // for (int j=0; jic && findFirst(e2c[i],ic2,e2nc[i]) != -1) { 739 | // facePts(nmatched,0) = c2xc[ic]; 740 | // facePts(nmatched,1) = c2xc[ic2]; 741 | // nmatched++; 742 | // } 743 | // } 744 | // } 745 | 746 | // Get area of dual face 747 | for (int j=0; j dualEdges(e2nc[ie],2); // each 'edge' of the dual face (neighboring cell IDs) 784 | // int nmatched = 0; 785 | // for (int k=0; kic && findFirst(e2c[ie],ic2,e2nc[ie]) != -1) { 792 | // dualEdges(nmatched,0) = ic; 793 | // dualEdges(nmatched,1) = ic2; 794 | // nmatched++; 795 | // } 796 | // } 797 | // } 798 | 799 | // // Have the dual-mesh-face edges; get the dual-tet volumes 800 | // // http://mathworld.wolfram.com/Tetrahedron.html 801 | 802 | // point midPt = (point(xv[e2v(ie,0)])+point(xv[e2v(ie,1)]))/2.; 803 | // Vec3 a = midPt - vert; 804 | 805 | // for (int k=0; k bpts; 821 | // for (int j=0; jsetCommunicator(MPI_COMM_WORLD,params->rank,params->nproc); 840 | 841 | // // Setup iwall, iover (nodes on wall & overset boundaries) 842 | // iover.resize(0); 843 | // iwall.resize(0); 844 | // for (int ib=0; ibregisterGridData(gridID,nVerts,xv.getData(),iblank.data(),nwall,nover,iwall.data(), 869 | // iover.data(),ntypes,nodesPerCell,&nEles,&conn[0]); 870 | 871 | // // Give iblankCell to TIOGA for Flurry to access later 872 | // tg->set_cell_iblank(iblankCell.data()); 873 | //} 874 | 875 | //void geo::updateOversetConnectivity(void) 876 | //{ 877 | // // Pre-process the grids 878 | // tg->profile(); 879 | 880 | // // This appears to be needed in addition to the high-order-specific processing below? 881 | // tg->performConnectivity(); 882 | //} 883 | 884 | //void geo::writeOversetConnectivity(void) 885 | //{ 886 | // // Write out only the mesh with IBLANK info (no solution data) 887 | // tg->writeData(0,NULL,0); 888 | //} 889 | 890 | void geo::matchMPIFaces(void) 891 | { 892 | #ifndef _NO_MPI 893 | if (nprocPerGrid <= 1) return; 894 | 895 | if (gridRank == 0) { 896 | if (meshType == OVERSET_MESH) 897 | cout << "Geo: Grid block " << gridID << ": Matching MPI faces" << endl; 898 | else 899 | cout << "Geo: Matching MPI faces" << endl; 900 | } 901 | 902 | /* --- Split MPI Processes Based Upon gridID: Create MPI_Comm for each grid --- */ 903 | MPI_Comm_split(MPI_COMM_WORLD, gridID, params->rank, &gridComm); 904 | 905 | MPI_Comm_rank(gridComm,&gridRank); 906 | MPI_Comm_size(gridComm,&nprocPerGrid); 907 | 908 | // 1) Get a list of all the MPI faces on the processor 909 | // These will be all unassigned boundary faces (bcType == -1) - copy over to mpiEdges 910 | for (int i=0; i mpiFaceNodes; 933 | vector mpiFptr(nMpiFaces+1); 934 | for (int i=0; i nMpiFaces_proc(nprocPerGrid); 944 | MPI_Allgather(&nMpiFaces,1,MPI_INT,nMpiFaces_proc.data(),1,MPI_INT,gridComm); 945 | 946 | // 2 for 2D, 4 for 3D; recall that we're treating all elements as being linear, as 947 | // the extra nodes for quadratic edges or faces are unimportant for determining connectivity 948 | int maxNodesPerFace = (nDims==2) ? 2 : 4; 949 | int maxNMpiFaces = getMax(nMpiFaces_proc); 950 | matrix mpiFaceNodes_proc(nprocPerGrid,maxNMpiFaces*maxNodesPerFace); 951 | matrix mpiFptr_proc(nprocPerGrid,maxNMpiFaces+1); 952 | MPI_Allgather(mpiFaceNodes.getData(),mpiFaceNodes.getSize(),MPI_INT,mpiFaceNodes_proc.getData(),maxNMpiFaces*maxNodesPerFace,MPI_INT,gridComm); 953 | MPI_Allgather(mpiFptr.data(),mpiFptr.size(),MPI_INT,mpiFptr_proc.getData(),maxNMpiFaces+1,MPI_INT,gridComm); 954 | 955 | matrix mpiCells_proc, mpiLocF_proc; 956 | if (nDims == 3) { 957 | // Needed for 3D face-matching (to find relRot) 958 | mpiCells_proc.setup(nprocPerGrid,maxNMpiFaces); 959 | mpiLocF_proc.setup(nprocPerGrid,maxNMpiFaces); 960 | MPI_Allgather(mpiCells.data(),mpiCells.size(),MPI_INT,mpiCells_proc.getData(),maxNMpiFaces,MPI_INT,gridComm); 961 | MPI_Allgather(mpiLocF.data(),mpiLocF.size(),MPI_INT,mpiLocF_proc.getData(),maxNMpiFaces,MPI_INT,gridComm); 962 | } 963 | 964 | // Now that we have each processor's boundary nodes, start matching faces 965 | // Again, note that this is written for to be entirely general instead of 2D-specific 966 | // Find out what processor each face is adjacent to 967 | procR.resize(nMpiFaces); 968 | locF_R.resize(nMpiFaces); 969 | if (nDims == 3) { 970 | gIC_R.resize(nMpiFaces); 971 | mpiLocF_R.resize(nMpiFaces); 972 | } 973 | for (auto &P:procR) P = -1; 974 | 975 | vector tmpFace(maxNodesPerFace); 976 | vector myFace(maxNodesPerFace); 977 | for (int p=0; prank == 0) 1014 | //cout << "rank " << params->rank << ": "; 1015 | if (gridRank == 0) 1016 | cout << "Geo: Grid " << gridID << ": All MPI faces matched! nMpiFaces = " << nMpiFaces << endl; 1017 | 1018 | MPI_Barrier(gridComm); 1019 | 1020 | //cout << "rank " << params->rank << " leaving matchMPIFaces()" << endl; 1021 | #endif 1022 | } 1023 | 1024 | //void geo::setupElesFaces(vector &eles, vector> &faces, vector> &mpiFacesVec) 1025 | //{ 1026 | // if (nEles<=0) FatalError("Cannot setup elements array - nEles = 0"); 1027 | 1028 | // eles.resize(nEles); 1029 | // faces.resize(nIntFaces+nBndFaces); 1030 | // mpiFacesVec.resize(nMpiFaces); 1031 | 1032 | // if (gridRank==0) { 1033 | // if (meshType == OVERSET_MESH) 1034 | // cout << "Geo: Grid " << gridID << ": Setting up elements" << endl; 1035 | // else 1036 | // cout << "Geo: Setting up elements" << endl; 1037 | // } 1038 | 1039 | // // Setup the elements 1040 | // int ic = 0; 1041 | // for (auto& e:eles) { 1042 | // e.ID = ic; 1043 | // e.eType = ctype[ic]; 1044 | // e.nNodes = c2nv[ic]; 1045 | 1046 | // // Shape [mesh] nodes 1047 | // e.nodeID.resize(c2nv[ic]); 1048 | // e.nodes.resize(c2nv[ic]); 1049 | // for (int iv=0; iv cellFaces; 1068 | 1069 | // if (gridRank==0) { 1070 | // if (meshType == OVERSET_MESH) 1071 | // cout << "Geo: Grid " << gridID << ": Setting up internal faces" << endl; 1072 | // else 1073 | // cout << "Geo: Setting up internal faces" << endl; 1074 | // } 1075 | 1076 | 1077 | // // Internal Faces 1078 | // for (int i=0; i(); 1080 | // // Find global face ID of current interior face 1081 | // int ff = intFaces[i]; 1082 | // int ic1 = f2c(ff,0); 1083 | // // Find local face ID of global face within first element [on left] 1084 | // cellFaces.assign(c2f[ic1],c2f[ic1]+c2nf[ic1]); 1085 | // int fid1 = findFirst(cellFaces,ff); 1086 | // if (f2c(ff,1) == -1) { 1087 | // FatalError("Interior face does not have a right element assigned."); 1088 | // } 1089 | // else { 1090 | // int ic2 = f2c(ff,1); 1091 | // cellFaces.assign(c2f[ic2], c2f[ic2]+c2nf[ic2]); // List of cell's faces 1092 | // int fid2 = findFirst(cellFaces,ff); // Which one is this face 1093 | // int relRot = compareOrientation(ic1,fid1,ic2,fid2); 1094 | // struct faceInfo info; 1095 | // info.locF_R = fid2; 1096 | // info.relRot = relRot; 1097 | // faces[i]->initialize(&eles[f2c(ff,0)],&eles[f2c(ff,1)],ff,fid1,info,params); 1098 | // } 1099 | // } 1100 | 1101 | // if (gridRank==0) { 1102 | // if (meshType == OVERSET_MESH) 1103 | // cout << "Geo: Grid " << gridID << ": Setting up boundary faces" << endl; 1104 | // else 1105 | // cout << "Geo: Setting up boundary faces" << endl; 1106 | // } 1107 | 1108 | // // Boundary Faces 1109 | // for (int i=0; i(); 1111 | // // Find global face ID of current boundary face 1112 | // int ff = bndFaces[i]; 1113 | // ic = f2c(ff,0); 1114 | // // Find local face ID of global face within element 1115 | // cellFaces.assign(c2f[ic],c2f[ic]+c2nf[ic]); 1116 | // int fid1 = findFirst(cellFaces,ff); 1117 | // if (f2c(ff,1) != -1) { 1118 | // FatalError("Boundary face has a right element assigned."); 1119 | // }else{ 1120 | // struct faceInfo info; 1121 | // info.bcType = bcType[i]; 1122 | // faces[nIntFaces+i]->initialize(&eles[f2c(ff,0)],NULL,ff,fid1,info,params); 1123 | // } 1124 | // } 1125 | 1126 | //#ifndef _NO_MPI 1127 | // // MPI Faces 1128 | // if (params->nproc > 1) { 1129 | 1130 | // if (gridRank==0) { 1131 | // if (meshType == OVERSET_MESH) 1132 | // cout << "Geo: Grid " << gridID << ": Setting up MPI faces" << endl; 1133 | // else 1134 | // cout << "Geo: Setting up MPI faces" << endl; 1135 | // } 1136 | 1137 | // for (int i=0; i(); 1139 | // // Find global face ID of current boundary face 1140 | // int ff = mpiFaces[i]; 1141 | // ic = f2c(ff,0); 1142 | // // Find local face ID of global face within element 1143 | // int fid1 = mpiLocF[i]; 1144 | // if (f2c(ff,1) != -1) { 1145 | // FatalError("MPI face has a right element assigned."); 1146 | // }else{ 1147 | // int relRot = 0; 1148 | // if (nDims == 3) { 1149 | // // Find the relative orientation (rotation) between left & right faces 1150 | // relRot = compareOrientationMPI(ic,fid1,gIC_R[i],mpiLocF_R[i]); 1151 | // } 1152 | // struct faceInfo info; 1153 | // info.IDR = locF_R[i]; 1154 | // info.locF_R = mpiLocF_R[i]; 1155 | // info.relRot = relRot; 1156 | // info.procL = gridRank; 1157 | // info.procR = procR[i]; 1158 | // info.isMPI = 1; 1159 | // info.gridComm = gridComm; // Note that this is equivalent to MPI_COMM_WORLD if non-overset (ngrids = 1) 1160 | // mpiFacesVec[i]->initialize(&eles[f2c(ff,0)],NULL,i,fid1,info,params); 1161 | // } 1162 | // } 1163 | // } 1164 | //#endif 1165 | //} 1166 | 1167 | void geo::readGmsh(string fileName) 1168 | { 1169 | ifstream meshFile; 1170 | string str; 1171 | 1172 | if (meshType == OVERSET_MESH) { 1173 | if (gridRank==0) cout << "Geo: Grid " << gridID << ": Reading mesh file " << fileName << endl; 1174 | } 1175 | else { 1176 | if (gridRank==0) cout << "Geo: Reading mesh file " << fileName << endl; 1177 | } 1178 | 1179 | meshFile.open(fileName.c_str()); 1180 | if (!meshFile.is_open()) 1181 | FatalError("Unable to open mesh file."); 1182 | 1183 | /* --- Read Boundary Conditions & Fluid Field(s) --- */ 1184 | 1185 | // Move cursor to $PhysicalNames 1186 | while(1) { 1187 | getline(meshFile,str); 1188 | if (str.find("$PhysicalNames")!=string::npos) break; 1189 | if(meshFile.eof()) FatalError("$PhysicalNames tag not found in Gmsh file!"); 1190 | } 1191 | 1192 | // Read number of boundaries and fields defined 1193 | int nBnds; // Temp. variable for # of Gmsh regions ("PhysicalNames") 1194 | meshFile >> nBnds; 1195 | getline(meshFile,str); // clear rest of line 1196 | 1197 | nBounds = 0; 1198 | for (int i=0; i> bcdim >> bcid >> bcStr; 1206 | 1207 | // Remove quotation marks from around boundary condition 1208 | size_t ind = bcStr.find("\""); 1209 | while (ind!=string::npos) { 1210 | bcStr.erase(ind,1); 1211 | ind = bcStr.find("\""); 1212 | } 1213 | 1214 | // Convert to lowercase to match Flurry's boundary condition strings 1215 | std::transform(bcStr.begin(), bcStr.end(), bcStr.begin(), ::tolower); 1216 | 1217 | // First, map mesh boundary to boundary condition in input file 1218 | if (!params->meshBounds.count(bcStr)) { 1219 | string errS = "Unrecognized mesh boundary: \"" + bcStr + "\"\n"; 1220 | errS += "Boundary names in input file must match those in mesh file."; 1221 | FatalError(errS.c_str()); 1222 | } 1223 | 1224 | // Map the Gmsh PhysicalName to the input-file-specified Flurry boundary condition 1225 | bcStr = params->meshBounds[bcStr]; 1226 | 1227 | // Next, check that the requested boundary condition exists 1228 | if (!bcStr2Num.count(bcStr)) { 1229 | string errS = "Unrecognized boundary condition: \"" + bcStr + "\""; 1230 | FatalError(errS.c_str()); 1231 | } 1232 | 1233 | if (bcStr.compare("fluid")==0) { 1234 | nDims = bcdim; 1235 | params->nDims = bcdim; 1236 | bcIdMap[bcid] = -1; 1237 | } 1238 | else { 1239 | bcList.push_back(bcStr2Num[bcStr]); 1240 | bcIdMap[bcid] = nBounds; // Map Gmsh bcid to Flurry bound index 1241 | nBounds++; 1242 | } 1243 | } 1244 | 1245 | /* --- Read Mesh Vertex Locations --- */ 1246 | 1247 | // Move cursor to $Nodes 1248 | meshFile.clear(); 1249 | meshFile.seekg(0, ios::beg); 1250 | while(1) { 1251 | getline(meshFile,str); 1252 | if (str.find("$Nodes")!=string::npos) break; 1253 | if(meshFile.eof()) FatalError("$Nodes tag not found in Gmsh file!"); 1254 | } 1255 | 1256 | uint iv; 1257 | meshFile >> nVerts; 1258 | xv.setup(nVerts,nDims); 1259 | getline(meshFile,str); // Clear end of line, just in case 1260 | 1261 | for (int i=0; i> iv >> xv(i,0) >> xv(i,1); 1263 | if (nDims == 3) meshFile >> xv(i,2); 1264 | } 1265 | 1266 | /* --- Read Element Connectivity --- */ 1267 | 1268 | // Move cursor to $Elements 1269 | meshFile.clear(); 1270 | meshFile.seekg(0, ios::beg); 1271 | while(1) { 1272 | getline(meshFile,str); 1273 | if (str.find("$Elements")!=string::npos) break; 1274 | if(meshFile.eof()) FatalError("$Elements tag not found in Gmsh file!"); 1275 | } 1276 | 1277 | int nElesGmsh; 1278 | vector c2v_tmp(9,0); // Maximum number of nodes/element possible 1279 | vector> boundPoints(nBounds); 1280 | map eType2nv; 1281 | eType2nv[3] = 4; // Linear quad 1282 | eType2nv[16] = 4; // Quadratic serendipity quad 1283 | eType2nv[10] = 4; // Quadratic Lagrange quad 1284 | eType2nv[8] = 8; // Linear hex 1285 | 1286 | // Setup bndPts matrix - Just an initial estimate; will be adjusted on the fly 1287 | //bndPts.setup(nBounds,std::round(nNodes/nBounds)); 1288 | nBndPts.resize(nBounds); 1289 | 1290 | // Read total number of interior + boundary elements 1291 | meshFile >> nElesGmsh; 1292 | getline(meshFile,str); // Clear end of line, just in case 1293 | 1294 | // For Gmsh node ordering, see: http://geuz.org/gmsh/doc/texinfo/gmsh.html#Node-ordering 1295 | int ic = 0; 1296 | for (int k=0; k> id >> eType >> nTags; 1299 | meshFile >> bcid; 1300 | bcid = bcIdMap[bcid]; 1301 | for (int tag=0; tag> tmp; 1303 | 1304 | if (bcid == -1) { 1305 | // NOTE: Currently, only quads are supported 1306 | switch(eType) { 1307 | case 2: 1308 | // linear triangle 1309 | c2nv.push_back(3); 1310 | c2nf.push_back(3); 1311 | c2ne.push_back(3); 1312 | ctype.push_back(TRI); 1313 | meshFile >> c2v_tmp[0] >> c2v_tmp[1] >> c2v_tmp[2]; 1314 | break; 1315 | 1316 | case 4: 1317 | // linear tetrahedron 1318 | c2nv.push_back(4); 1319 | c2nf.push_back(4); 1320 | c2ne.push_back(6); 1321 | ctype.push_back(QUAD); 1322 | meshFile >> c2v_tmp[0] >> c2v_tmp[1] >> c2v_tmp[2]; 1323 | c2v_tmp[3] = c2v_tmp[2]; 1324 | break; 1325 | 1326 | case 9: 1327 | // quadratic triangle [corner nodes, then edge-center nodes] 1328 | c2nv.push_back(6); 1329 | c2nf.push_back(3); 1330 | c2ne.push_back(3); 1331 | ctype.push_back(TRI); 1332 | meshFile >> c2v_tmp[0] >> c2v_tmp[1] >> c2v_tmp[2] >> c2v_tmp[3] >> c2v_tmp[4] >> c2v_tmp[5]; 1333 | break; 1334 | 1335 | case 3: 1336 | // linear quadrangle 1337 | c2nv.push_back(4); 1338 | c2nf.push_back(4); 1339 | c2ne.push_back(4); 1340 | ctype.push_back(QUAD); 1341 | meshFile >> c2v_tmp[0] >> c2v_tmp[1] >> c2v_tmp[2] >> c2v_tmp[3]; 1342 | break; 1343 | 1344 | case 16: 1345 | // quadratic 8-node (serendipity) quadrangle 1346 | c2nv.push_back(8); 1347 | c2nf.push_back(4); 1348 | c2ne.push_back(4); 1349 | ctype.push_back(QUAD); 1350 | meshFile >> c2v_tmp[0] >> c2v_tmp[1] >> c2v_tmp[2] >> c2v_tmp[3] >> c2v_tmp[4] >> c2v_tmp[5] >> c2v_tmp[6] >> c2v_tmp[7]; 1351 | break; 1352 | 1353 | case 10: 1354 | // quadratic (9-node Lagrange) quadrangle (read as 8-node serendipity) 1355 | c2nv.push_back(8); 1356 | c2nf.push_back(4); 1357 | c2ne.push_back(4); 1358 | ctype.push_back(QUAD); 1359 | meshFile >> c2v_tmp[0] >> c2v_tmp[1] >> c2v_tmp[2] >> c2v_tmp[3] >> c2v_tmp[4] >> c2v_tmp[5] >> c2v_tmp[6] >> c2v_tmp[7]; 1360 | break; 1361 | 1362 | case 5: 1363 | // Linear hexahedron 1364 | c2nv.push_back(8); 1365 | c2nf.push_back(6); 1366 | c2ne.push_back(12); 1367 | ctype.push_back(HEX); 1368 | meshFile >> c2v_tmp[0] >> c2v_tmp[1] >> c2v_tmp[2] >> c2v_tmp[3] >> c2v_tmp[4] >> c2v_tmp[5] >> c2v_tmp[6] >> c2v_tmp[7]; 1369 | break; 1370 | 1371 | case 6: 1372 | // Linear prism 1373 | c2nv.push_back(6); 1374 | c2nf.push_back(5); 1375 | c2ne.push_back(9); 1376 | ctype.push_back(PRISM); 1377 | meshFile >> c2v_tmp[0] >> c2v_tmp[1] >> c2v_tmp[2] >> c2v_tmp[3] >> c2v_tmp[4] >> c2v_tmp[5]; 1378 | break; 1379 | 1380 | default: 1381 | cout << "Gmsh element ID " << k << ", Gmsh Element Type = " << eType << endl; 1382 | FatalError("element type not recognized"); 1383 | break; 1384 | } 1385 | 1386 | // Increase the size of c2v (max # of vertices per cell) if needed 1387 | // if (c2v.getDim1()<(uint)c2nv[ic]) { 1388 | // for (int dim=c2v.getDim1(); dim> iv; iv--; 1441 | boundPoints[bcid].insert(iv); 1442 | } 1443 | getline(meshFile,str); 1444 | } 1445 | } // End of loop over entities 1446 | 1447 | int maxNBndPts = 0; 1448 | for (int i=0; inx; 1471 | int ny = params->ny; 1472 | int nz = params->nz; 1473 | nDims = params->nDims; 1474 | 1475 | if (nDims == 2) 1476 | nz = 1; 1477 | 1478 | if (params->rank==0) 1479 | cout << "Geo: Creating " << nx << "x" << ny << "x" << nz << " cartesian mesh" << endl; 1480 | 1481 | double xmin = params->xmin; 1482 | double xmax = params->xmax; 1483 | double ymin = params->ymin; 1484 | double ymax = params->ymax; 1485 | double zmin = params->zmin; 1486 | double zmax = params->zmax; 1487 | 1488 | double dx = (xmax-xmin)/nx; 1489 | double dy = (ymax-ymin)/ny; 1490 | double dz = (zmax-zmin)/nz; 1491 | 1492 | params->periodicDX = xmax-xmin; 1493 | params->periodicDY = ymax-ymin; 1494 | params->periodicDZ = zmax-zmin; 1495 | 1496 | nEles = nx*ny*nz; 1497 | vector c2v_tmp; 1498 | 1499 | if (nDims == 2) { 1500 | nVerts = (nx+1)*(ny+1); 1501 | xv.setup(nVerts,nDims); 1502 | 1503 | c2nv.assign(nEles,4); 1504 | c2nf.assign(nEles,4); 1505 | ctype.assign(nEles,QUAD); 1506 | 1507 | /* --- Setup Vertices --- */ 1508 | 1509 | c2v_tmp.assign(4,0); 1510 | 1511 | int nv = 0; 1512 | 1513 | for (int i=0; iint) 1583 | bcList.push_back(bcStr2Num[params->create_bcBottom]); 1584 | bcList.push_back(bcStr2Num[params->create_bcRight]); 1585 | bcList.push_back(bcStr2Num[params->create_bcTop]); 1586 | bcList.push_back(bcStr2Num[params->create_bcLeft]); 1587 | 1588 | if (nDims == 3) { 1589 | bcList.push_back(bcStr2Num[params->create_bcFront]); 1590 | bcList.push_back(bcStr2Num[params->create_bcBack]); 1591 | } 1592 | 1593 | // Sort the list & remove any duplicates 1594 | std::sort(bcList.begin(), bcList.end()); 1595 | vector::iterator vIt = std::unique(bcList.begin(), bcList.end()); 1596 | nBounds = std::distance(bcList.begin(), vIt); // will I need both an nBounds (i.e., in mesh) and an nBC's (current nBounds)? 1597 | bcList.resize(nBounds); 1598 | 1599 | // Setup a map so we know where each BC# is inside of bcList 1600 | map bc2bcList; 1601 | 1602 | // Setup boundary connectivity storage 1603 | nFacesPerBnd.assign(nBounds,0); 1604 | if (nDims == 2) { 1605 | bndPts.setup(nBounds,2*4*(std::max(nx,ny)+1)); 1606 | } 1607 | else if (nDims == 3) { 1608 | int maxN_BFace = std::max(nx*ny,nx*nz); 1609 | maxN_BFace = std::max(maxN_BFace,ny*nz); 1610 | bndPts.setup(nBounds,6*4*maxN_BFace); 1611 | } 1612 | nBndPts.resize(nBounds); 1613 | for (int i=0; icreate_bcBottom]]; 1622 | int ne = nFacesPerBnd[ib]; 1623 | for (int ix=0; ixcreate_bcTop]]; 1632 | ne = nFacesPerBnd[ib]; 1633 | for (int ix=0; ixcreate_bcLeft]]; 1642 | ne = nFacesPerBnd[ib]; 1643 | for (int iy=0; iycreate_bcRight]]; 1652 | ne = nFacesPerBnd[ib]; 1653 | for (int iy=0; iycreate_bcBottom]]; 1663 | int nf = nFacesPerBnd[ib]; 1664 | for (int ix=0; ixcreate_bcTop]]; 1677 | nf = nFacesPerBnd[ib]; 1678 | for (int ix=0; ixcreate_bcLeft]]; 1691 | nf = nFacesPerBnd[ib]; 1692 | for (int iz=0; izcreate_bcRight]]; 1705 | nf = nFacesPerBnd[ib]; 1706 | for (int iz=0; izcreate_bcFront]]; 1720 | nf = nFacesPerBnd[ib]; 1721 | for (int iz=0; izcreate_bcBack]]; 1734 | nf = nFacesPerBnd[ib]; 1735 | for (int iz=0; iz iPeriodic(0); 1761 | 1762 | for (int i=0; i 0) 1772 | FatalError("Periodic boundaries not implemented yet with MPI! Recompile for serial."); 1773 | #endif 1774 | 1775 | if (nPeriodic == 0) return; 1776 | if (nPeriodic%2 != 0) FatalError("Expecting even number of periodic faces; have odd number."); 1777 | if (params->rank==0) cout << "Geo: Processing periodic boundaries" << endl; 1778 | 1779 | for (auto& i:iPeriodic) { 1780 | if (bndFaces[i]==-1) continue; 1781 | for (auto& j:iPeriodic) { 1782 | if (i==j || bndFaces[i]==-1 || bndFaces[j]==-1) continue; 1783 | bool match; 1784 | if (nDims == 2) { 1785 | match = checkPeriodicFaces(f2v[bndFaces[i]],f2v[bndFaces[j]]); 1786 | } 1787 | else if (nDims == 3) { 1788 | auto face1 = f2v.getRow(bndFaces[i]); 1789 | auto face2 = f2v.getRow(bndFaces[j]); 1790 | match = checkPeriodicFaces3D(face1, face2); 1791 | } 1792 | if (match) { 1793 | 1794 | /* --- Match found - now take care of transfer from boundary -> internal --- */ 1795 | 1796 | if (i>j) FatalError("How did this happen?!"); 1797 | 1798 | bi = bndFaces[i]; 1799 | bj = bndFaces[j]; 1800 | 1801 | // Transfer combined edge from boundary to internal list 1802 | intFaces.push_back(bi); 1803 | 1804 | // Flag global edge IDs as internal faces 1805 | isBnd[bi] = false; 1806 | isBnd[bj] = false; 1807 | 1808 | // Fix f2c - add right cell to combined face, make left cell = -1 in 'deleted' face 1809 | f2c(bi,1) = f2c[bj][0]; 1810 | f2c(bj,0) = -1; 1811 | 1812 | // Fix c2f - replace 'deleted' edge from right cell with combined face 1813 | ic = f2c[bi][1]; 1814 | int fID = findFirst(c2f[ic],(int)bj,c2nf[ic]); 1815 | c2f(f2c(bi,1),fID) = bi; 1816 | 1817 | // Fix c2b - set element-local face to be internal face 1818 | c2b(f2c(bi,1),fID) = false; 1819 | 1820 | // Flag edges as gone in boundary edges list 1821 | bndFaces[i] = -1; 1822 | bndFaces[j] = -1; 1823 | } 1824 | } 1825 | } 1826 | 1827 | // Remove no-longer-existing periodic boundary edges and update nBndEdges 1828 | bndFaces.erase(std::remove(bndFaces.begin(), bndFaces.end(), -1), bndFaces.end()); 1829 | nBndFaces = bndFaces.size(); 1830 | nIntFaces = intFaces.size(); 1831 | } 1832 | 1833 | bool geo::compareFaces(vector &face1, vector &face2) 1834 | { 1835 | uint nv = face1.size(); 1836 | if (face2.size() != nv) return false; 1837 | 1838 | std::sort(face1.begin(),face1.end()); 1839 | std::sort(face2.begin(),face2.end()); 1840 | 1841 | bool found = true; 1842 | for (uint i=0; iperiodicTol; 1858 | double dx = params->periodicDX; 1859 | double dy = params->periodicDY; 1860 | 1861 | if ( abs(abs(x21-x11)-dx)[0] and [1]->[1] in each edge 1863 | return true; 1864 | } 1865 | else if ( abs(abs(x22-x11)-dx)[1] and [1]->[0] in each edge 1867 | return true; 1868 | } 1869 | else if ( abs(abs(y21-y11)-dy)[0] and [1]->[1] in each edge 1871 | return true; 1872 | } 1873 | else if ( abs(abs(y22-y11)-dy)[1] and [1]->[0] in each edge 1875 | return true; 1876 | } 1877 | 1878 | // None of the above 1879 | return false; 1880 | } 1881 | 1882 | bool geo::checkPeriodicFaces3D(vector &face1, vector &face2) 1883 | { 1884 | if (face1.size() != face2.size()) 1885 | return false; 1886 | 1887 | double tol = params->periodicTol; 1888 | double dx = params->periodicDX; 1889 | double dy = params->periodicDY; 1890 | double dz = params->periodicDZ; 1891 | 1892 | /* --- Compare faces using normal vectors: normals should be aligned 1893 | * and offset by norm .dot. {dx,dy,dz} --- */ 1894 | 1895 | Vec3 vec1, vec2; 1896 | 1897 | // Calculate face normal & centriod for face 1 1898 | Vec3 norm1; 1899 | point c1; 1900 | vec1 = point(xv[face1[1]]) - point(xv[face1[0]]); 1901 | vec2 = point(xv[face1[2]]) - point(xv[face1[0]]); 1902 | for (uint j=0; jtol) return false; 1931 | 1932 | // Check offset distance - norm .times. {dx,dy,dz} should equal centroid2 - centriod1 1933 | Vec3 nDXYZ; 1934 | nDXYZ.x = norm1[0]*dx; 1935 | nDXYZ.y = norm1[1]*dy; 1936 | nDXYZ.z = norm1[2]*dz; 1937 | nDXYZ.abs(); 1938 | 1939 | Vec3 Offset = c2 - c1; 1940 | Offset.abs(); 1941 | 1942 | Vec3 Diff = Offset - nDXYZ; 1943 | Diff.abs(); 1944 | 1945 | for (int i=0; i<3; i++) { 1946 | if (Diff[i]>tol) return false; 1947 | } 1948 | 1949 | // The faces are aligned across a periodic direction 1950 | return true; 1951 | } 1952 | 1953 | int geo::compareOrientation(int ic1, int f1, int ic2, int f2) 1954 | { 1955 | if (nDims == 2) return 1; 1956 | 1957 | int nv = f2nv[c2f(ic1,f1)]; 1958 | 1959 | vector tmpFace1(nv), tmpFace2(nv); 1960 | 1961 | switch (ctype[ic1]) { 1962 | case HEX: 1963 | // Flux points arranged in 2D grid on each face oriented with each 1964 | // dimension increasing in its +'ve direction ['btm-left' to 'top-right'] 1965 | // Node ordering reflects this: CCW from 'bottom-left' node on each face 1966 | switch (f1) { 1967 | case 0: 1968 | // Bottom face (z = -1) 1969 | tmpFace1[0] = c2v(ic1,0); 1970 | tmpFace1[1] = c2v(ic1,1); 1971 | tmpFace1[2] = c2v(ic1,2); 1972 | tmpFace1[3] = c2v(ic1,3); 1973 | break; 1974 | case 1: 1975 | // Top face (z = +1) 1976 | tmpFace1[0] = c2v(ic1,5); 1977 | tmpFace1[1] = c2v(ic1,4); 1978 | tmpFace1[2] = c2v(ic1,7); 1979 | tmpFace1[3] = c2v(ic1,6); 1980 | break; 1981 | case 2: 1982 | // Left face (x = -1) 1983 | tmpFace1[0] = c2v(ic1,0); 1984 | tmpFace1[1] = c2v(ic1,3); 1985 | tmpFace1[2] = c2v(ic1,7); 1986 | tmpFace1[3] = c2v(ic1,4); 1987 | break; 1988 | case 3: 1989 | // Right face (x = +1) 1990 | tmpFace1[0] = c2v(ic1,2); 1991 | tmpFace1[1] = c2v(ic1,1); 1992 | tmpFace1[2] = c2v(ic1,5); 1993 | tmpFace1[3] = c2v(ic1,6); 1994 | break; 1995 | case 4: 1996 | // Front face (y = -1) 1997 | tmpFace1[0] = c2v(ic1,1); 1998 | tmpFace1[1] = c2v(ic1,0); 1999 | tmpFace1[2] = c2v(ic1,4); 2000 | tmpFace1[3] = c2v(ic1,5); 2001 | break; 2002 | case 5: 2003 | // Back face (y = +1) 2004 | tmpFace1[0] = c2v(ic1,3); 2005 | tmpFace1[1] = c2v(ic1,2); 2006 | tmpFace1[2] = c2v(ic1,6); 2007 | tmpFace1[3] = c2v(ic1,7); 2008 | break; 2009 | } 2010 | break; 2011 | 2012 | default: 2013 | FatalError("Element type not supported."); 2014 | break; 2015 | } 2016 | 2017 | switch (ctype[ic2]) { 2018 | case HEX: 2019 | switch (f2) { 2020 | case 0: 2021 | // Bottom face (z = -1) 2022 | tmpFace2[0] = c2v(ic2,0); 2023 | tmpFace2[1] = c2v(ic2,1); 2024 | tmpFace2[2] = c2v(ic2,2); 2025 | tmpFace2[3] = c2v(ic2,3); 2026 | break; 2027 | case 1: 2028 | // Top face (z = +1) 2029 | tmpFace2[0] = c2v(ic2,5); 2030 | tmpFace2[1] = c2v(ic2,4); 2031 | tmpFace2[2] = c2v(ic2,7); 2032 | tmpFace2[3] = c2v(ic2,6); 2033 | break; 2034 | case 2: 2035 | // Left face (x = -1) 2036 | tmpFace2[0] = c2v(ic2,0); 2037 | tmpFace2[1] = c2v(ic2,3); 2038 | tmpFace2[2] = c2v(ic2,7); 2039 | tmpFace2[3] = c2v(ic2,4); 2040 | break; 2041 | case 3: 2042 | // Right face (x = +1) 2043 | tmpFace2[0] = c2v(ic2,2); 2044 | tmpFace2[1] = c2v(ic2,1); 2045 | tmpFace2[2] = c2v(ic2,5); 2046 | tmpFace2[3] = c2v(ic2,6); 2047 | break; 2048 | case 4: 2049 | // Front face (y = -1) 2050 | tmpFace2[0] = c2v(ic2,1); 2051 | tmpFace2[1] = c2v(ic2,0); 2052 | tmpFace2[2] = c2v(ic2,4); 2053 | tmpFace2[3] = c2v(ic2,5); 2054 | break; 2055 | case 5: 2056 | // Back face (y = +1) 2057 | tmpFace2[0] = c2v(ic2,3); 2058 | tmpFace2[1] = c2v(ic2,2); 2059 | tmpFace2[2] = c2v(ic2,6); 2060 | tmpFace2[3] = c2v(ic2,7); 2061 | break; 2062 | } 2063 | break; 2064 | 2065 | default: 2066 | FatalError("Element type not supported."); 2067 | break; 2068 | } 2069 | 2070 | // Now, compare the two faces to see the relative orientation [rotation] 2071 | if (tmpFace1[0] == tmpFace2[0]) return 0; 2072 | else if (tmpFace1[1] == tmpFace2[0]) return 1; 2073 | else if (tmpFace1[2] == tmpFace2[0]) return 2; 2074 | else if (tmpFace1[3] == tmpFace2[0]) return 3; 2075 | else if (checkPeriodicFaces3D(tmpFace1,tmpFace2)) { 2076 | point c1, c2; 2077 | for (auto iv:tmpFace1) c1 += point(xv[iv]); 2078 | for (auto iv:tmpFace2) c2 += point(xv[iv]); 2079 | c1 /= tmpFace1.size(); 2080 | c2 /= tmpFace2.size(); 2081 | Vec3 fDist = c2 - c1; 2082 | fDist /= sqrt(fDist*fDist); // Normalize 2083 | 2084 | point pt1; 2085 | point pt2 = point(xv[tmpFace2[0]]); 2086 | 2087 | for (int i=0; i<4; i++) { 2088 | pt1 = point(xv[tmpFace1[i]]); 2089 | Vec3 ptDist = pt2 - pt1; // Vector between points 2090 | ptDist /= sqrt(ptDist*ptDist); // Normalize 2091 | 2092 | double dot = fDist*ptDist; 2093 | if (abs(1-abs(dot))periodicTol) return i; // These points align 2094 | } 2095 | // Matching points not found 2096 | FatalError("Unable to orient periodic faces using simple algorithm."); 2097 | } 2098 | else FatalError("Internal faces improperly matched."); 2099 | 2100 | } 2101 | 2102 | int geo::compareOrientationMPI(int ic1, int f1, int ic2, int f2) 2103 | { 2104 | #ifndef _NO_MPI 2105 | if (nDims == 2) return 1; 2106 | 2107 | int nv = f2nv[c2f(ic1,f1)]; 2108 | 2109 | vector tmpFace1(nv), tmpFace2(nv); 2110 | 2111 | switch (ctype[ic1]) { 2112 | case HEX: 2113 | // Flux points arranged in 2D grid on each face oriented with each 2114 | // dimension increasing in its +'ve direction ['btm-left' to 'top-right'] 2115 | // Node ordering reflects this: CCW from 'bottom-left' node on each face 2116 | switch (f1) { 2117 | case 0: 2118 | // Bottom face (z = -1) 2119 | tmpFace1[0] = c2v(ic1,0); 2120 | tmpFace1[1] = c2v(ic1,1); 2121 | tmpFace1[2] = c2v(ic1,2); 2122 | tmpFace1[3] = c2v(ic1,3); 2123 | break; 2124 | case 1: 2125 | // Top face (z = +1) 2126 | tmpFace1[0] = c2v(ic1,5); 2127 | tmpFace1[1] = c2v(ic1,4); 2128 | tmpFace1[2] = c2v(ic1,7); 2129 | tmpFace1[3] = c2v(ic1,6); 2130 | break; 2131 | case 2: 2132 | // Left face (x = -1) 2133 | tmpFace1[0] = c2v(ic1,0); 2134 | tmpFace1[1] = c2v(ic1,3); 2135 | tmpFace1[2] = c2v(ic1,7); 2136 | tmpFace1[3] = c2v(ic1,4); 2137 | break; 2138 | case 3: 2139 | // Right face (x = +1) 2140 | tmpFace1[0] = c2v(ic1,2); 2141 | tmpFace1[1] = c2v(ic1,1); 2142 | tmpFace1[2] = c2v(ic1,5); 2143 | tmpFace1[3] = c2v(ic1,6); 2144 | break; 2145 | case 4: 2146 | // Front face (y = -1) 2147 | tmpFace1[0] = c2v(ic1,1); 2148 | tmpFace1[1] = c2v(ic1,0); 2149 | tmpFace1[2] = c2v(ic1,4); 2150 | tmpFace1[3] = c2v(ic1,5); 2151 | break; 2152 | case 5: 2153 | // Back face (y = +1) 2154 | tmpFace1[0] = c2v(ic1,3); 2155 | tmpFace1[1] = c2v(ic1,2); 2156 | tmpFace1[2] = c2v(ic1,6); 2157 | tmpFace1[3] = c2v(ic1,7); 2158 | break; 2159 | } 2160 | break; 2161 | 2162 | default: 2163 | FatalError("Element type not supported."); 2164 | break; 2165 | } 2166 | 2167 | switch (ctype_g[ic2]) { 2168 | case HEX: 2169 | switch (f2) { 2170 | case 0: 2171 | // Bottom face (z = -1) 2172 | tmpFace2[0] = c2v_g(ic2,0); 2173 | tmpFace2[1] = c2v_g(ic2,1); 2174 | tmpFace2[2] = c2v_g(ic2,2); 2175 | tmpFace2[3] = c2v_g(ic2,3); 2176 | break; 2177 | case 1: 2178 | // Top face (z = +1) 2179 | tmpFace2[0] = c2v_g(ic2,5); 2180 | tmpFace2[1] = c2v_g(ic2,4); 2181 | tmpFace2[2] = c2v_g(ic2,7); 2182 | tmpFace2[3] = c2v_g(ic2,6); 2183 | break; 2184 | case 2: 2185 | // Left face (x = -1) 2186 | tmpFace2[0] = c2v_g(ic2,0); 2187 | tmpFace2[1] = c2v_g(ic2,3); 2188 | tmpFace2[2] = c2v_g(ic2,7); 2189 | tmpFace2[3] = c2v_g(ic2,4); 2190 | break; 2191 | case 3: 2192 | // Right face (x = +1) 2193 | tmpFace2[0] = c2v_g(ic2,2); 2194 | tmpFace2[1] = c2v_g(ic2,1); 2195 | tmpFace2[2] = c2v_g(ic2,5); 2196 | tmpFace2[3] = c2v_g(ic2,6); 2197 | break; 2198 | case 4: 2199 | // Front face (y = -1) 2200 | tmpFace2[0] = c2v_g(ic2,1); 2201 | tmpFace2[1] = c2v_g(ic2,0); 2202 | tmpFace2[2] = c2v_g(ic2,4); 2203 | tmpFace2[3] = c2v_g(ic2,5); 2204 | break; 2205 | case 5: 2206 | // Back face (y = +1) 2207 | tmpFace2[0] = c2v_g(ic2,3); 2208 | tmpFace2[1] = c2v_g(ic2,2); 2209 | tmpFace2[2] = c2v_g(ic2,6); 2210 | tmpFace2[3] = c2v_g(ic2,7); 2211 | break; 2212 | } 2213 | break; 2214 | 2215 | default: 2216 | FatalError("Element type not supported."); 2217 | break; 2218 | } 2219 | 2220 | // Now, compare the two faces to see the relative orientation [rotation] 2221 | if (iv2ivg[tmpFace1[0]] == tmpFace2[0]) return 0; 2222 | else if (iv2ivg[tmpFace1[1]] == tmpFace2[0]) return 1; 2223 | else if (iv2ivg[tmpFace1[2]] == tmpFace2[0]) return 2; 2224 | else if (iv2ivg[tmpFace1[3]] == tmpFace2[0]) return 3; 2225 | else FatalError("MPI faces improperly matched."); 2226 | #else 2227 | return 0; 2228 | #endif 2229 | } 2230 | 2231 | 2232 | void geo::partitionMesh(void) 2233 | { 2234 | #ifndef _NO_MPI 2235 | int rank, nproc; 2236 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 2237 | MPI_Comm_size(MPI_COMM_WORLD, &nproc); 2238 | 2239 | if (nproc <= 1) return; 2240 | 2241 | if (meshType == OVERSET_MESH) { 2242 | if (nproc % params->nGrids != 0) FatalError("Expcting # of processes to be evenly divisible by # of grids."); 2243 | // Partitioning each grid independantly; local 'grid rank' is the important rank 2244 | gridRank = rank % nprocPerGrid; 2245 | rank = gridRank; 2246 | nproc = nprocPerGrid; 2247 | 2248 | if (nproc <= 1) return; // No additional partitioning needed 2249 | 2250 | if (rank == 0) cout << "Geo: Partitioning mesh block " << gridID << " across " << nprocPerGrid << " processes" << endl; 2251 | if (rank == 0) cout << "Geo: Number of elements in block " << gridID << " : " << nEles << endl; 2252 | } 2253 | else { 2254 | if (rank == 0) cout << "Geo: Partitioning mesh across " << nproc << " processes" << endl; 2255 | if (rank == 0) cout << "Geo: Number of elements globally: " << nEles << endl; 2256 | } 2257 | 2258 | vector eptr(nEles+1); 2259 | vector eind; 2260 | 2261 | int nn = 0; 2262 | for (int i=0; i epart(nEles); 2277 | vector npart(nVerts); 2278 | 2279 | // int errVal = METIS PartMeshDual(idx_t *ne, idx_t *nn, idx_t *eptr, idx_t *eind, idx_t *vwgt, idx_t *vsize, 2280 | // idx_t *ncommon, idx_t *nparts, real_t *tpwgts, idx_t *options, idx_t *objval,idx_t *epart, idx_t *npart) 2281 | 2282 | int ncommon; // 2 for 2D, ~3 for 3D [#nodes per face: 2 for quad/tri, 3 for tet, 4 for hex] 2283 | if (nDims == 2) ncommon = 2; 2284 | else if (nDims == 3) ncommon = 4; 2285 | 2286 | idx_t options[METIS_NOPTIONS]; 2287 | METIS_SetDefaultOptions(options); 2288 | options[METIS_OPTION_NUMBERING] = 0; 2289 | options[METIS_OPTION_IPTYPE] = METIS_IPTYPE_NODE; // needed? 2290 | options[METIS_OPTION_OBJTYPE] = METIS_OBJTYPE_CUT; 2291 | options[METIS_OPTION_PTYPE] = METIS_PTYPE_KWAY; 2292 | 2293 | METIS_PartMeshDual(&nEles,&nVerts,eptr.data(),eind.data(),NULL,NULL, 2294 | &ncommon,&nproc,NULL,options,&objval,epart.data(),npart.data()); 2295 | 2296 | // Copy data to the global arrays & reset local arrays 2297 | nEles_g = nEles; 2298 | nVerts_g = nVerts; 2299 | c2v_g = c2v; c2v.setup(0,0); 2300 | xv_g = xv; xv.setup(0,0); 2301 | ctype_g = ctype; ctype.resize(0); 2302 | c2nv_g = c2nv; c2nv.resize(0); 2303 | c2ne_g = c2nf; c2nf.resize(0); 2304 | bndPts_g = bndPts; bndPts.setup(0,0); 2305 | nBndPts_g = nBndPts; nBndPts.resize(0); 2306 | 2307 | // Each processor will now grab its own data according to its rank (proc ID) 2308 | for (int i=0; i myNodes; 2322 | for (int i=0; i ivg2iv; 2332 | ivg2iv.assign(nVerts_g,-1); 2333 | 2334 | // Transfer over all needed vertices to local array 2335 | int nv = 0; 2336 | for (auto &iv: myNodes) { 2337 | xv.insertRow(xv_g.getRow(iv)); 2338 | iv2ivg.push_back(iv); 2339 | ivg2iv[iv] = nv; 2340 | nv++; 2341 | } 2342 | 2343 | // bndPts array was already setup globally, so remake keeping only local nodes 2344 | vector> boundPoints(nBounds); 2345 | 2346 | for (int i=0; i local] 2362 | bndPts.setup(nBounds,maxNBndPts); 2363 | for (int i=0; i local node IDs 2372 | std::transform(c2v.getData(),c2v.getData()+c2v.getSize(),c2v.getData(), [=](int ivg){return ivg2iv[ivg];}); 2373 | 2374 | if (meshType == OVERSET_MESH) 2375 | cout << "Geo: Grid block " << gridID << " on rank " << rank << ": nEles = " << nEles << endl; 2376 | else 2377 | cout << "Geo: On rank " << rank << ": nEles = " << nEles << endl; 2378 | 2379 | if (rank == 0) cout << "Geo: Done partitioning mesh" << endl; 2380 | 2381 | MPI_Barrier(MPI_COMM_WORLD); 2382 | #endif 2383 | } 2384 | 2385 | ///#include "../include/geo.inl" 2386 | -------------------------------------------------------------------------------- /src/global.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file global.cpp 3 | * \brief Definition of global constants, objects, and variables 4 | * 5 | * \author - Jacob Crabill 6 | * Aerospace Computing Laboratory (ACL) 7 | * Aero/Astro Department. Stanford University 8 | * 9 | * \version 0.0.1 10 | * 11 | * Flux Reconstruction in C++ (Flurry++) Code 12 | * Copyright (C) 2014 Jacob Crabill. 13 | * 14 | */ 15 | 16 | #include "../include/global.hpp" 17 | 18 | #include 19 | #include 20 | 21 | #ifndef _NO_MPI 22 | #include "mpi.h" 23 | #endif 24 | 25 | /* --- Misc. Common Constants --- */ 26 | double pi = 4.0*atan(1); 27 | 28 | map bcStr2Num; 29 | 30 | int factorial(int n) 31 | { 32 | return (n == 1 || n == 0) ? 1 : factorial(n - 1) * n; 33 | } 34 | 35 | void setGlobalVariables(void) { 36 | bcStr2Num["none"] = NONE; 37 | bcStr2Num["fluid"] = NONE; 38 | bcStr2Num["periodic"] = PERIODIC; 39 | bcStr2Num["char"] = CHAR; 40 | bcStr2Num["sup_in"] = SUP_IN; 41 | bcStr2Num["sup_out"] = SUP_OUT; 42 | bcStr2Num["sub_in"] = SUB_IN; 43 | bcStr2Num["sub_out"] = SUB_OUT; 44 | bcStr2Num["slip_wall"] = SLIP_WALL; 45 | bcStr2Num["isothermal_noslip"] = ISOTHERMAL_NOSLIP; 46 | bcStr2Num["adiabatic_noslip"] = ADIABATIC_NOSLIP; 47 | bcStr2Num["overset"] = OVERSET; 48 | bcStr2Num["symmetry"] = SYMMETRY; 49 | // NOTE: 'symmetry' is just a psuedonym for 'slip_wall' which will not be 50 | // considered a "wall" boundary condition for overset grids, force calc, etc. 51 | } 52 | 53 | 54 | bool checkNaN(vector &vec) 55 | { 56 | for (auto& i:vec) 57 | if (std::isnan(i)) return true; 58 | 59 | return false; 60 | } 61 | 62 | bool checkNaN(double* vec, int size) 63 | { 64 | for (int i=0; i& mat, Vec3 &vec) 72 | { 73 | Vec3 out; 74 | int nDims = mat.getDim1(); 75 | for (int i=0; i( finalTime - initTime ).count(); 105 | double execTime = (double)duration/1000.; 106 | cout.setf(ios::fixed, ios::floatfield); 107 | if (execTime > 60) { 108 | int minutes = floor(execTime/60); 109 | double seconds = execTime-(minutes*60); 110 | cout << "Execution time = " << minutes << "min " << setprecision(3) << seconds << "s" << endl; 111 | } 112 | else { 113 | cout << setprecision(3) << "Execution time = " << execTime << "s" << endl; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/input.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file input.cpp 3 | * \brief Class to read & store simulation parameters from file 4 | * \author - Jacob Crabill 5 | * Aerospace Computing Laboratory (ACL) 6 | * Aero/Astro Department. Stanford University 7 | * 8 | * \version 0.0.1 9 | * 10 | * Fux Reconstruction in C++ (Flurry++) Code 11 | * Copyright (C) 2014 Jacob Crabill. 12 | * 13 | */ 14 | 15 | #include "input.hpp" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | fileReader::fileReader() 24 | { 25 | 26 | } 27 | 28 | fileReader::fileReader(string fileName) 29 | { 30 | this->fileName = fileName; 31 | } 32 | 33 | fileReader::~fileReader() 34 | { 35 | if (optFile.is_open()) optFile.close(); 36 | } 37 | 38 | void fileReader::setFile(string fileName) 39 | { 40 | this->fileName = fileName; 41 | } 42 | 43 | void fileReader::openFile(void) 44 | { 45 | optFile.open(fileName.c_str(), ifstream::in); 46 | } 47 | 48 | void fileReader::closeFile() 49 | { 50 | optFile.close(); 51 | } 52 | 53 | template 54 | void fileReader::getScalarValue(string optName, T &opt, T defaultVal) 55 | { 56 | string str, optKey; 57 | 58 | openFile(); 59 | 60 | if (!optFile.is_open() || !getline(optFile,str)) { 61 | optFile.open(fileName.c_str()); 62 | if (!optFile.is_open()) 63 | FatalError("Cannont open input file for reading."); 64 | } 65 | 66 | // Rewind to the start of the file 67 | optFile.seekg(0,optFile.beg); 68 | 69 | // Search for the given option string 70 | while (getline(optFile,str)) { 71 | // Remove any leading whitespace & see if first word is the input option 72 | stringstream ss; 73 | ss.str(str); 74 | ss >> optKey; 75 | if (optKey.compare(optName)==0) { 76 | if (!(ss >> opt)) { 77 | // This could happen if, for example, trying to assign a string to a double 78 | cout << "WARNING: Unable to assign value to option " << optName << endl; 79 | cout << "Using default value of " << defaultVal << " instead." << endl; 80 | opt = defaultVal; 81 | } 82 | 83 | closeFile(); 84 | return; 85 | } 86 | } 87 | 88 | opt = defaultVal; 89 | closeFile(); 90 | } 91 | 92 | template 93 | void fileReader::getScalarValue(string optName, T &opt) 94 | { 95 | string str, optKey; 96 | 97 | openFile(); 98 | 99 | if (!optFile.is_open()) { 100 | optFile.open(fileName.c_str()); 101 | if (!optFile.is_open()) 102 | FatalError("Cannont open input file for reading."); 103 | } 104 | 105 | // Rewind to the start of the file 106 | optFile.seekg(0,optFile.beg); 107 | 108 | // Search for the given option string 109 | while (getline(optFile,str)) { 110 | // Remove any leading whitespace & see if first word is the input option 111 | stringstream ss; 112 | ss.str(str); 113 | ss >> optKey; 114 | if (optKey.compare(optName)==0) { 115 | if (!(ss >> opt)) { 116 | // This could happen if, for example, trying to assign a string to a double 117 | cerr << "WARNING: Unable to assign value to option " << optName << endl; 118 | string errMsg = "Required option not set: " + optName; 119 | FatalError(errMsg.c_str()) 120 | } 121 | 122 | closeFile(); 123 | return; 124 | } 125 | } 126 | 127 | // Option was not found; throw error & exit 128 | string errMsg = "Required option not found: " + optName; 129 | FatalError(errMsg.c_str()); 130 | } 131 | 132 | template 133 | void fileReader::getMap(string optName, map &opt) { 134 | string str, optKey; 135 | T tmpT; 136 | U tmpU; 137 | bool found; 138 | 139 | openFile(); 140 | 141 | if (!optFile.is_open()) { 142 | optFile.open(fileName.c_str()); 143 | if (!optFile.is_open()) 144 | FatalError("Cannont open input file for reading."); 145 | } 146 | 147 | // Rewind to the start of the file 148 | optFile.seekg(0,optFile.beg); 149 | 150 | // Search for the given option string 151 | while (getline(optFile,str)) { 152 | // Remove any leading whitespace & see if first word is the input option 153 | stringstream ss; 154 | ss.str(str); 155 | ss >> optKey; 156 | if (optKey.compare(optName)==0) { 157 | found = true; 158 | if (!(ss >> tmpT >> tmpU)) { 159 | // This could happen if, for example, trying to assign a string to a double 160 | cerr << "WARNING: Unable to assign value to option " << optName << endl; 161 | string errMsg = "Required option not set: " + optName; 162 | FatalError(errMsg.c_str()) 163 | } 164 | 165 | opt[tmpT] = tmpU; 166 | optKey = ""; 167 | } 168 | } 169 | 170 | if (!found) { 171 | // Option was not found; throw error & exit 172 | string errMsg = "Required option not found: " + optName; 173 | FatalError(errMsg.c_str()); 174 | } 175 | 176 | closeFile(); 177 | } 178 | 179 | template 180 | void fileReader::getVectorValue(string optName, vector &opt) 181 | { 182 | string str, optKey; 183 | 184 | openFile(); 185 | 186 | if (!optFile.is_open()) { 187 | optFile.open(fileName.c_str()); 188 | if (!optFile.is_open()) 189 | FatalError("Cannont open input file for reading."); 190 | } 191 | 192 | // Rewind to the start of the file 193 | optFile.seekg(0,optFile.beg); 194 | 195 | // Search for the given option string 196 | while (getline(optFile,str)) { 197 | // Remove any leading whitespace & see if first word is the input option 198 | stringstream ss; 199 | ss.str(str); 200 | ss >> optKey; 201 | if (optKey.compare(optName)==0) { 202 | int nVals; 203 | if (!(ss >> nVals)) { 204 | // This could happen if, for example, trying to assign a string to a double 205 | cerr << "WARNING: Unable to read number of entries for vector option " << optName << endl; 206 | string errMsg = "Required option not set: " + optName; 207 | FatalError(errMsg.c_str()); 208 | } 209 | 210 | opt.resize(nVals); 211 | for (int i=0; i> opt[i])) { 213 | cerr << "WARNING: Unable to assign all values to vector option " << optName << endl; 214 | string errMsg = "Required option not set: " + optName; 215 | FatalError(errMsg.c_str()) 216 | } 217 | } 218 | 219 | closeFile(); 220 | return; 221 | } 222 | } 223 | 224 | // Option was not found; throw error & exit 225 | string errMsg = "Required option not found: " + optName; 226 | FatalError(errMsg.c_str()); 227 | } 228 | 229 | input::input() 230 | { 231 | 232 | } 233 | 234 | void input::readInputFile(char *filename) 235 | { 236 | /* --- Open Input File --- */ 237 | string fName; 238 | fName.assign(filename); 239 | 240 | opts.setFile(fName); 241 | 242 | /* --- Read input file & store all simulation parameters --- */ 243 | 244 | opts.getScalarValue("equation",equation,1); 245 | opts.getScalarValue("icType",icType,0); 246 | if (equation==ADVECTION_DIFFUSION) { 247 | opts.getScalarValue("advectVx",advectVx,1.); 248 | opts.getScalarValue("advectVy",advectVy,1.); 249 | opts.getScalarValue("advectVz",advectVz,0.); 250 | opts.getScalarValue("diffD",diffD,1.); 251 | opts.getScalarValue("lambda",lambda,1.); 252 | nFields = 1; 253 | } 254 | else if (equation==NAVIER_STOKES) { 255 | opts.getScalarValue("gamma",gamma,1.4); 256 | opts.getScalarValue("RGas",RGas,286.9); 257 | opts.getScalarValue("rhoBound",rhoBound,1.); 258 | opts.getScalarValue("uBound",uBound,.2); 259 | opts.getScalarValue("vBound",vBound,0.); 260 | opts.getScalarValue("wBound",wBound,0.); 261 | opts.getScalarValue("pBound",pBound,.7142857143); 262 | opts.getScalarValue("TBound",TBound,300.); 263 | opts.getScalarValue("TWall",TWall,300.); 264 | opts.getScalarValue("entropySensor",calcEntropySensor,false); 265 | if (icType == 0) { 266 | opts.getScalarValue("rhoIC",rhoIC,rhoBound); 267 | opts.getScalarValue("vxIC",vxIC,uBound); 268 | opts.getScalarValue("vyIC",vyIC,vBound); 269 | opts.getScalarValue("vzIC",vzIC,wBound); 270 | opts.getScalarValue("pIC",pIC,pBound); 271 | } 272 | } 273 | 274 | opts.getScalarValue("timeType",timeType,4); 275 | opts.getScalarValue("dtType",dtType,0); 276 | if (dtType == 1) 277 | opts.getScalarValue("CFL",CFL); 278 | else 279 | opts.getScalarValue("dt",dt); 280 | 281 | opts.getScalarValue("viscous",viscous,0); 282 | opts.getScalarValue("motion",motion,0); 283 | opts.getScalarValue("riemannType",riemannType,0); 284 | opts.getScalarValue("testCase",test_case,0); 285 | opts.getScalarValue("iterMax",iterMax); 286 | 287 | if (viscous && equation == NAVIER_STOKES) { 288 | opts.getScalarValue("Re",Re); 289 | opts.getScalarValue("Lref",Lref,1.0); 290 | opts.getScalarValue("muGas",muGas,1.827e-5); 291 | opts.getScalarValue("prandtl",prandtl,.72); 292 | opts.getScalarValue("SGas",SGas,120.); 293 | opts.getScalarValue("TGas",TGas,291.15); 294 | 295 | opts.getScalarValue("MachBound",MachBound,1.); 296 | opts.getScalarValue("nxBound",nxBound,1.); 297 | opts.getScalarValue("nyBound",nyBound,0.); 298 | opts.getScalarValue("nzBound",nzBound,0.); 299 | 300 | /* --- LDG Flux Parameters --- */ 301 | opts.getScalarValue("LDG_penFact",penFact,0.5); 302 | opts.getScalarValue("LDG_tau",tau,1.0); 303 | } 304 | 305 | opts.getScalarValue("restart",restart,0); 306 | if (restart) { 307 | opts.getScalarValue("restartIter",restartIter); 308 | } 309 | 310 | opts.getScalarValue("meshType",meshType,1); // CREATE_MESH by default 311 | 312 | if (meshType == CREATE_MESH) { 313 | opts.getScalarValue("nDims",nDims,2); 314 | opts.getScalarValue("nx",nx,10); 315 | opts.getScalarValue("ny",ny,10); 316 | opts.getScalarValue("nz",nz,10); 317 | opts.getScalarValue("xmin",xmin,-10.); 318 | opts.getScalarValue("xmax",xmax,10.); 319 | opts.getScalarValue("ymin",ymin,-10.); 320 | opts.getScalarValue("ymax",ymax,10.); 321 | opts.getScalarValue("zmin",zmin,-10.); 322 | opts.getScalarValue("zmax",zmax,10.); 323 | opts.getScalarValue("create_bcTop",create_bcTop,string("periodic")); 324 | opts.getScalarValue("create_bcBottom",create_bcBottom,string("periodic")); 325 | opts.getScalarValue("create_bcLeft",create_bcLeft,string("periodic")); 326 | opts.getScalarValue("create_bcRight",create_bcRight,string("periodic")); 327 | opts.getScalarValue("create_bcFront",create_bcFront,string("periodic")); 328 | opts.getScalarValue("create_bcBack",create_bcBack,string("periodic")); 329 | } 330 | else { 331 | // Reading in the mesh in one form or another 332 | if (meshType == READ_MESH) { 333 | opts.getScalarValue("meshFileName",meshFileName); 334 | } 335 | else if (meshType == OVERSET_MESH) { 336 | opts.getVectorValue("oversetGrids",oversetGrids); 337 | nGrids = oversetGrids.size(); 338 | } 339 | 340 | // Get mesh boundaries, boundary conditions & convert to lowercase 341 | map meshBndTmp; 342 | opts.getMap("mesh_bound",meshBndTmp); 343 | for (auto& B:meshBndTmp) { 344 | string tmp1, tmp2; 345 | tmp1 = B.first; tmp2 = B.second; 346 | std::transform(tmp1.begin(), tmp1.end(), tmp1.begin(), ::tolower); 347 | std::transform(tmp2.begin(), tmp2.end(), tmp2.begin(), ::tolower); 348 | meshBounds[tmp1] = tmp2; 349 | } 350 | } 351 | 352 | opts.getScalarValue("periodicDX",periodicDX,(double)INFINITY); 353 | opts.getScalarValue("periodicDY",periodicDY,(double)INFINITY); 354 | opts.getScalarValue("periodicDZ",periodicDZ,(double)INFINITY); 355 | opts.getScalarValue("periodicTol",periodicTol,1e-6); 356 | 357 | opts.getScalarValue("monitorResFreq",monitorResFreq,10); 358 | opts.getScalarValue("resType",resType,2); 359 | opts.getScalarValue("plotFreq",plotFreq,100); 360 | opts.getScalarValue("plotType",plotType,1); 361 | opts.getScalarValue("restart_freq",restart_freq,100); 362 | opts.getScalarValue("dataFileName",dataFileName,string("simData")); 363 | 364 | /* --- Cleanup ---- */ 365 | opts.closeFile(); 366 | 367 | /* --- Additional Processing --- */ 368 | if (restart) { 369 | initIter = restartIter; 370 | }else{ 371 | initIter = 0; 372 | } 373 | 374 | iter = initIter; 375 | 376 | if (viscous) nonDimensionalize(); 377 | } 378 | 379 | 380 | void input::nonDimensionalize(void) 381 | { 382 | /* --- Calculate Reference / Freestream Non-Dimensionalized Values --- */ 383 | 384 | // Normalize the boundary flow direction 385 | double nMag = sqrt(nxBound*nxBound+nyBound*nyBound+nzBound*nzBound); 386 | nxBound /= nMag; 387 | nyBound /= nMag; 388 | nzBound /= nMag; 389 | 390 | double Tref = TBound; 391 | 392 | // Calculate total velocity & individual components 393 | double UBound = MachBound * sqrt(gamma*RGas*TBound); 394 | uBound = UBound * nxBound; 395 | vBound = UBound * nyBound; 396 | wBound = UBound * nzBound; 397 | 398 | muBound = muGas * pow(TBound/TGas, 1.5) * ((TGas+SGas) / (TBound+SGas)); 399 | 400 | rhoBound = muBound*Re/(UBound*Lref); 401 | pBound = rhoBound * RGas * TBound; 402 | 403 | double rhoRef = rhoBound; 404 | double pRef = rhoRef*UBound*UBound; 405 | double muRef = rhoRef*UBound*Lref; 406 | //double timeRef = Lref / UBound; 407 | //double RRef = (RGas*Tref) / (UBound*UBound); 408 | 409 | c_sth = SGas / TGas; // Sutherland's Law parameter 410 | mu_inf = muGas / muRef; 411 | rt_inf = TGas * RGas / (UBound*UBound); 412 | 413 | // Set up the dimensionless conditions at free-stream boundaries 414 | 415 | rhoBound = 1.; 416 | uBound = uBound / UBound; 417 | vBound = vBound / UBound; 418 | wBound = wBound / UBound; 419 | pBound = pBound / pRef; 420 | TtBound = (TBound/Tref) * (1. + 0.5*(gamma-1.)*MachBound*MachBound); 421 | PtBound = pBound * pow(1. + 0.5*(gamma-1.)*MachBound*MachBound, gamma/(gamma-1.)); 422 | 423 | rhoIC = rhoBound; 424 | vxIC = uBound; 425 | vyIC = vBound; 426 | vzIC = wBound; 427 | pIC = pBound; 428 | muIC = muBound; 429 | TIC = TBound; 430 | } 431 | -------------------------------------------------------------------------------- /src/matrix.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file matrix.cpp 3 | * \brief Class for simplified matrix storage & manipulation 4 | * 5 | * \author - Jacob Crabill 6 | * Aerospace Computing Laboratory (ACL) 7 | * Aero/Astro Department. Stanford University 8 | * 9 | * \version 0.0.1 10 | * 11 | * Flux Reconstruction in C++ (Flurry++) Code 12 | * Copyright (C) 2014 Jacob Crabill. 13 | * 14 | */ 15 | #include "../include/matrix.hpp" 16 | 17 | #include 18 | 19 | template 20 | matrixBase::matrixBase() 21 | { 22 | data.resize(0); 23 | dims = {{0,0,0,0}}; 24 | } 25 | 26 | template 27 | matrixBase::matrixBase(uint inDim0, uint inDim1, uint inDim2, uint inDim3) 28 | { 29 | data.resize(inDim0*inDim1*inDim2*inDim3); 30 | dims = {{inDim0,inDim1,inDim2,inDim3}}; 31 | } 32 | 33 | template 34 | matrix::matrix() 35 | { 36 | 37 | } 38 | 39 | template 40 | matrix::matrix(uint inDim0, uint inDim1) 41 | { 42 | this->data.resize(inDim0*inDim1); 43 | this->dims = {{inDim0,inDim1,1,1}}; 44 | } 45 | 46 | template 47 | matrixBase::matrixBase(const matrixBase &inMatrix) 48 | { 49 | data = inMatrix.data; 50 | dims = inMatrix.dims; 51 | } 52 | 53 | template 54 | matrixBase matrixBase::operator=(const matrixBase &inMatrix) 55 | { 56 | data = inMatrix.data; 57 | dims = inMatrix.dims; 58 | return *this; 59 | } 60 | 61 | template 62 | void matrixBase::setup(uint inDim0, uint inDim1, uint inDim2, uint inDim3) 63 | { 64 | dims = {{inDim0,inDim1,inDim2,inDim3}}; 65 | data.resize(inDim0*inDim1*inDim2*inDim3); 66 | } 67 | 68 | template 69 | T* matrixBase::operator[](int inRow) 70 | { 71 | if (inRow < (int)this->dims[0] && inRow >= 0) { 72 | return &data[inRow*dims[1]]; 73 | } 74 | else { 75 | FatalError("Attempted out-of-bounds access in matrix."); 76 | } 77 | } 78 | 79 | template 80 | T& matrixBase::operator()(int i, int j, int k, int l) 81 | { 82 | if (i<(int)this->dims[0] && i>=0 && j<(int)this->dims[1] && j>=0 && 83 | k<(int)this->dims[2] && k>=0 && l<(int)this->dims[3] && l>= 0) 84 | { 85 | return data[l+dims[3]*(k+dims[2]*(j+dims[1]*i))]; 86 | } 87 | else { 88 | cout << "i=" << i << ", dim0=" << dims[0] << ", j=" << j << ", dim1=" << dims[1] << ", "; 89 | cout << "k=" << k << ", dim2=" << dims[2] << ", l=" << l << ", dim3=" << dims[3] << endl; 90 | FatalError("Attempted out-of-bounds access in Array."); 91 | } 92 | } 93 | 94 | template 95 | T& matrix::operator()(int i, int j) 96 | { 97 | if (i<(int)this->dims[0] && i>=0 && j<(int)this->dims[1] && j>=0) { 98 | return this->data[j+this->dims[1]*i]; 99 | } 100 | else { 101 | cout << "i=" << i << ", dim0=" << this->dims[0] << ", j=" << j << ", dim1=" << this->dims[1]; 102 | FatalError("Attempted out-of-bounds access in matrix."); 103 | } 104 | } 105 | 106 | 107 | template 108 | void matrix::initializeToZero(void) 109 | { 110 | for (uint i=0; idims[0]; i++) 111 | for (uint j=0; jdims[1]; j++) 112 | for (uint k=0; kdims[2]; k++) 113 | for (uint l=0; ldims[3]; l++) 114 | this->data[l+this->dims[3]*(k+this->dims[2]*(j+this->dims[1]*i))] = 0; 115 | } 116 | 117 | template 118 | void matrix::initializeToValue(T val) 119 | { 120 | for (uint i=0; idims[0]; i++) 121 | for (uint j=0; jdims[1]; j++) 122 | for (uint k=0; kdims[2]; k++) 123 | for (uint l=0; ldims[3]; l++) 124 | this->data[l+this->dims[3]*(k+this->dims[2]*(j+this->dims[1]*i))] = val; 125 | } 126 | 127 | //template 128 | //void matrix::insertRow(const vector &vec, int rowNum) 129 | //{ 130 | // data.insert(data.begin()+rowNum,vec.begin(),1); 131 | 132 | // if (dims[0] == 0) { 133 | // dims = {{1,1,1,1}}; 134 | // } 135 | // else { 136 | // dims = {{dims[0]+1,1,1,1}}; 137 | // } 138 | //} 139 | template 140 | void matrixBase::insertRow(const vector &vec, int rowNum) 141 | { 142 | if (N!=2) 143 | FatalError("InsertRow only supported for 2D arrays."); 144 | 145 | if (this->dims[1]!= 0 && vec.size()!=this->dims[1]) 146 | FatalError("Attempting to assign row of wrong size to matrix."); 147 | 148 | if (rowNum==INSERT_AT_END || rowNum==(int)this->dims[0]) { 149 | // Default action - add to end 150 | this->data.insert(this->data.end(),vec.begin(),vec.end()); 151 | }else{ 152 | // Insert at specified location 153 | this->data.insert(this->data.begin()+rowNum*this->dims[1],vec.begin(),vec.end()); 154 | } 155 | 156 | if (this->dims[1]==0) { 157 | this->dims[1] = vec.size(); // This may not be needed (i.e. may never have dim1==0). need to verify how I set up dim0, dim1... 158 | this->dims[2] = 1; 159 | this->dims[3] = 1; 160 | } 161 | this->dims[0]++; 162 | } 163 | 164 | template 165 | void matrix::insertRow(const vector &vec, int rowNum) 166 | { 167 | if (this->dims[1]!= 0 && vec.size()!=this->dims[1]) 168 | FatalError("Attempting to assign row of wrong size to matrix."); 169 | 170 | if (rowNum==INSERT_AT_END || rowNum==(int)this->dims[0]) { 171 | // Default action - add to end 172 | this->data.insert(this->data.end(),vec.begin(),vec.end()); 173 | }else{ 174 | // Insert at specified location 175 | this->data.insert(this->data.begin()+rowNum*this->dims[1],vec.begin(),vec.end()); 176 | } 177 | 178 | if (this->dims[1]==0) this->dims[1]=vec.size(); // This may not be needed (i.e. may never have dim1==0). need to verify how I set up dim0, dim1... 179 | this->dims[0]++; 180 | } 181 | 182 | template 183 | void matrix::insertRow(T *vec, uint rowNum, uint length) 184 | { 185 | if (this->dims[1]!=0 && length!=(int)this->dims[1]) 186 | FatalError("Attempting to assign row of wrong size to matrix."); 187 | 188 | if (rowNum==INSERT_AT_END || rowNum==(int)this->dims[0]) { 189 | // Default action - add to end 190 | this->data.insert(this->data.end(),vec,vec+length); 191 | }else{ 192 | // Insert at specified location 193 | this->data.insert(this->data.begin()+rowNum*this->dims[1],vec,vec+length); 194 | } 195 | 196 | if (this->dims[0]==0) 197 | this->dims = {{0,length,1,1}}; 198 | 199 | this->dims[0]++; 200 | } 201 | 202 | 203 | template 204 | void matrix::insertRowUnsized(const vector &vec) 205 | { 206 | // Add row to end, and resize matrix (add columns) if needed 207 | if (vec.size() > this->dims[1]) addCols(vec.size()-this->dims[1]); 208 | 209 | this->data.insert(this->data.end(),vec.begin(),vec.end()); 210 | 211 | // If row too short, finish filling with 0's 212 | if (vec.size() < this->dims[1]) this->data.insert(this->data.end(),this->dims[1]-vec.size(),(T)0); 213 | 214 | this->dims[0]++; 215 | } 216 | 217 | template 218 | void matrix::insertRowUnsized(T* vec, int length) 219 | { 220 | // Add row to end, and resize matrix (add columns) if needed 221 | if (length > this->dims[1]) addCols(length-this->dims[1]); 222 | 223 | this->data.insert(this->data.end(),vec,vec+length); 224 | 225 | // If row too short, finish filling with 0's 226 | if (length < this->dims[1]) this->data.insert(this->data.end(),this->dims[1]-length,(T)0); 227 | 228 | this->dims[0]++; 229 | } 230 | 231 | template 232 | void matrix::addCol(void) 233 | { 234 | typename vector::iterator it; 235 | for (uint row=0; rowdims[0]; row++) { 236 | it = this->data.begin() + (row+1)*(this->dims[1]+1) - 1; 237 | this->data.insert(it,1,(T)0); 238 | } 239 | this->dims[1]++; 240 | } 241 | 242 | template 243 | void matrix::addCols(int nCols) 244 | { 245 | typename vector::iterator it; 246 | for (uint row=0; rowdims[0]; row++) { 247 | it = this->data.begin() + (row+1)*(this->dims[1]+nCols) - nCols; 248 | this->data.insert(it,nCols,(T)0); 249 | } 250 | this->dims[1] += nCols; 251 | } 252 | 253 | //template 254 | //void matrix::addCol(void) 255 | //{ 256 | // FatalError("addCol not supported for 1D matrices."); 257 | //} 258 | 259 | 260 | //template 261 | //void matrix::addCols(int) 262 | //{ 263 | // FatalError("addCols not supported for 1D matrices."); 264 | //} 265 | 266 | 267 | template 268 | void matrix::removeCols(int nCols) 269 | { 270 | if (nCols == 0) return; 271 | 272 | typename vector::iterator it; 273 | for (uint row=this->dims[0]; row>0; row--) { 274 | it = this->data.begin() + row*this->dims[1]; 275 | this->data.erase(it-nCols,it); 276 | } 277 | this->dims[1] -= nCols; 278 | } 279 | 280 | template 281 | vector matrix::getRow(uint row) 282 | { 283 | vector out; 284 | out.assign(&(this->data[row*this->dims[1]]),&(this->data[row*this->dims[1]])+this->dims[1]); 285 | return out; 286 | } 287 | 288 | template 289 | matrix matrix::getRows(vector ind) 290 | { 291 | matrix out; 292 | for (auto& i:ind) out.insertRow(&(this->data[i*this->dims[1]]),-1,this->dims[1]); 293 | return out; 294 | } 295 | 296 | template 297 | vector matrix::getCol(int col) 298 | { 299 | vector out; 300 | for (uint i=0; idims[0]; i++) out.push_back(this->data[i*this->dims[1]+col]); 301 | return out; 302 | } 303 | 304 | template 305 | void matrixBase::print() 306 | { 307 | // for (uint i=0; i 316 | void matrix::unique(matrix& out, vector &iRow) 317 | { 318 | out.setup(0,0); 319 | 320 | // Setup vector for rows of final matrix that map to each row of initial matrix 321 | iRow.resize(this->dims[0]); 322 | iRow.assign(this->dims[0],-1); 323 | 324 | /* --- For each row in the matrix, compare to all 325 | previous rows to get first unique occurence --- */ 326 | typename vector::iterator itI, itJ; 327 | for (uint i=0; idims[0]; i++) { 328 | itI = this->data.begin() + i*this->dims[1]; 329 | for (uint j=0; jdata.begin() + j*this->dims[1]; 331 | if (equal(itI,itI+this->dims[1],itJ)) { 332 | iRow[i] = iRow[j]; 333 | break; 334 | } 335 | } 336 | 337 | // If no prior occurance found, put in 'out' matrix 338 | if (iRow[i]==-1) { 339 | out.insertRow(&(this->data[i*this->dims[1]]),-1,this->dims[1]); 340 | iRow[i] = out.getDim0() - 1; 341 | } 342 | } 343 | } 344 | 345 | template 346 | T *matrixBase::getData(void) 347 | { 348 | return data.data(); 349 | } 350 | 351 | 352 | // Fix for compiler to know which template types will be needed later (and therefore must now be compiled): 353 | template class matrixBase; 354 | template class matrixBase; 355 | template class matrixBase; 356 | template class matrixBase; 357 | 358 | template class matrixBase; 359 | template class matrixBase; 360 | template class matrixBase; 361 | template class matrixBase; 362 | 363 | template class matrixBase; 364 | template class matrixBase; 365 | template class matrixBase; 366 | template class matrixBase; 367 | 368 | template class matrixBase; 369 | template class matrixBase; 370 | template class matrixBase; 371 | template class matrixBase; 372 | 373 | template class matrixBase,1>; 374 | template class matrixBase,2>; 375 | template class matrixBase,3>; 376 | template class matrixBase,4>; 377 | 378 | template class matrix; 379 | template class matrix; 380 | -------------------------------------------------------------------------------- /src/output.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file output.cpp 3 | * \brief Functions for solution restart & visualization data output 4 | * 5 | * \author - Jacob Crabill 6 | * Aerospace Computing Laboratory (ACL) 7 | * Aero/Astro Department. Stanford University 8 | * 9 | * \version 0.0.1 10 | * 11 | * Flux Reconstruction in C++ (Flurry++) Code 12 | * Copyright (C) 2014 Jacob Crabill. 13 | * 14 | */ 15 | #include "../include/output.hpp" 16 | 17 | #include 18 | 19 | // Used for making sub-directories 20 | #ifndef _NO_MPI 21 | #include 22 | #include 23 | #include 24 | #include "mpi.h" 25 | #endif 26 | 27 | void writeData(solver *Solver, input *params) 28 | { 29 | if (params->plotType == 0) { 30 | writeCSV(Solver,params); 31 | } 32 | else if (params->plotType == 1) { 33 | writeParaview(Solver,Solver->Geo,params); 34 | } 35 | 36 | } 37 | 38 | void writeCSV(solver *Solver, input *params) 39 | { 40 | // ofstream dataFile; 41 | // int iter = params->iter; 42 | 43 | // char fileNameC[100]; 44 | // string fileName = params->dataFileName; 45 | // sprintf(fileNameC,"%s.csv.%.09d",&fileName[0],iter); 46 | 47 | // dataFile.precision(15); 48 | // dataFile.setf(ios_base::fixed); 49 | 50 | // dataFile.open(fileNameC); 51 | 52 | // // Vector of primitive variables 53 | // vector V; 54 | // // Location of solution point 55 | // point pt; 56 | 57 | // // Write header: 58 | // // x y z(=0) rho [u v p] 59 | // dataFile << "x,y,z,"; 60 | // if (params->equation == ADVECTION_DIFFUSION) { 61 | // dataFile << "rho" << endl; 62 | // } 63 | // else if (params->equation == NAVIER_STOKES) { 64 | // dataFile << "rho,u,v,p" << endl; 65 | // } 66 | 67 | // // Solution data 68 | // for (auto& e:Solver->eles) { 69 | // if (params->motion != 0) { 70 | // e.updatePosSpts(); 71 | // e.updatePosFpts(); 72 | // e.setPpts(); 73 | // } 74 | // for (uint spt=0; sptiter; 114 | 115 | char fileNameC[100]; 116 | string fileName = params->dataFileName; 117 | 118 | #ifndef _NO_MPI 119 | /* --- All processors write their solution to their own .vtu file --- */ 120 | sprintf(fileNameC,"%s_%.09d/%s_%.09d_%d.vtu",&fileName[0],iter,&fileName[0],iter,params->rank); 121 | #else 122 | /* --- Filename to write to --- */ 123 | sprintf(fileNameC,"%s_%.09d.vtu",&fileName[0],iter); 124 | #endif 125 | 126 | if (params->rank == 0) 127 | cout << "Writing ParaView file " << string(fileNameC) << "... " << flush; 128 | 129 | #ifndef _NO_MPI 130 | /* --- Write 'master' .pvtu file --- */ 131 | if (params->rank == 0) { 132 | ofstream pVTU; 133 | char pvtuC[100]; 134 | sprintf(pvtuC,"%s_%.09d.pvtu",&fileName[0],iter); 135 | 136 | pVTU.open(pvtuC); 137 | 138 | pVTU << "" << endl; 139 | pVTU << "" << endl; 140 | pVTU << " " << endl; 141 | // NOTE: Must be careful with order here [particularly of vector data], or else ParaView gets confused 142 | pVTU << " " << endl; 143 | pVTU << " " << endl; 144 | if (params->equation == NAVIER_STOKES) { 145 | pVTU << " " << endl; 146 | pVTU << " " << endl; 147 | if (params->calcEntropySensor) { 148 | pVTU << " " << endl; 149 | } 150 | if (params->motion) { 151 | pVTU << " " << endl; 152 | } 153 | } 154 | pVTU << " " << endl; 155 | pVTU << " " << endl; 156 | pVTU << " " << endl; 157 | pVTU << " " << endl; 158 | 159 | char filnameTmpC[100]; 160 | for (int p=0; pnproc; p++) { 161 | sprintf(filnameTmpC,"%s_%.09d/%s_%.09d_%d.vtu",&fileName[0],iter,&fileName[0],iter,p); 162 | pVTU << " " << endl; 163 | } 164 | pVTU << " " << endl; 165 | pVTU << "" << endl; 166 | 167 | pVTU.close(); 168 | 169 | char datadirC[100]; 170 | char *datadir = &datadirC[0]; 171 | sprintf(datadirC,"%s_%.09d",&fileName[0],iter); 172 | 173 | /* --- Master node creates a subdirectory to store .vtu files --- */ 174 | if (params->rank == 0) { 175 | struct stat st = {0}; 176 | if (stat(datadir, &st) == -1) { 177 | mkdir(datadir, 0755); 178 | } 179 | } 180 | } 181 | 182 | /* --- Wait for all processes to get here, otherwise there won't be a 183 | * directory to put .vtus into --- */ 184 | MPI_Barrier(MPI_COMM_WORLD); 185 | #endif 186 | 187 | dataFile.open(fileNameC); 188 | dataFile.precision(16); 189 | 190 | // File header 191 | dataFile << "" << endl; 192 | dataFile << "" << endl; 193 | dataFile << " " << endl; 194 | 195 | 196 | // Write cell header 197 | dataFile << " nVerts << "\" NumberOfCells=\"" << Geo->nEles << "\">" << endl; 198 | 199 | /* ==== Write out solution to file ==== */ 200 | 201 | dataFile << " " << endl; 202 | 203 | /* --- Density --- */ 204 | dataFile << " " << endl; 205 | for(int i=0; inVerts; i++) { 206 | dataFile << Solver->U(i,0) << " "; 207 | } 208 | dataFile << endl; 209 | dataFile << " " << endl; 210 | 211 | if (params->equation == NAVIER_STOKES) { 212 | /* --- Velocity --- */ 213 | dataFile << " " << endl; 214 | for(int i=0; inVerts; i++) { 215 | dataFile << Solver->U(i,1)/Solver->U(i,0) << " " << Solver->U(i,2)/Solver->U(i,0) << " " << Solver->U(i,3)/Solver->U(i,0) << " "; 216 | } 217 | dataFile << endl; 218 | dataFile << " " << endl; 219 | 220 | /* --- Pressure --- */ 221 | dataFile << " " << endl; 222 | for(int i=0; inVerts; i++) { 223 | double rho = Solver->U(i,0); 224 | double u = Solver->U(i,1)/rho; 225 | double v = Solver->U(i,2)/rho; 226 | double w = Solver->U(i,3)/rho; 227 | double p = (params->gamma-1)*(Solver->U(i,4) - 0.5*rho*(u*u+v*v+w*w)); 228 | dataFile << p << " "; 229 | } 230 | dataFile << endl; 231 | dataFile << " " << endl; 232 | } 233 | 234 | // if (params->equation == NAVIER_STOKES && params->calcEntropySensor) { 235 | // /* --- Entropy Error Estimate --- */ 236 | // dataFile << " " << endl; 237 | // for(int k=0; k" << endl; 242 | // } 243 | 244 | /* --- End of Cell's Solution Data --- */ 245 | 246 | dataFile << " " << endl; 247 | 248 | /* ==== Write Out Cell Points & Connectivity==== */ 249 | 250 | /* --- Write out the plot point coordinates --- */ 251 | dataFile << " " << endl; 252 | dataFile << " " << endl; 253 | 254 | // Loop over plot points in element 255 | for(int i=0; inVerts; i++) { 256 | for(int j=0;jnDims;j++) { 257 | dataFile << Geo->xv(i,j) << " "; 258 | } 259 | } 260 | 261 | dataFile << endl; 262 | dataFile << " " << endl; 263 | dataFile << " " << endl; 264 | 265 | /* --- Write out Cell data: connectivity, offsets, element types --- */ 266 | dataFile << " " << endl; 267 | 268 | /* --- Write connectivity array --- */ 269 | dataFile << " " << endl; 270 | 271 | for (int i=0; inEles; i++) { 272 | for (int j=0; jc2nv[i]; j++) { 273 | dataFile << Geo->c2v(i,j) << " "; 274 | } 275 | dataFile << endl; 276 | } 277 | dataFile << " " << endl; 278 | 279 | // Write cell-node offsets 280 | 281 | dataFile << " " << endl; 282 | 283 | int offset = 0; 284 | for (int i=0; inEles; i++) { 285 | offset += Geo->c2nv[i]; 286 | dataFile << offset << " "; 287 | } 288 | dataFile << endl; 289 | 290 | dataFile << " " << endl; 291 | 292 | // Write VTK element type 293 | // 5 = tri, 9 = quad, 10 = tet, 12 = hex 294 | map et2et; 295 | et2et[TRI] = 5; 296 | et2et[QUAD] = 9; 297 | et2et[TET] = 10; 298 | et2et[HEX] = 12; 299 | et2et[PRISM] = 13; 300 | et2et[PYRAMID] = 14; 301 | 302 | dataFile << " " << endl; 303 | for(int i=0; inEles; i++) { 304 | dataFile << et2et[Geo->ctype[i]] << " "; 305 | } 306 | dataFile << endl; 307 | dataFile << " " << endl; 308 | 309 | /* --- Write cell and piece footers --- */ 310 | dataFile << " " << endl; 311 | dataFile << " " << endl; 312 | 313 | /* --- Write footer of file & close --- */ 314 | dataFile << " " << endl; 315 | dataFile << "" << endl; 316 | 317 | dataFile.close(); 318 | 319 | if (params->rank == 0) cout << "done." << endl; 320 | } 321 | 322 | 323 | void writeResidual(solver *Solver, input *params) 324 | { 325 | vector res(params->nFields); 326 | int iter = params->iter; 327 | 328 | if (params->resType == 3) { 329 | // Infinity Norm 330 | for (uint iv=0; ivnVerts; iv++) { 331 | if (Solver->Geo->v2b[iv]) continue; 332 | for (int i=0; inFields; i++) { 333 | double resTmp = Solver->U(iv,0) / Solver->vol[iv]; 334 | if(std::isnan(resTmp)) FatalError("NaN Encountered in Solution Residual!"); 335 | 336 | res[i] = max(res[i],resTmp); 337 | } 338 | } 339 | } 340 | else if (params->resType == 1) { 341 | // 1-Norm 342 | for (uint iv=0; ivnVerts; iv++) { 343 | if (Solver->Geo->v2b[iv]) continue; 344 | for (int i=0; inFields; i++) { 345 | double resTmp = Solver->U(iv,0) / Solver->vol[iv]; 346 | if(std::isnan(resTmp)) FatalError("NaN Encountered in Solution Residual!"); 347 | 348 | res[i] += resTmp; 349 | } 350 | } 351 | } 352 | else if (params->resType == 2) { 353 | // 2-Norm 354 | for (uint iv=0; ivnVerts; iv++) { 355 | if (Solver->Geo->v2b[iv]) continue; 356 | for (int i=0; inFields; i++) { 357 | double resTmp = Solver->U(iv,0) / Solver->vol[iv]; 358 | resTmp *= resTmp; 359 | if(std::isnan(resTmp)) FatalError("NaN Encountered in Solution Residual!"); 360 | 361 | res[i] += resTmp; 362 | } 363 | } 364 | } 365 | 366 | #ifndef _NO_MPI 367 | if (params->nproc > 1) { 368 | if (params->resType == 3) { 369 | vector resTmp = res; 370 | MPI_Reduce(resTmp.data(), res.data(), params->nFields, MPI_DOUBLE, MPI_MAX, 0,MPI_COMM_WORLD); 371 | } 372 | else if (params->resType == 1 || params->resType == 2) { 373 | vector resTmp = res; 374 | MPI_Reduce(resTmp.data(), res.data(), params->nFields, MPI_DOUBLE, MPI_SUM, 0,MPI_COMM_WORLD); 375 | } 376 | } 377 | #endif 378 | 379 | // If taking 2-norm, res is sum squared; take sqrt to complete 380 | if (params->rank == 0) { 381 | if (params->resType == 2) { 382 | for (auto& R:res) R = sqrt(R); 383 | } 384 | 385 | int colW = 16; 386 | cout.precision(6); 387 | cout.setf(ios::scientific, ios::floatfield); 388 | if (iter==1 || (iter/params->monitorResFreq)%25==0) { 389 | cout << endl; 390 | cout << setw(8) << left << "Iter"; 391 | if (params->equation == ADVECTION_DIFFUSION) { 392 | cout << " Residual " << endl; 393 | }else if (params->equation == NAVIER_STOKES) { 394 | cout << setw(colW) << left << "rho"; 395 | cout << setw(colW) << left << "rhoU"; 396 | cout << setw(colW) << left << "rhoV"; 397 | if (params->nDims == 3) 398 | cout << setw(colW) << left << "rhoW"; 399 | cout << setw(colW) << left << "rhoE"; 400 | if (params->dtType == 1) 401 | cout << setw(colW) << left << "deltaT"; 402 | } 403 | cout << endl; 404 | } 405 | 406 | cout << setw(8) << left << iter; 407 | for (int i=0; inFields; i++) { 408 | cout << setw(colW) << left << res[i]; 409 | } 410 | if (params->dtType == 1) 411 | cout << setw(colW) << left << params->dt; 412 | cout << endl; 413 | } 414 | } 415 | 416 | void writeMeshTecplot(solver* Solver, input* params) 417 | { 418 | if (params->nDims == 2) return; 419 | 420 | ofstream dataFile; 421 | 422 | char fileNameC[100]; 423 | string fileName = params->dataFileName; 424 | 425 | geo* Geo = Solver->Geo; 426 | 427 | #ifndef _NO_MPI 428 | /* --- All processors write their solution to their own .vtu file --- */ 429 | sprintf(fileNameC,"%s/%s_%d.plt",&fileName[0],&fileName[0],params->rank); 430 | #else 431 | /* --- Filename to write to --- */ 432 | sprintf(fileNameC,"%s.plt",&fileName[0]); 433 | #endif 434 | 435 | //if (params->rank == 0) 436 | // cout << "Writing Tecplot mesh file " << string(fileNameC) << "... " << flush; 437 | cout << "Writing Tecplot mesh file " << string(fileNameC) << "... " << endl; 438 | 439 | #ifndef _NO_MPI 440 | /* --- Write folder for output mesh files --- */ 441 | if (params->rank == 0) { 442 | 443 | char datadirC[100]; 444 | char *datadir = &datadirC[0]; 445 | sprintf(datadirC,"%s",&fileName[0]); 446 | 447 | /* --- Master node creates a subdirectory to store .vtu files --- */ 448 | if (params->rank == 0) { 449 | struct stat st = {0}; 450 | if (stat(datadir, &st) == -1) { 451 | mkdir(datadir, 0755); 452 | } 453 | } 454 | } 455 | 456 | /* --- Wait for all processes to get here, otherwise there won't be a 457 | * directory to put .vtus into --- */ 458 | MPI_Barrier(MPI_COMM_WORLD); 459 | #endif 460 | 461 | cout << "rank " << params->rank << ": Opening file for writing" << endl; 462 | 463 | dataFile.open(fileNameC); 464 | dataFile.precision(16); 465 | 466 | // Count wall-boundary nodes 467 | int nNodesWall = Geo->iwall.size(); 468 | // Count overset-boundary nodes 469 | int nNodesOver = Geo->iover.size(); 470 | 471 | int gridID = Geo->gridID; 472 | 473 | cout << "nNodesWall = " << nNodesWall << ", nNodesOver = " << nNodesOver << ", gridID = " << gridID << endl; 474 | 475 | int nPrism = 0; 476 | int nNodes = Geo->nVerts; 477 | int nCells = Geo->nEles; 478 | int nHex = nCells; 479 | 480 | cout << "nNodes = " << nNodes << ", nEles = " << nCells << endl; 481 | 482 | dataFile << "# " << nPrism << " " << nHex << " " << nNodes << " " << nCells << " " << nNodesWall << " " << nNodesOver << endl; 483 | dataFile << "TITLE = \"" << fileName << "\"" << endl; 484 | dataFile << "VARIABLES = \"X\", \"Y\", \"Z\", \"bodyTag\", \"IBLANK\"" << endl; 485 | dataFile << "ZONE T = \"VOL_MIXED\", N=" << nNodes << ", E=" << nCells << ", ET=BRICK, F=FEPOINT" << endl; 486 | 487 | for (int iv=0; ivxv(iv,0) << " " << Geo->xv(iv,1) << " " << Geo->xv(iv,2) << " " << gridID << endl; 489 | dataFile << Geo->xv(iv,0) << " " << Geo->xv(iv,1) << " " << Geo->xv(iv,2) << " " << gridID << " " << Geo->iblank[iv] << endl; 490 | } 491 | 492 | for (int ic=0; icc2v(ic,j)+1 << " "; 495 | } 496 | dataFile << endl; 497 | } 498 | 499 | // output wall-boundary node IDs 500 | for (auto& iv:Geo->iwall) 501 | dataFile << iv+1 << endl; 502 | 503 | // output overset-boundary node IDs 504 | for (auto& iv:Geo->iover) 505 | dataFile << iv+1 << endl; 506 | 507 | dataFile.close(); 508 | 509 | if (params->rank == 0) cout << "done." << endl; 510 | } 511 | -------------------------------------------------------------------------------- /src/solver.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file solver.cpp 3 | * \brief Class to store all solution data & apply FR operators 4 | * 5 | * \author - Jacob Crabill 6 | * Aerospace Computing Laboratory (ACL) 7 | * Aero/Astro Department. Stanford University 8 | * 9 | * \version 0.0.1 10 | * 11 | * Flux Reconstruction in C++ (Flurry++) Code 12 | * Copyright (C) 2014 Jacob Crabill. 13 | * 14 | */ 15 | 16 | #include "../include/solver.hpp" 17 | 18 | #include 19 | #include 20 | 21 | #include "input.hpp" 22 | #include "flux.hpp" 23 | #include "geo.hpp" 24 | 25 | solver::solver() 26 | { 27 | //tg = NULL; 28 | } 29 | 30 | solver::~solver() 31 | { 32 | // if (tg != NULL) 33 | // delete tg; 34 | } 35 | 36 | void solver::setup(input *params, geo *Geo) 37 | { 38 | this->params = params; 39 | this->Geo = Geo; 40 | //this->tg = Geo->tg; // Geo will have initialized this already if needed 41 | 42 | params->time = 0.; 43 | 44 | if (params->equation == NAVIER_STOKES) { 45 | if (params->nDims == 2) 46 | params->nFields = 4; 47 | else if (params->nDims == 3) 48 | params->nFields = 5; 49 | } 50 | 51 | nDims = params->nDims; 52 | nFields = params->nFields; 53 | 54 | gridID = Geo->gridID; 55 | gridRank = Geo->gridRank; 56 | nprocPerGrid = Geo->nprocPerGrid; 57 | 58 | /* Setup the final dual-mesh data structures */ 59 | setupDualMesh(); 60 | 61 | if (params->restart) 62 | readRestartFile(); 63 | else 64 | initializeSolution(); 65 | 66 | if (params->meshType == OVERSET_MESH) 67 | setupOverset(); 68 | 69 | /* Additional Setup */ 70 | 71 | // Time advancement setup 72 | switch (params->timeType) { 73 | case 0: 74 | nRKSteps = 1; 75 | RKb = {1}; 76 | break; 77 | case 4: 78 | nRKSteps = 4; 79 | RKa = {.5, .5, 1.}; 80 | RKb = {1./6., 1./3., 1./3., 1./6.}; 81 | break; 82 | default: 83 | FatalError("Time-Stepping type not supported."); 84 | } 85 | 86 | F.setup(nEdges,nDims,nFields); 87 | 88 | tempFL.setup(nDims,nFields); 89 | tempFR.setup(nDims,nFields); 90 | 91 | waveSp.resize(nEdges); 92 | 93 | divF.setup(nRKSteps,nVerts,nFields); 94 | 95 | if (params->viscous) { 96 | gradU.setup(nVerts,nDims,nFields); 97 | } 98 | 99 | //#ifndef _NO_MPI 100 | // finishMpiSetup(); 101 | //#endif 102 | } 103 | 104 | void solver::update(void) 105 | { 106 | // For RK time-stepping, store the starting solution values 107 | if (nRKSteps>1) 108 | copyU_U0(); 109 | 110 | /* Intermediate residuals for Runge-Kutta time integration */ 111 | 112 | for (int step=0; steprkTime = params->time; 116 | else 117 | params->rkTime = params->time + RKa[step-1]*params->dt; 118 | 119 | moveMesh(step); 120 | 121 | calcResidual(step); 122 | 123 | /* If in first stage, compute CFL-based timestep */ 124 | if (step == 0 && params->dtType == 1) calcDt(); // -- NOT CONSISTENT WITH MOVING MESH SEQUENCE -- 125 | 126 | timeStepA(step); 127 | 128 | } 129 | 130 | /* Final Runge-Kutta time advancement step */ 131 | 132 | if (nRKSteps == 1) { 133 | params->rkTime = params->time; 134 | /* Calculate CFL-based timestep */ 135 | if (params->dtType == 1) calcDt(); 136 | } 137 | else { 138 | params->rkTime = params->time + params->dt; 139 | } 140 | 141 | moveMesh(nRKSteps-1); 142 | 143 | calcResidual(nRKSteps-1); 144 | 145 | // Reset solution to initial-stage values 146 | if (nRKSteps>1) 147 | copyU0_U(); 148 | 149 | for (int step=0; steptime += params->dt; 153 | } 154 | 155 | 156 | void solver::calcResidual(int step) 157 | { 158 | 159 | #ifndef _NO_MPI 160 | doCommunication(); 161 | #endif 162 | 163 | applyBoundaryConditions(); 164 | 165 | if (params->viscous) 166 | calcGradU(); 167 | 168 | calcInviscidFlux(); 169 | 170 | #ifndef _NO_MPI 171 | calcInviscidFlux_mpi(); 172 | #endif 173 | 174 | if (params->viscous) { 175 | 176 | calcViscousFlux(); 177 | 178 | #ifndef _NO_MPI 179 | calcViscousFlux_mpi(); 180 | #endif 181 | 182 | } 183 | 184 | calcFluxDivergence(step); 185 | 186 | } 187 | 188 | void solver::setupDualMesh(void) { 189 | 190 | if (params->rank==0) cout << "Solver: Setting up dual mesh arrays" << endl; 191 | 192 | nVerts = Geo->nVerts; 193 | nEdges = Geo->nEdges; 194 | 195 | xv = Geo->xv; 196 | v2ne = Geo->v2nv; 197 | v2e = Geo->v2e; 198 | v2v = Geo->v2v; 199 | Ae = Geo->e2A; 200 | vol = Geo->v2vol; 201 | bcList = Geo->bcList; 202 | bndNorm = Geo->bndNorm; 203 | bndArea = Geo->bndArea; 204 | bndPts = Geo->bndPts; 205 | nBndPts = Geo->nBndPts; 206 | 207 | // Solution at vertices 208 | U.setup(nVerts,nFields); 209 | 210 | // Flux at faces 211 | F.setup(nEdges,nFields); 212 | Fn.setup(nEdges,nFields); 213 | 214 | // Dual-mesh face areas 215 | A.setup(nVerts,getMax(v2ne)); 216 | normDir.setup(nVerts,getMax(v2ne)); 217 | normDir.initializeToValue(1); 218 | for (int i=0; ie2v(v2e(i,j),1)) { 222 | // This node is on "right" of edge, so "flip the normal" 223 | normDir(i,j) = -1; 224 | } 225 | } 226 | } 227 | } 228 | 229 | void solver::finishMpiSetup(void) 230 | { 231 | // if (params->rank==0) cout << "Solver: Setting up MPI face communicataions" << endl; 232 | } 233 | 234 | 235 | void solver::calcInviscidFlux(void) 236 | { 237 | #pragma omp parallel for 238 | for (int i=0; ie2v(i,0); 240 | int ivR = Geo->e2v(i,1); 241 | 242 | inviscidFlux(U[ivL],tempFL,params); 243 | inviscidFlux(U[ivR],tempFR,params); 244 | 245 | double tempFnL[5] = {0,0,0,0,0}; 246 | double tempFnR[5] = {0,0,0,0,0}; 247 | 248 | // Get primitive variables 249 | double rhoL = U(ivL,0); double rhoR = U(ivR,0); 250 | double uL = U(ivL,1)/rhoL; double uR = U(ivR,1)/rhoR; 251 | double vL = U(ivL,2)/rhoL; double vR = U(ivR,2)/rhoR; 252 | 253 | double wL, pL, vnL=0.; 254 | double wR, pR, vnR=0.; 255 | 256 | // Calculate pressure 257 | if (params->nDims==2) { 258 | pL = (params->gamma-1.0)*(U(ivL,3)-rhoL*(uL*uL+vL*vL)); 259 | pR = (params->gamma-1.0)*(U(ivR,3)-rhoR*(uR*uR+vR*vR)); 260 | } 261 | else { 262 | wL = U(ivL,3)/rhoL; wR = U(ivR,3)/rhoR; 263 | pL = (params->gamma-1.0)*(U(ivL,4)-rhoL*(uL*uL+vL*vL+wL*wL)); 264 | pR = (params->gamma-1.0)*(U(ivR,4)-rhoR*(uR*uR+vR*vR+wR*wR)); 265 | } 266 | 267 | // Get normal fluxes, normal velocities 268 | Vec3 norm = point(xv[ivR]) - point(xv[ivL]); 269 | norm /= norm.norm(); 270 | 271 | for (int dim=0; dimnDims; dim++) { 272 | vnL += norm[dim]*U(ivL,dim+1)/rhoL; 273 | vnR += norm[dim]*U(ivR,dim+1)/rhoR; 274 | for (int i=0; inFields; i++) { 275 | tempFnL[i] += norm[dim]*tempFL(dim,i); 276 | tempFnR[i] += norm[dim]*tempFR(dim,i); 277 | } 278 | } 279 | 280 | // Get maximum eigenvalue for diffusion coefficient 281 | double csqL = max(params->gamma*pL/rhoL,0.0); 282 | double csqR = max(params->gamma*pR/rhoR,0.0); 283 | double eigL = std::fabs(vnL) + sqrt(csqL); 284 | double eigR = std::fabs(vnR) + sqrt(csqR); 285 | waveSp[i] = max(eigL,eigR); 286 | 287 | // Calculate Rusanov flux 288 | for (int k=0; knFields; k++) { 289 | Fn(i,k) = 0.5*(tempFnL[k]+tempFnR[k] - waveSp[i]*(U(ivR,k)-U(ivL,k))); 290 | } 291 | } 292 | } 293 | 294 | void solver::doCommunication() 295 | { 296 | 297 | } 298 | 299 | void solver::calcInviscidFlux_mpi() 300 | { 301 | 302 | } 303 | 304 | void solver::calcViscousFlux(void) 305 | { 306 | 307 | } 308 | 309 | void solver::calcViscousFlux_mpi() 310 | { 311 | 312 | } 313 | 314 | 315 | void solver::calcFluxDivergence(int step) 316 | { 317 | #pragma omp parallel for 318 | for (int i=0; iv2b[i]) continue; 320 | for (int k=0; k tmpF(nDims,nFields); 330 | #pragma omp parallel for collapse(2) 331 | for (int ib=0; ibv2b[i]) continue; 366 | 367 | double rho = U(i,0); 368 | double rhovMagSq = U(i,1)*U(i,1) + U(i,2)*U(i,2); 369 | if (nDims == 3) 370 | rhovMagSq += U(i,3)*U(i,3); 371 | double p = (params->gamma-1)*U(i,nDims+1) - 0.5*rhovMagSq/rho; 372 | double a = sqrt(max(params->gamma*p/rho,0.)); 373 | 374 | double minA = INFINITY; 375 | for (int j=0; jCFL*dx/a); 380 | } 381 | 382 | #ifndef _NO_MPI 383 | double dtTmp = dt; 384 | MPI_Allreduce(&dtTmp, &dt, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD); 385 | #endif 386 | 387 | params->dt = dt; 388 | } 389 | 390 | 391 | void solver::timeStepA(int step) 392 | { 393 | #pragma omp parallel for 394 | for (int i=0; iv2b[i]) continue; 396 | for (int j=0; jdt*divF(step,i,j)/vol[i]; 398 | } 399 | } 400 | } 401 | 402 | 403 | void solver::timeStepB(int step) 404 | { 405 | #pragma omp parallel for 406 | for (int i=0; iv2b[i]) continue; 408 | for (int j=0; jdt*divF(step,i,j)/vol[i]; 410 | } 411 | } 412 | } 413 | 414 | void solver::copyU_U0(void) 415 | { 416 | U0 = U; 417 | } 418 | 419 | void solver::copyU0_U(void) 420 | { 421 | U = U0; 422 | } 423 | 424 | void solver::readRestartFile(void) { 425 | 426 | ifstream dataFile; 427 | dataFile.precision(15); 428 | 429 | // Get the file name & open the file 430 | char fileNameC[50]; 431 | string fileName = params->dataFileName; 432 | #ifndef _NO_MPI 433 | /* --- All processors write their solution to their own .vtu file --- */ 434 | sprintf(fileNameC,"%s_%.09d/%s_%.09d_%d.vtu",&fileName[0],params->restartIter,&fileName[0],params->restartIter,params->rank); 435 | #else 436 | sprintf(fileNameC,"%s_%.09d.vtu",&fileName[0],params->restartIter); 437 | #endif 438 | 439 | if (params->rank==0) cout << "Solver: Restarting from " << fileNameC << endl; 440 | 441 | dataFile.open(fileNameC); 442 | 443 | if (!dataFile.is_open()) 444 | FatalError("Cannont open restart file."); 445 | 446 | // Find the start of the UnstructuredData region 447 | bool found = false; 448 | string str; 449 | while (getline(dataFile,str)) { 450 | stringstream ss; 451 | ss.str(str); 452 | ss >> str; 453 | if (str.compare("")==0) { 454 | found = true; 455 | break; 456 | } 457 | } 458 | 459 | if (!found) 460 | FatalError("Cannot fine UnstructuredData tag in restart file."); 461 | 462 | dataFile.close(); 463 | 464 | if (params->rank==0) cout << "Solver: Done reading restart file." << endl; 465 | 466 | // Finish setting up MPI faces 467 | if (params->rank==0 && params->nproc>1) cout << "MPIFace: Setting up MPI face communications." << endl; 468 | 469 | } 470 | 471 | void solver::initializeSolution() 472 | { 473 | if (params->rank==0) cout << "Solver: Initializing Solution... " << flush; 474 | 475 | for (uint i=0; iicType) { 477 | case 0: // Uniform Flow 478 | U(i,0) = params->rhoBound; 479 | U(i,1) = params->rhoBound*params->uBound; 480 | U(i,2) = params->rhoBound*params->vBound; 481 | if (nDims == 3) 482 | U(i,3) = params->rhoBound*params->wBound; 483 | U(i,nDims+1) = params->pBound/(params->gamma-1) + 0.5*params->rhoBound*params->Uinf*params->Uinf; 484 | break; 485 | 486 | case 1: // Isentropic Vortex test case 487 | // Grab from HiFiLES or Flurry 488 | break; 489 | 490 | case 2: // Fake shock tube 491 | if (xv(i,2) > 0) { 492 | U(i,0) = 10; 493 | U(i,1) = 0; 494 | U(i,2) = 0; 495 | U(i,3) = 0; 496 | U(i,4) = 1000; 497 | } 498 | else { 499 | U(i,0) = .1; 500 | U(i,1) = 0; 501 | U(i,2) = 0; 502 | U(i,3) = 0; 503 | U(i,4) = 10; 504 | } 505 | break; 506 | 507 | } 508 | } 509 | 510 | /* If running a moving-mesh case and using CFL-based time-stepping, 511 | * calc initial dt for grid velocity calculation */ 512 | /* If running a moving-mesh case and using CFL-based time-stepping, 513 | * calc initial dt for grid velocity calculation */ 514 | //if ( (params->motion!=0 || params->slipPenalty==1) && params->dtType == 1 ) { 515 | if (params->dtType == 1) 516 | calcDt(); 517 | 518 | if (params->rank == 0) cout << "done." << endl; 519 | } 520 | 521 | /* ---- Overset-Grid Functions ---- */ 522 | 523 | void solver::setupOverset(void) 524 | { 525 | //setupOversetData(); 526 | 527 | // Give TIOGA a pointer to this solver for access to callback functions 528 | //tg->setcallback(this); 529 | 530 | //Geo->updateOversetConnectivity(); 531 | } 532 | 533 | void solver::callDataUpdateTIOGA(void) 534 | { 535 | //tg->dataUpdate(params->nFields,U.getData(),0); 536 | } 537 | -------------------------------------------------------------------------------- /src/solver_bounds.cpp: -------------------------------------------------------------------------------- 1 | #include "solver.hpp" 2 | 3 | void solver::applyBoundaryConditions(void) 4 | { 5 | for (int bnd=0; bndnBounds; bnd++) { 6 | 7 | #pragma omp parallel for 8 | for (int i=0; inBndPts[bnd]; i++) { 9 | int iv = bndPts(bnd,i); 10 | 11 | switch (bcList[bnd]) { 12 | case SUP_IN: 13 | U(iv,0) = params->rhoBound; 14 | U(iv,1) = params->rhoBound*params->uBound; 15 | U(iv,2) = params->rhoBound*params->vBound; 16 | if (params->nDims == 3) 17 | U(iv,3) = params->rhoBound*params->wBound; 18 | U(iv,params->nDims+1) = params->pBound/(params->gamma-1) + 0.5*U(iv,0)*params->Uinf*params->Uinf; 19 | 20 | case SUP_OUT: 21 | // Extrapolated, so do nothing 22 | break; 23 | 24 | case SLIP_WALL: { 25 | Vec3 norm = bndNorm(bnd,i); 26 | double rho = U(iv,0); 27 | double u = U(iv,1)/rho; 28 | double v = U(iv,2)/rho; 29 | double w = 0; 30 | if (nDims == 3) 31 | w = U(iv,3)/rho; 32 | double vn = u*norm[0]+v*norm[1]+w*norm[2]; 33 | // Reflect velocity at wall (Strong enforcement, not weak) 34 | u -= vn*norm[0]; 35 | v -= vn*norm[1]; 36 | w -= vn*norm[2]; 37 | U(iv,1) = rho*u; 38 | U(iv,2) = rho*v; 39 | if (nDims == 3) 40 | U(iv,3) = rho*w; 41 | break; 42 | } 43 | case CHAR: { 44 | double gamma = params->gamma; 45 | Vec3 norm = bndNorm(bnd,i); 46 | double rho = U(iv,0); 47 | double u = U(iv,1)/rho; 48 | double v = U(iv,2)/rho; 49 | double w = 0; 50 | if (nDims == 3) 51 | w = U(iv,3)/rho; 52 | double p = (gamma-1)*(U(iv,nDims+1) - 0.5*rho*(u*u+v*v+w*w)); 53 | double vn = u*norm[0]+v*norm[1]+w*norm[2]; 54 | 55 | double vnBound = params->uBound*norm[0]+params->vBound*norm[1]+params->wBound*norm[2]; 56 | 57 | double r_plus = vn + 2./(gamma-1.)*sqrt(gamma*p/rho); 58 | double r_minus = vnBound - 2./(gamma-1.)*sqrt(gamma*params->pBound/params->rhoBound); 59 | 60 | double cStar = 0.25*(gamma-1.)*(r_plus-r_minus); 61 | double vn_star = 0.5*(r_plus+r_minus); 62 | 63 | // Inflow 64 | if (vn<0) { 65 | // HACK 66 | double one_over_s = pow(params->rhoBound,gamma)/params->pBound; 67 | 68 | // freestream total enthalpy 69 | double vSq = params->uBound*params->uBound+params->vBound*params->vBound+params->wBound*params->wBound; 70 | double h_free_stream = gamma/(gamma-1.)*params->pBound/params->rhoBound + 0.5*vSq; 71 | 72 | U(iv,0) = pow(1./gamma*(one_over_s*cStar*cStar),1./(gamma-1.)); 73 | 74 | // Compute velocity on the right side 75 | U(iv,1) = U(iv,0)*(vn_star*norm[0] + params->uBound - vnBound*norm[0]); 76 | U(iv,2) = U(iv,0)*(vn_star*norm[1] + params->vBound - vnBound*norm[1]); 77 | U(iv,3) = U(iv,0)*(vn_star*norm[2] + params->wBound - vnBound*norm[2]); 78 | 79 | double pR = U(iv,0)/gamma*cStar*cStar; 80 | U(iv,4) = U(iv,0)*h_free_stream - pR; 81 | } 82 | // Outflow 83 | else { 84 | double one_over_s = pow(rho,gamma)/p; 85 | 86 | // freestream total enthalpy 87 | U(iv,0) = pow(1./gamma*(one_over_s*cStar*cStar), 1./(gamma-1.)); 88 | 89 | // Compute velocity on the right side 90 | double uR = vn_star*norm[0] + (u - vn*norm[0]); 91 | double vR = vn_star*norm[1] + (v - vn*norm[1]); 92 | double wR = vn_star*norm[2] + (w - vn*norm[2]); 93 | 94 | U(iv,1) = U(iv,0)*uR; 95 | U(iv,2) = U(iv,0)*vR; 96 | U(iv,3) = U(iv,0)*wR; 97 | 98 | double pR = U(iv,0)/gamma*cStar*cStar; 99 | double vSq = uR*uR+vR*vR+wR*wR; 100 | U(iv,4) = (pR/(gamma-1.0)) + 0.5*U(iv,0)*vSq; 101 | } 102 | break; 103 | } 104 | } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/tempest.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file flurry.cpp 3 | * \brief Main file to run a whole simulation 4 | * 5 | * Will make into a class in the future in order to interface with Python 6 | * 7 | * \author - Jacob Crabill 8 | * Aerospace Computing Laboratory (ACL) 9 | * Aero/Astro Department. Stanford University 10 | * 11 | * \version 0.0.1 12 | * 13 | * Flux Reconstruction in C++ (Flurry++) Code 14 | * Copyright (C) 2014 Jacob Crabill. 15 | * 16 | */ 17 | 18 | #include "tempest.hpp" 19 | 20 | #ifndef _NO_MPI 21 | #include 22 | #endif 23 | 24 | int main(int argc, char *argv[]) { 25 | input params; 26 | geo Geo; 27 | solver Solver; 28 | 29 | int rank = 0; 30 | int nproc = 1; 31 | #ifndef _NO_MPI 32 | MPI_Init(&argc, &argv); 33 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 34 | MPI_Comm_size(MPI_COMM_WORLD, &nproc); 35 | #endif 36 | params.rank = rank; 37 | params.nproc = nproc; 38 | 39 | if (rank == 0) { 40 | cout << endl; 41 | cout << " ====== Tempest ======" << endl; 42 | cout << " 3D Overset Finite Volume Code " << endl; 43 | cout << " Aerospace Computing Lab, Stanford University" << endl; 44 | cout << endl; 45 | } 46 | 47 | if (argc<2) FatalError("No input file specified."); 48 | 49 | setGlobalVariables(); 50 | 51 | /* Read input file & set simulation parameters */ 52 | params.readInputFile(argv[1]); 53 | 54 | /* Setup the mesh and connectivity for the simulation */ 55 | Geo.setup(¶ms); 56 | 57 | /* Setup the solver, all elements and faces, and all FR operators for computation */ 58 | Solver.setup(¶ms,&Geo); 59 | 60 | /* Stat timer for simulation (ignoring pre-processing) */ 61 | simTimer runTime; 62 | runTime.startTimer(); 63 | 64 | /* Write initial data file */ 65 | writeData(&Solver,¶ms); 66 | 67 | //writeMeshTecplot(&Solver,¶ms); 68 | 69 | #ifndef _NO_MPI 70 | // Allow all processes to finish initial file writing before starting computation 71 | MPI_Barrier(MPI_COMM_WORLD); 72 | #endif 73 | 74 | /* --- Calculation Loop --- */ 75 | for (params.iter=params.initIter+1; params.iter<=params.iterMax; params.iter++) { 76 | 77 | Solver.update(); 78 | 79 | if ((params.iter)%params.monitorResFreq == 0 || params.iter==1) writeResidual(&Solver,¶ms); 80 | if ((params.iter)%params.plotFreq == 0) writeData(&Solver,¶ms); 81 | 82 | } 83 | 84 | // Get simulation wall time 85 | runTime.stopTimer(); 86 | runTime.showTime(); 87 | 88 | #ifndef _NO_MPI 89 | MPI_Finalize(); 90 | #endif 91 | } 92 | --------------------------------------------------------------------------------