├── .gitignore ├── example ├── README ├── cuboids.ccc └── input.ccc ├── TODO ├── src ├── main.cc ├── MyMPI.cc ├── SGS.cc ├── Controller.cc ├── IBM.cc ├── Stat.cc ├── Grid.cc ├── Psolver.cc ├── Initialiser.cc ├── H5IO.cc ├── Discretisation.cc ├── Fluid.cc └── MyVTK.cc ├── README ├── include ├── SGS.h ├── Discretisation.h ├── H5IO.h ├── Stat.h ├── Initialiser.h ├── Psolver.h ├── IBM.h ├── parameter.h ├── MyVTK.h ├── Fluid.h ├── VectorField.h ├── Vec.h ├── MyMPI.h ├── Field.h ├── Grid.h ├── BC.h ├── Controller.h └── Array.h ├── Makefile ├── cmake └── FindHYPRE.cmake └── CMakeLists.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | *.so 3 | *~ 4 | build/ 5 | bin/ 6 | obj/ 7 | -------------------------------------------------------------------------------- /example/README: -------------------------------------------------------------------------------- 1 | A test case of flow past an array of 2-by-2 wall-mounted cubes. Run with 2 | mpirun -n 4 CCC 3 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Technique 2 | --------- 3 | * OpenMP acceleration 4 | 5 | Simulation 6 | ---------- 7 | * Wall function 8 | * Passive scalar transport equation 9 | * Complex terrian 10 | 11 | ... 12 | -------------------------------------------------------------------------------- /src/main.cc: -------------------------------------------------------------------------------- 1 | // main.cc 2 | // You know what it means :) 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #include "Fluid.h" 6 | 7 | int main(int argc, char **argv){ 8 | Fluid fluid(argc, argv); 9 | fluid.flows(); 10 | } -------------------------------------------------------------------------------- /example/cuboids.ccc: -------------------------------------------------------------------------------- 1 | 4 2 | 2.00000e+00 2.00000e+00 5.00000e-01 1.00000e+00 1.00000e+00 1.00000e+00 3 | 2.00000e+00 6.00000e+00 5.00000e-01 1.00000e+00 1.00000e+00 1.00000e+00 4 | 6.00000e+00 2.00000e+00 5.00000e-01 1.00000e+00 1.00000e+00 1.00000e+00 5 | 6.00000e+00 6.00000e+00 5.00000e-01 1.00000e+00 1.00000e+00 1.00000e+00 6 | -------------------------------------------------------------------------------- /example/input.ccc: -------------------------------------------------------------------------------- 1 | $grid$ 2 | 0 1 0.0 8.0 0.0625 3 | 4 | $grid$ 5 | 1 1 0.0 8.0 0.0625 6 | 7 | $grid$ 8 | 2 1 0.0 3.5 0.0625 9 | 10 | $mpi$ 11 | 1 2 2 12 | 13 | $case$ 14 | 4 15 | 16 | $nu$ 17 | 0.000002 18 | 19 | $bodyforce$ 20 | 0 0.28571429 21 | 22 | $uref$ 23 | 10 24 | 25 | $ibmtype$ 26 | 1 27 | 28 | $totaltime$ 29 | 1.001 30 | 31 | $adaptive$ 32 | 1 33 | 34 | $cfl$ 35 | 0.5 36 | 37 | $stat_time$ 38 | 0. 39 | 40 | $visu_time$ 41 | 0. 42 | 43 | $visu_dt$ 44 | 0.2 45 | 46 | $restart$ 47 | 0 48 | 49 | $restatistics$ 50 | 0 51 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Code_CartesianCraft 2 | =================== 3 | 4 | Code_CartesianCraft (CCC) is an incompressible turbulent flow solver, which is 5 | used in my turbulent boundary layer simulations. The name CCC sounds stupid, 6 | but is easy to remember (I hope). 7 | 8 | CCC depends on HYPRE for the parallel Poisson solver and optionally depends on 9 | HDF5 for parallel IO. One needs to enable the ``parallel'' feature when 10 | compiling HYPRE and HDF5. The paths to the header files and libraries should 11 | be appended to the environment variables CPATH and LIBRARY_PATH respectively. 12 | This is also applicable to build CCC with CMake. Then CCC can be compiled by 13 | ``make -j'' with the provided Makefile or the CMake-generated Makefile. 14 | 15 | Contact me at chen_yongxin at outlook dot com for questions and enquiry. 16 | -------------------------------------------------------------------------------- /include/SGS.h: -------------------------------------------------------------------------------- 1 | // SGS.h 2 | // Sub-grid scale model for LES. 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #ifndef SGS_H 6 | #define SGS_H 7 | 8 | #include 9 | 10 | #include "Grid.h" 11 | #include "Field.h" 12 | #include "parameter.h" 13 | #include "Controller.h" 14 | #include "VectorField.h" 15 | 16 | namespace SGS{ 17 | 18 | // Compute subgrid-scale stress. 19 | // TODO: Add multiple SGS options. 20 | // ed: SGS eddy viscosity. 21 | // v: velocity 22 | // S: velocity gradient tensor. 23 | void compute_SGS(Fieldd &ed, const VectorFieldd &v, const VectorFieldd *S, const Grid &grid, const Controller &controller); 24 | 25 | // Vreman SGS model. 26 | // An eddy-viscosity subgrid-scale model for turbulent shear flow: 27 | // Algebraic theory and applications. PoF, 2004 28 | void Vreman(Fieldd &ed, const VectorFieldd *S, const Grid &grid); 29 | 30 | } 31 | 32 | #endif // SGS_H 33 | -------------------------------------------------------------------------------- /src/MyMPI.cc: -------------------------------------------------------------------------------- 1 | // MyMPI.cc 2 | // Customised MPI class. 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #include "MyMPI.h" 6 | 7 | MyMPI::MyMPI(int &argc, char **argv): _comm(MPI_COMM_WORLD) { 8 | MPI_Init(&argc, &argv); 9 | MPI_Comm_rank(_comm, &_rank); 10 | MPI_Comm_size(_comm, &_size); 11 | } 12 | 13 | MyMPI::~MyMPI(){ 14 | MPI_Finalize(); 15 | } 16 | 17 | void MyMPI::init(const int *dims){ 18 | int size = dims[0]*dims[1]*dims[2]; 19 | if(size != _size){ 20 | if(_rank == 0){ 21 | cerr << "MPI processors not matched. Set " << size << " while " << _size << " given." << endl; 22 | } 23 | exit(1); 24 | } 25 | int periodic[3] = {1,1,1}; 26 | MPI_Cart_create(MPI_COMM_WORLD, 3, dims, periodic, 1, &_comm); 27 | MPI_Cart_coords(_comm, _rank, 3, _coords); 28 | int l, r; 29 | for(int d=0; d<3; d++){ 30 | _blocks[d] = dims[d]; 31 | MPI_Cart_shift(_comm, d, 1, &l, &r); 32 | _left[d] = l; 33 | _right[d] = r; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Target executable 2 | TARGET := CCC 3 | 4 | # Compiler 5 | CXX := mpicxx 6 | 7 | # HDF5 library (use by default) 8 | use_hdf := 1 9 | 10 | # Folders 11 | OBJ_DIR := obj 12 | BIN_DIR := bin 13 | SRC_DIR := src 14 | INC_DIR := include 15 | INCLUDE := -I$(INC_DIR) 16 | 17 | # Flags and libraries 18 | CXXFLAGS := -std=c++11 -O3 19 | LDFLAGS := -lHYPRE 20 | 21 | ifneq ($(use_hdf), 0) 22 | CXXFLAGS += -DUSE_HDF 23 | LDFLAGS += -lhdf5 24 | endif 25 | 26 | # Files 27 | SRC := $(wildcard $(SRC_DIR)/*.cc) 28 | OBJ := $(patsubst $(SRC_DIR)/%.cc, $(OBJ_DIR)/%.o, $(SRC)) 29 | 30 | all: build $(BIN_DIR)/$(TARGET) 31 | 32 | $(BIN_DIR)/$(TARGET): $(OBJ) 33 | $(CXX) $(CXXFLAGS) $^ -o $@ $(LDFLAGS) 34 | 35 | $(OBJ_DIR)/%.o: $(SRC_DIR)/%.cc 36 | $(CXX) $(CXXFLAGS) $(INCLUDE) -c $< -o $@ 37 | 38 | build: 39 | @mkdir -p $(OBJ_DIR) 40 | @mkdir -p $(BIN_DIR) 41 | 42 | clean: 43 | rm -rf $(OBJ_DIR) $(BIN_DIR) 44 | 45 | cleaner: 46 | rm $(SRC_DIR)/*~ 47 | rm $(INC_DIR)/*~ 48 | -------------------------------------------------------------------------------- /cmake/FindHYPRE.cmake: -------------------------------------------------------------------------------- 1 | #[=======================================================================[.rst: 2 | FindHYPRE 3 | --------- 4 | 5 | Find HYPRE library. 6 | 7 | This module will set the following variables in your project: 8 | 9 | ``HYPRE_FOUND`` 10 | HYPRE was found on the system 11 | ``HYPRE_INCLUDE_DIR`` 12 | HYPRE include directory 13 | ``HYPRE_LIBRARY`` 14 | HYPRE library path 15 | 16 | #]=======================================================================] 17 | 18 | include(FindPackageHandleStandardArgs) 19 | 20 | ## Additional search paths 21 | # Initialize variables 22 | set(HYPRE_LIBRARY_PATH) 23 | set(HYPRE_INCLUDE_PATH) 24 | 25 | # Make sure the environment variable is not empty 26 | # i.e. 4 arguments for string replacement. 27 | if(NOT "$ENV{LIBRARY_PATH}" STREQUAL "") 28 | string(REPLACE ":" ";" HYPRE_LIBRARY_PATH $ENV{LIBRARY_PATH}) 29 | endif() 30 | if(NOT "$ENV{CPATH}" STREQUAL "") 31 | string(REPLACE ":" ";" HYPRE_INCLUDE_PATH $ENV{CPATH}) 32 | endif() 33 | 34 | find_path(HYPRE_INCLUDE_DIR 35 | NAMES HYPRE.h Hypre.h hypre.h 36 | HINTS 37 | /usr 38 | /usr/local 39 | $ENV{HYPRE_HOME} 40 | $ENV{HYPRE_DIR} 41 | ${HYPRE_INCLUDE_PATH} 42 | PATH_SUFFIXES 43 | include Include 44 | ) 45 | 46 | find_library(HYPRE_LIBRARY 47 | NAMES 48 | HYPRE Hypre hypre 49 | HINTS 50 | /usr 51 | /usr/local 52 | $ENV{HYPRE} 53 | $ENV{HYPRE_HOME} 54 | $ENV{HYPRE_DIR} 55 | ${HYPRE_LIBRARY_PATH} 56 | PATH_SUFFIXES 57 | lib lib64 Lib 58 | ) 59 | 60 | find_package_handle_standard_args(HYPRE REQUIRED_VARS HYPRE_LIBRARY HYPRE_INCLUDE_DIR) -------------------------------------------------------------------------------- /include/Discretisation.h: -------------------------------------------------------------------------------- 1 | // Discretisation.h 2 | // Namespace to discretise the governing equations. 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #ifndef DISCRETISATION_H 6 | #define DISCRETISATION_H 7 | 8 | #include 9 | #include 10 | 11 | #include "Vec.h" 12 | #include "Grid.h" 13 | #include "MyMPI.h" 14 | #include "Field.h" 15 | #include "parameter.h" 16 | #include "Controller.h" 17 | #include "VectorField.h" 18 | 19 | namespace Discretisation{ 20 | 21 | // Project pressure `p' to velocity field `v'. 22 | void project(VectorFieldd &v, const Fieldd &p, const Grid &grid, const Controller &controller); 23 | 24 | // Compute velocity divergence. 25 | void divergence(Fieldd &f, const VectorFieldd &v, const Grid &grid); 26 | 27 | // Compute velocity gradient tensor. Both staggered and central tensor are computed. 28 | void gradtensor(VectorFieldd *S_stag, VectorFieldd *S_cen, const VectorFieldd &v, const Grid &grid); 29 | 30 | // Compute convective-diffisive term and saved in VectorField a. 31 | void conv_diff(VectorFieldd &a, const VectorFieldd &v, const Grid &grid, const double nu, const Fieldd &ed, const VectorFieldd *uij); 32 | 33 | // Adam-Bashforth integration: v = v + (1.5r - 0.5h + bforce)*dt 34 | void AB(VectorFieldd &u, const VectorFieldd &r, const VectorFieldd &h, const Vec3d &bforce, const double dt); 35 | 36 | // Get drag force 37 | void get_dragforce(const VectorFieldd &v, VectorFieldd &D, const Grid &grid, const double Cd, const double h); 38 | 39 | // Add additional terms 40 | void add_vectorfields(VectorFieldd &u, const VectorFieldd &other); 41 | }; 42 | #endif // DISCRETISATION 43 | -------------------------------------------------------------------------------- /include/H5IO.h: -------------------------------------------------------------------------------- 1 | // H5IO.h 2 | // Write/Read field data to/from a HDF5 file collectively. 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #ifndef H5IO_H 6 | #define H5IO_H 7 | 8 | #ifdef USE_HDF 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "hdf5.h" 17 | #include "Grid.h" 18 | #include "MyMPI.h" 19 | #include "Field.h" 20 | #include "VectorField.h" 21 | 22 | using std::endl; 23 | using std::vector; 24 | using std::string; 25 | 26 | // Write double precision field 27 | void H5write_field(hid_t file_id, 28 | const char *dataset_name, 29 | const MyMPI &mympi, 30 | const Grid &grid, 31 | const Fieldd &f); 32 | 33 | // Read double precision field 34 | void H5read_field(hid_t file_id, 35 | const char *dataset_name, 36 | const MyMPI &mympi, 37 | const Grid &grid, 38 | Fieldd &f); 39 | 40 | // Write single precision fluid fields 41 | void H5fluid_fields(const VectorFieldd &u , 42 | const Fieldd &p , 43 | const vector &other , 44 | const vector &fieldnames , 45 | const Grid &grid , 46 | const MyMPI &mympi , 47 | const char *prefix = "./" , 48 | const char *filename = "Step" ); 49 | 50 | #endif // USE_HDF 51 | 52 | #endif // H5IO_H 53 | -------------------------------------------------------------------------------- /include/Stat.h: -------------------------------------------------------------------------------- 1 | // Stat.h 2 | // Class holds time average statistics operations 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #ifndef STAT_H 6 | #define STAT_H 7 | 8 | #include "Grid.h" 9 | #include "H5IO.h" 10 | #include "Field.h" 11 | #include "MyMPI.h" 12 | #include "MyVTK.h" 13 | #include "parameter.h" 14 | #include "Controller.h" 15 | #include "VectorField.h" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | using std::string; 22 | using std::vector; 23 | using std::ofstream; 24 | using std::ifstream; 25 | using std::ios; 26 | 27 | class Stat{ 28 | 29 | private: 30 | VectorFieldd u; // velocity field 31 | Fieldd p; // pressure 32 | Fieldd uiui[3]; // Reynolds normal stress 33 | Fieldd uiuj[3]; // Reynolds shear stress 34 | int samples=0; // number of samples 35 | MyVTK myvtk; // Visualisation object 36 | 37 | public: 38 | Stat(){} 39 | ~Stat(){} 40 | 41 | void init(const Grid &); // initialise an object 42 | void do_statistics(const VectorFieldd &, const Fieldd &); // do statistics 43 | void write(const Grid &, const MyMPI &) const; // write time average data 44 | void save(const MyMPI &) const; // save for restart 45 | void read(const MyMPI &); // read for restart 46 | void save_hdf(const MyMPI &, const Grid &) const; // save with hdf5 for restart 47 | void read_hdf(const MyMPI &, const Grid &); // read with hdf5 for restart 48 | }; 49 | 50 | void mean1(Fieldd &mf, const Fieldd &f, int n); 51 | void mean2(Fieldd &mf, const Fieldd &f1, const Fieldd &f2, int n); 52 | 53 | #endif // STAT_H 54 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | SET(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, 4 | options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release 5 | RelWithDebInfo MinSizeRel.") 6 | # Toggleable build type 7 | SET_PROPERTY(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release;RelWithDebInfo;MinSizeRel") 8 | 9 | # Find the modules included with CCC by appending the `cmake' folder 10 | set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) 11 | 12 | project(Code_CartesianCraft LANGUAGES C CXX) 13 | 14 | # CCC requires C++11 15 | SET(CMAKE_CXX_STANDARD 11 CACHE STRING "Default value for CXX_STANDARD property of targets") 16 | SET(CMAKE_CXX_STANDARD_REQUIRED ON) 17 | SET(CMAKE_CXX_EXTENSIONS OFF) 18 | 19 | # Default C/CXX compilers 20 | set(CMAKE_C_COMPILER mpicc) 21 | set(CMAKE_CXX_COMPILER mpicxx) 22 | 23 | # Dependencies 24 | INCLUDE(FindHYPRE) 25 | INCLUDE(FindHDF5) 26 | 27 | if(NOT HYPRE_FOUND) 28 | message(FATAL_ERROR "No HYPRE found!") 29 | endif() 30 | 31 | # Compile 32 | set(EXE CCC) 33 | file(GLOB ccfiles "${PROJECT_SOURCE_DIR}/src/*.cc") 34 | add_executable(${EXE} ${ccfiles}) 35 | target_include_directories(${EXE} PRIVATE ${PROJECT_SOURCE_DIR}/include) 36 | target_include_directories(${EXE} PRIVATE ${HYPRE_INCLUDE_DIR}) 37 | target_link_libraries(${EXE} PRIVATE ${HYPRE_LIBRARY}) 38 | if(HDF5_FOUND) 39 | target_compile_definitions(${EXE} PRIVATE USE_HDF) 40 | target_include_directories(${EXE} PRIVATE ${HDF5_INCLUDE_DIR}) 41 | target_link_libraries(${EXE} PRIVATE ${HDF5_LIBRARY}) 42 | endif() 43 | 44 | # Install 45 | include(GNUInstallDirs) 46 | 47 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 48 | set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR} CACHE PATH "" FORCE) 49 | endif() 50 | 51 | install(TARGETS ${EXE} 52 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) -------------------------------------------------------------------------------- /include/Initialiser.h: -------------------------------------------------------------------------------- 1 | // Initialiser.h 2 | // Initialise control parameters and flow fields 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #ifndef INITIALISER_H 6 | #define INITIALISER_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "BC.h" 17 | #include "Vec.h" 18 | #include "Grid.h" 19 | #include "MyMPI.h" 20 | #include "parameter.h" 21 | #include "Controller.h" 22 | 23 | using std::cout; 24 | using std::cerr; 25 | using std::endl; 26 | using std::vector; 27 | using std::string; 28 | using std::ifstream; 29 | using std::istringstream; 30 | 31 | class Initialiser{ 32 | 33 | private: 34 | 35 | // Temporal parameters saved for init objects. 36 | // Grid 37 | int grid_types[3] = {0,0,0}; 38 | 39 | // MPI 40 | int mpi_blocks[3] = {0,0,0}; 41 | 42 | // New run or re-start 43 | bool _restart = false; 44 | 45 | // New time-averaged statistics or resume it 46 | bool _restatistics = false; 47 | 48 | // Probe 49 | vector probe_index; 50 | vector probe_coords; 51 | 52 | // Slice 53 | vector slice_index; 54 | vector slice_direction; 55 | vector slice_coords; 56 | 57 | public: 58 | 59 | Initialiser(){} 60 | ~Initialiser(){} 61 | 62 | // Initailise controller and MPI by reading parameters from input file. 63 | void read_file(const MyMPI &, Controller &, Grid &); 64 | 65 | // Initialise controller, MPI and grid and IBM. 66 | void init(MyMPI &, Controller &, Grid &) const; 67 | 68 | // Enquiry if resume running the case. 69 | bool restart() const {return _restart;} 70 | 71 | // Enquiry if resume time-averaged statistics 72 | bool restatistics() const {return _restatistics;} 73 | }; 74 | #endif // INITIALISER_H 75 | -------------------------------------------------------------------------------- /include/Psolver.h: -------------------------------------------------------------------------------- 1 | // Psolver.h 2 | // Poisson solver with HYPRE 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #ifndef PSOLVER_H 6 | #define PSOLVER_H 7 | 8 | #include 9 | #include 10 | 11 | #include "Vec.h" 12 | #include "Grid.h" 13 | #include "MyMPI.h" 14 | #include "Field.h" 15 | #include "parameter.h" 16 | #include "Controller.h" 17 | #include "HYPRE_krylov.h" 18 | #include "HYPRE_parcsr_ls.h" 19 | #include "HYPRE_sstruct_ls.h" 20 | 21 | using std::ios; 22 | using std::cout; 23 | using std::cerr; 24 | using std::endl; 25 | using std::ofstream; 26 | 27 | class Psolver{ 28 | 29 | private: 30 | 31 | // HYPRE objects 32 | HYPRE_SStructGrid grid; 33 | HYPRE_SStructGraph graph; 34 | HYPRE_SStructStencil stencil; 35 | HYPRE_SStructMatrix A; 36 | HYPRE_SStructVector b; 37 | HYPRE_SStructVector x; 38 | HYPRE_ParCSRMatrix parA; 39 | HYPRE_ParVector parb; 40 | HYPRE_ParVector parx; 41 | HYPRE_Solver solver; 42 | HYPRE_Solver precond; 43 | HYPRE_SStructVariable vartypes[1] = {HYPRE_SSTRUCT_VARIABLE_CELL}; 44 | int object_type = HYPRE_PARCSR; 45 | 46 | // Only one part and one variable for HYPRE solver. 47 | int nparts = 1; 48 | int nvars = 1; 49 | int part = 0; 50 | int var = 0; 51 | 52 | // View 53 | int ilower[3]; 54 | int iupper[3]; 55 | 56 | // Data for passing rhs and collecting solution. 57 | double *data; 58 | 59 | // Flag, enquery if the object was initialised. 60 | bool initialised = false; 61 | 62 | public: 63 | Psolver(); 64 | ~Psolver(); 65 | 66 | // Initialise a Poisson solver. 67 | void init(const MyMPI &, const Grid &, const Controller &); 68 | 69 | // Solve linear equation Ap=rhs. 70 | void solve(const Fieldd &rhs, Fieldd &p, const MyMPI &); 71 | }; 72 | 73 | #endif // PSOLVER_H 74 | -------------------------------------------------------------------------------- /src/SGS.cc: -------------------------------------------------------------------------------- 1 | // SGS.cc 2 | // Sub-grid scale model for LES. 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #include 6 | 7 | namespace SGS{ 8 | 9 | void compute_SGS(Fieldd &ed, const VectorFieldd &v, const VectorFieldd *S, const Grid &grid, const Controller &controller){ 10 | // TODO: Add options for different SGS models 11 | // if( controller.SGS() == VREMAN ) Vreman(...); 12 | Vreman(ed, S, grid); 13 | } 14 | 15 | void Vreman(Fieldd &ed, const VectorFieldd *S, const Grid &grid){ 16 | 17 | double a[3][3], b[3][3]; 18 | 19 | for(int k=ed.ks(); k 1.e-8){ 62 | ed(i,j,k) = C_VREMAN * sqrt(bb_aa); 63 | } 64 | 65 | } 66 | } 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /include/IBM.h: -------------------------------------------------------------------------------- 1 | // IBM.h 2 | // Immersed boundary method in 3D. 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #ifndef IBM_H 6 | #define IBM_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "Vec.h" 16 | #include "Grid.h" 17 | #include "Array.h" 18 | #include "Field.h" 19 | #include "MyMPI.h" 20 | #include "parameter.h" 21 | #include "Controller.h" 22 | #include "VectorField.h" 23 | 24 | using std::min; 25 | using std::max; 26 | using std::endl; 27 | using std::cerr; 28 | using std::string; 29 | using std::ifstream; 30 | using std::istringstream; 31 | 32 | class IBM{ 33 | 34 | private: 35 | 36 | // General variables. 37 | // Index [0,1,2] for vector and index 3 for scalar components 38 | Fieldd _distance[4]; // signed distance function 39 | VectorFieldd _normal [4]; // normalised normal vector 40 | Fieldb _mask [4]; // true for first grid point 41 | 42 | // Case specific variables 43 | // Cuboids 44 | int ncuboids; // number of cuboids 45 | Array2d xc; // centre coordinate, shape(xc) = [ncuboids, 3] 46 | Array2d dh; // cuboid side length, shape(dh) = [ncuboids, 3] 47 | 48 | // TODO: urban roughness 49 | 50 | public: 51 | 52 | IBM(){} 53 | ~IBM(){} 54 | 55 | // Initialise fields, read geom file and compute. 56 | void init(const Grid &, const Controller &, const MyMPI &); 57 | 58 | // Apply immersed boundary condition for velocity with no-slip BC. 59 | void ibc (VectorFieldd &v) const; 60 | 61 | // Get signed distance. 62 | double distance(int d, int i, int j, int k) const {return _distance[d](i,j,k); } 63 | 64 | // Get unit normal vector. 65 | Vec3d normal (int d, int i, int j, int k) const {return _normal[d].get(i,j,k);} 66 | 67 | // Get mask. 68 | bool mask (int d, int i, int j, int k) const {return _mask[d](i,j,k); } 69 | 70 | }; 71 | 72 | // Get distance and normal directions for an array of aligned cuboids. 73 | void distance_normal_aligned_cuboids(int n, const Grid &grid, const Array2d &xc, 74 | const Array2d &dh, Fieldd *distance, VectorFieldd *normal); 75 | 76 | // Get distance and normal directions for an aligned cuboid. 77 | void distance_normal_aligned_cuboid(const Vec3d &pt, const Vec3d &xc, const Vec3d &dh, 78 | double &distance, Vec3d &normal); 79 | 80 | #endif // IBM_H 81 | -------------------------------------------------------------------------------- /include/parameter.h: -------------------------------------------------------------------------------- 1 | // parameter.h 2 | // Definition of parameters in the programme. 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | // DO NOT CHANGE unless you know how to do so. 5 | 6 | #ifndef PARAMETER_H 7 | #define PARAMETER_H 8 | 9 | // Ghost cell 10 | #define GC 2 11 | 12 | // Constant 13 | #define PI 3.141592653589793 14 | #define TWO_PI 6.283185307179586 15 | #define C_VREMAN 0.1 // for Cs=0.2 16 | //#define C_VREMAN 0.07225 // for Cs=0.17 17 | #define aND 0.2 // for stability 18 | #define BETA 0.1 // Gamma scheme coefficient 19 | #define MAGNITUDE 0.05 // initial white noise magnitude 20 | 21 | // Boundary condition 22 | #define NONE 0 23 | #define PERIODIC 1 24 | #define NEUMANN 2 25 | #define DIRICHLET 3 26 | #define INLET 4 27 | #define OUTFLOW 5 28 | #define CONVECTIVE 6 29 | #define SYMMETRY 7 30 | #define WALL 8 // no-slip wall 31 | #define TURBINLET 9 // turbulent boundary inlet bc 32 | #define WALLMODEL 10 // wall function 33 | 34 | // Cases for initial and boundary conditions 35 | //#define CASE_USER 0 36 | #define CASE_TBL 1 37 | #define CASE_URBAN 2 38 | #define CASE_CYLINDER 3 39 | #define CASE_OPENCHANNEL 4 40 | 41 | // IBM roughness type 42 | #define CUBOIDS 1 // homogeneous cuboids 43 | #define URBAN 2 // urban roughness 44 | 45 | // Folder name 46 | #define HDF_DIR "HDF" // save HDF data 47 | #define GRID_DIR "GRID" // save grid data 48 | #define DATA_DIR "DATA" // save all data 49 | #define SAVE_DIR "SAVE" // save checkpoint data 50 | #define STAT_DIR "STAT" // save time averaged statistics VTK data 51 | #define SLICE_DIR "SLICE" // save slice VTK data 52 | #define VTK_DIR "VTK" // save whole flow field VTK data 53 | 54 | // File name 55 | #define MONITOR_HYPRE "MONITOR_Psolver" 56 | #define MONITOR_TIMER "MONITOR_Timer" 57 | #define MONITOR_VISU "MONITOR_Visu" 58 | #define MONITOR_SLICE "MONITOR_Slice" 59 | 60 | // Maximum passive scalars 61 | #define MAX_SCALARS 2 62 | 63 | // Maximum slices for visualisation in each direction 64 | #define MAX_SLICES 10 65 | 66 | // Maximum probes in fluid 67 | #define MAX_PROBES 50 68 | 69 | // Timing record intervals 70 | #define INTERVALS 10 71 | 72 | // Turbulent inflow generation intervals, an integer number greater than 1 73 | // can reduce the time in fluctuation generation. 74 | #define TURBINLET_INTERVALS 5 75 | 76 | // HYPRE solver 77 | #define HYPRE_TOLERANCE 1.0e-6 78 | #define HYPRE_AMG_PRINT_LV 0 79 | #define HYPRE_PCG_PRINT_LV 0 80 | #define HYPRE_MAX_ITERATION 100 81 | 82 | #endif // PARAMETER_H 83 | -------------------------------------------------------------------------------- /include/MyVTK.h: -------------------------------------------------------------------------------- 1 | // MyVTK.h 2 | // Output VTK visualisation with point data 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #ifndef MYVTK_H 6 | #define MYVTK_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "Vec.h" 15 | #include "Grid.h" 16 | #include "Field.h" 17 | #include "parameter.h" 18 | #include "Controller.h" 19 | #include "VectorField.h" 20 | 21 | using std::vector; 22 | using std::endl; 23 | using std::ios; 24 | using std::ofstream; 25 | using std::string; 26 | 27 | class MyVTK{ 28 | 29 | public: 30 | 31 | MyVTK(){} 32 | ~MyVTK(){} 33 | 34 | // Output a single field with a path prefix 35 | // Path prefix ends with /. 36 | void single_field(const Fieldd & , 37 | const Grid & , 38 | const MyMPI & , 39 | const char *fieldname = "Data" , 40 | const char *prefix = "./" , 41 | const char *filename = "fluid") const; 42 | 43 | // Output fluid fields with default velocity and pressure, and optional 44 | // other fields. 45 | // Path prefix ends with /. 46 | // Filename is an integer that indicates a contiguous time step. 47 | // All the filenames should keep the same digits. 48 | void fluid_fields(const VectorFieldd &u , 49 | const Fieldd &p , 50 | const vector &other , 51 | const vector &fieldnames , 52 | const Grid &grid , 53 | const MyMPI &mympi , 54 | const char *prefix = "./" , 55 | const char *filename = "Step" ) const; 56 | 57 | // Output a slice. 58 | // Path prefix ends with /. 59 | // Filename is an integer that indicates a contiguous time step. 60 | // All the filenames should keep the same digits. 61 | void slice_fields(const VectorFieldd &u , 62 | const Fieldd &p , 63 | const Fieldd *other , 64 | char **fieldnames , 65 | const int nfields , 66 | const Grid &grid , 67 | const MyMPI &mympi , 68 | const Controller &controller , 69 | const char *prefix = "./" , 70 | const char *filename = "Step" ) const; 71 | }; 72 | 73 | #endif // MYVTK_H 74 | -------------------------------------------------------------------------------- /include/Fluid.h: -------------------------------------------------------------------------------- 1 | // Fluid.h 2 | // Top class of the solver. 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #ifndef FLUID_H 6 | #define FLUID_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "BC.h" 16 | #include "Vec.h" 17 | #include "IBM.h" 18 | #include "SGS.h" 19 | #include "Grid.h" 20 | #include "Stat.h" 21 | #include "H5IO.h" 22 | #include "MyVTK.h" 23 | #include "MyMPI.h" 24 | #include "Field.h" 25 | #include "Psolver.h" 26 | #include "parameter.h" 27 | #include "Controller.h" 28 | #include "Initialiser.h" 29 | #include "VectorField.h" 30 | #include "Discretisation.h" 31 | 32 | using std::ios; 33 | using std::cout; 34 | using std::cerr; 35 | using std::endl; 36 | using std::string; 37 | using std::to_string; 38 | using std::vector; 39 | 40 | class Fluid{ 41 | 42 | private: 43 | 44 | // Necessary objects 45 | BC bc; 46 | IBM ibm; 47 | Grid grid; 48 | MyMPI mympi; 49 | MyVTK myvtk; 50 | Psolver psolver; 51 | Controller controller; 52 | Initialiser initialiser; 53 | Stat stat; 54 | 55 | // Primary fields 56 | VectorFieldd u; // velocities 57 | VectorFieldd h; // history terms for Adam-Bashforth 58 | Fieldd p; // pressure 59 | Fieldd ed; // eddy viscosity 60 | 61 | // Assistant fields 62 | Fieldd div; // divergence of velocity 63 | VectorFieldd r; // conv-diff term 64 | VectorFieldd uij[3]; // staggered velocity gradient tensor 65 | VectorFieldd uij_cen[3]; // velocity gradient tensor in cell-centre 66 | Fieldd cfl; // compute local cfl number 67 | VectorFieldd D; // drag force due to unresolved element 68 | 69 | // Variables 70 | double nu; // kinematic viscosity 71 | int visu_step =0; // whole field visualisation step 72 | int slice_step=0; // slice visualisation step 73 | 74 | // Functions 75 | void init (); // initialisation 76 | void updateAB(); // Adam-Bashforth time integration 77 | void updateUP(VectorFieldd&); // update velocity U and pressure P 78 | void read (); // read all fluid and statistics data 79 | void save () const; // save all fluid and statistics data for restart 80 | double cfl_limit(); // compute time step based on the CFL limit 81 | void field_visu(); // visualise the whole field 82 | void slice_visu(); // visualise a slice 83 | void time_average(); // do time average statitics 84 | void stat_visu(); // visualise statistics result 85 | 86 | public: 87 | Fluid(int &argc, char **argv): mympi(argc, argv) {} 88 | ~Fluid(); 89 | 90 | void flows(); 91 | }; 92 | 93 | #endif // FLUID_H 94 | -------------------------------------------------------------------------------- /src/Controller.cc: -------------------------------------------------------------------------------- 1 | // Controller.cc 2 | // Controller for the programme. 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #include "Controller.h" 6 | 7 | Controller::Controller(): 8 | _probe_index(MAX_PROBES, 3), 9 | _probe_mask (MAX_PROBES ), 10 | _slice_index(MAX_SLICES, 3), 11 | _slice_mask (MAX_SLICES, 3) 12 | {} 13 | 14 | void Controller::init(const MyMPI &mympi){ 15 | 16 | // Correct BC 17 | for(int d=0; d<3; d++){ 18 | if(!mympi.lboundary(d)){ 19 | _left_vbc[d] = NONE; 20 | _left_sbc[d] = NONE; 21 | _left_pbc[d] = NONE; 22 | } 23 | 24 | if(!mympi.rboundary(d)){ 25 | _right_vbc[d] = NONE; 26 | _right_sbc[d] = NONE; 27 | _right_pbc[d] = NONE; 28 | } 29 | } 30 | 31 | // Init timer and make initial recording 32 | t0 = std::chrono::steady_clock::now(); 33 | t_last = t0; 34 | tick(mympi); 35 | } 36 | 37 | void Controller::set_probe(const int i, const Vec3d &coords, const Grid &grid, const MyMPI &mympi){ 38 | if(i >= MAX_PROBES){ 39 | if(mympi.rank() == 0) {cerr << "Probe index exceeds the capacity." << endl;} 40 | exit(1); 41 | } 42 | if( grid.inside(coords, mympi) ){ 43 | _probe_mask(i) = true; 44 | Vec3i index = grid.coords2index(coords); 45 | for(int d=0; d<3; d++) {_probe_index(i, d) = index(d);} 46 | } 47 | } 48 | 49 | void Controller::set_slice(const int i, const int d, const double coord, const Grid &grid, const MyMPI &mympi){ 50 | if(i >= MAX_SLICES){ 51 | if(mympi.rank() == 0) {cerr << "Slice index exceeds the capacity." << endl;} 52 | exit(1); 53 | } 54 | if( (grid.lrange(d) <= coord) && (coord < grid.rrange(d)) ){ 55 | _slice_mask(i, d) = true; 56 | for(int j=grid.start(d); j elapsed_seconds = t_now-t0; 70 | std::chrono::duration timestep_seconds = t_now-t_last; 71 | t_last = t_now; 72 | 73 | // Output result 74 | if(mympi.rank() == 0){ 75 | int width = 25; 76 | ofstream file; 77 | file.open(MONITOR_TIMER, ios::app); 78 | if( _step % INTERVALS == 0 ){ 79 | file << setw(width) << "Steps"; 80 | file << setw(width) << "Time_step"; 81 | file << setw(width) << "Simulation_time"; 82 | file << setw(width) << "Time_per_step_(s)"; 83 | file << setw(width) << "Elapsed_time_(s)"; 84 | file << setw(width) << "Elapsed_time_(h:m:s)"; 85 | file << endl; 86 | } 87 | double t = elapsed_seconds.count(); 88 | int h = t/3600; 89 | int m = t/60-h*60; 90 | int s = t-h*3600-m*60; 91 | string time_breakdown = to_string(h).append(":").append(to_string(m).append(":").append(to_string(s))); 92 | file << setw(width) << _step; 93 | file << setw(width) << _dt; 94 | file << setw(width) << _time; 95 | file << setw(width) << timestep_seconds.count(); 96 | file << setw(width) << t; 97 | file << setw(width) << time_breakdown; 98 | file << endl; 99 | file.close(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /include/VectorField.h: -------------------------------------------------------------------------------- 1 | // VectorField.h 2 | // Container and operator for a 3D vector field. 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #ifndef VECTORFIELD_H 6 | #define VECTORFIELD_H 7 | 8 | #include 9 | #include 10 | 11 | #include "Vec.h" 12 | #include "Field.h" 13 | #include "Array.h" 14 | #include "MyMPI.h" 15 | #include "parameter.h" 16 | 17 | using std::cerr; 18 | using std::endl; 19 | using std::ifstream; 20 | using std::ofstream; 21 | 22 | template 23 | class VectorField{ 24 | 25 | private: 26 | 27 | // Set views for interior domain 28 | void set_view(){ 29 | view_start = GC; 30 | view_end = _n+GC; 31 | _size = 3*_n.product(); 32 | } 33 | 34 | // Data and info 35 | Field x,y,z; // 3 components 36 | Vec3i _n; // shape for interior domain 37 | int _size =0; // size of number of interior cells 38 | Vec3i view_start; // view for interior domain 39 | Vec3i view_end ; 40 | 41 | public: 42 | 43 | // Constructors and deconstructor 44 | VectorField(){} 45 | 46 | VectorField(const Vec3i &n): 47 | x(n), y(n), z(n), _n(n) { 48 | set_view(); 49 | } 50 | 51 | VectorField(const int nx, const int ny, const int nz): 52 | x(nx, ny, nz), y(nx, ny, nz), z(nx, ny, nz), 53 | _n(nx, ny, nz) { 54 | set_view(); 55 | } 56 | 57 | VectorField(const VectorField &b): 58 | x(b.x), y(b.y), z(b.z), _n(b._n) { 59 | set_view(); 60 | } 61 | 62 | ~VectorField(){} 63 | 64 | // Allocation for an empty vector field 65 | void allocate(const int nx, const int ny, const int nz){ 66 | _n = Vec3i(nx, ny, nz); 67 | x.allocate(nx, ny, nz); 68 | y.allocate(nx, ny, nz); 69 | z.allocate(nx, ny, nz); 70 | set_view(); 71 | } 72 | 73 | void allocate(const Vec3i &n){ 74 | _n = n; 75 | x.allocate(n); 76 | y.allocate(n); 77 | z.allocate(n); 78 | set_view(); 79 | } 80 | 81 | // Operator overloading 82 | // = a vector 83 | VectorField& operator = (const Vec3 b){ 84 | x = b(0); 85 | y = b(1); 86 | z = b(2); 87 | return *this; 88 | } 89 | 90 | // = another field by reshaping and copying its value 91 | VectorField& operator = (const VectorField &b){ 92 | _n = b._n ; 93 | x = b.x ; 94 | y = b.y ; 95 | z = b.z ; 96 | set_view(); // change view 97 | return *this; 98 | } 99 | 100 | // [] take ith component 101 | Field& operator [] (const int d) { 102 | if(d == 0){ 103 | return x; 104 | } 105 | else if(d == 1){ 106 | return y; 107 | } 108 | else if(d == 2){ 109 | return z; 110 | } 111 | else { 112 | cerr << "Index error in [] when used VectorField." << endl; 113 | exit(1); 114 | } 115 | } 116 | 117 | const Field& operator [] (const int d) const { 118 | if(d == 0){ 119 | return x; 120 | } 121 | else if(d == 1){ 122 | return y; 123 | } 124 | else if(d == 2){ 125 | return z; 126 | } 127 | else { 128 | cerr << "Index error in [] when used VectorField." << endl; 129 | exit(1); 130 | } 131 | } 132 | 133 | // () take (d,i,j,k) data 134 | T& operator () (const int d, const int i, const int j, const int k){ 135 | return (*this)[d](i,j,k); 136 | } 137 | 138 | const T& operator () (const int d, const int i, const int j, const int k) const{ 139 | return (*this)[d](i,j,k); 140 | } 141 | 142 | // Set values of a vector at (i,j,k) 143 | void set(const int i, const int j, const int k, const Vec3 &v){ 144 | for(int d=0; d<3; d++) (*this)[d](i,j,k) = v(d); 145 | } 146 | 147 | // Get values at (i,j,k) and return a vector 148 | Vec3 get(const int i, const int j, const int k) const { 149 | Vec3 v; 150 | for(int d=0; d<3; d++) v(d) = (*this)[d](i,j,k); 151 | return v; 152 | } 153 | 154 | // Enquiry 155 | Vec3i start() const {return view_start; } // get start view 156 | int start(int d) const {return view_start(d);} // get start view in direction d 157 | Vec3i end () const {return view_end; } // get end view 158 | int end (int d) const {return view_end(d); } // get end view in direction d 159 | 160 | // Get size and shape 161 | int size() const {return _size;} 162 | Vec3i n() const {return _n; } 163 | 164 | // Start and end range 165 | int is() const {return view_start(0);} 166 | int js() const {return view_start(1);} 167 | int ks() const {return view_start(2);} 168 | 169 | int ie() const {return view_end(0);} 170 | int je() const {return view_end(1);} 171 | int ke() const {return view_end(2);} 172 | 173 | void save(ofstream &file) const { 174 | x.save(file); 175 | y.save(file); 176 | z.save(file); 177 | } 178 | 179 | void read(ifstream &file){ 180 | x.read(file); 181 | y.read(file); 182 | z.read(file); 183 | } 184 | }; 185 | 186 | // User defined types 187 | typedef VectorField VectorFieldi; 188 | typedef VectorField VectorFieldf; 189 | typedef VectorField VectorFieldd; 190 | typedef VectorField VectorFieldb; 191 | 192 | #endif // VECTORFIELD_H -------------------------------------------------------------------------------- /include/Vec.h: -------------------------------------------------------------------------------- 1 | // Vec.h 2 | // Class template for vector with 3 entries. 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #ifndef VEC_H 6 | #define VEC_H 7 | 8 | #include 9 | #include 10 | 11 | // Class template for vector 12 | template 13 | class Vec3{ 14 | 15 | protected: 16 | T v[3]; // vector with 3 entries 17 | 18 | public: 19 | 20 | // Constructors 21 | Vec3(){ 22 | for(int i=0; i<3; i++){ 23 | v[i] = 0; 24 | } 25 | } 26 | 27 | Vec3(const T a, const T b, const T c){ 28 | v[0] = a; 29 | v[1] = b; 30 | v[2] = c; 31 | } 32 | 33 | Vec3(const Vec3 &b){ 34 | for(int i=0; i<3; i++){ 35 | v[i] = b.v[i]; 36 | } 37 | } 38 | 39 | Vec3(const T b){ 40 | for(int i=0; i<3; i++){ 41 | v[i] = b; 42 | } 43 | } 44 | 45 | // Calculation 46 | // Dot product 47 | T dot(const Vec3& b) const { 48 | T a = 0; 49 | for(int i=0; i<3; i++){ 50 | a += v[i]*b.v[i]; 51 | } 52 | return a; 53 | } 54 | 55 | // Product of 3 entries 56 | T product() const { return v[0] * v[1] * v[2]; } 57 | 58 | // Cross product 59 | Vec3 cross(const Vec3& b) const { 60 | return Vec3(v[1]*b.v[2] - v[2]*b.v[1], 61 | v[2]*b.v[0] - v[0]*b.v[2], 62 | v[0]*b.v[1] - v[1]*b.v[0]); 63 | } 64 | 65 | // Normalise itself 66 | Vec3& normal(){ 67 | T a = norm2(); 68 | for(int i=0; i<3; i++){ 69 | v[i] /= a; 70 | } 71 | return *this; 72 | } 73 | 74 | // Get 2nd norm 75 | T norm2() const { 76 | return sqrt(dot(*this)); 77 | } 78 | 79 | // Operator overloading 80 | // All entries equal to a constant 81 | Vec3& operator = (const T b){ 82 | for(int i=0; i<3; i++){ 83 | v[i] = b; 84 | } 85 | return *this; 86 | } 87 | 88 | // Copy from another vector 89 | Vec3& operator = (const Vec3 &b){ 90 | for(int i=0; i<3; i++){ 91 | v[i] = b.v[i]; 92 | } 93 | return *this; 94 | } 95 | 96 | // Equivalent to another vector 97 | bool operator == (const Vec3 &b){ 98 | bool a = true; 99 | for(int i=0; i<3; i++) { 100 | a = a && (v[i] == b.v[i]); 101 | } 102 | return a; 103 | } 104 | 105 | // Get ith reference 106 | T& operator () (const int i){ 107 | if(i<0 || i>=3){ 108 | std::cerr << std::endl << "Index out of bound in vector!" << std::endl; 109 | exit(1); 110 | } 111 | return v[i]; 112 | } 113 | 114 | // Get ith value 115 | const T& operator () (const int i) const { 116 | if(i<0 || i>=3){ 117 | std::cerr << std::endl << "Index out of bound in vector!" << std::endl; 118 | exit(1); 119 | } 120 | return v[i]; 121 | } 122 | 123 | Vec3 operator + (const T b) const { 124 | return Vec3(v[0]+b, v[1]+b, v[2]+b); 125 | } 126 | 127 | Vec3 operator + (const Vec3& b) const { 128 | return Vec3(v[0]+b.v[0], v[1]+b.v[1], v[2]+b.v[2]); 129 | } 130 | 131 | Vec3& operator += (const T b){ 132 | for(int i=0; i<3; i++){ 133 | v[i] += b; 134 | } 135 | return *this; 136 | } 137 | 138 | Vec3& operator += (const Vec3 &b){ 139 | for(int i=0; i<3; i++){ 140 | v[i] += b.v[i]; 141 | } 142 | return *this; 143 | } 144 | 145 | Vec3 operator - (const T b) const { 146 | return Vec3(v[0]-b, v[1]-b, v[2]-b); 147 | } 148 | 149 | Vec3 operator - (const Vec3 &b) const { 150 | return Vec3(v[0]-b.v[0], v[1]-b.v[1], v[2]-b.v[2]); 151 | } 152 | 153 | Vec3& operator -= (const T b){ 154 | for(int i=0; i<3; i++){ 155 | v[i] -= b; 156 | } 157 | return *this; 158 | } 159 | 160 | Vec3& operator -= (const Vec3 &b){ 161 | for(int i=0; i<3; i++){ 162 | v[i] -= b.v[i]; 163 | } 164 | return *this; 165 | } 166 | 167 | Vec3 operator * (const T b) const { 168 | return Vec3(v[0]*b, v[1]*b, v[2]*b); 169 | } 170 | 171 | Vec3 operator * (const Vec3 &b) const { 172 | return Vec3(v[0]*b.v[0], v[1]*b.v[1], v[2]*b.v[2]); 173 | } 174 | 175 | Vec3& operator *= (const T b){ 176 | for(int i=0; i<3; i++){ 177 | v[i] *= b; 178 | } 179 | return *this; 180 | } 181 | 182 | Vec3& operator *= (const Vec3 &b){ 183 | for(int i=0; i<3; i++){ 184 | v[i] *= b.v[i]; 185 | } 186 | return *this; 187 | } 188 | 189 | Vec3 operator / (const T b) const { 190 | return Vec3(v[0]/b, v[1]/b, v[2]/b); 191 | } 192 | 193 | Vec3 operator / (const Vec3 &b) const { 194 | return Vec3(v[0]/b.v[0], v[1]/b.v[1], v[2]/b.v[2]); 195 | } 196 | 197 | Vec3& operator /= (const T b){ 198 | for(int i=0; i<3; i++){ 199 | v[i] /= b; 200 | } 201 | return *this; 202 | } 203 | 204 | Vec3& operator /= (const Vec3 &b){ 205 | for(int i=0; i<3; i++){ 206 | v[i] /= b.v[i]; 207 | } 208 | return *this; 209 | } 210 | 211 | void print(){ 212 | for(int i=0; i<3; i++){ 213 | std::cout << v[i] << " "; 214 | } 215 | std::cout << std::endl; 216 | } 217 | 218 | }; 219 | 220 | // User defined types 221 | typedef Vec3 Vec3i; 222 | typedef Vec3 Vec3f; 223 | typedef Vec3 Vec3d; 224 | typedef Vec3 Vec3b; 225 | 226 | #endif // VEC_H 227 | -------------------------------------------------------------------------------- /src/IBM.cc: -------------------------------------------------------------------------------- 1 | // IBM.cc 2 | // Immersed boundary method in 3D. 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #include "IBM.h" 6 | 7 | void IBM::init(const Grid &grid, const Controller &controller, const MyMPI &mympi){ 8 | 9 | // Allocate fields if IBM is needed 10 | if(controller.get_ibmtype() != 0){ 11 | for(int d=0; d<4; d++){ 12 | _distance[d].allocate(grid.n()); 13 | _normal[d].allocate(grid.n()); 14 | _mask[d].allocate(grid.n()); 15 | } 16 | } 17 | 18 | // Read geometry input file 19 | if(controller.get_ibmtype() == CUBOIDS){ 20 | ifstream file("cuboids.ccc"); 21 | if(!file.is_open()){ 22 | if(mympi.rank()==0){ cerr << "Input file ``cuboids.ccc'' does not exist." << endl;} 23 | exit(1); 24 | } 25 | 26 | // Get number of cuboids and allocate 2D arrays 27 | string line; 28 | getline(file, line); 29 | istringstream(line) >> ncuboids; 30 | xc.allocate(ncuboids, 3); 31 | dh.allocate(ncuboids, 3); 32 | 33 | // Read geometry info and assign values 34 | double value; 35 | string values; 36 | for(int i=0; i> value; 41 | xc(i,j) = value; 42 | } 43 | for(int j=0; j<3; j++){ 44 | values >> value; 45 | dh(i,j) = value; 46 | } 47 | } 48 | 49 | // Compute wall normal and distance 50 | distance_normal_aligned_cuboids(ncuboids, grid, xc, dh, _distance, _normal); 51 | } 52 | else if(controller.get_ibmtype() == URBAN){ 53 | if(mympi.rank()==0) cerr << "Urban roughness module is under development." << endl; 54 | exit(1); 55 | } 56 | 57 | // Compute mask arrays to find the first grid point 58 | if(controller.get_ibmtype() != 0){ 59 | for(int d=0; d<4; d++){ 60 | for(int k=grid.start(2); k=0)) _mask[d](i,j,k) = true; 67 | } 68 | } 69 | } 70 | } 71 | } 72 | } 73 | 74 | 75 | void IBM::ibc (VectorFieldd &v) const { 76 | 77 | // Only correct 3 velocity components 78 | for(int d=0; d<3; d++){ 79 | for(int k=v.ks(); k distance){ 119 | distance = dist; 120 | normal = n; 121 | } 122 | 123 | // right plane 124 | cen = xc + o*dh/2.; 125 | n = 0.; n(d) = 1.; 126 | dist = distance2plane(pt, cen, n); 127 | if(dist > distance){ 128 | distance = dist; 129 | normal = n; 130 | } 131 | } 132 | } 133 | 134 | void distance_normal_aligned_cuboids(int n, const Grid &grid, const Array2d &xc, 135 | const Array2d &dh, Fieldd *distance, VectorFieldd *normal){ 136 | Vec3d pt, norm, newnorm, side, centre; 137 | 138 | // 4 components 139 | for(int d=0; d<4; d++){ 140 | 141 | // Loop each point. Get 1 more points in each side of the domain. 142 | for(int k=grid.start(2)-1; k 9 | #include 10 | #include 11 | 12 | using std::cout; 13 | using std::endl; 14 | using std::cerr; 15 | 16 | class MyMPI{ 17 | 18 | private: 19 | int _rank = 0; // MPI rank 20 | int _size = 0; // MPI communicator world size 21 | MPI_Comm _comm; // MPI_COMM_WORLD 22 | int _coords[3] = { 0, 0, 0}; // MPI topo coordinates 23 | int _left [3] = {-1,-1,-1}; // left neighbour rank 24 | int _right [3] = {-1,-1,-1}; // right neighbour rank 25 | int _blocks[3] = { 1, 1, 1}; // number of blocks in 3D 26 | 27 | public: 28 | 29 | // Constructor and deconstructor 30 | MyMPI(int &argc, char **argv); // MPI initialisation 31 | ~MyMPI(); // MPI finalisation 32 | 33 | // Initialise MPI with Cartesian topology. 34 | // Input is a dimension with 3 entries. 35 | void init(const int *dims); // called after MyMPI initialised 36 | 37 | // Enquiry 38 | int rank () const; // get MPI rank 39 | int size () const; // get number of processors 40 | MPI_Comm comm () const; // get MPI_COMM_WORLD 41 | int coords (const int d) const; // get MPI coordinate in direction d 42 | int left (const int d) const; // get left neighbour's id in direction d 43 | int right (const int d) const; // get right neighbour's id in direction d 44 | int blocks (const int d) const; // get number of blocks in direction d 45 | bool lboundary(const int d) const; // if left wall is the boundary 46 | bool rboundary(const int d) const; // if right wall is the boundary 47 | bool master () const; // if the current processor is the master processor 48 | 49 | // Collective operator 50 | template void max (T &) const; // get the maximum of a specified value 51 | template void min (T &) const; // get the minimum of a specified value 52 | template void land(T &) const; // logical and 53 | template void lor (T &) const; // logical or 54 | template void sum (T &) const; // sum 55 | 56 | // Broadcast data from root to other processes 57 | template void bcast(T *data, int count, int root=0) const; 58 | 59 | // Send and receive data for halo cells 60 | template 61 | void sendrecv(T *sendbuff, T *recvbuff, int count, int dest, int src) const; 62 | 63 | // Wait until all processes reach the point 64 | void wait(){ MPI_Barrier(_comm); } 65 | }; 66 | 67 | 68 | inline int MyMPI::rank () const {return _rank; } 69 | inline int MyMPI::size () const {return _size; } 70 | inline MPI_Comm MyMPI::comm () const {return _comm; } 71 | inline int MyMPI::coords (const int d) const {return _coords[d]; } 72 | inline int MyMPI::left (const int d) const {return _left[d]; } 73 | inline int MyMPI::right (const int d) const {return _right[d]; } 74 | inline int MyMPI::blocks (const int d) const {return _blocks[d]; } 75 | inline bool MyMPI::lboundary (const int d) const {return _coords[d]==0; } 76 | inline bool MyMPI::rboundary (const int d) const {return _coords[d]==_blocks[d]-1;} 77 | inline bool MyMPI::master () const {return _rank==0; } 78 | 79 | // Function template for choosing a data type used in MPI 80 | template MPI_Datatype choose_dtype(){ 81 | if(typeid(T) == typeid(int)){ 82 | return MPI_INT; 83 | } 84 | else if(typeid(T) == typeid(float)){ 85 | return MPI_FLOAT; 86 | } 87 | else if(typeid(T) == typeid(double)){ 88 | return MPI_DOUBLE; 89 | } 90 | else if(typeid(T) == typeid(bool)){ 91 | return MPI_C_BOOL; 92 | } 93 | else{ 94 | cerr << "Unsupported data type in MyMPI." << endl; 95 | exit(1); 96 | } 97 | } 98 | 99 | template 100 | void MyMPI::bcast(T *data, int count, int root) const { 101 | MPI_Datatype dtype = choose_dtype(); 102 | MPI_Bcast(data, count, dtype, root, _comm); 103 | } 104 | 105 | template 106 | void MyMPI::sendrecv(T *sendbuff, T *recvbuff, int count, int dest, int src) const { 107 | MPI_Request requests[2]; 108 | MPI_Status statuses[2]; 109 | MPI_Datatype dtype = choose_dtype(); 110 | MPI_Isend(sendbuff, count, dtype, dest, 1, _comm, &requests[0]); 111 | MPI_Irecv(recvbuff, count, dtype, src, 1, _comm, &requests[1]); 112 | MPI_Waitall(2, requests, statuses); 113 | } 114 | 115 | template 116 | void MyMPI::max(T &b) const { 117 | T all, each = b; 118 | MPI_Datatype dtype = choose_dtype(); 119 | MPI_Allreduce(&each, &all, 1, dtype, MPI_MAX, _comm); 120 | b = all; 121 | } 122 | 123 | template 124 | void MyMPI::min(T &b) const { 125 | T all, each = b; 126 | MPI_Datatype dtype = choose_dtype(); 127 | MPI_Allreduce(&each, &all, 1, dtype, MPI_MIN, _comm); 128 | b = all; 129 | } 130 | 131 | template 132 | void MyMPI::land(T &b) const { 133 | T all, each = b; 134 | MPI_Datatype dtype = choose_dtype(); 135 | MPI_Allreduce(&each, &all, 1, dtype, MPI_LAND, _comm); 136 | b = all; 137 | } 138 | 139 | template 140 | void MyMPI::lor(T &b) const { 141 | T all, each = b; 142 | MPI_Datatype dtype = choose_dtype(); 143 | MPI_Allreduce(&each, &all, 1, dtype, MPI_LOR, _comm); 144 | b = all; 145 | } 146 | 147 | template 148 | void MyMPI::sum(T &b) const { 149 | T all, each = b; 150 | MPI_Datatype dtype = choose_dtype(); 151 | MPI_Allreduce(&each, &all, 1, dtype, MPI_SUM, _comm); 152 | b = all; 153 | } 154 | 155 | #endif // MYMPI_H 156 | -------------------------------------------------------------------------------- /src/Stat.cc: -------------------------------------------------------------------------------- 1 | // Stat.cc 2 | // Class holds time average statistics operations 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #include "Stat.h" 6 | #ifdef USE_HDF 7 | #include 8 | #include "hdf5.h" 9 | #endif 10 | 11 | void Stat::init(const Grid &grid){ 12 | 13 | // Init fields 14 | u.allocate(grid.n()); 15 | p.allocate(grid.n()); 16 | for(int d=0; d<3; d++){ 17 | uiui[d].allocate(grid.n()); 18 | uiuj[d].allocate(grid.n()); 19 | } 20 | } 21 | 22 | void Stat::do_statistics(const VectorFieldd &u, const Fieldd &p){ 23 | 24 | for(int d=0; d<3; d++){ 25 | mean1(this->u[d], u[d], samples); 26 | mean2(uiui[d], u[d], u[d], samples); 27 | } 28 | mean2(uiuj[0], u[0], u[1], samples); 29 | mean2(uiuj[1], u[0], u[2], samples); 30 | mean2(uiuj[2], u[1], u[2], samples); 31 | 32 | mean1(this->p, p, samples); 33 | 34 | samples++; 35 | } 36 | 37 | void Stat::write(const Grid &grid, const MyMPI &mympi) const { 38 | 39 | vector fields; 40 | fields.reserve(10); // reserve a big enough place first 41 | fields.push_back(uiui[0]); 42 | fields.push_back(uiui[1]); 43 | fields.push_back(uiui[2]); 44 | fields.push_back(uiuj[0]); 45 | fields.push_back(uiuj[1]); 46 | fields.push_back(uiuj[2]); 47 | 48 | vector fieldnames; 49 | fieldnames.push_back(string("u1u1")); 50 | fieldnames.push_back(string("u2u2")); 51 | fieldnames.push_back(string("u3u3")); 52 | fieldnames.push_back(string("u1u2")); 53 | fieldnames.push_back(string("u1u3")); 54 | fieldnames.push_back(string("u2u3")); 55 | 56 | // Do substraction 57 | for(int i=0; i 9 | #include "Vec.h" 10 | #include "Array.h" 11 | #include "MyMPI.h" 12 | #include "parameter.h" 13 | 14 | using std::ifstream; 15 | using std::ofstream; 16 | 17 | // Class template for 3D field 18 | template 19 | class Field { 20 | 21 | private: 22 | 23 | // Set views for interior domain. Called followed by shape initalised 24 | void set_view(){ 25 | view_start = GC; 26 | view_end = _n+GC; 27 | _size = _n.product(); 28 | } 29 | 30 | // Data and info 31 | Array::Array3 p; // data 32 | Vec3i _n; // shape for interior domain 33 | int _size=0; // size of number of interior cells 34 | 35 | // View: Indices [start, end) for the part of a 3D field. 36 | Vec3i view_start; // view for interior domain 37 | Vec3i view_end; 38 | 39 | public: 40 | 41 | // Constructors and deconstructor 42 | Field(){} 43 | 44 | Field(const Vec3i &n): 45 | p(n(0)+2*GC, n(1)+2*GC, n(2)+2*GC), 46 | _n(n) { 47 | set_view(); 48 | } 49 | 50 | Field(const int nx, const int ny, const int nz): 51 | p(nx+2*GC, ny+2*GC, nz+2*GC), 52 | _n(Vec3i(nx, ny, nz)) { 53 | set_view(); 54 | } 55 | 56 | Field(const Field &b): 57 | p(b.p), 58 | _n(b._n) { 59 | set_view(); 60 | } 61 | 62 | ~Field(){} 63 | 64 | 65 | // Allocation for an empty field 66 | void allocate(const int nx, const int ny, const int nz){ 67 | _n = Vec3i(nx, ny, nz); 68 | p.allocate(nx+2*GC, ny+2*GC, nz+2*GC); 69 | set_view(); 70 | } 71 | 72 | void allocate(const Vec3i &n){ 73 | _n = n; 74 | p.allocate(n(0)+2*GC, n(1)+2*GC, n(2)+2*GC); 75 | set_view(); 76 | } 77 | 78 | // Operator overloading 79 | // = a value 80 | Field& operator = (const T b){ 81 | p = b; // assign a value to an allocated array and field 82 | return *this; 83 | } 84 | 85 | // = another field by reshaping and copying its value 86 | Field& operator = (const Field &b){ 87 | _n = b._n; 88 | p = b.p; 89 | set_view(); // change view 90 | return *this; 91 | } 92 | 93 | // (i,j,k) 94 | T& operator () (const int i, const int j, const int k) { 95 | return p(i,j,k); 96 | } 97 | 98 | const T& operator () (const int i, const int j, const int k) const { 99 | return p(i,j,k); 100 | } 101 | 102 | // Fill data to field with specified view 103 | void fill(const Vec3i &start, const Vec3i &end, const T *data){ 104 | int n = 0; 105 | for(int k=start(2); k(a); 144 | return a; 145 | } 146 | 147 | // Get min 148 | T min(const MyMPI &mympi) const { 149 | T a = 1e10; 150 | for(int k=start(2); k(a); 158 | return a; 159 | } 160 | 161 | // Scale a field 162 | void scale(double factor){ 163 | for(int k=start(2); k& data() {return p;} 187 | const Array::Array3& data() const {return p;} 188 | 189 | // Save binary data with binary file object 190 | void save(ofstream &file) const { 191 | for(int k=0; k<_n(2)+2*GC; k++){ 192 | for(int j=0; j<_n(1)+2*GC; j++){ 193 | for(int i=0; i<_n(0)+2*GC; i++){ 194 | double x = (double)p(i, j, k); 195 | file.write((char*)&x, sizeof(double)); 196 | } 197 | } 198 | } 199 | } 200 | 201 | // Read binary data with binary file object 202 | void read(ifstream &file){ 203 | double x; 204 | for(int k=0; k<_n(2)+2*GC; k++){ 205 | for(int j=0; j<_n(1)+2*GC; j++){ 206 | for(int i=0; i<_n(0)+2*GC; i++){ 207 | file.read((char*)&x, sizeof(double)); 208 | p(i, j, k) = x; 209 | } 210 | } 211 | } 212 | } 213 | }; 214 | 215 | // User defined types 216 | typedef Field Fieldi; 217 | typedef Field Fieldf; 218 | typedef Field Fieldd; 219 | typedef Field Fieldb; 220 | 221 | #endif // FIELD_H -------------------------------------------------------------------------------- /include/Grid.h: -------------------------------------------------------------------------------- 1 | // Grid.h 2 | // Grid class holds global and local coordinate, grid spacing and enquiry. 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #ifndef GRID_H 6 | #define GRID_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "Vec.h" 14 | #include "Array.h" 15 | #include "MyMPI.h" 16 | #include "Field.h" 17 | #include "parameter.h" 18 | 19 | using std::cerr; 20 | using std::cout; 21 | using std::endl; 22 | using std::ios; 23 | using std::string; 24 | 25 | class Grid{ 26 | 27 | private: 28 | 29 | // Global 30 | Array1d _xg[3]; // global coordinate 31 | int _gn[3]; // global dimension for cells 32 | double _lbox[3]; // global logical left box (whole domain) vertex coordinate 33 | double _rbox[3]; // global logical right box (whole domain) vertex coordinate 34 | int _piece_start[3]; // piece view start 35 | int _piece_end[3]; // piece view end 36 | 37 | // Local 38 | int _n[3]; // local dimension for cells 39 | int _start[3]; // local start index 40 | int _end[3]; // local end index 41 | int _size=0 ; // local number of cells 42 | Array1d _x[3]; // local coordinate 43 | Array1d _dx[3]; // cell spacing 44 | Array1d _dx2[3]; // cross cell spacing 45 | Array1d _dxi[3]; // cell spacing inverse 46 | Array1d _dxi2[3]; // cross cell spacing inverse 47 | 48 | // Local fields 49 | Fieldd _delta ; // LES filter length scale 50 | Fieldd _dv ; // cell volume 51 | Fieldd _area[3]; // project areas 52 | double _lrange[3]; // partition range on the logical left 53 | double _rrange[3]; // partition range on the logical right 54 | 55 | public: 56 | 57 | // Constructor and deconstructor 58 | Grid(){} 59 | ~Grid(){} 60 | 61 | // Set up a global uniform grid in direction d [0,1,2] with grid spacing h. 62 | void set_uniform_line(const int d, double neg, double pos, double h); 63 | 64 | // Set up a global stretched grid in direction d [0,1,2]. 65 | // The grid start with a uniform region and then the spacing is 66 | // exponentially increased with a factor. 67 | void set_stretch_line(const int d, double uneg, double upos, double pos, double h, double factor); 68 | 69 | // Initialise the grid after all 3 global grids have been set. 70 | void init(const MyMPI &); 71 | 72 | // Get size (number of cells) in the current partition. 73 | int size() const {return _size;} 74 | 75 | // Get values either with index tuple (i,j,k) or direction d. Return a vector if no argument given. 76 | double dv (const int i, const int j, const int k) const {return _dv(i,j,k); } 77 | double delta (const int i, const int j, const int k) const {return _delta(i,j,k); } 78 | double lrange (const int d) const {return _lrange[d]; } 79 | double rrange (const int d) const {return _rrange[d]; } 80 | int start (const int d) const {return _start[d]; } 81 | int end (const int d) const {return _end[d]; } 82 | int piece_start (const int d) const {return _piece_start[d]; } 83 | int piece_end (const int d) const {return _piece_end[d]; } 84 | 85 | double xg (const int d, const int i) const {return _xg[d](i); } 86 | double x (const int d, const int i) const {return _x[d](i); } 87 | double dx (const int d, const int i) const {return _dx[d](i); } 88 | double dx2 (const int d, const int i) const {return _dx2[d](i); } 89 | double dxi (const int d, const int i) const {return _dxi[d](i); } 90 | double dxi2 (const int d, const int i) const {return _dxi2[d](i); } 91 | 92 | int gn (const int d) const {return _gn[d]; } 93 | int n (const int d) const {return _n[d]; } 94 | double lbox (const int d) const {return _lbox[d]; } 95 | double rbox (const int d) const {return _rbox[d]; } 96 | 97 | Vec3d lrange () const {return Vec3d(_lrange[0], _lrange[1], _lrange[2]);} 98 | Vec3d rrange () const {return Vec3d(_rrange[0], _rrange[1], _rrange[2]);} 99 | 100 | Vec3i start () const {return Vec3i(_start[0], _start[1], _start[2]);} 101 | Vec3i end () const {return Vec3i( _end[0], _end[1], _end[2]);} 102 | 103 | Vec3i gn () const {return Vec3i(_gn[0], _gn[1], _gn[2]);} 104 | Vec3i n () const {return Vec3i( _n[0], _n[1], _n[2]);} 105 | 106 | Vec3d lbox () const {return Vec3d(_lbox[0], _lbox[1], _lbox[2]);} 107 | Vec3d rbox () const {return Vec3d(_rbox[0], _rbox[1], _rbox[2]);} 108 | 109 | Vec3i piece_start () const {return Vec3i(_piece_start[0], _piece_start[1], _piece_start[2]);} 110 | Vec3i piece_end () const {return Vec3i( _piece_end[0], _piece_end[1], _piece_end[2]);} 111 | 112 | // First and last cell index 113 | int first (const int d) const {return GC; } 114 | int last (const int d) const {return GC+_n[d]; } 115 | 116 | // Enquiry the project area with a specified normal direction and index 117 | double area(const int d, const int i, const int j, const int k) const {return _area[d](i,j,k); } 118 | 119 | const Fieldd area(const int d) const {return _area[d];} 120 | 121 | // Converter 122 | // Index to coordinate with a specified direction d. 123 | // A <0 or >=3 of direction d means cell centre for scalars. 124 | Vec3d index2coords(const int d, const int i, const int j, const int k) const; 125 | 126 | // Coordinate to the index of cell centre. 127 | // Need to test if a point is inside the partition first. 128 | Vec3i coords2index(const Vec3d &coords) const; 129 | 130 | // Enquiry if a point is inside the current partition. 131 | bool inside(const Vec3d &, const MyMPI &) const; 132 | }; 133 | 134 | #endif // GRID_H 135 | -------------------------------------------------------------------------------- /include/BC.h: -------------------------------------------------------------------------------- 1 | // BC.h 2 | // Boundary condition class. 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #ifndef BC_H 6 | #define BC_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "Vec.h" 15 | #include "Grid.h" 16 | #include "Array.h" 17 | #include "Field.h" 18 | #include "MyMPI.h" 19 | #include "parameter.h" 20 | #include "Controller.h" 21 | #include "VectorField.h" 22 | 23 | using std::ios; 24 | using std::string; 25 | using std::ifstream; 26 | using std::istringstream; 27 | 28 | class BC{ 29 | 30 | private: 31 | 32 | // View for MPI data exchange 33 | Vec3i left_view_set_start [3]; // view for logical left set ghost cell start and end 34 | Vec3i left_view_set_end [3]; // exterior 35 | 36 | Vec3i left_view_get_start [3]; // view for logical left get ghost cell start and end 37 | Vec3i left_view_get_end [3]; // interior 38 | 39 | Vec3i right_view_set_start [3]; // view for logical right set ghost cell start and end 40 | Vec3i right_view_set_end [3]; // exterior 41 | 42 | Vec3i right_view_get_start [3]; // view for logical right set ghost cell start and end 43 | Vec3i right_view_get_end [3]; // interior 44 | 45 | // View for slices: -1 offset ghost cell layer 46 | Vec3i left_view_slice_start [3]; 47 | Vec3i left_view_slice_end [3]; 48 | Vec3i right_view_slice_start [3]; 49 | Vec3i right_view_slice_end [3]; 50 | 51 | // MPI rank for different BC types 52 | Vec3i rank_left_vbc; 53 | Vec3i rank_right_vbc; 54 | Vec3i rank_left_pbc; 55 | Vec3i rank_right_pbc; 56 | Vec3i rank_left_sbc; 57 | Vec3i rank_right_sbc; 58 | 59 | // Slice values for applying boundary conditions: xxx_slice[direction][component] 60 | Array3d left_slice[3][3]; 61 | Array3d right_slice[3][3]; 62 | 63 | // Projected area. A slice that includes ghost cells. 64 | Array3d area[3]; 65 | 66 | // Turbulent inflow generation 67 | // Profiles along the height 68 | Array1d umean, vmean, wmean; // local mean velocity profile, *mean[nz] 69 | Array1d gumean, gvmean, gwmean; // global mean velocity profile 70 | Array1d uu, vv, ww; // global normal Reynolds stress, xx[nz] 71 | Array1d uv, uw, vw; // global shear Reynolds stress, xy[nz] 72 | Array1d Lt, Lx, Ly, Lz; // global integral length scales, Lx[nz] and Langragian time scale 73 | Array1d a11,a21,a22; // global Lund matrix 74 | Array1d a31,a32,a33; // global Lund matrix 75 | Array1i ny, nz; // global integral length scale in grid cells 76 | // 2D slice 77 | Array2d random_u, random_v, random_w; // global random slice for u,v,w 78 | Array2d bfilty, bfiltz; // global filter in y and z, bij = bi*bj 79 | Array2d psiu, psiv, psiw; // global Psi slice 80 | Array2d psiu_old, psiv_old, psiw_old; // global Psi slice in the last step 81 | // Fluctuation slice 82 | Array2d uf, vf, wf; // local fluctuation for u,v,w, same size with inner inlet plane 83 | Array2d guf, gvf, gwf; // global velocity fluctuation planes 84 | 85 | // Random number generator 86 | std::random_device rd{}; 87 | std::mt19937 gen{rd()}; 88 | std::normal_distribution distribution{0,1}; // 0 mean, 1 variance distribution 89 | 90 | // Set view for MPI data exchange regions and boundary slices. 91 | void set_view(const Grid &); 92 | 93 | // Read and assign profile along the whole height for turbulent inflow generator. 94 | void read_turbulent_inflow_file(Array1d &var, const Grid &grid, const char *filename); 95 | 96 | // Initialise turbulent inflow generation related variables. 97 | void init_turbulent_inflow_generation(const Grid &); 98 | 99 | // Generate random number to random slices and update psi 100 | void random_gen(); 101 | 102 | public: 103 | 104 | BC(){}; 105 | ~BC(){} 106 | 107 | // Initialise a BC object and setup ranks for specific BC types. 108 | void init (const MyMPI &, const Controller &, const Grid &); 109 | 110 | // Apply velocity bounday condition. 111 | void apply_vbc(VectorFieldd &v, const MyMPI &, const Controller &, const Grid &); 112 | 113 | // Apply pressure boundary condition. 114 | void apply_pbc( Fieldd &f, const MyMPI &, const Controller &) const; 115 | 116 | // Apply boundary condition for scalars. 117 | void apply_sbc( Fieldd &f, const MyMPI &, const Controller &) const; 118 | 119 | // Apply velocity initial condition by using boundary condition. 120 | void apply_vic(VectorFieldd &v, const Controller &, const Grid &); 121 | 122 | // Generate new velocity fluctuation. 123 | void fluctuation_gen(const MyMPI &, const Controller &, const Grid &); 124 | 125 | // Halo cells swap with specific left and right ranks 126 | template void swap(Field &f, const MyMPI &, const Vec3i &left, const Vec3i &right) const; 127 | 128 | }; 129 | 130 | // Function template for swapping ghost cell data. 131 | template 132 | void BC::swap(Field &f, const MyMPI &mympi, const Vec3i &left, const Vec3i &right) const { 133 | 134 | // Get left halo; send to the left and receive from the right 135 | for(int d=0; d<3; d++){ 136 | int size = (left_view_get_end[d] - left_view_get_start[d]).product(); 137 | T *sendbuff = new T[size]; 138 | T *recvbuff = new T[size]; 139 | f.pack(left_view_get_start[d], left_view_get_end[d], sendbuff); 140 | mympi.sendrecv(sendbuff, recvbuff, size, mympi.left(d), mympi.right(d)); 141 | if(right(d) >= 0){ // get data but only fill into the field as it has the right 142 | f.fill(right_view_set_start[d], right_view_set_end[d], recvbuff); 143 | } 144 | delete [] sendbuff; 145 | delete [] recvbuff; 146 | } 147 | 148 | // Get right halo; send to the right and recive from the left 149 | for(int d=0; d<3; d++){ 150 | int size = (right_view_get_end[d] - right_view_get_start[d]).product(); 151 | T *sendbuff = new T[size]; 152 | T *recvbuff = new T[size]; 153 | f.pack(right_view_get_start[d], right_view_get_end[d], sendbuff); 154 | mympi.sendrecv(sendbuff, recvbuff, size, mympi.right(d), mympi.left(d)); 155 | if(left(d) >= 0){ // get data but only fill into the field as it has the left 156 | f.fill(left_view_set_start[d], left_view_set_end[d], recvbuff); 157 | } 158 | delete [] sendbuff; 159 | delete [] recvbuff; 160 | } 161 | } 162 | 163 | // Neumann BC 164 | void Neumann_bc(Fieldd &f, 165 | const Vec3i &set_start, const Vec3i &set_end, 166 | const Vec3i &get_start, const Vec3i &get_end); 167 | 168 | // Dirichlet BC for surface in between cell centres with value 0 169 | void Dirichlet_bc(Fieldd &f, 170 | const Vec3i &set_start, const Vec3i &set_end, 171 | const Vec3i &get_start, const Vec3i &get_end); 172 | 173 | // Dirichlet BC for a surface in between cell centres with a specified value 174 | void Dirichlet_bc(Fieldd &f, 175 | const Vec3i &set_start, const Vec3i &set_end, 176 | const Vec3i &get_start, const Vec3i &get_end, 177 | const double *data); 178 | 179 | // Dirichlet BC for a surface with a specified value 180 | void Dirichlet_bc(Fieldd &f, 181 | const Vec3i &set_start, const Vec3i &set_end, const double *data); 182 | 183 | // Dirichlet BC for a specified value on the slice 184 | void Dirichlet_bc(Fieldd &f, const Vec3i &set_start, const Vec3i &set_end, 185 | const double value); 186 | 187 | #endif // BC_H 188 | -------------------------------------------------------------------------------- /src/Grid.cc: -------------------------------------------------------------------------------- 1 | // Grid.cc 2 | // Grid class holds global and local coordinate, grid spacing and enquiry. 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #include "Grid.h" 6 | 7 | void Grid::set_uniform_line(const int d, double neg, double pos, double h){ 8 | 9 | // Sanity check 10 | if((d<0) || (d>=3)){ 11 | cerr << "Error: Exceeding dimension of grid!" << endl; 12 | exit(1); 13 | } 14 | if(neg >= pos){ 15 | cerr << "Error: Negative position greater than positive position in line " << d << "!" << endl; 16 | exit(1); 17 | } 18 | if(h <= 0){ 19 | cerr << "Error: Non-positive grid spacing in line " << d << "!" << endl; 20 | exit(1); 21 | } 22 | 23 | // Assignment 24 | double len = pos - neg; // total length 25 | int n = ceil(len/h); // num of cell 26 | int ntotal = n+1+2*GC; // num of cell + 1 for num of vertices + 2 * ghost cells for each side 27 | pos = neg + n*h; // overwrite positive location 28 | _gn[d] = n; 29 | _xg[d].allocate(ntotal); 30 | 31 | // Get the vertex location 32 | _xg[d](0) = neg-GC*h; 33 | for(int i=1; i=3)){ 44 | cerr << "Error: Exceeding dimension of grid!" << endl; 45 | exit(1); 46 | } 47 | if(uneg >= upos){ 48 | cerr << "Error: Negative uniform position greater than positive uniform position in stretched line " << d << "!" << endl; 49 | exit(1); 50 | } 51 | if(upos >= pos){ 52 | cerr << "Error: Positive stretched end less than positive uniform position in stretched line " << d << "!" << endl; 53 | exit(1); 54 | } 55 | if(h <= 0) { 56 | cerr << "Error: Non-positive minimum grid spacing in stretched line " << d << "!" << endl; 57 | exit(1); 58 | } 59 | if(factor <= 0) { 60 | cerr << "Error: Non-positive stretching factor in stretched line " << d << "!" << endl; 61 | exit(1); 62 | } 63 | 64 | // Compute and overwrite 65 | // Uniform part 66 | double ulen = upos - uneg; // uniform region length 67 | int n_uniform = ceil(ulen/h); // number of cells in uniform region 68 | ulen = n_uniform*h; // overwrite the length in the uniform region 69 | upos = uneg + n_uniform*h; // overwrite the positive uniform position 70 | 71 | // Stretched part 72 | int n_stretch = 0; // number of cells in stretched region 73 | double slen = pos - upos; // stretched part length 74 | double s = 0.; // cumulated stretched length 75 | double dx; // spacing 76 | while(true){ 77 | n_stretch++; 78 | dx = h*pow(factor, n_stretch); 79 | s += dx; 80 | if(s >= slen) {break;} 81 | } 82 | pos = upos + slen; // overwrite the positive location 83 | int ntotal = n_uniform+n_stretch+1+2*GC; // total number of vertices 84 | 85 | // Assignment 86 | _gn[d] = n_uniform + n_stretch; 87 | _xg[d].allocate(n_uniform+n_stretch+1+2*GC); 88 | 89 | // Get the vertex location 90 | // Uniform part 91 | _xg[d](0) = uneg-GC*h; 92 | for(int i=1; i<=GC+n_uniform; i++){ 93 | _xg[d](i) = _xg[d](i-1)+h; 94 | } 95 | 96 | // Stretched part 97 | int n = 1; 98 | for(int i=GC+n_uniform+1; i<=GC+n_uniform+n_stretch; i++){ 99 | dx = h*pow(factor, n); 100 | n++; 101 | _xg[d](i) = _xg[d](i-1)+dx; 102 | } 103 | 104 | // Positive ghost cells 105 | for(int i=GC+n_uniform+n_stretch+1; i= _end[d]) break; 249 | } 250 | index(d) = i-1; // give it back then 251 | } 252 | return index; 253 | } 254 | -------------------------------------------------------------------------------- /src/Psolver.cc: -------------------------------------------------------------------------------- 1 | // Psolver.cc 2 | // Poisson solver with HYPRE 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #include "Psolver.h" 6 | 7 | Psolver::Psolver(){} 8 | 9 | Psolver::~Psolver(){ 10 | if(initialised){ 11 | HYPRE_ParCSRPCGDestroy(solver); 12 | HYPRE_BoomerAMGDestroy(precond); 13 | } 14 | } 15 | 16 | void Psolver::init(const MyMPI &mympi, const Grid &mygrid, const Controller &controller){ 17 | 18 | // Dimension and stencils 19 | const int ndims = 3; 20 | const int nstencils = 7; 21 | int stencil_indices[nstencils] = {0,1,2,3,4,5,6}; 22 | int offsets[nstencils][ndims] = {{ 0, 0, 0}, 23 | {-1, 0, 0}, 24 | { 0,-1, 0}, 25 | { 0, 0,-1}, 26 | { 1, 0, 0}, 27 | { 0, 1, 0}, 28 | { 0, 0, 1}}; 29 | 30 | // Periodicity 31 | int periodic[3] = {0, 0, 0}; 32 | 33 | // 0. Get control variables. 34 | // 0.1 Set view 35 | for(int d=0; d<3; d++){ 36 | ilower[d] = mygrid.piece_start(d); 37 | iupper[d] = mygrid.piece_end(d)-1; 38 | } 39 | // 0.2 Set up stencils 40 | Fieldd left[3], right[3], diagonal; // left, right off diagonal and diagonal matrix. 41 | diagonal.allocate(mygrid.n()); 42 | for(int d=0; d<3; d++){ 43 | left[d].allocate(mygrid.n()); 44 | right[d].allocate(mygrid.n()); 45 | } 46 | for(int d=0; d<3; d++){ 47 | Vec3i o(0,0,0); o(d) = 1; // offset 48 | for(int k=mygrid.start(2); k= HYPRE_MAX_ITERATION) && (residual >= HYPRE_TOLERANCE)){ 253 | if(mympi.rank() == 0){ 254 | cerr << "Poisson solver divergers!" << endl; 255 | } 256 | exit(1); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /include/Controller.h: -------------------------------------------------------------------------------- 1 | // Controller.h 2 | // Controller for the programme. 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #ifndef CONTROLLER_H 6 | #define CONTROLLER_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "Vec.h" 16 | #include "Grid.h" 17 | #include "Array.h" 18 | #include "MyMPI.h" 19 | #include "parameter.h" 20 | 21 | using std::ios; 22 | using std::setw; 23 | using std::cerr; 24 | using std::endl; 25 | using std::vector; 26 | using std::string; 27 | using std::ofstream; 28 | using std::to_string; 29 | 30 | class Controller{ 31 | 32 | private: 33 | 34 | // Timing 35 | int _step = 0; // time step 36 | double _dt = -1.0; // time step Delta t 37 | double _time = 0.0; // simulation time 38 | double _walltime = -1.0; // wall time 39 | double _runtime = 0.0; // elapsed run time 40 | double _totaltime = -1.0; // total simulation time 41 | bool _adaptive = true; // adaptive time stepping 42 | double _cfl = 0.5; // CFL limit 43 | 44 | std::chrono::time_point t0; // t0 of simulation 45 | std::chrono::time_point t_last; // time in the last time step 46 | std::chrono::time_point t_now; // time in this time step 47 | 48 | // Fluid properties 49 | double _nu = -1.; // kinematic viscosity 50 | double _utau = -1.; // u_\tau 51 | double _z0 = -1.; // z0 52 | double _uref = 1.; // reference velocity 53 | double _Cd = 0.; // fluid drag coefficient, resulting from the unresolved canopy element 54 | double _cdheight = 0.; // canopy height, under where the drag is applied 55 | 56 | // Cases 57 | int _case = -1; // case for ic and bc types 58 | int _ibmtype = 0; // IBM roughness type 59 | int _dragmodel = 0; // Use drag model for the unresolved elements. 0 for off and 1 for on 60 | 61 | // Body force 62 | Vec3d _bforce; 63 | 64 | // Boundary conditions 65 | // Dummy velocity 66 | int _left_vbc[3] = {INLET, PERIODIC, SYMMETRY}; 67 | int _right_vbc[3] = {OUTFLOW, PERIODIC, SYMMETRY}; 68 | 69 | // Pressure 70 | int _left_pbc[3] = {DIRICHLET, PERIODIC, NEUMANN }; 71 | int _right_pbc[3] = {NEUMANN, PERIODIC, NEUMANN }; 72 | 73 | // Scalar 74 | int _left_sbc[3] = {DIRICHLET, PERIODIC, NEUMANN }; 75 | int _right_sbc[3] = {NEUMANN, PERIODIC, NEUMANN }; 76 | 77 | // Turbulent inflow generation 78 | bool _turbulent_inflow_generation = false; 79 | 80 | // Probes 81 | Array1b _probe_mask; 82 | Array2i _probe_index; 83 | 84 | // Slices 85 | Array2b _slice_mask; 86 | Array2i _slice_index; 87 | 88 | // Visualisation for the whole field 89 | double _visu_dt = 1.e8; // Time step increment for the next visualisation event 90 | double _visu_time = 1.e8; // Time to do visualisation. Can be used as t0 and timer 91 | 92 | // Visualisation for slice field 93 | double _slice_dt = 1.e8; // Time step increment for the next visualisation event 94 | double _slice_time = 1.e8; // Time to do visualisation. Can be used as t0 and timer 95 | 96 | // Time average statistics 97 | double _stat_time = 1.e8; // Time average statistics initialisation time 98 | 99 | // Initial velocity condition 100 | vector _vic_coords_start; // velocity initial condition coordinate start 101 | vector _vic_coords_end; // velocity initial condition coordinate end 102 | vector _vic_value; // velocity initial condition values 103 | 104 | public: 105 | 106 | // Contructor and deconstructor 107 | Controller(); 108 | ~Controller(){} 109 | 110 | // Set boundary conditions 111 | void set_left_vbc (const int d, const int x){ _left_vbc[d] = x; } 112 | void set_right_vbc(const int d, const int x){ _right_vbc[d] = x; } 113 | void set_left_pbc (const int d, const int x){ _left_pbc[d] = x; } 114 | void set_right_pbc(const int d, const int x){ _right_pbc[d] = x; } 115 | void set_left_sbc (const int d, const int x){ _left_sbc[d] = x; } 116 | void set_right_sbc(const int d, const int x){ _right_sbc[d] = x; } 117 | 118 | // Get boundary conditions 119 | int left_vbc (const int d) const { return _left_vbc[d]; } 120 | int right_vbc(const int d) const { return _right_vbc[d]; } 121 | int left_pbc (const int d) const { return _left_pbc[d]; } 122 | int right_pbc(const int d) const { return _right_pbc[d]; } 123 | int left_sbc (const int d) const { return _left_sbc[d]; } 124 | int right_sbc(const int d) const { return _right_sbc[d]; } 125 | 126 | // Initalise controller. Called after BC are set 127 | // by correcting MPI interface BC types to NONE and init timer. 128 | void init(const MyMPI &); 129 | 130 | // Set timing 131 | void set_step (const int x) {_step = x;} 132 | void set_dt (const double x) {_dt = x;} 133 | void set_time (const double x) {_time = x;} 134 | void set_walltime (const double x) {_walltime = x;} 135 | void set_totaltime(const double x) {_totaltime= x;} 136 | void set_adaptive (const bool x) {_adaptive = x;} 137 | void set_cfl (const double x) {_cfl = x;} 138 | 139 | // Get timing 140 | int step() const {return _step; } 141 | double dt() const {return _dt; } 142 | double time() const {return _time; } 143 | double walltime() const {return _walltime; } 144 | double totaltime() const {return _totaltime;} 145 | bool adaptive() const {return _adaptive; } 146 | double cfl() const {return _cfl; } 147 | double runtime() const {return _runtime; } 148 | 149 | // Reference velocity 150 | void set_uref (const double x) { _uref = x; } 151 | double uref () const {return _uref;} 152 | 153 | // Visualisation 154 | // Set values 155 | void set_visu_dt (const double x) {_visu_dt = x;} 156 | void set_visu_time (const double x) {_visu_time = x;} 157 | void set_slice_dt (const double x) {_slice_dt = x;} 158 | void set_slice_time(const double x) {_slice_time= x;} 159 | // Get values 160 | double visu_time() const {return _visu_time; } 161 | double slice_time() const {return _slice_time;} 162 | // Increment for the next event 163 | void increment_visu () {_visu_time += _visu_dt; } 164 | void increment_slice() {_slice_time += _slice_dt;} 165 | 166 | // Time average statistics 167 | void set_stat_time(const double x) {_stat_time = x;} 168 | double stat_time() const {return _stat_time;} 169 | 170 | // Set body force 171 | void set_bforce (const int d, const double x) {_bforce(d) = x;} 172 | 173 | // Get body force 174 | double bforce (const int d) const {return _bforce(d);} 175 | Vec3d bforce () const {return _bforce; } 176 | 177 | // Set IBM roughness type 178 | void set_ibmtype (const int x) {_ibmtype = x;} 179 | 180 | // Get IBM roughness type 181 | int get_ibmtype() const {return _ibmtype; } 182 | 183 | // Set drag model for unresolved elements, switch on 184 | void set_dragmodel(const int x) {_dragmodel = x;} 185 | 186 | // Get drag model status 187 | int get_dragmodel() const {return _dragmodel;} 188 | 189 | // Increment timing 190 | void increment_time () { _time += _dt;} // increment simulation time by adding time step 191 | void increment_step () { _step += 1;} // increment simulation step by adding 1 192 | void tick(const MyMPI &); // record iteration timing 193 | 194 | // Set ith probe with a specific coordinate 195 | void set_probe(const int i, const Vec3d &coords, const Grid &grid, const MyMPI &mympi); 196 | 197 | // Set ith slice location with a specific coordinate 198 | void set_slice(const int i, const int d, const double coord, const Grid &grid, const MyMPI &mympi); 199 | 200 | // Check if probe and slice is inside the partition. Loop with parameter MAX_.. 201 | bool probe_inside(const int i) const {return _probe_mask(i) ;} 202 | bool slice_inside(const int i, const int d) const {return _slice_mask(i, d);} 203 | 204 | // Get probe and slice index 205 | Vec3i probe_index(const int i) const {return Vec3i(_probe_index(i,0), 206 | _probe_index(i,1), 207 | _probe_index(i,2));} 208 | 209 | int slice_index (const int i, const int d) const {return _slice_index(i, d);} 210 | 211 | // Set and get case type 212 | void set_case (const int x) {_case = x;} 213 | int get_case () const {return _case;} 214 | 215 | // Set and get fluid properties 216 | void set_nu (const double x) {_nu = x;} 217 | double nu () const {return _nu; } 218 | 219 | void set_utau (const double x) {_utau = x;} 220 | double utau () const {return _utau;} 221 | 222 | void set_z0 (const double x) {_z0 = x;} 223 | double z0 () const {return _z0;} 224 | 225 | void set_Cd (const double x) {_Cd = x;} 226 | double Cd () const {return _Cd; } 227 | 228 | void set_cdheight(const double x) {_cdheight = x;} 229 | double cdheight () const {return _cdheight;} 230 | 231 | // Set and get turbulence inflow generation. 232 | void set_turbulent_inflow_generation(const bool x) {_turbulent_inflow_generation = x;} 233 | bool turbulent_inflow_generation() const {return _turbulent_inflow_generation;} 234 | 235 | // Set and get velocity initial condition. 236 | void set_vic(const Vec3d &start, const Vec3d &end, const Vec3d &value){ 237 | _vic_coords_start.push_back(start); 238 | _vic_coords_end.push_back(end); 239 | _vic_value.push_back(value); 240 | } 241 | Vec3d vic_coords_start(const int i) const {return _vic_coords_start[i];} 242 | Vec3d vic_coords_end (const int i) const {return _vic_coords_end[i]; } 243 | Vec3d vic_value (const int i) const {return _vic_value[i]; } 244 | int vic_number () const {return _vic_value.size(); } 245 | }; 246 | 247 | #endif // CONTROLLER_H 248 | -------------------------------------------------------------------------------- /src/Initialiser.cc: -------------------------------------------------------------------------------- 1 | // Initialiser.cc 2 | // Initialise control parameters and flow fields 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #include "Initialiser.h" 6 | 7 | // Make string character lower case 8 | void lower_string(string &str){ 9 | for(int i=0; i= 'A' && str[i] <= 'Z') { str[i] += 32; } 11 | } 12 | } 13 | 14 | void Initialiser::read_file(const MyMPI &mympi, Controller &controller, Grid &grid) { 15 | ifstream file("input.ccc"); 16 | if(!file.is_open()){ 17 | if(mympi.rank()==0) { cerr << "Input file ``input.ccc'' does not exist." << endl; } 18 | exit(1); 19 | } 20 | string line; 21 | while(getline(file, line)){ 22 | istringstream str(line); 23 | string word, keyword, list; 24 | str >> word; 25 | if(word[0] == '$' && word[word.length()-1] == '$') { 26 | keyword = word.substr(1, word.length()-2); 27 | lower_string(keyword); // get command name 28 | getline(file, line); // get command list 29 | istringstream list(line); // wrap to input stream 30 | if(keyword == "mpi"){ 31 | for(int d=0; d<3; d++) list >> mpi_blocks[d]; 32 | } 33 | else if(keyword == "grid"){ 34 | int d, type; 35 | list >> d >> type; 36 | grid_types[d] = type; 37 | if(type == 1){ 38 | double neg, pos, h; 39 | list >> neg >> pos >> h; 40 | grid.set_uniform_line(d, neg, pos, h); 41 | } 42 | else if(type == 2){ 43 | double uneg, upos, pos, h, factor; 44 | list >> uneg >> upos >> pos >> h >> factor; 45 | grid.set_stretch_line(d, uneg, upos, pos, h, factor); 46 | } 47 | else{ 48 | if(mympi.rank()==0) cerr << "Unknown type when initialising grid."; 49 | exit(1); 50 | } 51 | } 52 | else if(keyword == "case"){ 53 | int icase; 54 | list >> icase; 55 | controller.set_case(icase); 56 | } 57 | else if(keyword == "utau"){ 58 | double utau; 59 | list >> utau; 60 | controller.set_utau(utau); 61 | } 62 | else if(keyword == "z0"){ 63 | double z0; 64 | list >> z0; 65 | controller.set_z0(z0); 66 | } 67 | else if(keyword == "nu"){ 68 | double nu; 69 | list >> nu; 70 | controller.set_nu(nu); 71 | } 72 | else if(keyword == "inflow"){ 73 | int x; 74 | list >> x; 75 | controller.set_turbulent_inflow_generation(x); 76 | } 77 | else if(keyword == "totaltime"){ 78 | double totaltime; 79 | list >> totaltime; 80 | controller.set_totaltime(totaltime); 81 | } 82 | else if(keyword == "walltime"){ 83 | double walltime; 84 | list >> walltime; 85 | controller.set_walltime(walltime); 86 | } 87 | else if(keyword == "dt"){ 88 | double dt; 89 | list >> dt; 90 | controller.set_dt(dt); 91 | } 92 | else if(keyword == "adaptive"){ 93 | int adaptive; 94 | list >> adaptive; 95 | controller.set_adaptive(adaptive); 96 | } 97 | else if(keyword == "cfl"){ 98 | double cfl; 99 | list >> cfl; 100 | controller.set_cfl(cfl); 101 | } 102 | else if(keyword == "bodyforce"){ 103 | int d; 104 | double x; 105 | list >> d >> x; 106 | controller.set_bforce(d, x); 107 | } 108 | else if(keyword == "probe"){ 109 | int i; 110 | double x, y, z; 111 | list >> i >> x >> y >> z; 112 | probe_index.push_back(i); 113 | probe_coords.push_back(Vec3d(x,y,z)); 114 | } 115 | else if(keyword == "slice"){ 116 | int i, d; 117 | double coord; 118 | list >> i >> d >> coord; 119 | slice_index.push_back(i); 120 | slice_direction.push_back(d); 121 | slice_coords.push_back(coord); 122 | } 123 | else if(keyword == "restart"){ 124 | list >> _restart; 125 | } 126 | else if(keyword == "restatistics"){ 127 | list >> _restatistics; 128 | } 129 | else if(keyword == "ibmtype"){ 130 | int type; 131 | list >> type; 132 | controller.set_ibmtype(type); 133 | } 134 | else if(keyword == "dragmodel"){ 135 | int x; 136 | list >> x; 137 | controller.set_dragmodel(x); 138 | } 139 | else if(keyword == "cd"){ 140 | double x; 141 | list >> x; 142 | controller.set_Cd(x); 143 | } 144 | else if(keyword == "cdheight"){ 145 | double x; 146 | list >> x; 147 | controller.set_cdheight(x); 148 | } 149 | else if(keyword == "visu_dt"){ 150 | double dt; 151 | list >> dt; 152 | controller.set_visu_dt(dt); 153 | } 154 | else if(keyword == "visu_time"){ 155 | double time; 156 | list >> time; 157 | controller.set_visu_time(time); 158 | } 159 | else if(keyword == "slice_dt"){ 160 | double dt; 161 | list >> dt; 162 | controller.set_slice_dt(dt); 163 | } 164 | else if(keyword == "slice_time"){ 165 | double time; 166 | list >> time; 167 | controller.set_slice_time(time); 168 | } 169 | else if(keyword == "uref"){ 170 | double x; 171 | list >> x; 172 | controller.set_uref(x); 173 | } 174 | else if(keyword == "stat_time"){ 175 | double x; 176 | list >> x; 177 | controller.set_stat_time(x); 178 | } 179 | else if(keyword == "vic"){ 180 | double x1, x2, y1, y2, z1, z2, valx, valy, valz; 181 | list >> x1 >> y1 >> z1 >> x2 >> y2 >> z2 >> valx >> valy >> valz; 182 | controller.set_vic(Vec3d(x1,y1,z1), Vec3d(x2,y2,z2), Vec3d(valx, valy, valz)); 183 | } 184 | else{ 185 | if(mympi.master()){ 186 | cerr << "Unknown input item ``" << keyword << "''\n"; 187 | } 188 | exit(1); 189 | } 190 | } // end if keyword and control variable list 191 | } // end while 192 | } 193 | 194 | void Initialiser::init(MyMPI &mympi, Controller &controller, Grid &grid) const { 195 | 196 | // Sanity check 197 | bool error = false; 198 | for(int i=0; i<3; i++){ 199 | if(grid_types[i] == 0) error = true; 200 | } 201 | if(error){ 202 | if(mympi.rank() == 0) cerr << "Initialisation: Grid type error." << endl; 203 | exit(1); 204 | } 205 | for(int i=0; i<3; i++){ 206 | if(mpi_blocks[i] <= 0) error = true; 207 | } 208 | if(error){ 209 | if(mympi.rank() == 0) cerr << "Initialisation: MPI blocks should be a positive integer." << endl; 210 | exit(1); 211 | } 212 | if(controller.nu() < 0){ 213 | if(mympi.rank() == 0) cerr << "Initialisation: Did not specify a proper kinematic viscosity." << endl; 214 | exit(1); 215 | } 216 | if(controller.get_case() < 0){ 217 | if(mympi.rank() == 0) cerr << "Initialisation: Error in specified case." << endl; 218 | exit(1); 219 | } 220 | if(controller.get_case() == CASE_URBAN){ 221 | if(controller.utau() < 0){ 222 | if(mympi.rank() == 0) cerr << "Initialisation: Did not specify friction velocity u_tau." << endl; 223 | exit(1); 224 | } 225 | if(controller.z0() < 0){ 226 | if(mympi.rank() == 0) cerr << "Initialisation: Did not specify z0." << endl; 227 | exit(1); 228 | } 229 | } 230 | if(!controller.adaptive()){ 231 | if(controller.dt() < 0){ 232 | if(mympi.rank() == 0) 233 | cerr << "Initialisation: Did not specify a time step properly in a constant time stepping simulation." << endl; 234 | exit(1); 235 | } 236 | } 237 | if(controller.totaltime()<0){ 238 | if(mympi.rank() == 0) cerr << "Initialisation: Did not specify a simulation total time properly." << endl; 239 | exit(1); 240 | } 241 | 242 | // Set controller 243 | // Pressure boundary conditions 244 | if((controller.get_case() == CASE_URBAN) || (controller.get_case() == CASE_TBL)){ 245 | 246 | // Left pressure BC 247 | //controller.set_left_pbc(0, DIRICHLET); 248 | controller.set_left_pbc(0, NEUMANN ); 249 | controller.set_left_pbc(1, PERIODIC ); 250 | controller.set_left_pbc(2, NEUMANN ); 251 | 252 | // Right pressure BC 253 | //controller.set_right_pbc(0, NEUMANN ); 254 | controller.set_right_pbc(0, DIRICHLET); 255 | controller.set_right_pbc(1, PERIODIC ); 256 | controller.set_right_pbc(2, NEUMANN ); 257 | 258 | // Left scalar BC 259 | controller.set_left_sbc(0, NEUMANN ); 260 | controller.set_left_sbc(1, PERIODIC ); 261 | controller.set_left_sbc(2, NEUMANN ); 262 | 263 | // Right scalar BC 264 | controller.set_right_sbc(0, NEUMANN ); 265 | controller.set_right_sbc(1, PERIODIC); 266 | controller.set_right_sbc(2, NEUMANN ); 267 | } 268 | 269 | if(controller.get_case() == CASE_CYLINDER){ 270 | 271 | // Left pressure BC 272 | controller.set_left_pbc(0, NEUMANN ); 273 | controller.set_left_pbc(1, PERIODIC ); 274 | controller.set_left_pbc(2, NEUMANN ); 275 | 276 | // Right pressure BC 277 | controller.set_right_pbc(0, DIRICHLET); 278 | controller.set_right_pbc(1, PERIODIC ); 279 | controller.set_right_pbc(2, NEUMANN ); 280 | 281 | // Left scalar BC 282 | controller.set_left_sbc(0, NEUMANN ); 283 | controller.set_left_sbc(1, PERIODIC ); 284 | controller.set_left_sbc(2, NEUMANN ); 285 | 286 | // Right scalar BC 287 | controller.set_right_sbc(0, NEUMANN ); 288 | controller.set_right_sbc(1, PERIODIC); 289 | controller.set_right_sbc(2, NEUMANN ); 290 | } 291 | 292 | if(controller.get_case() == CASE_OPENCHANNEL){ 293 | 294 | // Left pressure BC 295 | controller.set_left_pbc(0, PERIODIC ); 296 | controller.set_left_pbc(1, PERIODIC ); 297 | controller.set_left_pbc(2, NEUMANN ); 298 | 299 | // Right pressure BC 300 | controller.set_right_pbc(0, PERIODIC); 301 | controller.set_right_pbc(1, PERIODIC); 302 | controller.set_right_pbc(2, NEUMANN ); 303 | 304 | // Left scalar BC 305 | controller.set_left_sbc(0, PERIODIC ); 306 | controller.set_left_sbc(1, PERIODIC ); 307 | controller.set_left_sbc(2, NEUMANN ); 308 | 309 | // Right scalar BC 310 | controller.set_right_sbc(0, PERIODIC); 311 | controller.set_right_sbc(1, PERIODIC); 312 | controller.set_right_sbc(2, NEUMANN ); 313 | } 314 | 315 | // Make new directories 316 | mkdir( HDF_DIR, 0777); 317 | mkdir( GRID_DIR, 0777); 318 | mkdir( DATA_DIR, 0777); 319 | mkdir( SAVE_DIR, 0777); 320 | mkdir( STAT_DIR, 0777); 321 | mkdir(SLICE_DIR, 0777); 322 | mkdir( VTK_DIR, 0777); 323 | 324 | // Initialise objects 325 | mympi.init(mpi_blocks); 326 | controller.init(mympi); // init after BC are set 327 | grid.init(mympi); 328 | 329 | // Probes 330 | for(int i=0; i &other , 196 | const vector &fieldnames , 197 | const Grid &grid , 198 | const MyMPI &mympi , 199 | const char *prefix , 200 | const char *filename ){ 201 | 202 | // Retrieve number of fields 203 | int nfields = other.size(); 204 | 205 | // Make file name 206 | char name[200]; 207 | sprintf(name, "%s%s.h5", prefix, filename); 208 | 209 | // Part 1: Output data 210 | // 1. Open file collectively 211 | hid_t fapl = H5Pcreate(H5P_FILE_ACCESS); 212 | H5Pset_fapl_mpio(fapl, MPI_COMM_WORLD, MPI_INFO_NULL); 213 | hid_t file_id = H5Fcreate(name, H5F_ACC_TRUNC, H5P_DEFAULT, fapl); 214 | H5Pclose(fapl); 215 | 216 | // 2. Write data collectively 217 | H5write_field_float(file_id, "u", mympi, grid, u[0]); 218 | H5write_field_float(file_id, "v", mympi, grid, u[1]); 219 | H5write_field_float(file_id, "w", mympi, grid, u[2]); 220 | H5write_field_float(file_id, "p", mympi, grid, p ); 221 | for(int i=0; i" << endl; 243 | xdmf << "" << endl; 244 | xdmf << "" << endl; 245 | xdmf << "" << endl; 246 | xdmf << "" << endl; 247 | sprintf(buffer, "", grid.gn(0)+1, grid.gn(1)+1, grid.gn(2)+1); 248 | xdmf << buffer << endl; 249 | xdmf << "" << endl; 250 | char coords[3] = {'x', 'y', 'z'}; 251 | for(int d=0; d<3; d++){ 252 | sprintf(buffer, "\ngrid.h5:/%c\n", 253 | grid.gn(0)+1, grid.gn(1)+1, grid.gn(2)+1, coords[d]); 254 | xdmf << buffer << endl; 255 | } 256 | xdmf << "" << endl; 257 | 258 | // Velocity vector 259 | xdmf << "" << endl; 260 | sprintf(buffer, "", 261 | grid.gn(0)+1, grid.gn(1)+1, grid.gn(2)+1); 262 | xdmf << buffer << endl; 263 | 264 | // u 265 | sprintf(buffer, "\n%s.h5:/%s\n", 266 | grid.gn(0)+1, grid.gn(1)+1, grid.gn(2)+1, filename, "u"); 267 | xdmf << buffer << endl; 268 | 269 | // v 270 | sprintf(buffer, "\n%s.h5:/%s\n", 271 | grid.gn(0)+1, grid.gn(1)+1, grid.gn(2)+1, filename, "v"); 272 | xdmf << buffer << endl; 273 | 274 | // w 275 | sprintf(buffer, "\n%s.h5:/%s\n", 276 | grid.gn(0)+1, grid.gn(1)+1, grid.gn(2)+1, filename, "w"); 277 | xdmf << buffer << endl; 278 | 279 | xdmf << "" << endl << "" << endl; 280 | 281 | // Pressure 282 | xdmf << "" << endl; 283 | sprintf(buffer, "\n%s.h5:/%s\n", 284 | grid.gn(0)+1, grid.gn(1)+1, grid.gn(2)+1, filename, "p"); 285 | xdmf << buffer << endl; 286 | xdmf << "" << endl; 287 | 288 | // Other fields 289 | for(int i=0; i" << endl; 291 | sprintf(buffer, "\n%s.h5:/%s\n", 292 | grid.gn(0)+1, grid.gn(1)+1, grid.gn(2)+1, filename, fieldnames[i].c_str()); 293 | xdmf << buffer << endl; 294 | xdmf << "" << endl; 295 | } 296 | 297 | // Ending 298 | xdmf << "" << endl; 299 | xdmf << "" << endl; 300 | xdmf << "" << endl; 301 | 302 | // Close file 303 | xdmf.close(); 304 | } 305 | 306 | } 307 | 308 | #endif // USE_HDF 309 | -------------------------------------------------------------------------------- /src/Discretisation.cc: -------------------------------------------------------------------------------- 1 | // Discretisation.cc 2 | // Namespace to discretise the governing equations. 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #include "Discretisation.h" 6 | namespace Discretisation{ 7 | 8 | double GammaScheme(double uj, double vm1, double v, double vp1, double vp2); 9 | 10 | void project(VectorFieldd &v, const Fieldd &p, const Grid &grid, const Controller &controller){ 11 | 12 | // Get time step. 13 | double dt = controller.dt(); 14 | 15 | // Loop over components and make piecewise update. 16 | for(int k=p.ks(); k= 0){ 283 | c = v; 284 | u = vm1; // v - 1 285 | d = vp1; // v + 1 286 | } 287 | else{ 288 | c = vp1; 289 | u = vp2; 290 | d = v; 291 | } 292 | if(d == u){ 293 | phic = (c-u)/(1.e-8); 294 | } 295 | else{ 296 | phic = (c-u)/(d-u); 297 | } 298 | if( (phic <= 0) || (phic >= 1) ){ 299 | result = c; // upwind scheme 300 | } 301 | if( (phic >= BETA) && (phic < 1) ){ 302 | result = 0.5*(c+d); // central difference scheme 303 | } 304 | if( (phic > 0) && (phic < BETA) ){ 305 | double r = phic/BETA; 306 | result = (1-0.5*r)*c+0.5*r*d; // blended 307 | } 308 | return result; 309 | } 310 | 311 | } // end namespace Discretisation 312 | 313 | -------------------------------------------------------------------------------- /include/Array.h: -------------------------------------------------------------------------------- 1 | // Array.h 2 | // Class template for array up to three-dimension. 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #ifndef ARRAY_H 6 | #define ARRAY_H 7 | 8 | #include 9 | 10 | namespace Array{ 11 | 12 | const int unallocated = 0; // status 13 | 14 | inline void ArrayExit(const char* x){ 15 | std::cerr << std::endl << "Error: " << x << "." << std::endl; 16 | exit(1); 17 | } 18 | 19 | // 1D array 20 | template 21 | class Array1{ 22 | 23 | private: 24 | T *v; // data 25 | int _size; // total size 26 | int status; // allocation status 27 | 28 | public: 29 | 30 | // Constructors and deconstructor 31 | Array1(): v(NULL), _size(0), status(unallocated) {} 32 | 33 | Array1(const int n): v(NULL), _size(0), status(unallocated){ 34 | allocate(n); 35 | } 36 | 37 | Array1(const Array1 &b): v(NULL), _size(0), status(unallocated){ 38 | allocate(b._size); 39 | for(int i=0; i& operator = (const Array1 &b){ 82 | if(allocated()){ 83 | if(_size != b._size){ 84 | deallocate(); 85 | allocate(b._size); 86 | } 87 | } 88 | else{ 89 | allocate(b._size); 90 | } 91 | for(int i=0; i& operator = (const T b){ 98 | if(!allocated()){ 99 | ArrayExit("Try to assign a value to a unallocated Array1"); 100 | } 101 | for(int i=0; i<_size; i++) { 102 | v[i] = b; 103 | } 104 | return *this; 105 | } 106 | 107 | T& operator () (const int i) { 108 | if((i<0) || (i>=_size)) { 109 | ArrayExit("Array1 index out of bound"); 110 | } 111 | return v[i]; 112 | } 113 | 114 | const T& operator () (const int i) const { 115 | if((i<0) || (i>=_size)) { 116 | ArrayExit("Array1 index out of bound"); 117 | } 118 | return v[i]; 119 | } 120 | 121 | // Take data 122 | T* data() {return v;} 123 | const T* data() const {return v;} 124 | 125 | // Get size 126 | int size() const {return _size;} 127 | }; 128 | 129 | // 2D array 130 | template 131 | class Array2{ 132 | 133 | private: 134 | T *v; // data 135 | int _size; // total size 136 | int status; // allocation status 137 | int nx = 0; // 1st dimension 138 | int ny = 0; // 2nd dimension 139 | 140 | public: 141 | 142 | // Constructor and deconstructor 143 | Array2(): v(NULL), _size(0), status(unallocated) {} 144 | 145 | Array2(const int nx0, const int ny0): v(NULL), _size(0), status(unallocated) { 146 | allocate(nx0, ny0); 147 | } 148 | 149 | Array2(const Array2 &b): v(NULL), _size(0), status(unallocated) { 150 | allocate(b.nx, b.ny); 151 | for(int i=0; i& operator = (const Array2 &b){ 199 | if(allocated()){ 200 | if((nx != b.nx) || (ny != b.ny)){ 201 | deallocate(); 202 | allocate(b.nx, b.ny); 203 | } 204 | } 205 | else{ 206 | allocate(b.nx, b.ny); 207 | } 208 | for(int i=0; i& operator = (const T b){ 215 | if(!allocated()){ 216 | ArrayExit("Try to assign a value to a unallocated Array2"); 217 | } 218 | for(int i=0; i<_size; i++){ 219 | v[i] = b; 220 | } 221 | return *this; 222 | } 223 | 224 | T& operator () (const int i, const int j) { 225 | if((i<0) || (i>=nx)){ 226 | ArrayExit("Array2 1st index out of bound"); 227 | } 228 | if((j<0) || (j>=ny)){ 229 | ArrayExit("Array2 2nd index out of bound"); 230 | } 231 | return v[j*nx+i]; 232 | } 233 | 234 | const T& operator () (const int i, const int j) const { 235 | if((i<0) || (i>=nx)){ 236 | ArrayExit("Array2 1st index out of bound"); 237 | } 238 | if((j<0) || (j>=ny)){ 239 | ArrayExit("Array2 2nd index out of bound"); 240 | } 241 | return v[j*nx+i]; 242 | } 243 | 244 | // Take data 245 | T* data() {return v;} 246 | const T* data() const {return v;} 247 | 248 | // Get size 249 | int size() const {return _size;} 250 | }; 251 | 252 | // 3D Array 253 | template 254 | class Array3{ 255 | 256 | private: 257 | T *v; // data 258 | int _size; // total size 259 | int status; // allocation status 260 | int nx = 0; // 1st dimension 261 | int ny = 0; // 2nd dimension 262 | int nz = 0; // 3rd dimension 263 | 264 | public: 265 | 266 | // Constructor and deconstructor 267 | Array3(): v(NULL), _size(0), status(unallocated) {} 268 | 269 | Array3(const int nx0, const int ny0, const int nz0): 270 | v(NULL), _size(0), status(unallocated){ 271 | allocate(nx0, ny0, nz0); 272 | } 273 | 274 | Array3(const Array3 &b): v(NULL), _size(0), status(unallocated){ 275 | allocate(b.nx, b.ny, b.nz); 276 | for(int i=0; i& operator = (const Array3& b){ 327 | if(allocated()) { 328 | if((nx != b.nx) || (ny != b.ny) || (nz != b.nz)){ 329 | deallocate(); 330 | allocate(b.nx, b.ny, b.nz); 331 | } 332 | } 333 | else { 334 | allocate(b.nx, b.ny, b.nz); 335 | } 336 | for(int i=0; i& operator = (const T b){ 343 | if(!allocated()){ 344 | ArrayExit("Try to assign a value to a unallocated Array3"); 345 | } 346 | for(int i=0; i<_size; i++){ 347 | v[i] = b; 348 | } 349 | return *this; 350 | } 351 | 352 | T& operator () (const int i, const int j, const int k) { 353 | if((i<0) || (i>=nx)){ 354 | ArrayExit("Array3 1st index out of bound"); 355 | } 356 | if((j<0) || (j>=ny)){ 357 | ArrayExit("Array3 2nd index out of bound"); 358 | } 359 | if((k<0) || (k>=nz)){ 360 | ArrayExit("Array3 3rd index out of bound"); 361 | } 362 | return v[k*nx*ny+j*nx+i]; 363 | } 364 | 365 | const T& operator () (const int i, const int j, const int k) const { 366 | if((i<0) || (i>=nx)){ 367 | ArrayExit("Array3 1st index out of bound"); 368 | } 369 | if((j<0) || (j>=ny)){ 370 | ArrayExit("Array3 2nd index out of bound"); 371 | } 372 | if((k<0) || (k>=nz)){ 373 | ArrayExit("Array3 3rd index out of bound"); 374 | } 375 | return v[k*nx*ny+j*nx+i]; 376 | } 377 | 378 | // Take data 379 | T* data() {return v;} 380 | const T* data() const {return v;} 381 | 382 | // Get size 383 | int size() const {return _size;} 384 | }; 385 | 386 | }; 387 | 388 | // User defined types 389 | typedef Array::Array1 Array1i; 390 | typedef Array::Array1 Array1f; 391 | typedef Array::Array1 Array1d; 392 | typedef Array::Array1 Array1b; 393 | 394 | typedef Array::Array2 Array2i; 395 | typedef Array::Array2 Array2f; 396 | typedef Array::Array2 Array2d; 397 | typedef Array::Array2 Array2b; 398 | 399 | typedef Array::Array3 Array3i; 400 | typedef Array::Array3 Array3f; 401 | typedef Array::Array3 Array3d; 402 | typedef Array::Array3 Array3b; 403 | 404 | #endif // ARRAY_H 405 | -------------------------------------------------------------------------------- /src/Fluid.cc: -------------------------------------------------------------------------------- 1 | // Fluid.cc 2 | // Top class of the solver. 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #include "Fluid.h" 6 | #ifdef USE_HDF 7 | #include 8 | #include "hdf5.h" 9 | #endif 10 | 11 | using namespace std; 12 | 13 | void Fluid::init(){ 14 | 15 | // Init objects and make directories 16 | initialiser.read_file(mympi, controller, grid); 17 | initialiser.init(mympi, controller, grid); 18 | psolver.init(mympi, grid, controller); 19 | bc.init(mympi, controller, grid); 20 | ibm.init(grid, controller, mympi); 21 | stat.init(grid); 22 | 23 | // Init fields 24 | u.allocate (grid.n()); 25 | h.allocate (grid.n()); 26 | p.allocate (grid.n()); 27 | ed.allocate(grid.n()); 28 | r.allocate(grid.n()); 29 | for(int d=0; d<3; d++){ 30 | uij[d].allocate(grid.n()); 31 | uij_cen[d].allocate(grid.n()); 32 | } 33 | div.allocate(grid.n()); 34 | cfl.allocate(grid.n()); 35 | if(controller.get_dragmodel() != 0){ 36 | D.allocate(grid.n()); 37 | } 38 | 39 | // Assign variables 40 | nu = controller.nu(); 41 | 42 | // Read fields if restart 43 | if(initialiser.restart()){ 44 | 45 | // Read fields and controls 46 | read(); 47 | 48 | // Resume time-averaged statistics 49 | if(initialiser.restatistics()){ 50 | #ifdef USE_HDF 51 | stat.read_hdf(mympi, grid); 52 | #else 53 | stat.read(mympi); 54 | #endif 55 | } 56 | } 57 | else{ 58 | // new run, apply initial condition. 59 | bc.apply_vic(u, controller, grid); 60 | } // end restart 61 | 62 | bc.apply_vbc(u, mympi, controller, grid); 63 | 64 | // Initial step velocity gradient tensor and eddy viscosity 65 | Discretisation::gradtensor(uij, uij_cen, u, grid); 66 | SGS::compute_SGS(ed, u, uij_cen, grid, controller); 67 | bc.apply_sbc(ed, mympi, controller); 68 | } 69 | 70 | void Fluid::updateAB(){ 71 | 72 | // Turbulent inlet 73 | if(controller.turbulent_inflow_generation()){ 74 | if((controller.step() % TURBINLET_INTERVALS) == 0) {bc.fluctuation_gen(mympi, controller, grid);} 75 | } 76 | 77 | Discretisation::conv_diff(r, u, grid, nu, ed, uij); 78 | 79 | // Get drag force term 80 | if(controller.get_dragmodel() != 0){ 81 | Discretisation::get_dragforce(u, D, grid, controller.Cd(), controller.cdheight()); 82 | } 83 | 84 | if(controller.step() == 0) {h = r;} // It will reduce the init step to Euler 85 | Discretisation::AB(u, r, h, controller.bforce(), controller.dt()); 86 | 87 | // Add additional terms 88 | if(controller.get_dragmodel() != 0){ 89 | Discretisation::add_vectorfields(u, D); 90 | } 91 | 92 | h = r; // Update history term 93 | updateUP(u); 94 | 95 | // Get new velocity gradient tensor and eddy viscosity based on the new velocitiy 96 | Discretisation::gradtensor(uij, uij_cen, u, grid); 97 | SGS::compute_SGS(ed, u, uij_cen, grid, controller); 98 | bc.apply_sbc(ed, mympi, controller); 99 | } 100 | 101 | void Fluid::updateUP(VectorFieldd &v){ 102 | bc.apply_vbc(v, mympi, controller, grid); 103 | if(controller.get_ibmtype() != 0) { 104 | ibm.ibc(v); 105 | bc.apply_vbc(v, mympi, controller, grid); 106 | } 107 | Discretisation::divergence(div, v, grid); 108 | div.scale(-1./controller.dt()); 109 | psolver.solve(div, p, mympi); 110 | bc.apply_pbc(p, mympi, controller); 111 | Discretisation::project(v, p, grid, controller); 112 | bc.apply_vbc(v, mympi, controller, grid); 113 | } 114 | 115 | double Fluid::cfl_limit(){ 116 | cfl = 0.0; // reset du/dx 117 | for(int d=0; d<3; d++){ 118 | for(int k=u.ks(); k 1) && (controller.step()%50000 == 0) ){ save(); } 159 | } 160 | } 161 | 162 | void Fluid::read(){ 163 | 164 | #ifdef USE_HDF 165 | // Collectively read fields 166 | hid_t file_id = H5Fopen( (string(HDF_DIR)+string("/")+string("Fluid.h5")).c_str(), H5F_ACC_RDONLY, H5P_DEFAULT); 167 | 168 | H5read_field(file_id, "u0", mympi, grid, u[0]); 169 | H5read_field(file_id, "u1", mympi, grid, u[1]); 170 | H5read_field(file_id, "u2", mympi, grid, u[2]); 171 | H5read_field(file_id, "h0", mympi, grid, h[0]); 172 | H5read_field(file_id, "h1", mympi, grid, h[1]); 173 | H5read_field(file_id, "h2", mympi, grid, h[2]); 174 | H5read_field(file_id, "p", mympi, grid, p ); 175 | 176 | H5Fclose(file_id); 177 | 178 | // Read controls 179 | ifstream file; 180 | file.open( (string(SAVE_DIR)+string("/")+string("Fluid")).c_str(), ios::binary ); 181 | 182 | int i; 183 | double d; 184 | file.read((char*)&i, sizeof(int)); // read time step 185 | file.read((char*)&d, sizeof(double)); // read simulation time 186 | controller.set_step(i); 187 | controller.set_time(d); 188 | 189 | file.read((char*)&d, sizeof(double)); // read Delta_t 190 | controller.set_dt(d); 191 | 192 | file.read((char*)&i, sizeof(int)); // read visu step 193 | file.read((char*)&d, sizeof(double)); // read visu time 194 | visu_step = i; 195 | controller.set_visu_time(d); 196 | 197 | file.read((char*)&i, sizeof(int)); // read slice step 198 | file.read((char*)&d, sizeof(double)); // read slice time 199 | slice_step = i; 200 | controller.set_slice_time(d); 201 | 202 | file.close(); 203 | #else 204 | // Open a file 205 | ifstream file; 206 | char name[100]; 207 | sprintf(name, "%s/Fluid_x%dx%dx%d", SAVE_DIR, mympi.coords(0), mympi.coords(1), mympi.coords(2)); 208 | file.open(name, ios::binary); 209 | 210 | // Read controls 211 | int i; 212 | double d; 213 | file.read((char*)&i, sizeof(int)); // read time step 214 | file.read((char*)&d, sizeof(double)); // read simulation time 215 | controller.set_step(i); 216 | controller.set_time(d); 217 | 218 | file.read((char*)&d, sizeof(double)); // read Delta_t 219 | controller.set_dt(d); 220 | 221 | file.read((char*)&i, sizeof(int)); // read visu step 222 | file.read((char*)&d, sizeof(double)); // read visu time 223 | visu_step = i; 224 | controller.set_visu_time(d); 225 | 226 | file.read((char*)&i, sizeof(int)); // read slice step 227 | file.read((char*)&d, sizeof(double)); // read slice time 228 | slice_step = i; 229 | controller.set_slice_time(d); 230 | 231 | // Read fields 232 | u.read(file); 233 | h.read(file); 234 | p.read(file); 235 | 236 | // Close a file 237 | file.close(); 238 | #endif 239 | } 240 | 241 | void Fluid::save() const { 242 | 243 | #ifdef USE_HDF 244 | // Collectively write fields 245 | hid_t fapl = H5Pcreate(H5P_FILE_ACCESS); 246 | H5Pset_fapl_mpio(fapl, MPI_COMM_WORLD, MPI_INFO_NULL); 247 | hid_t file_id = H5Fcreate( (string(HDF_DIR)+string("/")+string("Fluid.h5")).c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, fapl); 248 | H5Pclose(fapl); 249 | 250 | H5write_field(file_id, "u0", mympi, grid, u[0]); 251 | H5write_field(file_id, "u1", mympi, grid, u[1]); 252 | H5write_field(file_id, "u2", mympi, grid, u[2]); 253 | H5write_field(file_id, "h0", mympi, grid, h[0]); 254 | H5write_field(file_id, "h1", mympi, grid, h[1]); 255 | H5write_field(file_id, "h2", mympi, grid, h[2]); 256 | H5write_field(file_id, "p", mympi, grid, p ); 257 | 258 | H5Fclose(file_id); 259 | 260 | // Write controls 261 | if(mympi.master()){ 262 | ofstream file; 263 | file.open( (string(SAVE_DIR)+string("/")+string("Fluid")).c_str(), ios::binary ); 264 | 265 | int i = controller.step(); 266 | double d = controller.time(); 267 | file.write((char*)&i, sizeof(int)); // write time step 268 | file.write((char*)&d, sizeof(double)); // write simulation time 269 | 270 | d = controller.dt(); 271 | file.write((char*)&d, sizeof(double)); // write Delta_t 272 | 273 | i = visu_step; 274 | d = controller.visu_time(); 275 | file.write((char*)&i, sizeof(int)); // write visu step 276 | file.write((char*)&d, sizeof(double)); // write visu time 277 | 278 | i = slice_step; 279 | d = controller.slice_time(); 280 | file.write((char*)&i, sizeof(int)); // write slice step 281 | file.write((char*)&d, sizeof(double)); // write slice time 282 | 283 | file.close(); 284 | } 285 | 286 | // Save statistics 287 | stat.save_hdf(mympi, grid); 288 | #else 289 | // Open a file 290 | ofstream file; 291 | char name[100]; 292 | sprintf(name, "%s/Fluid_x%dx%dx%d", SAVE_DIR, mympi.coords(0), mympi.coords(1), mympi.coords(2)); 293 | file.open(name, ios::binary); 294 | 295 | // Write controls 296 | int i = controller.step(); 297 | double d = controller.time(); 298 | file.write((char*)&i, sizeof(int)); // write time step 299 | file.write((char*)&d, sizeof(double)); // write simulation time 300 | 301 | d = controller.dt(); 302 | file.write((char*)&d, sizeof(double)); // write Delta_t 303 | 304 | i = visu_step; 305 | d = controller.visu_time(); 306 | file.write((char*)&i, sizeof(int)); // write visu step 307 | file.write((char*)&d, sizeof(double)); // write visu time 308 | 309 | i = slice_step; 310 | d = controller.slice_time(); 311 | file.write((char*)&i, sizeof(int)); // write slice step 312 | file.write((char*)&d, sizeof(double)); // write slice time 313 | 314 | // Write fields 315 | u.save(file); 316 | h.save(file); 317 | p.save(file); 318 | 319 | // Close a file 320 | file.close(); 321 | 322 | // Save Statistics. Only save the data. Whether to read statistics depends on controller. 323 | stat.save(mympi); 324 | #endif 325 | } 326 | 327 | void Fluid::field_visu(){ 328 | if(controller.time() >= controller.visu_time()){ 329 | int total_digits = 4; 330 | int n_reserve = 5; 331 | visu_step++; 332 | vector additional_fields; 333 | vector additional_fieldnames; 334 | additional_fields.reserve(n_reserve); 335 | additional_fieldnames.reserve(n_reserve); 336 | 337 | // Add additional fields and their names 338 | additional_fields.push_back(ed); 339 | additional_fieldnames.push_back(string("nu_sgs")); 340 | 341 | #ifdef USE_HDF 342 | H5fluid_fields(u, p, additional_fields, additional_fieldnames, grid, mympi, (string(HDF_DIR)+string("/")).c_str(), 343 | string(total_digits-to_string(visu_step).length(), '0').append(to_string(visu_step)).c_str()); 344 | #else 345 | myvtk.fluid_fields(u, p, additional_fields, additional_fieldnames, grid, mympi, (string(VTK_DIR)+string("/")).c_str(), 346 | string(total_digits-to_string(visu_step).length(), '0').append(to_string(visu_step)).c_str()); 347 | #endif 348 | controller.increment_visu(); 349 | 350 | // Write file name and its simulation time to file. 351 | if(mympi.rank() == 0){ 352 | ofstream file; 353 | file.open(MONITOR_VISU, ios::app); 354 | file << string(total_digits-to_string(visu_step).length(), '0').append(to_string(visu_step)) << " "; 355 | file << controller.time() << endl; 356 | file.close(); 357 | } 358 | } // end if current time greater than visualisation time 359 | } 360 | 361 | void Fluid::slice_visu() { 362 | if(controller.time() >= controller.slice_time()){ 363 | int total_digits = 4; 364 | slice_step++; 365 | void *dummy; 366 | myvtk.slice_fields(u, p, (Fieldd *) dummy, (char **) &dummy, 0, grid, mympi, controller, (string(SLICE_DIR)+string("/")).c_str(), 367 | string(total_digits-to_string(visu_step).length(), '0').append(to_string(visu_step)).c_str()); 368 | controller.increment_slice(); 369 | 370 | // Write file name and its simulation time to file. 371 | if(mympi.rank() == 0){ 372 | ofstream file; 373 | file.open(MONITOR_SLICE, ios::app); 374 | file << string(total_digits-to_string(slice_step).length(), '0').append(to_string(slice_step)) << " "; 375 | file << controller.time() << endl; 376 | file.close(); 377 | } 378 | } 379 | } 380 | 381 | void Fluid::time_average(){ 382 | if(controller.time() >= controller.stat_time()) { 383 | stat.do_statistics(u, p); 384 | } 385 | if(controller.step()%100000 == 0) {stat_visu();} 386 | } 387 | 388 | void Fluid::stat_visu(){ 389 | stat.write(grid, mympi); 390 | } 391 | 392 | Fluid::~Fluid(){ 393 | stat_visu(); 394 | save(); 395 | } 396 | -------------------------------------------------------------------------------- /src/MyVTK.cc: -------------------------------------------------------------------------------- 1 | // MyVTK.cc 2 | // Output VTK visualisation with point data 3 | // Code_CartesianCraft (c) Yongxin Chen 4 | 5 | #include "MyVTK.h" 6 | 7 | // 0-based number to char converter (from 'a') 8 | char num2char(int n){return (char)('a'+n);} 9 | 10 | // Only output single field 11 | void MyVTK::single_field(const Fieldd &f, 12 | const Grid &grid, 13 | const MyMPI &mympi, 14 | const char *fieldname, 15 | const char *prefix , 16 | const char *filename ) const { 17 | 18 | // Make file name and open file 19 | ofstream vtr; 20 | char name[200]; 21 | sprintf(name, "%s%s.x%dx%dx%d.vtr", prefix, filename, mympi.coords(0), mympi.coords(1), mympi.coords(2)); 22 | vtr.open(name, ios::binary); 23 | 24 | // Some variables 25 | int offset = 0; 26 | char buffer[300]; 27 | 28 | // Write head 29 | vtr << "" << endl; 30 | sprintf(buffer, "", 31 | 0, grid.gn(0), 0, grid.gn(1), 0, grid.gn(2)); 32 | vtr << buffer << endl; 33 | sprintf(buffer, "", 34 | grid.piece_start(0), grid.piece_end(0), 35 | grid.piece_start(1), grid.piece_end(1), 36 | grid.piece_start(2), grid.piece_end(2)); 37 | vtr << buffer << endl; 38 | 39 | // Write coordinate 40 | char coords[3] = {'X', 'Y', 'Z'}; 41 | vtr << "" << endl; 42 | for(int d=0; d<3; d++){ 43 | vtr << "" << endl; 45 | offset += sizeof(int)+(grid.n(d)+1)*sizeof(float); 46 | } 47 | vtr << "" << endl; 48 | 49 | // Write point data 50 | vtr << "" << endl; 51 | vtr << "" << endl; 53 | vtr << "" << endl; 54 | vtr << "" << endl; 55 | vtr << "" << endl; 56 | vtr << "" << endl; 57 | vtr << "_"; 58 | 59 | // Write data 60 | // Write coordinate 61 | float out; // ouptut buffer 62 | int bsize; // size in bytes 63 | for(int d=0; d<3; d++){ 64 | bsize = (grid.n(d)+1)*sizeof(float); 65 | vtr.write((char*)&bsize, sizeof(int)); 66 | for(int i=grid.start(d); i" << endl << ""; 85 | vtr.close(); 86 | 87 | // Write .pvtr file 88 | if(mympi.rank() == 0) { 89 | sprintf(name, "%s%s.collection.pvtr", prefix, filename); 90 | ofstream pvtr; 91 | pvtr.open(name); 92 | pvtr << "" << endl; 93 | sprintf(buffer, "", \ 94 | 0, grid.gn(0), 0, grid.gn(1), 0, grid.gn(2)); 95 | pvtr << buffer << endl; 96 | pvtr << "" << endl; 97 | pvtr << "" << endl; 98 | pvtr << "" << endl; 99 | pvtr << "" << endl; 100 | pvtr << "" << endl; 101 | pvtr << "" << endl; 102 | pvtr << "" << endl; 103 | pvtr << "" << endl; 104 | 105 | // Append sources 106 | Vec3i n, start, end; // piece dimension, start, end 107 | 108 | // Loop over all the partitions 109 | for(int k=0; k", \ 132 | start(0), end(0), start(1), end(1), start(2), end(2), name); 133 | pvtr << buffer << endl; 134 | } 135 | } 136 | } // end each partition 137 | 138 | pvtr << "" << endl; 139 | pvtr << ""; 140 | pvtr.close(); 141 | } 142 | } 143 | 144 | void MyVTK::fluid_fields(const VectorFieldd &u , 145 | const Fieldd &p , 146 | const vector &other , 147 | const vector &fieldnames, 148 | const Grid &grid , 149 | const MyMPI &mympi , 150 | const char *prefix , 151 | const char *filename ) const { 152 | // Retrieve number of fields 153 | int nfields = other.size(); 154 | 155 | // Make file name and open file 156 | ofstream vtr; 157 | char name[200]; 158 | sprintf(name, "%s%s.x%dx%dx%d.vtr", prefix, filename, mympi.coords(0), mympi.coords(1), mympi.coords(2)); 159 | vtr.open(name, ios::binary); 160 | 161 | // Some variables 162 | int offset = 0; 163 | char buffer[300]; 164 | 165 | // Write head 166 | vtr << "" << endl; 167 | sprintf(buffer, "", 168 | 0, grid.gn(0), 0, grid.gn(1), 0, grid.gn(2)); 169 | vtr << buffer << endl; 170 | sprintf(buffer, "", 171 | grid.piece_start(0), grid.piece_end(0), 172 | grid.piece_start(1), grid.piece_end(1), 173 | grid.piece_start(2), grid.piece_end(2)); 174 | vtr << buffer << endl; 175 | 176 | // Write coordinate 177 | char coords[3] = {'X', 'Y', 'Z'}; 178 | vtr << "" << endl; 179 | for(int d=0; d<3; d++){ 180 | vtr << "" << endl; 182 | offset += sizeof(int)+(grid.n(d)+1)*sizeof(float); 183 | } 184 | vtr << "" << endl; 185 | 186 | // Write point data 187 | vtr << "" << endl; 188 | vtr << "" << endl; 190 | offset += sizeof(int)+3*(u.n()+1).product()*sizeof(float); // point data 191 | vtr << "" << endl; 193 | offset += sizeof(int)+(p.n()+1).product()*sizeof(float); // point data 194 | 195 | // write other fields if exist 196 | for(int i=0; i" << endl; 199 | offset += sizeof(int)+(other[i].n()+1).product()*sizeof(float); // point data 200 | } 201 | vtr << "" << endl; 202 | vtr << "" << endl; 203 | vtr << "" << endl; 204 | vtr << "" << endl; 205 | vtr << "_"; 206 | 207 | // Write data 208 | // Write coordinate 209 | float out; // ouptut buffer 210 | int bsize; // size in bytes 211 | for(int d=0; d<3; d++){ 212 | bsize = (grid.n(d)+1)*sizeof(float); 213 | vtr.write((char*)&bsize, sizeof(int)); 214 | for(int i=grid.start(d); i" << endl << ""; 268 | vtr.close(); 269 | 270 | // Write .pvtr file 271 | if(mympi.rank() == 0) { 272 | sprintf(name, "%s%s.collection.pvtr", prefix, filename); 273 | ofstream pvtr; 274 | pvtr.open(name); 275 | pvtr << "" << endl; 276 | sprintf(buffer, "", \ 277 | 0, grid.gn(0), 0, grid.gn(1), 0, grid.gn(2)); 278 | pvtr << buffer << endl; 279 | pvtr << "" << endl; 280 | pvtr << "" << endl; 281 | pvtr << "" << endl; 282 | pvtr << "" << endl; 283 | pvtr << "" << endl; 284 | pvtr << "" << endl; 285 | pvtr << "" << endl; 286 | pvtr << "" << endl; 287 | for(int i=0; i" << endl; 289 | } 290 | pvtr << "" << endl; 291 | 292 | // Append sources 293 | Vec3i n, start, end; // piece dimension, start, end 294 | 295 | // Loop over all the partitions 296 | for(int k=0; k", \ 319 | start(0), end(0), start(1), end(1), start(2), end(2), name); 320 | pvtr << buffer << endl; 321 | } 322 | } 323 | } // end each partition 324 | 325 | pvtr << "" << endl; 326 | pvtr << ""; 327 | pvtr.close(); 328 | } 329 | } 330 | 331 | void MyVTK::slice_fields(const VectorFieldd &u , 332 | const Fieldd &p , 333 | const Fieldd *other , 334 | char **fieldnames, 335 | const int nfields , 336 | const Grid &grid , 337 | const MyMPI &mympi , 338 | const Controller &controller, 339 | const char *prefix , 340 | const char *filename ) const { 341 | 342 | // Loop over slice normal directions 343 | char coords[3] = {'X', 'Y', 'Z'}; 344 | for(int norm=0; norm<3; norm++){ 345 | 346 | // MPI coordinates 347 | int mpicoords[2], m = 0; 348 | for(int d=0; d<3; d++){ 349 | if(d == norm) continue; 350 | mpicoords[m] = mympi.coords(d); 351 | m++; 352 | } 353 | 354 | // Loop over slice 355 | for(int slice=0; slice" << endl; 375 | sprintf(buffer, "", 376 | 0, whole_end(0), 0, whole_end(1), 0, whole_end(2)); 377 | vtr << buffer << endl; 378 | sprintf(buffer, "", 379 | piece_start(0), piece_end(0), 380 | piece_start(1), piece_end(1), 381 | piece_start(2), piece_end(2)); 382 | vtr << buffer << endl; 383 | 384 | // Write coordinate 385 | vtr << "" << endl; 386 | for(int d=0; d<3; d++){ 387 | vtr << "" << endl; 389 | offset += sizeof(int)+(piece_end(d)-piece_start(d)+1)*sizeof(float); 390 | } 391 | vtr << "" << endl; 392 | 393 | // Write data 394 | vtr << "" << endl; // point data 395 | vtr << "" << endl; 397 | offset += sizeof(int)+3*(u[0].n()+1).product()/(grid.n(norm)+1)*sizeof(float); // point data 398 | vtr << "" << endl; 400 | offset += sizeof(int)+(p.n()+1).product()/(grid.n(norm)+1)*sizeof(float); // point data 401 | 402 | // write other fields if exist 403 | for(int i=0; i" << endl; 406 | offset += sizeof(int)+(other[i].n()+1).product()/(grid.n(norm)+1)*sizeof(float); // point data 407 | } 408 | vtr << "" << endl; 409 | vtr << "" << endl; 410 | vtr << "" << endl; 411 | vtr << "" << endl; 412 | vtr << "_"; 413 | 414 | // Write data 415 | // Write coordinate 416 | float out; // ouptut buffer 417 | int bsize; // size in bytes 418 | for(int d=0; d<3; d++){ 419 | bsize = (piece_end(d)-piece_start(d)+1)*sizeof(float); 420 | vtr.write((char*)&bsize, sizeof(int)); 421 | if(d == norm){ 422 | out = (float)grid.x(d, controller.slice_index(slice, norm)); 423 | vtr.write((char*)&out, sizeof(float)); 424 | } 425 | else{ 426 | for(int i=grid.start(d); i" << endl << ""; 488 | vtr.close(); 489 | 490 | } // end slice inside partition 491 | } // end loop over slices 492 | } // end loop over slice normal directions 493 | 494 | 495 | 496 | // Write .pvtr file 497 | for(int norm=0; norm<3; norm++){ 498 | 499 | // Loop over slice 500 | for(int slice=0; slice" << endl; 520 | sprintf(buffer, "", 521 | 0, whole_end(0), 0, whole_end(1), 0, whole_end(2)); 522 | pvtr << buffer << endl; 523 | pvtr << "" << endl; 524 | pvtr << "" << endl; 525 | pvtr << "" << endl; 526 | pvtr << "" << endl; 527 | pvtr << "" << endl; 528 | pvtr << "" << endl; 529 | pvtr << "" << endl; 530 | pvtr << "" << endl; 531 | for(int i=0; i" << endl; 533 | } 534 | pvtr << "" << endl; 535 | 536 | // Append sources 537 | Vec3i n, start, end; // piece dimension, start, end 538 | int ijke[3]={mympi.blocks(0), mympi.blocks(1), mympi.blocks(2)}; // MPI coords 539 | int ie, je, ke; 540 | ijke[norm] = 1; // flatten MPI coords 541 | ie = ijke[0]; je = ijke[1]; ke = ijke[2]; // get back 542 | 543 | // Loop over all the partitions 544 | for(int k=0; k", \ 577 | start(0), end(0), start(1), end(1), start(2), end(2), name); 578 | pvtr << buffer << endl; 579 | } // end each partition 580 | 581 | pvtr << "" << endl << ""; 582 | pvtr.close(); 583 | 584 | } // end if rank==0 output 585 | } // end loop over slices 586 | } // end looop over slice normal directions 587 | } 588 | --------------------------------------------------------------------------------