├── .build.yml ├── CMakeLists.txt ├── COPYING ├── README.md ├── examples ├── CMakeLists.txt ├── dense │ ├── CMakeLists.txt │ ├── DENSE_TEST │ └── main.cpp ├── memory.h ├── opts.h ├── post-tess │ ├── CMakeLists.txt │ ├── POST_TEST │ └── main.cpp ├── pread-voronoi │ ├── BOV_TEST │ ├── CMakeLists.txt │ ├── HACC_TEST │ ├── MOAB_TEST │ ├── PREAD_VORONOI │ ├── balance.cpp │ ├── common.h │ ├── hacc-voronoi.cpp │ ├── io │ │ ├── gadget │ │ │ ├── particles.cpp │ │ │ └── particles.h │ │ ├── hacc │ │ │ ├── particles.cpp │ │ │ └── particles.h │ │ ├── hdf5 │ │ │ ├── pread-hdf5.cpp │ │ │ └── pread.h │ │ └── moab │ │ │ ├── particles.cpp │ │ │ └── particles.h │ ├── pread-bov.cpp │ ├── pread-moab.cpp │ ├── pread-voronoi.cpp │ └── unit-cube.h5 ├── tess-dense │ ├── CMakeLists.txt │ ├── TESS_DENSE_TEST │ └── main.cpp └── tess │ ├── CMakeLists.txt │ ├── TESS_TEST │ └── main.cpp ├── include └── tess │ ├── backward.hpp │ ├── delaunay.h │ ├── delaunay.hpp │ ├── dense.hpp │ ├── swap.hpp │ ├── tess-cgal.h │ ├── tess-qhull.h │ ├── tess.h │ ├── tess.hpp │ ├── tet-neighbors.h │ ├── tet.h │ ├── tet.hpp │ └── volume.h ├── src ├── CMakeLists.txt ├── backward.cpp ├── dense.cpp ├── swap.cpp ├── tess-cgal.cpp ├── tess-kdtree.cpp ├── tess-qhull.c ├── tess-regular.cpp ├── tess.cpp ├── tet.cpp └── volume.cpp └── tools ├── CMakeLists.txt ├── dense-plot.py ├── draw.cpp └── stats.cpp /.build.yml: -------------------------------------------------------------------------------- 1 | image: archlinux 2 | triggers: 3 | - action: email 4 | condition: failure 5 | to: Dmitriy Morozov 6 | packages: 7 | - cmake 8 | - cgal 9 | - boost 10 | - openmpi 11 | sources: 12 | - https://github.com/diatomic/tess2 13 | - https://github.com/diatomic/diy 14 | tasks: 15 | - checkout-diy: | 16 | cd diy 17 | git checkout $DIY_REV 18 | - configure: | 19 | cd tess2 20 | mkdir build 21 | cd build 22 | cmake .. -DDIY_INCLUDE_DIRS=~/diy/include -Dserial=CGAL -Ddraw=off 23 | - build: | 24 | cd tess2/build 25 | make 26 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (tess) 2 | cmake_minimum_required (VERSION 3.9) 3 | 4 | option (BUILD_SHARED_LIBS "Build shared libraries" OFF) 5 | option (timing "Build tess with timing" ON) 6 | option (memory "Build tess with memory profiling" OFF) 7 | option (draw "Build draw" ON) 8 | option (bgq "Build on BG/Q" OFF) 9 | option (pread "Build pread-voronoi example (requires HDF5)" OFF) 10 | option (diy_thread "Enable diy threading" OFF) 11 | option (omp_thread "Enable openmp threading" OFF) 12 | option (build_examples "Build examples" ON) 13 | option (build_tools "Build tools" OFF) 14 | 15 | set (serial "QHull" CACHE STRING "serial Delaunay library to use") 16 | set_property (CACHE serial PROPERTY STRINGS CGAL QHull) 17 | 18 | # CMAKE_BUILD_TYPE defaults to Release 19 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 20 | message(STATUS "Setting build type to 'Release' as none was specified.") 21 | set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) 22 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") 23 | endif() 24 | 25 | if (timing) 26 | add_definitions (-DTIMING) 27 | endif (timing) 28 | 29 | if (memory) 30 | add_definitions (-DMEMORY) 31 | endif (memory) 32 | 33 | if (bgq) 34 | add_definitions (-DBGQ) 35 | endif (bgq) 36 | 37 | if (${CMAKE_BUILD_TYPE} MATCHES DEBUG) 38 | add_definitions (-DDEBUG) 39 | endif () 40 | 41 | # OSX flags 42 | if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 43 | add_definitions (-DMAC_OSX) 44 | set (CMAKE_MACOSX_RPATH on) 45 | 46 | # --- following RPATH settings are for Sierra w/ Clang, hopefully they don't hurt other versions 47 | # ref: https://cmake.org/Wiki/CMake_RPATH_handling 48 | # use, i.e. don't skip, the full RPATH for the build tree 49 | set (CMAKE_SKIP_BUILD_RPATH false) 50 | # when building, don't use the install RPATH already (but later on when installing) 51 | set (CMAKE_BUILD_WITH_INSTALL_RPATH false) 52 | # set RPATH to install path 53 | set (CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") 54 | # add the automatically determined parts of the RPATH 55 | # which point to directories outside the build tree to the install RPATH 56 | set (CMAKE_INSTALL_RPATH_USE_LINK_PATH true) 57 | # the RPATH to be used when installing, but only if it's not a system directory 58 | list (FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES 59 | "${CMAKE_INSTALL_PREFIX}/lib" 60 | isSystemDir) 61 | if ("${isSystemDir}" STREQUAL "-1") 62 | set (CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") 63 | endif() 64 | endif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 65 | 66 | # QHull vs. CGAL 67 | if (${serial} MATCHES "CGAL") 68 | message ("Using CGAL") 69 | find_package (CGAL) 70 | find_package (Boost) 71 | include (${CGAL_USE_FILE}) 72 | include_directories (${CGAL_INCLUDE_DIRS} SYSTEM ${Boost_INCLUDE_DIR}) 73 | set (libraries ${libraries} ${CGAL_LIBRARY} ${CGAL_3RD_PARTY_LIBRARIES}) 74 | set (CMAKE_EXE_LINKER_FLAGS "-dynamic ${CMAKE_EXE_LINKER_FLAGS}") 75 | # TODO: this should really be based on whether the compiler suffers from the bug in std::nth_element() 76 | add_definitions (-DTESS_CGAL_ALLOW_SPATIAL_SORT) 77 | add_definitions (-DTESS_USE_CGAL) 78 | elseif (${serial} MATCHES "QHull") 79 | message ("Using QHull") 80 | find_path (QHull_INCLUDE_DIRS libqhull.h) 81 | find_library (QHull_LIBRARY NAMES qhullstatic) 82 | include_directories (${QHull_INCLUDE_DIRS}) 83 | set (libraries ${libraries} ${QHull_LIBRARY}) 84 | add_definitions (-DTESS_USE_QHull) 85 | else () 86 | message ("Uknown serial library: ${serial}") 87 | endif () 88 | 89 | # C++14 90 | set (CMAKE_CXX_STANDARD 14) 91 | 92 | # MPI 93 | find_package (MPI REQUIRED) 94 | set (libraries ${libraries} ${MPI_C_LIBRARIES} ${MPI_CXX_LIBRARIES}) 95 | 96 | # Threads 97 | if (omp_thread) 98 | find_package (Threads) 99 | find_package (OpenMP) 100 | if (OPENMP_FOUND) 101 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") 102 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") 103 | else () 104 | message ("OpenMP not found") 105 | add_definitions (-DTESS_NO_OPENMP) 106 | endif () 107 | else (omp_thread) 108 | message ("OpenMP not being used") 109 | add_definitions (-DTESS_NO_OPENMP) 110 | endif (omp_thread) 111 | if (NOT diy_thread) 112 | message ("Diy threading is disabled; setting diy threads will have no effect") 113 | add_definitions (-DDIY_NO_THREADS) 114 | endif (NOT diy_thread) 115 | 116 | # OpenGL 117 | if (draw) 118 | find_package (GLUT) 119 | find_package (OpenGL) 120 | endif (draw) 121 | 122 | # DIY 123 | find_path (DIY_INCLUDE_DIRS diy/types.h) 124 | 125 | # Include path 126 | set (CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-isystem") 127 | include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include 128 | ${DIY_INCLUDE_DIRS} 129 | SYSTEM ${MPI_INCLUDE_PATH} 130 | ) 131 | 132 | # Libraries 133 | set (libraries 134 | ${libraries} 135 | ${CMAKE_THREAD_LIBS_INIT} 136 | ) 137 | if (debug) 138 | find_library (LIBDW_LIBRARY NAMES dw) 139 | if (LIBDW_LIBRARY) 140 | set (DEBUG_SOURCES ${CMAKE_SOURCE_DIR}/lib/backward.cpp) 141 | add_definitions (-DBACKWARD_HAS_DW=1) 142 | set (libraries ${libraries} ${LIBDW_LIBRARY}) 143 | else (LIBDW_LIBRARY) 144 | message (STATUS "LibDW not found; backward.cpp won't be used") 145 | endif (LIBDW_LIBRARY) 146 | endif (debug) 147 | 148 | # Subdirectories 149 | add_subdirectory (src) 150 | if (build_examples) 151 | add_subdirectory (examples) 152 | endif () 153 | if (build_tools) 154 | add_subdirectory (tools) 155 | endif () 156 | 157 | # Install the headers 158 | file (GLOB DEPLOY_FILES_AND_DIRS "${PROJECT_SOURCE_DIR}/include/*") 159 | foreach (ITEM ${DEPLOY_FILES_AND_DIRS}) 160 | if (IS_DIRECTORY "${ITEM}" ) 161 | list (APPEND DIRS_TO_DEPLOY "${ITEM}") 162 | else () 163 | list (APPEND FILES_TO_DEPLOY "${ITEM}") 164 | endif () 165 | endforeach () 166 | install (FILES ${FILES_TO_DEPLOY} DESTINATION ${CMAKE_INSTALL_PREFIX}/include ) 167 | install (DIRECTORY ${DIRS_TO_DEPLOY} DESTINATION ${CMAKE_INSTALL_PREFIX}/include ) 168 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (C) 2015, UChicago Argonne, LLC 2 | 3 | All Rights Reserved 4 | 5 | TESS Software (ANL-14-081) 6 | 7 | Thomas Peterka, Argonne National Laboratory 8 | Dmitriy Morozov, Lawrence Berkeley National Laboratory 9 | 10 | OPEN SOURCE LICENSE 11 | 12 | Under the terms of Contract No. DE-AC02-06CH11357 with UChicago Argonne, LLC, the U.S. Government retains certain rights in this software. 13 | 14 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 15 | 16 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 17 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 18 | 3. Neither the names of UChicago Argonne, LLC or the Department of Energy nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 19 | 20 | *************************************************************************************************** 21 | 22 | DISCLAIMER 23 | 24 | THE SOFTWARE IS SUPPLIED "AS IS" WITHOUT WARRANTY OF ANY KIND. 25 | 26 | NEITHER THE UNTED STATES GOVERNMENT, NOR THE UNITED STATES DEPARTMENT OF ENERGY, NOR UCHICAGO ARGONNE, LLC, NOR ANY OF THEIR EMPLOYEES, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, DATA, APPARATUS, PRODUCT, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS. 27 | 28 | ********************************************************************************************* 29 | 30 | 31 | 32 | ANL-OSS-15-1028 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Parallel Delaunay and Voronoi Tessellation and Density Estimation 2 | 3 | ## Licensing 4 | 5 | Tess is released as open source software under a BSD style [license](./COPYING). 6 | 7 | ## Requirements 8 | 9 | - a C++11 compiler 10 | - the [DIY](https://github.com/diatomic/diy) block parallel library 11 | - either the [Qhull](http://qhull.org/) or [CGAL](http://www.cgal.org/) computational geometry library 12 | 13 | ## Installation 14 | 15 | The following instructions assume Qhull is the serial geometry engine. 16 | 17 | Build Dependencies 18 | 19 | a. [DIY](https://github.com/diatomic/diy) 20 | 21 | ``` 22 | git clone https://github.com/diatomic/diy2 23 | ``` 24 | 25 | b. [Qhull](http://qhull.org/) 26 | 27 | ``` 28 | wget http://www.qhull.org/download/qhull-2020-src-8.0.2.tgz 29 | tar -xvf qhull-2020-src-8.0.2.tgz 30 | cd qhull-2020.2/ 31 | make 32 | ``` 33 | 34 | Build Tess 35 | 36 | ``` 37 | git clone https://github.com/diatomic/tess2 38 | 39 | cmake /path/to/tess \ 40 | -DCMAKE_CXX_COMPILER=mpicxx \ 41 | -DCMAKE_C_COMPILER=mpicc \ 42 | -DCMAKE_INSTALL_PREFIX=/path/to/tess2/install \ 43 | -Dserial=QHull \ 44 | -DDIY_INCLUDE_DIRS=/path/to/diy/include \ 45 | -DQHull_INCLUDE_DIRS=/path/to/qhull-2020.2/src/libqhull \ 46 | -DQHull_LIBRARY=/path/to/qhull-2020.2/lib/libqhullstatic.a 47 | 48 | make 49 | make install 50 | ``` 51 | 52 | Optionally, Tess can use [CGAL](http://www.cgal.org/) instead of Qhull. To do so, 53 | pass `-Dserial=CGAL` as the choice to `cmake`. 54 | 55 | ## Execution 56 | 57 | 1. Test tessellation only 58 | 59 | ``` 60 | cd path/to/tess2/install/examples/tess 61 | ``` 62 | 63 | Edit TESS_TEST: select ARCH, num_procs, dsize (number of particles) 64 | 65 | ``` 66 | ./TESS_TEST 67 | path/to/tess2/install/tools/draw del.out 68 | ``` 69 | 70 | Mouse interaction with drawing: mouse move to rotate, ‘z’ + mouse up, down to zoom, ‘t’ to toggle voronoi tessellation, ‘y’ to toggle delaunay tessellation, ‘f’ to toggle shaded rendering 71 | 72 | 2. Test tessellation + density estimator 73 | 74 | (from tess top level directory) 75 | 76 | ``` 77 | cd path/to/tess2/install/examples/tess-dense 78 | ``` 79 | 80 | Edit TESS_DENSE_TEST; select ARCH, num_procs, dsize (number of particles), gsize (number of grid points) 81 | 82 | ``` 83 | ./TESS_DENSE_TEST 84 | path/to/tess2/install/tools/dense-plot.py --raw=dense.raw --numpts=512 85 | ``` 86 | (assuming outfile was dense.raw and gsize was 512 512 512 in TESS_DENSE_TEST) 87 | 88 | Dense-plot.py is a python script using numpy and matplotlib, but you can use your favorite visualization/plotting tool (VisIt, ParaView, R, Octave, Matlab, etc.) to plot the output. It is just an array of 32-bit floating-point density values listed in C-order (x changes fastest). 89 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory (tess) 2 | add_subdirectory (dense) 3 | add_subdirectory (tess-dense) 4 | if (pread) 5 | add_subdirectory (pread-voronoi) 6 | endif (pread) 7 | 8 | -------------------------------------------------------------------------------- /examples/dense/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable (dense main.cpp) 2 | target_link_libraries (dense tess ${libraries}) 3 | 4 | install (TARGETS dense 5 | DESTINATION ${CMAKE_INSTALL_PREFIX}/examples/dense/ 6 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE 7 | GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) 8 | 9 | install (FILES DENSE_TEST 10 | DESTINATION ${CMAKE_INSTALL_PREFIX}/examples/dense/ 11 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE 12 | GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) 13 | -------------------------------------------------------------------------------- /examples/dense/DENSE_TEST: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ##PBS -A CSC033 4 | #PBS -A CI-CCR000086 5 | #PBS -N t 6 | #PBS -j oe 7 | #PBS -l walltime=0:10:00,size=12 8 | 9 | #---------------------------------------------------------------------------- 10 | # 11 | # mpi run script 12 | # 13 | # Tom Peterka 14 | # Argonne National Laboratory 15 | # 9700 S. Cass Ave. 16 | # Argonne, IL 60439 17 | # tpeterka@mcs.anl.gov 18 | # 19 | #---------------------------------------------------------------------------- 20 | ARCH=MAC_OSX 21 | #ARCH=LINUX 22 | #ARCH=BGQ 23 | #ARCH=FUSION 24 | #ARCH=XT 25 | #ARCH=XE 26 | 27 | # number of procs 28 | # must be <= tb 29 | num_procs=4 30 | 31 | # procs per node 32 | ppn=2 # adjustable for BG/Q, allowed 1, 2, 4, 8, 16, 32, 64 33 | 34 | # number of BG/P nodes for vn mode 35 | num_nodes=$[$num_procs / $ppn] 36 | if [ $num_nodes -lt 1 ]; then 37 | num_nodes=1 38 | fi 39 | 40 | # executable 41 | exe=./dense 42 | 43 | # input file 44 | #infile="/projects/SSSPPg/tpeterka/128p4096b-vor.out.nc" 45 | #infile="../driver/vor.out.nc" 46 | #infile="../tess-driver/del.out.nc" 47 | #infile="vor.out-100K-2b.nc" 48 | #infile="vor-99.out.nc" 49 | #infile="7445077095.out.nc" 50 | #infile="${HOME}/tess/post-tess-driver/6606356352.out.nc" 51 | #infile="${HOME}/tess/post-tess-driver/7445077095.out.nc" 52 | #infile="${HOME}/tess/post-tess-driver/nfw-1e5.out.nc" 53 | #infile="cnfw_2e5.out.nc" 54 | #infile="${HOME}/hacc/voronoi/density-estimator/tests/nfw/voronoi-results/nfw.out.nc" 55 | #infile="${HOME}/hacc/voronoi/density-estimator/tests/cnfw/cic-results/cnfw_2e5.out.nc" 56 | infile="../tess/del.out" 57 | 58 | # output file 59 | outfile="dense.raw" 60 | 61 | # algorithm (0=tess, 1 = cic) 62 | alg=0 63 | 64 | # sample grid size (number of points) x y z 65 | #gsize="16 16 16" 66 | #gsize="32 32 32" 67 | #gsize="64 64 64" 68 | #gsize="128 128 128" 69 | #gsize="256 256 256" 70 | gsize="512 512 512" 71 | #gsize="1024 1024 1024" 72 | #gsize="2048 2048 2048" 73 | #gsize="4096 4096 4096" 74 | #gsize="8192 8192 8192" 75 | 76 | #projection plane 77 | #project=! 78 | project="0.0 0.0 1.0" #normal to plane, xy plane is the only one supported so far" 79 | 80 | # particle mass 81 | mass=1 82 | 83 | # given bounds 84 | ng=0 85 | gmin="-1.5 -1.5" 86 | gmax="1.5 1.5" 87 | 88 | #------ 89 | # 90 | # program arguments 91 | # 92 | 93 | args="$infile $outfile $alg $gsize $project $mass $ng $gmin $gmax" 94 | 95 | #------ 96 | # 97 | # run commands 98 | # 99 | 100 | if [ "$ARCH" = "MAC_OSX" ]; then 101 | 102 | mpiexec -l -n $num_procs $exe $args 103 | 104 | #dsymutil $exe ; mpiexec -l -n $num_procs xterm -e gdb -x gdb.run --args $exe $args 105 | 106 | #dsymutil $exe ; mpiexec -l -n $num_procs valgrind -q $exe $args 107 | 108 | #dsymutil $exe ; mpiexec -n $num_procs valgrind -q --leak-check=yes $exe $args 109 | 110 | fi 111 | 112 | if [ "$ARCH" = "LINUX" ]; then 113 | 114 | mpiexec -n $num_procs $exe $args 115 | 116 | #mpiexec -n $num_procs xterm -e gdb -x gdb.run --args $exe $args 117 | 118 | #mpiexec -n $num_procs valgrind -q $exe $args 119 | 120 | #mpiexec -n $num_procs valgrind -q --leak-check=yes $exe $args 121 | 122 | fi 123 | 124 | if [ "$ARCH" = "BGQ" ]; then 125 | 126 | qsub -n $num_nodes --mode c$ppn -A SSSPPg -t 60 $exe $args 127 | 128 | fi 129 | 130 | if [ "$ARCH" = "FUSION" ]; then 131 | 132 | mpiexec $exe $args 133 | 134 | fi 135 | 136 | if [ "$ARCH" = "XT" ]; then 137 | 138 | cd /tmp/work/$USER 139 | aprun -n $num_procs $exe $args 140 | 141 | fi 142 | 143 | if [ "$ARCH" = "XE" ]; then 144 | 145 | aprun -n $num_procs $exe $args 146 | 147 | fi 148 | -------------------------------------------------------------------------------- /examples/dense/main.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | // 3 | // density field regular grid computation from voronoi tessellation 4 | // 5 | // Tom Peterka 6 | // Argonne National Laboratory 7 | // 9700 S. Cass Ave. 8 | // Argonne, IL 60439 9 | // tpeterka@mcs.anl.gov 10 | // 11 | //-------------------------------------------------------------------------- 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "tess/tess.h" 18 | #include "tess/tess.hpp" 19 | #include "tess/dense.hpp" 20 | 21 | using namespace std; 22 | 23 | void ParseArgs(int argc, 24 | char ** argv, 25 | alg &alg_type, 26 | int *num_given_bounds, 27 | float *given_mins, 28 | float *given_maxs, 29 | bool &project, 30 | float *proj_plane, 31 | float &mass, 32 | int *glo_num_idx) 33 | { 34 | assert(argc >= 10); 35 | if (atoi(argv[3]) == 0) 36 | alg_type = DENSE_TESS; 37 | else 38 | alg_type = DENSE_CIC; 39 | glo_num_idx[0] = atoi(argv[4]); 40 | glo_num_idx[1] = atoi(argv[5]); 41 | glo_num_idx[2] = atoi(argv[6]); 42 | if (!strcmp(argv[7], "!")) 43 | { 44 | project = false; 45 | mass = atof(argv[8]); 46 | *num_given_bounds = atoi(argv[9]); 47 | if (*num_given_bounds == 1) { 48 | given_mins[0] = atof(argv[10]); 49 | given_maxs[0] = atof(argv[11]); 50 | } 51 | else if (*num_given_bounds == 2) { 52 | given_mins[0] = atof(argv[10]); 53 | given_mins[1] = atof(argv[11]); 54 | given_maxs[0] = atof(argv[12]); 55 | given_maxs[1] = atof(argv[13]); 56 | } 57 | else if (*num_given_bounds == 3) { 58 | given_mins[0] = atof(argv[10]); 59 | given_mins[1] = atof(argv[11]); 60 | given_mins[2] = atof(argv[12]); 61 | given_maxs[0] = atof(argv[13]); 62 | given_maxs[1] = atof(argv[14]); 63 | given_maxs[2] = atof(argv[15]); 64 | } 65 | } 66 | if (strcmp(argv[7], "!")) 67 | { 68 | project = true; 69 | proj_plane[0] = atof(argv[7]); 70 | proj_plane[1] = atof(argv[8]); 71 | proj_plane[2] = atof(argv[9]); 72 | mass = atof(argv[10]); 73 | *num_given_bounds = atoi(argv[11]); 74 | if (*num_given_bounds == 1) 75 | { 76 | given_mins[0] = atof(argv[12]); 77 | given_maxs[0] = atof(argv[13]); 78 | } 79 | else if (*num_given_bounds == 2) 80 | { 81 | given_mins[0] = atof(argv[12]); 82 | given_mins[1] = atof(argv[13]); 83 | given_maxs[0] = atof(argv[14]); 84 | given_maxs[1] = atof(argv[15]); 85 | } 86 | else if (*num_given_bounds == 3) 87 | { 88 | given_mins[0] = atof(argv[12]); 89 | given_mins[1] = atof(argv[13]); 90 | given_mins[2] = atof(argv[14]); 91 | given_maxs[0] = atof(argv[15]); 92 | given_maxs[1] = atof(argv[16]); 93 | given_maxs[2] = atof(argv[17]); 94 | } 95 | } 96 | } 97 | 98 | int main(int argc, char** argv) 99 | { 100 | int tot_blocks; // global number of blocks 101 | int nblocks; // my local number of blocks 102 | int maxblocks; // max blocks in any process 103 | MPI_Comm comm = MPI_COMM_WORLD; // MPI communicator 104 | dblock_t *dblocks; // delaunay local blocks 105 | float eps = 0.0001; // epsilon for floating point values to be equal 106 | float data_mins[3], data_maxs[3]; // data global bounds 107 | float mass; // particle mass 108 | alg alg_type; // tess or cic 109 | 110 | // grid bounds 111 | int num_given_bounds; // number of given bounds 112 | float given_mins[3], given_maxs[3]; // the given bounds 113 | int glo_num_idx[3]; // global grid number of points 114 | float grid_phys_mins[3], grid_phys_maxs[3]; // grid physical bounds 115 | float grid_step_size[3]; // physical size of one grid space 116 | 117 | // 2D projection 118 | bool project; // whether to project to 2D 119 | float proj_plane[3]; // normal to projection plane 120 | 121 | ParseArgs(argc, argv, alg_type, &num_given_bounds, given_mins,given_maxs, project, proj_plane, 122 | mass, glo_num_idx); 123 | 124 | // ensure projection plane normal vector is unit length 125 | float length = sqrt(proj_plane[0] * proj_plane[0] + 126 | proj_plane[1] * proj_plane[1] + 127 | proj_plane[2] * proj_plane[2]); 128 | proj_plane[0] /= length; 129 | proj_plane[1] /= length; 130 | proj_plane[2] /= length; 131 | 132 | // init 133 | MPI_Init(&argc, &argv); 134 | 135 | // timing 136 | double times[DENSE_MAX_TIMES]; // timing 137 | MPI_Barrier(comm); 138 | times[TOTAL_TIME] = MPI_Wtime(); 139 | times[INPUT_TIME] = MPI_Wtime(); 140 | 141 | MPI_Barrier(comm); 142 | times[INPUT_TIME] = MPI_Wtime() - times[INPUT_TIME]; 143 | times[COMP_TIME] = MPI_Wtime(); 144 | 145 | // init diy 146 | int num_threads = 4; 147 | int mem_blocks = -1; 148 | diy::mpi::communicator world(comm); 149 | diy::FileStorage storage("./DIY.XXXXXX"); 150 | diy::Master master(world, 151 | num_threads, 152 | mem_blocks, 153 | &create_block, 154 | &destroy_block, 155 | &storage, 156 | &save_block, 157 | &load_block); 158 | diy::RoundRobinAssigner assigner(world.size(), -1); // tot_blocks found by read_blocks 159 | 160 | // read the tessellation 161 | diy::io::read_blocks(argv[1], world, assigner, master, &load_block_light); 162 | tot_blocks = assigner.nblocks(); 163 | 164 | // get global block quantities 165 | nblocks = master.size(); // local number of blocks 166 | MPI_Allreduce(&nblocks, &maxblocks, 1, MPI_INT, MPI_MAX, comm); 167 | MPI_Allreduce(&nblocks, &tot_blocks, 1, MPI_INT, MPI_SUM, comm); 168 | 169 | // compute the density 170 | dense(alg_type, num_given_bounds, given_mins, given_maxs, project, proj_plane, 171 | mass, data_mins, data_maxs, grid_phys_mins, grid_phys_maxs, grid_step_size, eps, 172 | glo_num_idx, master); 173 | 174 | MPI_Barrier(comm); 175 | times[COMP_TIME] = MPI_Wtime() - times[COMP_TIME]; 176 | 177 | // write file 178 | // NB: all blocks need to be in memory; WriteGrid is not diy2'ed yet 179 | times[OUTPUT_TIME] = MPI_Wtime(); 180 | WriteGrid(maxblocks, tot_blocks, argv[2], project, glo_num_idx, eps, data_mins, data_maxs, 181 | num_given_bounds, given_mins, given_maxs, master, assigner); 182 | MPI_Barrier(comm); 183 | times[OUTPUT_TIME] = MPI_Wtime() - times[OUTPUT_TIME]; 184 | 185 | 186 | MPI_Barrier(comm); 187 | times[TOTAL_TIME] = MPI_Wtime() - times[TOTAL_TIME]; 188 | 189 | dense_stats(times, master, grid_step_size, grid_phys_mins, glo_num_idx); 190 | 191 | MPI_Finalize(); 192 | 193 | return 0; 194 | } 195 | -------------------------------------------------------------------------------- /examples/memory.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // From http://stackoverflow.com/questions/669438/how-to-get-memory-usage-at-run-time-in-c 9 | 10 | ////////////////////////////////////////////////////////////////////////////// 11 | // 12 | // process_mem_usage(double &, double &) - takes two doubles by reference, 13 | // attempts to read the system-dependent data for a process' virtual memory 14 | // size and resident set size, and return the results in KB. 15 | // 16 | // On failure, returns 0.0, 0.0 17 | 18 | inline void process_mem_usage(size_t& vm_usage, size_t& resident_set) 19 | { 20 | using std::ios_base; 21 | using std::ifstream; 22 | using std::string; 23 | 24 | vm_usage = 0; 25 | resident_set = 0; 26 | 27 | #ifdef __linux 28 | // 'file' stat seems to give the most reliable results 29 | // 30 | ifstream stat_stream("/proc/self/stat",ios_base::in); 31 | 32 | // dummy vars for leading entries in stat that we don't care about 33 | // 34 | string pid, comm, state, ppid, pgrp, session, tty_nr; 35 | string tpgid, flags, minflt, cminflt, majflt, cmajflt; 36 | string utime, stime, cutime, cstime, priority, nice; 37 | string O, itrealvalue, starttime; 38 | 39 | // the two fields we want 40 | // 41 | unsigned long vsize; 42 | long rss; 43 | 44 | stat_stream >> pid >> comm >> state >> ppid >> pgrp >> session >> tty_nr 45 | >> tpgid >> flags >> minflt >> cminflt >> majflt >> cmajflt 46 | >> utime >> stime >> cutime >> cstime >> priority >> nice 47 | >> O >> itrealvalue >> starttime >> vsize >> rss; // don't care about the rest 48 | 49 | stat_stream.close(); 50 | 51 | long page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; // in case x86-64 is configured to use 2MB pages 52 | vm_usage = vsize / 1024; 53 | resident_set = rss * page_size_kb; 54 | #endif 55 | } 56 | 57 | inline size_t process_mem_usage() 58 | { 59 | size_t vm, rss; 60 | process_mem_usage(vm, rss); 61 | return rss; 62 | } 63 | 64 | inline size_t proc_status_value(const std::string& field) 65 | { 66 | #ifdef __linux 67 | std::ifstream in("/proc/self/status"); 68 | std::string line; 69 | while (in) 70 | { 71 | std::getline(in, line); 72 | if (line.compare(0, field.length(), field) == 0) 73 | { 74 | std::istringstream iss(line); 75 | std::string f; size_t res; 76 | iss >> f >> res; 77 | return res; 78 | } 79 | } 80 | #endif 81 | return 0; 82 | } 83 | -------------------------------------------------------------------------------- /examples/opts.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Author: Dmitriy Morozov 3 | * The interface is heavily influenced by GetOptPP (https://code.google.com/p/getoptpp/). 4 | */ 5 | 6 | #ifndef OPTS_OPTS_H 7 | #define OPTS_OPTS_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace opts { 16 | 17 | // Converters 18 | template 19 | struct Converter 20 | { 21 | Converter() {} 22 | static 23 | T convert(const std::string& val) { std::istringstream iss(val); T res; iss >> res; return res; } 24 | }; 25 | 26 | // Type 27 | template 28 | struct Traits 29 | { 30 | static std::string type_string() { return "UNKNOWN TYPE"; } 31 | }; 32 | 33 | template<> 34 | struct Traits 35 | { 36 | static std::string type_string() { return "INT"; } 37 | }; 38 | 39 | template<> 40 | struct Traits 41 | { 42 | static std::string type_string() { return "SHORT INT"; } 43 | }; 44 | 45 | template<> 46 | struct Traits 47 | { 48 | static std::string type_string() { return "UNSIGNED INT"; } 49 | }; 50 | 51 | template<> 52 | struct Traits 53 | { 54 | static std::string type_string() { return "SHORT UNSIGNED INT"; } 55 | }; 56 | 57 | template<> 58 | struct Traits 59 | { 60 | static std::string type_string() { return "FLOAT"; } 61 | }; 62 | 63 | template<> 64 | struct Traits 65 | { 66 | static std::string type_string() { return "DOUBLE"; } 67 | }; 68 | 69 | template<> 70 | struct Traits 71 | { 72 | static std::string type_string() { return "STRING"; } 73 | }; 74 | 75 | 76 | struct BasicOption 77 | { 78 | BasicOption(char s_, 79 | std::string l_, 80 | std::string default_, 81 | std::string type_, 82 | std::string help_): 83 | s(s_), l(l_), d(default_), t(type_), help(help_) {} 84 | 85 | int long_size() const { return l.size() + 1 + t.size(); } 86 | 87 | void output(std::ostream& out, int max_long) const 88 | { 89 | out << " "; 90 | if (s) 91 | out << '-' << s << ", "; 92 | else 93 | out << " "; 94 | 95 | out << "--" << l << ' '; 96 | 97 | if (!t.empty()) 98 | out << t; 99 | 100 | for (int i = long_size(); i < max_long; ++i) 101 | out << ' '; 102 | 103 | out << " " << help; 104 | 105 | if (!d.empty()) 106 | { 107 | out << " [default: " << d << "]"; 108 | } 109 | out << '\n'; 110 | } 111 | 112 | char s; 113 | std::string l; 114 | std::string d; 115 | std::string t; 116 | std::string help; 117 | }; 118 | 119 | // Option 120 | template 121 | struct OptionContainer: public BasicOption 122 | { 123 | OptionContainer(char s_, 124 | const std::string& l_, 125 | T& var_, 126 | const std::string& help_, 127 | const std::string& type_ = Traits::type_string()): 128 | BasicOption(s_, l_, default_value(var_), type_, help_), 129 | var(&var_) {} 130 | 131 | static 132 | std::string default_value(const T& def) 133 | { 134 | std::ostringstream oss; 135 | oss << def; 136 | return oss.str(); 137 | } 138 | 139 | void parse(std::list& args) const 140 | { 141 | std::string short_opt = "-"; short_opt += s; 142 | std::string long_opt = "--" + l; 143 | for (std::list::iterator cur = args.begin(); cur != args.end(); ++cur) 144 | { 145 | if (*cur == short_opt || *cur == long_opt) 146 | { 147 | cur = args.erase(cur); 148 | if (cur != args.end()) 149 | { 150 | *var = Converter::convert(*cur); 151 | cur = args.erase(cur); 152 | break; // finds first occurrence 153 | } 154 | else 155 | break; // if the last option's value is missing, it remains default 156 | 157 | } 158 | } 159 | } 160 | 161 | T* var; 162 | }; 163 | 164 | template 165 | struct OptionContainer< std::vector >: public BasicOption 166 | { 167 | OptionContainer(char s_, 168 | const std::string& l_, 169 | std::vector& var_, 170 | const std::string& help_, 171 | const std::string& type_ = "SEQUENCE"): 172 | BasicOption(s_, l_, default_value(var_), type_, help_), 173 | var(&var_) { } 174 | 175 | static 176 | std::string default_value(const std::vector& def) 177 | { 178 | std::ostringstream oss; 179 | oss << "("; 180 | if (def.size()) 181 | oss << def[0]; 182 | for (int i = 1; i < def.size(); ++i) 183 | oss << ", " << def[i]; 184 | oss << ")"; 185 | return oss.str(); 186 | } 187 | 188 | void parse(std::list& args) const 189 | { 190 | std::string short_opt = "-"; short_opt += s; 191 | std::string long_opt = "--" + l; 192 | for (std::list::iterator cur = args.begin(); cur != args.end(); ++cur) 193 | { 194 | if (*cur == short_opt || *cur == long_opt) 195 | { 196 | cur = args.erase(cur); 197 | if (cur != args.end()) 198 | { 199 | var->push_back(Converter::convert(*cur)); 200 | cur = args.erase(cur); 201 | } 202 | --cur; 203 | } 204 | } 205 | } 206 | 207 | std::vector* var; 208 | }; 209 | 210 | 211 | template 212 | OptionContainer 213 | Option(char s, const std::string& l, T& var, const std::string& help) { return OptionContainer(s, l, var, help); } 214 | 215 | template 216 | OptionContainer 217 | Option(char s, const std::string& l, T& var, 218 | const std::string& type, const std::string& help) { return OptionContainer(s, l, var, help, type); } 219 | 220 | template 221 | OptionContainer 222 | Option(const std::string& l, T& var, const std::string& help) { return OptionContainer(0, l, var, help); } 223 | 224 | template 225 | OptionContainer 226 | Option(const std::string& l, T& var, 227 | const std::string& type, const std::string& help) { return OptionContainer(0, l, var, help, type); } 228 | 229 | // Present 230 | struct PresentContainer: public BasicOption 231 | { 232 | PresentContainer(char s, const std::string& l, const std::string& help): 233 | BasicOption(s,l,"","",help) {} 234 | }; 235 | 236 | inline 237 | PresentContainer 238 | Present(char s, const std::string& l, const std::string& help) { return PresentContainer(s, l, help); } 239 | 240 | inline 241 | PresentContainer 242 | Present(const std::string& l, const std::string& help) { return PresentContainer(0, l, help); } 243 | 244 | // PosOption 245 | template 246 | struct PosOptionContainer 247 | { 248 | PosOptionContainer(T& var_): 249 | var(&var_) {} 250 | 251 | bool parse(std::list& args) const 252 | { 253 | if (args.empty()) 254 | return false; 255 | 256 | *var = Converter::convert(args.front()); 257 | args.pop_front(); 258 | return true; 259 | } 260 | 261 | T* var; 262 | }; 263 | 264 | template 265 | PosOptionContainer 266 | PosOption(T& var) { return PosOptionContainer(var); } 267 | 268 | 269 | // Options 270 | struct Options 271 | { 272 | Options(int argc_, char** argv_): 273 | args(argv_ + 1, argv_ + argc_), 274 | failed(false) {} 275 | 276 | template 277 | Options& operator>>(const OptionContainer& oc); 278 | bool operator>>(const PresentContainer& pc); 279 | template 280 | Options& operator>>(const PosOptionContainer& poc); 281 | 282 | operator bool() { return !failed; } 283 | 284 | 285 | friend 286 | std::ostream& 287 | operator<<(std::ostream& out, const Options& ops) 288 | { 289 | int max_long = 0; 290 | for (std::list::const_iterator cur = ops.options.begin(); 291 | cur != ops.options.end(); 292 | ++cur) 293 | { 294 | int cur_long = cur->long_size(); 295 | if (cur_long > max_long) 296 | max_long = cur_long; 297 | } 298 | 299 | out << "Options:\n"; 300 | for (std::list::const_iterator cur = ops.options.begin(); 301 | cur != ops.options.end(); 302 | ++cur) 303 | cur->output(out, max_long); 304 | 305 | return out; 306 | } 307 | 308 | 309 | private: 310 | std::list args; 311 | std::list options; 312 | bool failed; 313 | }; 314 | 315 | template 316 | Options& 317 | Options::operator>>(const OptionContainer& oc) 318 | { 319 | options.push_back(oc); 320 | oc.parse(args); 321 | return *this; 322 | } 323 | 324 | inline 325 | bool 326 | Options::operator>>(const PresentContainer& pc) 327 | { 328 | options.push_back(pc); 329 | 330 | for(std::list::iterator cur = args.begin(); cur != args.end(); ++cur) 331 | { 332 | std::string short_opt = "-"; short_opt += pc.s; 333 | std::string long_opt = "--" + pc.l; 334 | if (*cur == short_opt || *cur == long_opt) 335 | { 336 | args.erase(cur); 337 | return true; 338 | } 339 | } 340 | return false; 341 | } 342 | 343 | template 344 | Options& 345 | Options::operator>>(const PosOptionContainer& poc) 346 | { 347 | failed = !poc.parse(args); 348 | return *this; 349 | } 350 | 351 | } 352 | 353 | #endif 354 | -------------------------------------------------------------------------------- /examples/post-tess/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable (post-delaunay main.cpp) 2 | target_link_libraries (post-delaunay tess ${libraries}) 3 | 4 | install (TARGETS post-delaunay 5 | DESTINATION ${CMAKE_INSTALL_PREFIX}/examples/post-tess/ 6 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE 7 | GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) 8 | 9 | install (FILES POST_TEST 10 | DESTINATION ${CMAKE_INSTALL_PREFIX}/examples/post-tess/ 11 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE 12 | GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) 13 | -------------------------------------------------------------------------------- /examples/post-tess/POST_TEST: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ##PBS -A CSC033 4 | #PBS -A CI-CCR000086 5 | #PBS -N t 6 | #PBS -j oe 7 | #PBS -l walltime=0:10:00,size=12 8 | 9 | #---------------------------------------------------------------------------- 10 | # 11 | # mpi run script 12 | # 13 | # Tom Peterka 14 | # Argonne National Laboratory 15 | # 9700 S. Cass Ave. 16 | # Argonne, IL 60439 17 | # tpeterka@mcs.anl.gov 18 | # 19 | # All rights reserved. May not be used, modified, or copied 20 | # without permission 21 | # 22 | #---------------------------------------------------------------------------- 23 | ARCH=MAC_OSX 24 | #ARCH=LINUX 25 | #ARCH=BGP 26 | #ARCH=BGQ 27 | #ARCH=FUSION 28 | #ARCH=XT 29 | #ARCH=XE 30 | 31 | # number of procs 32 | num_procs=1 33 | 34 | # procs per node 35 | #ppn=4 # for BG/P 36 | ppn=1 # adjustable for BG/Q, allowed 1, 2, 4, 8, 16, 32, 64 37 | 38 | # number of BG/P nodes for vn mode 39 | num_nodes=$[$num_procs / $ppn] 40 | if [ $num_nodes -lt 1 ]; then 41 | num_nodes=1 42 | fi 43 | 44 | # executable 45 | exe=./post-delaunay 46 | 47 | # total number of blocks in the domain 48 | tb=$[$num_procs * 1] 49 | #tb=128 50 | 51 | # inout file 52 | #infile="${HOME}/hacc/voronoi/density-estimator/tests/halos/input-data/7445077095.dat" 53 | #infile="${HOME}/hacc/voronoi/density-estimator/tests/halos/input-data/6606356352.dat" 54 | infile="${HOME}/hacc/voronoi/density-estimator/tests/nfw/input-data/nfw_particles_1e4.dat" 55 | #infile="${HOME}/hacc/voronoi/density-estimator/tests/cnfw/input-data/cnfw_particles_2e5.dat" 56 | #infile="./pts.out" 57 | 58 | # output file, "!" to disable output 59 | #outfile="7445077095.out" 60 | #outfile="6606356352.out" 61 | outfile="nfw.out" 62 | #outfile="mchalo00.out" 63 | #outfile="nor_ex.out" 64 | #outfile="cnfw_2e5.out" 65 | #outfile="pts-vor.out" 66 | 67 | #volume range (-1.0: unused) 68 | minv=-1.0 69 | maxv=-1.0 70 | 71 | # input file type 72 | # 0 = text 73 | # 1 = float x's followed by y's followed by z's 74 | # 2 = float interleaved x y z 75 | # 3 = double x's followed by y's followed by z's 76 | # 4 = double interleaved x y z 77 | it=0 78 | #it=3 79 | #it=2 80 | 81 | #byte swapping 82 | swap=0 83 | 84 | # wrapped neihbors 0 or 1 85 | wrap=0 86 | 87 | #------ 88 | # 89 | # program arguments 90 | # 91 | 92 | args="$tb $infile $outfile $minv $maxv $it $swap $wrap" 93 | 94 | #------ 95 | # 96 | # run commands 97 | # 98 | 99 | if [ "$ARCH" = "MAC_OSX" ]; then 100 | 101 | mpiexec -l -n $num_procs $exe $args 102 | 103 | #dsymutil $exe ; mpiexec -l -n $num_procs xterm -e gdb -x gdb.run --args $exe $args 104 | 105 | #dsymutil $exe ; mpiexec -l -n $num_procs valgrind -q $exe $args 106 | 107 | #dsymutil $exe ; mpiexec -n $num_procs valgrind -q --leak-check=yes $exe $args 108 | 109 | fi 110 | 111 | if [ "$ARCH" = "LINUX" ]; then 112 | 113 | mpiexec -n $num_procs $exe $args 114 | 115 | #mpiexec -n $num_procs xterm -e gdb -x gdb.run --args $exe $args 116 | 117 | #mpiexec -n $num_procs valgrind -q $exe $args 118 | 119 | #mpiexec -n $num_procs valgrind -q --leak-check=yes $exe $args 120 | 121 | fi 122 | 123 | if [ "$ARCH" = "BGP" ]; then 124 | 125 | #cqsub -n $num_nodes -c $num_procs -p UltraVis -t 30 -m vn $exe $args 126 | cqsub -n $num_procs -c $num_procs -p UltraVis -t 30 -m smp $exe $args 127 | 128 | # for use with valgrind_memcheck.o 129 | #cqsub -n $num_procs -c $num_procs -p UltraVis -t 30 -m smp $exe -- $args 130 | 131 | fi 132 | 133 | if [ "$ARCH" = "BGQ" ]; then 134 | 135 | qsub -n $num_nodes --mode c$ppn -A SDAV -t 60 $exe $args 136 | 137 | fi 138 | 139 | if [ "$ARCH" = "FUSION" ]; then 140 | 141 | mpiexec $exe $args 142 | 143 | fi 144 | 145 | if [ "$ARCH" = "XT" ]; then 146 | 147 | cd /tmp/work/$USER 148 | aprun -n $num_procs $exe $args 149 | 150 | fi 151 | 152 | if [ "$ARCH" = "XE" ]; then 153 | 154 | aprun -n $num_procs $exe $args 155 | 156 | fi 157 | -------------------------------------------------------------------------------- /examples/pread-voronoi/BOV_TEST: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ##PBS -A CSC033 4 | #PBS -A CI-CCR000086 5 | #PBS -N t 6 | #PBS -j oe 7 | #PBS -l walltime=0:10:00,size=12 8 | 9 | #---------------------------------------------------------------------------- 10 | # 11 | # mpi run script 12 | # 13 | # Tom Peterka 14 | # Argonne National Laboratory 15 | # 9700 S. Cass Ave. 16 | # Argonne, IL 60439 17 | # tpeterka@mcs.anl.gov 18 | # 19 | # All rights reserved. May not be used, modified, or copied 20 | # without permission 21 | # 22 | #---------------------------------------------------------------------------- 23 | #ARCH=MAC_OSX 24 | #ARCH=LINUX 25 | ARCH=BGQ 26 | #ARCH=FUSION 27 | #ARCH=XT 28 | #ARCH=XE 29 | 30 | # number of procs 31 | num_procs=512 32 | 33 | # procs per node 34 | ppn=1 # adjustable for BG/Q, allowed 1, 2, 4, 8, 16, 32, 64 35 | 36 | # number of BG/Q nodes 37 | num_nodes=$[$num_procs / $ppn] 38 | if [ $num_nodes -lt 1 ]; then 39 | num_nodes=1 40 | fi 41 | 42 | # executable 43 | exe=./pread-bov 44 | 45 | # inout file 46 | infile="/projects/SSSPPg/tpeterka/debug.bov" 47 | 48 | # output file 49 | #outfile="del.out" 50 | outfile="!" 51 | 52 | # options: single-phase and kd-tree 53 | # possibilities are --wrap --kdtree --blocks --chunk 54 | #opts="--blocks 64" 55 | # opts="--kdtree --blocks 64" 56 | opts="--wrap --kdtree --blocks 512 --chunk 3 --swap" 57 | 58 | #------ 59 | # 60 | # program arguments 61 | # 62 | 63 | args="$opts $infile $outfile" 64 | 65 | #------ 66 | # 67 | # run commands 68 | # 69 | 70 | if [ "$ARCH" = "MAC_OSX" ]; then 71 | 72 | mpiexec -l -n $num_procs $exe $args 73 | 74 | #dsymutil $exe ; mpiexec -l -n $num_procs xterm -e gdb -x gdb.run --args $exe $args 75 | 76 | #dsymutil $exe ; mpiexec -l -n $num_procs valgrind -q $exe $args 77 | 78 | #dsymutil $exe ; mpiexec -n $num_procs valgrind -q --leak-check=yes $exe $args 79 | 80 | fi 81 | 82 | if [ "$ARCH" = "LINUX" ]; then 83 | 84 | mpiexec -n $num_procs $exe $args 85 | 86 | #mpiexec -n $num_procs xterm -e gdb -x gdb.run --args $exe $args 87 | 88 | #mpiexec -n $num_procs valgrind -q $exe $args 89 | 90 | #mpiexec -n $num_procs valgrind -q --leak-check=yes $exe $args 91 | 92 | fi 93 | 94 | if [ "$ARCH" = "BGQ" ]; then 95 | 96 | qsub -n $num_nodes --mode c$ppn -A SDAV -t 60 $exe $args 97 | 98 | fi 99 | 100 | if [ "$ARCH" = "FUSION" ]; then 101 | 102 | mpiexec $exe $args 103 | 104 | fi 105 | 106 | if [ "$ARCH" = "XT" ]; then 107 | 108 | cd /tmp/work/$USER 109 | aprun -n $num_procs $exe $args 110 | 111 | fi 112 | 113 | if [ "$ARCH" = "XE" ]; then 114 | 115 | aprun -n $num_procs $exe $args 116 | 117 | fi 118 | -------------------------------------------------------------------------------- /examples/pread-voronoi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | option (gadget "Gadget IO support" on) 2 | option (hacc "HACC IO support" off) 3 | option (moab "MOAB IO support" off) 4 | 5 | # gadget 6 | if (gadget) 7 | find_package (Boost COMPONENTS filesystem system) 8 | include_directories (SYSTEM ${Boost_INCLUDE_DIR}) 9 | set (io_sources ${io_sources} io/gadget/particles.cpp) 10 | add_definitions (-DTESS_GADGET_IO) 11 | endif (gadget) 12 | 13 | # HDF5 is always required 14 | if (HDF5_LIBRARY) 15 | message ("HDF5_LIBRARY = " ${HDF5_LIBRARY}) 16 | else () 17 | message ("Looking for HDF5") 18 | find_package (HDF5 QUIET) 19 | if (HDF5_FOUND) 20 | set (HDF5_LIBRARY ${HDF5_hdf5_LIBRARY}) 21 | endif () 22 | endif () 23 | 24 | if (HDF5_LIBRARY) 25 | include_directories (SYSTEM ${HDF5_INCLUDE_DIR}) 26 | add_library (hdf5 STATIC IMPORTED) 27 | set_target_properties (hdf5 PROPERTIES IMPORTED_LOCATION ${HDF5_LIBRARY}) 28 | set (HDF5_libraries hdf5) 29 | else () 30 | find_path (HDF5_INCLUDE_DIR hdf5.h) 31 | find_library (HDF5_LIBRARY NAMES hdf5) 32 | include_directories (SYSTEM ${HDF5_INCLUDE_DIR}) 33 | message ("HDF5_INCLUDE_DIR=${HDF5_INCLUDE_DIR} HDF5_LIBRARY=${HDF5_LIBRARY}") 34 | if (HDF5_LIBRARY) 35 | if (ZLIB_LIBRARIES) 36 | message ("ZLIB_LIBRARIES = " ${ZLIB_LIBRARIES}) 37 | else () 38 | message ("Looking for Zlib") 39 | find_package (ZLIB REQUIRED) 40 | endif () 41 | include_directories (SYSTEM ${ZLIB_INCLUDE_DIRS}) 42 | set (HDF5_libraries ${HDF5_LIBRARY} ${ZLIB_LIBRARIES}) 43 | endif () 44 | endif () 45 | 46 | # pread-voronoi 47 | set (io_sources ${io_sources} io/hdf5/pread-hdf5.cpp) 48 | 49 | add_executable (pread-voronoi pread-voronoi.cpp ${io_sources}) 50 | target_link_libraries (pread-voronoi tess ${libraries} ${HDF5_libraries}) 51 | if (gadget) 52 | target_link_libraries (pread-voronoi ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY}) 53 | endif (gadget) 54 | 55 | # balance 56 | # add_executable (balance balance.cpp ${io_sources}) 57 | # set_target_properties (balance PROPERTIES COMPILE_FLAGS "-std=c++11") 58 | # target_link_libraries (balance tess ${libraries} ${HDF5_libraries}) 59 | # if (gadget) 60 | # target_link_libraries (balance ${Boost_FILESYSTEM_LIBRARY}) 61 | # endif (gadget) 62 | 63 | install (TARGETS pread-voronoi 64 | DESTINATION ${CMAKE_INSTALL_PREFIX}/examples/pread-voronoi/ 65 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE 66 | GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) 67 | 68 | install (FILES PREAD_VORONOI unit-cube.h5 69 | DESTINATION ${CMAKE_INSTALL_PREFIX}/examples/pread-voronoi/ 70 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE 71 | GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) 72 | 73 | # bov 74 | add_executable (pread-bov pread-bov.cpp) 75 | target_link_libraries (pread-bov tess ${libraries} ${HDF5_libraries}) 76 | 77 | install (TARGETS pread-bov 78 | DESTINATION ${CMAKE_INSTALL_PREFIX}/examples/pread-voronoi/ 79 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE 80 | GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) 81 | 82 | # moab 83 | if (moab) 84 | find_path (MOAB_INCLUDE_DIR moab_mpi.h) 85 | find_library (MOAB_LIBRARY NAMES MOAB) 86 | include_directories (SYSTEM ${MOAB_INCLUDE_DIR}) 87 | 88 | # zlib is required for HDF5 89 | find_package (ZLIB REQUIRED) 90 | include_directories (SYSTEM ${ZLIB_INCLUDE_DIRS}) 91 | set (HDF5_libraries ${HDF5_LIBRARY} ${ZLIB_LIBRARIES}) 92 | 93 | # finally, moab 94 | set (io_sources io/moab/particles.cpp) 95 | add_definitions (-DTESS_MOAB_IO) 96 | add_executable (moab-test pread-moab.cpp ${io_sources}) 97 | target_link_libraries (moab-test tess ${libraries} ${MOAB_LIBRARY} ${HDF5_libraries}) 98 | 99 | install (TARGETS moab-test 100 | DESTINATION ${CMAKE_INSTALL_PREFIX}/examples/pread-voronoi/ 101 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE 102 | GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) 103 | 104 | install (FILES MOAB_TEST 105 | DESTINATION ${CMAKE_INSTALL_PREFIX}/examples/pread-voronoi/ 106 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE 107 | GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) 108 | endif (moab) 109 | 110 | # hacc 111 | if (hacc) 112 | 113 | find_path (GenericIO_INCLUDE_DIR GenericIO.h) 114 | find_library (GenericIO_LIBRARY NAMES GenericIO) 115 | include_directories (SYSTEM ${GenericIO_INCLUDE_DIR}) 116 | set (io_sources io/hacc/particles.cpp) 117 | 118 | add_executable (hacc-test hacc-voronoi.cpp ${io_sources}) 119 | target_link_libraries (hacc-test tess ${libraries} ${GenericIO_LIBRARY}) 120 | 121 | install (TARGETS hacc-test 122 | DESTINATION ${CMAKE_INSTALL_PREFIX}/examples/pread-voronoi/ 123 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE 124 | GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) 125 | 126 | install (FILES HACC_TEST 127 | DESTINATION ${CMAKE_INSTALL_PREFIX}/examples/pread-voronoi/ 128 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE 129 | GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) 130 | 131 | endif (hacc) 132 | -------------------------------------------------------------------------------- /examples/pread-voronoi/HACC_TEST: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ##PBS -A CSC033 4 | #PBS -A CI-CCR000086 5 | #PBS -N t 6 | #PBS -j oe 7 | #PBS -l walltime=0:10:00,size=12 8 | 9 | #---------------------------------------------------------------------------- 10 | # 11 | # mpi run script 12 | # 13 | # Tom Peterka 14 | # Argonne National Laboratory 15 | # 9700 S. Cass Ave. 16 | # Argonne, IL 60439 17 | # tpeterka@mcs.anl.gov 18 | # 19 | # All rights reserved. May not be used, modified, or copied 20 | # without permission 21 | # 22 | #---------------------------------------------------------------------------- 23 | ARCH=MAC_OSX 24 | #ARCH=LINUX 25 | #ARCH=BGQ 26 | #ARCH=FUSION 27 | #ARCH=XT 28 | #ARCH=XE 29 | 30 | # number of procs 31 | num_procs=8 32 | 33 | # procs per node 34 | ppn=4 # adjustable for BG/Q, allowed 1, 2, 4, 8, 16, 32, 64 35 | 36 | # number of BG/Q nodes 37 | num_nodes=$[$num_procs / $ppn] 38 | if [ $num_nodes -lt 1 ]; then 39 | num_nodes=1 40 | fi 41 | 42 | # executable 43 | exe=./hacc-test 44 | 45 | # inout file 46 | infile="/Users/tpeterka/datasets/hacc/m000.mpicosmo.499" 47 | #infile="/projects/DarkUniverse_esp/heitmann/OuterRim/M000/L355/HACC000/output/STEP499/m000.full.mpicosmo.499" 48 | #infile="/projects/SSSPPg/tpeterka/STEP68/m003.full.mpicosmo.68" 49 | 50 | # output file 51 | outfile="del.out" 52 | #outfile="/projects/SSSPPg/tpeterka/hacc-vor.out" 53 | #outfile="!" 54 | 55 | # sample rate (1 = keep every particle, 10 = keep every 10th particle) 56 | sr=1 57 | 58 | # options: kd-tree 59 | # possibilities are --wrap --kdtree --blocks --debug --chunk 60 | # opts="--blocks 64" 61 | opts="--kdtree --blocks 64" 62 | 63 | #------ 64 | # 65 | # program arguments 66 | # 67 | 68 | args="$opts $infile $outfile $sr" 69 | 70 | #------ 71 | # 72 | # run commands 73 | # 74 | 75 | if [ "$ARCH" = "MAC_OSX" ]; then 76 | 77 | mpiexec -l -n $num_procs $exe $args 78 | 79 | #dsymutil $exe ; mpiexec -l -n $num_procs xterm -e gdb -x gdb.run --args $exe $args 80 | 81 | #dsymutil $exe ; mpiexec -l -n $num_procs valgrind -q $exe $args 82 | 83 | #dsymutil $exe ; mpiexec -n $num_procs valgrind -q --leak-check=yes $exe $args 84 | 85 | fi 86 | 87 | if [ "$ARCH" = "LINUX" ]; then 88 | 89 | mpiexec -n $num_procs $exe $args 90 | 91 | #mpiexec -n $num_procs xterm -e gdb -x gdb.run --args $exe $args 92 | 93 | #mpiexec -n $num_procs valgrind -q $exe $args 94 | 95 | #mpiexec -n $num_procs valgrind -q --leak-check=yes $exe $args 96 | 97 | fi 98 | 99 | if [ "$ARCH" = "BGQ" ]; then 100 | 101 | qsub -n $num_nodes --mode c$ppn -A SDAV -t 60 $exe $args 102 | 103 | fi 104 | 105 | if [ "$ARCH" = "FUSION" ]; then 106 | 107 | mpiexec $exe $args 108 | 109 | fi 110 | 111 | if [ "$ARCH" = "XT" ]; then 112 | 113 | cd /tmp/work/$USER 114 | aprun -n $num_procs $exe $args 115 | 116 | fi 117 | 118 | if [ "$ARCH" = "XE" ]; then 119 | 120 | aprun -n $num_procs $exe $args 121 | 122 | fi 123 | -------------------------------------------------------------------------------- /examples/pread-voronoi/MOAB_TEST: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ##PBS -A CSC033 4 | #PBS -A CI-CCR000086 5 | #PBS -N t 6 | #PBS -j oe 7 | #PBS -l walltime=0:10:00,size=12 8 | 9 | #---------------------------------------------------------------------------- 10 | # 11 | # mpi run script 12 | # 13 | # Tom Peterka 14 | # Argonne National Laboratory 15 | # 9700 S. Cass Ave. 16 | # Argonne, IL 60439 17 | # tpeterka@mcs.anl.gov 18 | # 19 | # All rights reserved. May not be used, modified, or copied 20 | # without permission 21 | # 22 | #---------------------------------------------------------------------------- 23 | ARCH=MAC_OSX 24 | #ARCH=LINUX 25 | #ARCH=BGQ 26 | #ARCH=FUSION 27 | #ARCH=XT 28 | #ARCH=XE 29 | 30 | # number of procs 31 | num_procs=1 32 | 33 | # procs per node 34 | ppn=4 # adjustable for BG/Q, allowed 1, 2, 4, 8, 16, 32, 64 35 | 36 | # number of BG/Q nodes 37 | num_nodes=$[$num_procs / $ppn] 38 | if [ $num_nodes -lt 1 ]; then 39 | num_nodes=1 40 | fi 41 | 42 | # executable 43 | exe=./moab-test 44 | 45 | # inout file 46 | infile="/Users/tpeterka/software/cian/data/64bricks_12ktet_1proc.h5m" 47 | 48 | # output file 49 | outfile="del.out" 50 | #outfile="!" 51 | 52 | # options: kd-tree 53 | # possibilities are --wrap --kdtree --blocks 54 | opts="--blocks 4" 55 | #opts="--kdtree --blocks 4" 56 | 57 | #------ 58 | # 59 | # program arguments 60 | # 61 | 62 | args="$opts $infile $outfile" 63 | 64 | #------ 65 | # 66 | # run commands 67 | # 68 | 69 | if [ "$ARCH" = "MAC_OSX" ]; then 70 | 71 | mpiexec -l -n $num_procs $exe $args 72 | 73 | #dsymutil $exe ; mpiexec -l -n $num_procs xterm -e gdb -x gdb.run --args $exe $args 74 | 75 | #dsymutil $exe ; mpiexec -l -n $num_procs xterm -e lldb -s lldb.run $exe -- $args 76 | 77 | #dsymutil $exe ; mpiexec -l -n $num_procs valgrind -q $exe $args 78 | 79 | #dsymutil $exe ; mpiexec -n $num_procs valgrind -q --leak-check=yes $exe $args 80 | 81 | fi 82 | 83 | if [ "$ARCH" = "LINUX" ]; then 84 | 85 | mpiexec -n $num_procs $exe $args 86 | 87 | #mpiexec -n $num_procs xterm -e gdb -x gdb.run --args $exe $args 88 | 89 | #mpiexec -n $num_procs valgrind -q $exe $args 90 | 91 | #mpiexec -n $num_procs valgrind -q --leak-check=yes $exe $args 92 | 93 | fi 94 | 95 | if [ "$ARCH" = "BGQ" ]; then 96 | 97 | qsub -n $num_nodes --mode c$ppn -A SDAV -t 60 $exe $args 98 | 99 | fi 100 | 101 | if [ "$ARCH" = "FUSION" ]; then 102 | 103 | mpiexec $exe $args 104 | 105 | fi 106 | 107 | if [ "$ARCH" = "XT" ]; then 108 | 109 | cd /tmp/work/$USER 110 | aprun -n $num_procs $exe $args 111 | 112 | fi 113 | 114 | if [ "$ARCH" = "XE" ]; then 115 | 116 | aprun -n $num_procs $exe $args 117 | 118 | fi 119 | -------------------------------------------------------------------------------- /examples/pread-voronoi/PREAD_VORONOI: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | procs=2 4 | tb=8 5 | threads=2 6 | mb=-1 7 | #infile=~/Data/astro/MattTurk/particles/particles.h5 8 | infile=./unit-cube.h5 9 | outfile=del.out 10 | #outfile=! 11 | coords="x y z" 12 | mins="0 0 0" 13 | maxs="1. 1. 1." 14 | 15 | opts="--kdtree" 16 | 17 | mpiexec -n $procs ./pread-voronoi $opts $infile $outfile $coords $mins $maxs 0 0 0 0 18 | -------------------------------------------------------------------------------- /examples/pread-voronoi/balance.cpp: -------------------------------------------------------------------------------- 1 | #include "mpi.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "tess/tess.h" 10 | #include "tess/tess.hpp" 11 | 12 | #include "io/hdf5/pread.h" 13 | #ifdef TESS_GADGET_IO 14 | #include "io/gadget/particles.h" 15 | #endif 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "../opts.h" 25 | #include "../memory.h" 26 | 27 | #include "common.h" 28 | 29 | 30 | // This class is coupled with how reading works in io/{hdf5,gadget} 31 | struct ParticleRange 32 | { 33 | ParticleRange(int size_, size_t total_): 34 | size(size_), total(total_) {} 35 | 36 | size_t from(int rank) const { if (rank >= size) return total; return total / size * rank; } 37 | size_t to(int rank) const { if (rank >= size - 1) return total; return from(rank + 1); } 38 | size_t count(int rank) const { return to(rank) - from(rank); } 39 | int rank(size_t p) const { int r = p / (total / size); if (r >= size) r = size - 1; return r; } 40 | 41 | int size; 42 | size_t total; 43 | }; 44 | 45 | int main(int argc, char *argv[]) 46 | { 47 | string infile; // input file name 48 | int rank,size; // MPI usual 49 | std::vector coordinates; // coordinates to read 50 | double times[TESS_MAX_TIMES]; // timing 51 | quants_t quants; // quantity stats 52 | 53 | diy::mpi::environment env(argc, argv); 54 | diy::mpi::communicator world; 55 | 56 | rank = world.rank(); 57 | size = world.size(); 58 | 59 | typedef diy::ContinuousBounds Bounds; 60 | Bounds domain; 61 | 62 | using namespace opts; 63 | 64 | // defaults 65 | int min_blocks = size, 66 | max_blocks = size; 67 | string prefix = "./DIY.XXXXXX"; 68 | 69 | Options ops(argc, argv); 70 | 71 | ops 72 | >> Option( "min-blocks", min_blocks, "Minimum number of blocks to use") 73 | >> Option( "max-blocks", max_blocks, "Maximum number of blocks to use") 74 | ; 75 | 76 | coordinates.resize(3); 77 | if ( ops >> Present('h', "help", "show help") || 78 | !(ops >> PosOption(infile) 79 | >> PosOption(coordinates[0]) >> PosOption(coordinates[1]) >> PosOption(coordinates[2]) 80 | >> PosOption(domain.min[0]) >> PosOption(domain.min[1]) >> PosOption(domain.min[2]) 81 | >> PosOption(domain.max[0]) >> PosOption(domain.max[1]) >> PosOption(domain.max[2]) 82 | ) 83 | ) 84 | { 85 | if (rank == 0) 86 | { 87 | fprintf(stderr, "Usage: %s [OPTIONS] infile coordinates mins maxs\n", argv[0]); 88 | std::cout << ops; 89 | } 90 | return 1; 91 | } 92 | 93 | // read or generate points 94 | std::vector particles; 95 | // read points 96 | #if defined TESS_GADGET_IO 97 | std::string infn(infile); 98 | if (infn.size() > 7 && infn.substr(0,7) == "gadget:") 99 | { 100 | std::string infilename = std::string(infile).substr(7); 101 | io::gadget::read_particles(world, 102 | infilename.c_str(), 103 | rank, 104 | size, 105 | particles, 106 | coordinates); 107 | } else // assume HDF5 108 | #endif 109 | io::hdf5::read_particles(world, 110 | infile.c_str(), 111 | rank, 112 | size, 113 | particles, 114 | coordinates); 115 | 116 | size_t total_particles; 117 | diy::mpi::all_reduce(world, particles.size()/3, total_particles, std::plus()); 118 | 119 | std::vector source_particles; 120 | source_particles.swap(particles); // keep a copy of the original for possible redistribution 121 | 122 | if (rank == 0) 123 | std::cout << "Particles read: " << total_particles << std::endl; 124 | 125 | for (int nblocks = min_blocks; nblocks <= max_blocks; nblocks *= 2) 126 | { 127 | world.barrier(); 128 | diy::mpi::communicator working_comm = world; 129 | if (nblocks < size) 130 | { 131 | // redistribute source particles into particles 132 | std::list inflight; 133 | 134 | int tag = 0; 135 | ParticleRange source(size, total_particles); 136 | ParticleRange target(nblocks, total_particles); 137 | particles.resize(target.count(world.rank())*3); 138 | 139 | // post receive requests 140 | diy::mpi::request r; 141 | size_t from = target.from(world.rank()); 142 | size_t i = from; 143 | size_t to = target.to(world.rank()); 144 | particles.resize((to - from)*3); 145 | //std::cout << "[" << world.rank() << "]: " << "source particles size = " << source_particles.size() << "; target particles size = " << particles.size() << std::endl; 146 | while (i < to) 147 | { 148 | int rk = source.rank(i); 149 | int count = std::min(source.count(rk), to - i); 150 | //std::cout << "[" << world.rank() << "]: " << "Posting receive at " << (i - from)*3 << " for " << count*3 << " from " << rk << std::endl; 151 | MPI_Irecv(&particles[(i - from)*3], count*3, MPI_FLOAT, rk, tag, world, &r.r); 152 | inflight.push_back(r); 153 | i += count; 154 | } 155 | 156 | // post send requests 157 | from = source.from(world.rank()); 158 | i = from; 159 | to = source.to(world.rank()); 160 | while (i < to) 161 | { 162 | int rk = target.rank(i); 163 | int count = std::min(target.count(rk), to - i); 164 | //std::cout << "[" << world.rank() << "]: " << "Posting send at " << (i - from)*3 << " for " << count*3 << " to " << rk << std::endl; 165 | MPI_Isend(&source_particles[(i - from)*3], count*3, MPI_FLOAT, rk, tag, world, &r.r); 166 | inflight.push_back(r); 167 | i += count; 168 | } 169 | 170 | // kick requests until done 171 | while(!inflight.empty()) 172 | for (auto it = inflight.begin(); it != inflight.end(); ++it) 173 | if (it->test()) 174 | inflight.erase(it--); 175 | 176 | // restrict the working_comm 177 | int color = world.rank() < nblocks ? 0 : 1; 178 | MPI_Comm newcomm; 179 | MPI_Comm_split(world, color, world.rank(), &newcomm); 180 | working_comm = newcomm; 181 | 182 | if (world.rank() >= nblocks) 183 | continue; 184 | } else 185 | { 186 | particles = source_particles; // just make a copy 187 | } 188 | rank = working_comm.rank(); 189 | 190 | size_t average = total_particles / nblocks; 191 | if (rank == 0) 192 | std::cout << "----\n" 193 | << "nblocks=" << nblocks << "; average=" << average << std::endl; 194 | 195 | // initialize DIY and decompose domain 196 | diy::FileStorage storage(prefix); 197 | diy::Master master(working_comm, 1, -1, 198 | &create_block, 199 | &destroy_block, 200 | &storage, 201 | &save_block, 202 | &load_block); 203 | 204 | diy::ContiguousAssigner assigner(working_comm.size(), nblocks); 205 | 206 | // decompose 207 | AddBlock add(master); 208 | std::vector local_gids; 209 | assigner.local_gids(rank, local_gids); 210 | size_t ngids = local_gids.size(); 211 | std::map lids; 212 | for (size_t i = 0; i < local_gids.size(); ++i) 213 | lids[local_gids[i]] = i; 214 | auto fill_block = [&add,&particles,&lids](int gid, const Bounds& core, const Bounds& bounds, const Bounds& domain, const RCLink& link) 215 | { 216 | DBlock* b = add(gid, core, bounds, domain, link); 217 | 218 | int lid = lids[gid]; 219 | size_t nparticles = particles.size() / 3; 220 | size_t width = nparticles / lids.size(); 221 | size_t from = lid * width * 3; 222 | size_t to = (lid != lids.size() - 1 ? (lid + 1) * width * 3 - 1 : particles.size() - 1); 223 | b->num_particles = (to - from + 1) / 3; 224 | b->num_orig_particles = b->num_particles; 225 | b->particles = (float *)realloc(b->particles, (to - from + 1) * sizeof(float)); 226 | for (size_t i = from; i <= to; ++i) 227 | b->particles[i - from] = particles[i]; 228 | 229 | for (int i = 0; i < 3; ++i) 230 | { 231 | b->box.min[i] = domain.min[i]; 232 | b->box.max[i] = domain.max[i]; 233 | } 234 | }; 235 | 236 | // regular decomposition 237 | diy::decompose(3, rank, domain, assigner, fill_block); 238 | 239 | tess_exchange(master, assigner, times); 240 | 241 | // figure out the maxs 242 | master.foreach([](DBlock* b, const diy::Master::ProxyWithLink& cp) 243 | { 244 | cp.collectives()->clear(); 245 | cp.all_reduce(b->num_particles, diy::mpi::maximum()); 246 | }); 247 | master.exchange(); 248 | 249 | int all_max_regular; 250 | if (rank == 0) 251 | { 252 | all_max_regular = master.proxy(0).get(); 253 | std::cout << "Regular: " << all_max_regular << ' ' 254 | << float(all_max_regular)/average << ' ' 255 | << times[EXCH_TIME] 256 | << std::endl; 257 | } 258 | 259 | // k-d tree histogram 260 | master.clear(); 261 | diy::decompose(3, rank, domain, assigner, fill_block); 262 | tess_kdtree_exchange(master, assigner, times, false); 263 | 264 | // figure out the maxs 265 | master.foreach([](DBlock* b, const diy::Master::ProxyWithLink& cp) 266 | { 267 | cp.collectives()->clear(); 268 | cp.all_reduce(b->num_particles, diy::mpi::maximum()); 269 | }); 270 | master.exchange(); 271 | 272 | int all_max_kdtree_hist; 273 | if (rank == 0) 274 | { 275 | all_max_kdtree_hist = master.proxy(0).get(); 276 | std::cout << "K-d tree (histogram): " 277 | << all_max_kdtree_hist << ' ' 278 | << float(all_max_kdtree_hist)/average << ' ' 279 | << times[EXCH_TIME] 280 | << std::endl; 281 | } 282 | 283 | // k-d tree sampling 284 | master.clear(); 285 | diy::decompose(3, rank, domain, assigner, fill_block); 286 | tess_kdtree_exchange(master, assigner, times, false, true); 287 | 288 | // figure out the maxs 289 | master.foreach([](DBlock* b, const diy::Master::ProxyWithLink& cp) 290 | { 291 | cp.collectives()->clear(); 292 | cp.all_reduce(b->num_particles, diy::mpi::maximum()); 293 | }); 294 | master.exchange(); 295 | 296 | int all_max_kdtree_sample; 297 | if (rank == 0) 298 | { 299 | all_max_kdtree_sample = master.proxy(0).get(); 300 | std::cout << "K-d tree (sampling): " 301 | << all_max_kdtree_sample << ' ' 302 | << float(all_max_kdtree_sample)/average << ' ' 303 | << times[EXCH_TIME] 304 | << std::endl; 305 | } 306 | } 307 | 308 | return 0; 309 | } 310 | -------------------------------------------------------------------------------- /examples/pread-voronoi/common.h: -------------------------------------------------------------------------------- 1 | #ifndef PREAD_COMMON_H 2 | #define PREAD_COMMON_H 3 | 4 | #include 5 | 6 | /** 7 | * Deduplicate functionality: get rid of duplicate points, since downstream 8 | * code can't handle them. 9 | */ 10 | 11 | typedef std::map DuplicateCountMap; 12 | 13 | struct DedupPoint 14 | { 15 | float data[3]; 16 | bool operator<(const DedupPoint& other) const { return std::lexicographical_compare(data, data + 3, other.data, other.data + 3); } 17 | bool operator==(const DedupPoint& other) const { return std::equal(data, data + 3, other.data); } 18 | }; 19 | void deduplicate(DBlock* b, 20 | const diy::Master::ProxyWithLink& cp, 21 | DuplicateCountMap& count) 22 | { 23 | if (!b->num_particles) 24 | return; 25 | 26 | // simple static_assert to ensure sizeof(Point) == sizeof(float[3]); 27 | // necessary to make this hack work 28 | typedef int static_assert_Point_size[sizeof(DedupPoint) == sizeof(float[3]) ? 1 : -1]; 29 | DedupPoint* bg = (DedupPoint*) &b->particles[0]; 30 | DedupPoint* end = (DedupPoint*) &b->particles[3*b->num_particles]; 31 | std::sort(bg,end); 32 | 33 | DedupPoint* out = bg + 1; 34 | for (DedupPoint* it = bg + 1; it != end; ++it) 35 | { 36 | if (*it == *(it - 1)) 37 | count[out - bg - 1]++; 38 | else 39 | { 40 | *out = *it; 41 | ++out; 42 | } 43 | 44 | } 45 | b->num_orig_particles = b->num_particles = out - bg; 46 | 47 | if (!count.empty()) 48 | { 49 | size_t total = 0; 50 | for (DuplicateCountMap::const_iterator it = count.begin(); it != count.end(); ++it) 51 | total += it->second; 52 | std::cout << b->gid << ": Found " << count.size() << " particles that appear more than once, with " << total << " total extra copies\n"; 53 | } 54 | } 55 | 56 | // check if the particles fall inside the block bounds 57 | void verify_particles(DBlock* b, 58 | const diy::Master::ProxyWithLink& cp) 59 | { 60 | /* fprintf(stderr, "gid %d has %d particles\n", b->gid, b->num_particles); */ 61 | 62 | for (size_t i = 0; i < b->num_particles; ++i) 63 | { 64 | for (int j = 0; j < 3; ++j) 65 | { 66 | if (b->particles[3*i + j] < b->bounds.min[j] || 67 | b->particles[3*i + j] > b->bounds.max[j]) 68 | { 69 | fprintf(stderr, "Particle outside the block: %f %f %f\n", 70 | b->particles[3*i], 71 | b->particles[3*i + 1], 72 | b->particles[3*i + 2]); 73 | fprintf(stderr, " block mins: %f %f %f\n", 74 | b->bounds.min[0], 75 | b->bounds.min[1], 76 | b->bounds.min[2]); 77 | fprintf(stderr, " block maxs: %f %f %f\n", 78 | b->bounds.max[0], 79 | b->bounds.max[1], 80 | b->bounds.max[2]); 81 | /* std::exit(1); */ 82 | } 83 | } 84 | } 85 | } 86 | 87 | void enumerate_cells(DBlock* b, 88 | const diy::Master::ProxyWithLink& cp) 89 | { 90 | cp.collectives()->clear(); 91 | 92 | size_t infinite = 0; 93 | for (size_t p = 0; p < b->num_orig_particles; ++p) 94 | { 95 | int t = b->vert_to_tet[p]; 96 | if (t < 0) 97 | fprintf(stderr, "[%d] Warning: no matching tet for point %ld\n", cp.gid(), p); 98 | vector< pair > nbrs; 99 | bool finite = neighbor_edges(nbrs, p, b->tets, t); 100 | if (!finite) 101 | ++infinite; 102 | } 103 | //fprintf(stderr, "[%d] %lu infinite Voronoi cells\n", cp.gid(), infinite); 104 | 105 | cp.all_reduce(infinite, std::plus()); 106 | } 107 | 108 | // Swaps 8 bytes from 1-2-3-4-5-6-7-8 to 8-7-6-5-4-3-2-1 order. 109 | // cast the input as a char and use on any 8 byte variable 110 | void swap8(char *n) { 111 | 112 | char *n1; 113 | char c; 114 | 115 | n1 = n + 7; 116 | c = *n; 117 | *n = *n1; 118 | *n1 = c; 119 | 120 | n++; 121 | n1--; 122 | c = *n; 123 | *n = *n1; 124 | *n1 = c; 125 | 126 | n++; 127 | n1--; 128 | c = *n; 129 | *n = *n1; 130 | *n1 = c; 131 | 132 | n++; 133 | n1--; 134 | c = *n; 135 | *n = *n1; 136 | *n1 = c; 137 | 138 | } 139 | 140 | // Swaps 4 bytes from 1-2-3-4 to 4-3-2-1 order. 141 | // cast the input as a char and use on any 4 byte variable 142 | void swap4(char *n) { 143 | 144 | char *n1; 145 | char c; 146 | 147 | n1 = n + 3; 148 | c = *n; 149 | *n = *n1; 150 | *n1 = c; 151 | 152 | n++; 153 | n1--; 154 | c = *n; 155 | *n = *n1; 156 | *n1 = c; 157 | 158 | } 159 | 160 | // Swaps 2 bytes from 1-2 to 2-1 order. 161 | // cast the input as a char and use on any 2 byte variable 162 | void swap2(char *n){ 163 | 164 | char c; 165 | 166 | c = *n; 167 | *n = n[1]; 168 | n[1] = c; 169 | 170 | } 171 | 172 | void swap_bytes(void *p, // address of items 173 | int nitems, // number of items 174 | int item_size) // 2, 4, or 8 bytes (returns quietly if 1 byte) 175 | { 176 | int i; 177 | char*n = (char*)p; 178 | switch(item_size) { 179 | case 1: 180 | return; 181 | break; 182 | case 2: 183 | for (i = 0; i < nitems; i++) { 184 | swap2(n); 185 | n += 2; 186 | } 187 | break; 188 | case 4: 189 | for (i = 0; i < nitems; i++) { 190 | swap4(n); 191 | n += 4; 192 | } 193 | break; 194 | case 8: 195 | for (i = 0; i < nitems; i++) { 196 | swap8(n); 197 | n += 8; 198 | } 199 | break; 200 | default: 201 | fprintf(stderr, "Error: size of data must be either 1, 2, 4, or 8 bytes per item\n"); 202 | 203 | } 204 | } 205 | 206 | #endif 207 | -------------------------------------------------------------------------------- /examples/pread-voronoi/hacc-voronoi.cpp: -------------------------------------------------------------------------------- 1 | #include "mpi.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "tess/tess.h" 10 | #include "tess/tess.hpp" 11 | 12 | #include "io/hdf5/pread.h" 13 | #include "io/hacc/particles.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "../opts.h" 24 | #include "../memory.h" 25 | 26 | #include "common.h" 27 | 28 | struct AddAndRead: public AddBlock 29 | { 30 | AddAndRead(diy::Master& m, 31 | int nblocks_, 32 | const char* infile_, 33 | int sample_rate_ = 1) : // only used for hacc 34 | AddBlock(m), 35 | nblocks(nblocks_), 36 | infile(infile_), 37 | sample_rate(sample_rate_) {} 38 | 39 | void operator()(int gid, const Bounds& core, const Bounds& bounds, const Bounds& domain, 40 | const RCLink& link) const 41 | { 42 | DBlock* b = AddBlock::operator()(gid, core, bounds, domain, link); 43 | 44 | // read points 45 | std::vector particles; 46 | 47 | // only read a new genericio block once for each mpi rank 48 | // following test assumes contiguous assignment, not round robin 49 | if (gid % (nblocks / master.communicator().size()) == 0) 50 | io::hacc::read_particles(master.communicator(), 51 | infile, 52 | particles, 53 | sample_rate); 54 | 55 | b->num_particles = particles.size()/3; 56 | b->num_orig_particles = b->num_particles; 57 | b->particles = (float *)malloc(particles.size() * sizeof(float)); 58 | for (size_t i = 0; i < particles.size(); ++i) 59 | b->particles[i] = particles[i]; 60 | 61 | for (int i = 0; i < 3; ++i) 62 | { 63 | b->box.min[i] = domain.min[i]; 64 | b->box.max[i] = domain.max[i]; 65 | } 66 | } 67 | 68 | int nblocks; 69 | const char* infile; 70 | int sample_rate; // for hacc only 71 | Bounds* data_bounds; // global data bounds (for hacc only) 72 | }; 73 | 74 | int main(int argc, char *argv[]) 75 | { 76 | int tot_blocks; // total number of blocks in the domain 77 | int num_threads; // number of threads diy can use 78 | int mem_blocks; // number of blocks to keep in memory 79 | string infile; // input file name 80 | string outfile; // output file name 81 | int rank, size; // MPI usual 82 | double times[TESS_MAX_TIMES]; // timing 83 | quants_t quants; // quantity stats 84 | int sample_rate; // keep every one out of this many particles 85 | 86 | diy::mpi::environment env(argc, argv); 87 | diy::mpi::communicator world; 88 | 89 | rank = world.rank(); 90 | size = world.size(); 91 | 92 | typedef diy::ContinuousBounds Bounds; 93 | Bounds domain; 94 | 95 | using namespace opts; 96 | 97 | // defaults 98 | tot_blocks = size; 99 | num_threads = 4; 100 | mem_blocks = -1; 101 | string prefix = "./DIY.XXXXXX"; 102 | sample_rate = 1; 103 | int chunk = 1; 104 | 105 | Options ops(argc, argv); 106 | 107 | ops 108 | >> Option('b', "blocks", tot_blocks, "Total number of blocks to use") 109 | >> Option('t', "threads", num_threads, "Number of threads to use") 110 | >> Option('m', "in-memory", mem_blocks, "Number of blocks to keep in memory") 111 | >> Option('s', "storage", prefix, "Path for out-of-core storage") 112 | >> Option('c', "chunk", chunk, "chunk size for writing BOV (for debugging)") 113 | ; 114 | bool wrap_ = ops >> Present('w', "wrap", "Use periodic boundary conditions"); 115 | bool kdtree = ops >> Present( "kdtree", "use kdtree decomposition"); 116 | bool debug = ops >> Present('d', "debug", "print debugging info"); 117 | bool swap = ops >> Present('s', "swap", "swap bytes when writing bov file (for debugging)"); 118 | 119 | if ( ops >> Present('h', "help", "show help") || 120 | !(ops >> PosOption(infile) >> PosOption(outfile) >> PosOption(sample_rate)) ) 121 | { 122 | if (rank == 0) 123 | { 124 | fprintf(stderr, "Usage: %s [OPTIONS] infile outfile bf sr\n", argv[0]); 125 | std::cout << ops; 126 | } 127 | return 1; 128 | } 129 | 130 | // debug 131 | // if (rank == 0) 132 | // fprintf(stderr, "infile %s outfile %s minv %.1f maxv %.1f wrap %d sr %d " 133 | // "th %d mb %d opts %d tb %d\n", 134 | // infile.c_str(), outfile.c_str(), minvol, maxvol, wrap_, 135 | // sample_rate, num_threads, mem_blocks, kdtree, tot_blocks); 136 | 137 | if (kdtree) 138 | { 139 | if (mem_blocks != -1) 140 | { 141 | if (rank == 0) 142 | std::cout << "kdtree doesn't yet support the out-of-core mode\n"; 143 | return 1; 144 | } 145 | } 146 | 147 | if (outfile == "!") 148 | outfile = ""; 149 | 150 | timing(times, -1, -1, world); 151 | timing(times, TOT_TIME, -1, world); 152 | 153 | // initialize DIY and decompose domain 154 | diy::FileStorage storage(prefix); 155 | diy::Master master(world, 156 | num_threads, 157 | mem_blocks, 158 | &create_block, 159 | &destroy_block, 160 | &storage, 161 | &save_block, 162 | &load_block); 163 | // NB: AddAndRead for hacc assumes contiguous; don't switch to round robin 164 | diy::ContiguousAssigner assigner(world.size(), tot_blocks); 165 | 166 | AddAndRead create_and_read(master, 167 | tot_blocks, 168 | infile.c_str(), 169 | sample_rate); 170 | io::hacc::read_domain(master.communicator(), 171 | infile.c_str(), 172 | domain); 173 | 174 | // decompose 175 | std::vector my_gids; 176 | assigner.local_gids(rank, my_gids); 177 | diy::RegularDecomposer::BoolVector wrap; 178 | diy::RegularDecomposer::BoolVector share_face; 179 | diy::RegularDecomposer::CoordinateVector ghosts; 180 | if (wrap_) 181 | wrap.assign(3, true); 182 | diy::decompose(3, rank, domain, assigner, create_and_read, share_face, wrap, ghosts); 183 | 184 | // get total number of particles 185 | size_t nparticles = ((DBlock*)master.block(0))->num_particles; 186 | size_t tot_particles; 187 | diy::mpi::all_reduce (world, nparticles, tot_particles, std::plus()); 188 | if (rank == 0) 189 | fprintf(stderr, "Total number of particles = %ld\n", tot_particles); 190 | 191 | // debug: write points to bov file 192 | if (debug) 193 | { 194 | size_t ofst = 0; 195 | MPI_Exscan(&nparticles, &ofst, 1, MPI_LONG_LONG, MPI_SUM, MPI_COMM_WORLD); 196 | // fprintf(stderr, "ofst (in particles) = %ld\n", ofst); // debug 197 | diy::mpi::io::file out(world, 198 | // "debug.bov", 199 | "/projects/SSSPPg/tpeterka/debug.bov", 200 | diy::mpi::io::file::wronly | diy::mpi::io::file::create); 201 | out.resize(0); // truncate file if it exists 202 | std::vector shape(1, tot_particles * 3 / chunk);// in chunks 203 | diy::io::BOV writer(out, shape); 204 | diy::DiscreteBounds box; 205 | box.min[0] = ofst * 3 / chunk; // in chunks 206 | box.max[0] = (ofst + nparticles) * 3 / chunk - 1; // in chunks 207 | if (swap) // swap bytes for writing bov file 208 | swap_bytes(((DBlock*)master.block(0))->particles, nparticles * 3, sizeof(float)); 209 | writer.write(box, ((DBlock*)master.block(0))->particles, true, chunk); 210 | if (swap) // swap back to continue with tess 211 | swap_bytes(((DBlock*)master.block(0))->particles, nparticles * 3, sizeof(float)); 212 | if (rank == 0) 213 | fprintf(stderr, "BOV file written\n"); 214 | } 215 | 216 | // sort and distribute particles to all blocks 217 | if (kdtree) 218 | tess_kdtree_exchange(master, assigner, times, wrap_); 219 | else 220 | tess_exchange(master, assigner, times); 221 | if (rank == 0) 222 | printf("particles exchanged\n"); 223 | 224 | DuplicateCountMap count; 225 | master.foreach([&](DBlock* b, const diy::Master::ProxyWithLink& cp) 226 | { deduplicate(b, cp, count); }); 227 | 228 | // debug purposes only: checks if the particles got into the right blocks 229 | // master.foreach(&verify_particles); 230 | 231 | size_t rounds = tess(master, quants, times); 232 | if (rank == 0) 233 | fprintf(stderr, "Done in %lu rounds\n", rounds); 234 | 235 | tess_save(master, outfile.c_str(), times); 236 | 237 | timing(times, -1, TOT_TIME, world); 238 | tess_stats(master, quants, times); 239 | 240 | // Storage + memory stats 241 | size_t max_storage = storage.max_size(), 242 | sum_max_storage; 243 | diy::mpi::reduce(world, max_storage, sum_max_storage, 0, std::plus()); 244 | 245 | size_t hwm = proc_status_value("VmHWM"), 246 | max_hwm; 247 | diy::mpi::reduce(world, hwm, max_hwm, 0, diy::mpi::maximum()); 248 | 249 | if (rank == 0) 250 | { 251 | fprintf(stderr, "Sum of max storage: %lu\n", sum_max_storage); 252 | fprintf(stderr, "Max high water mark: %lu\n", max_hwm); 253 | } 254 | 255 | return 0; 256 | } 257 | -------------------------------------------------------------------------------- /examples/pread-voronoi/io/gadget/particles.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "particles.h" 4 | 5 | #include 6 | #include 7 | 8 | 9 | ssize_t 10 | io::gadget:: 11 | read_particles(MPI_Comm comm_, 12 | const char *infile, int rank, int size, 13 | std::vector& particles, 14 | const std::vector& coordinates) 15 | { 16 | typedef std::vector PointContainer; 17 | typedef std::vector IntVector; 18 | 19 | diy::mpi::communicator comm(comm_); 20 | 21 | IntVector individual_count_, cumulative_count_; 22 | std::vector data_files_; 23 | 24 | int format; 25 | bool swap; 26 | int blockSize, blockSize2; 27 | 28 | std::string filename_(infile); 29 | std::vector coordinates_; 30 | 31 | using namespace detail; 32 | 33 | for(size_t i = 0; i < coordinates.size(); ++i) 34 | coordinates_.push_back(boost::lexical_cast(coordinates[i])); 35 | 36 | if (comm.rank() == 0) 37 | { 38 | bf::path p(filename_); 39 | if (!bf::exists(p)) 40 | throw std::runtime_error("Could not open the directory: " + boost::lexical_cast(p)); 41 | 42 | for (bf::directory_iterator cur = bf::directory_iterator(p); cur != bf::directory_iterator(); ++cur) 43 | data_files_.push_back(cur->path().string()); 44 | boost::sort(data_files_); 45 | 46 | for(size_t i = 0; i < data_files_.size(); ++i) 47 | { 48 | const std::string& fn = data_files_[i]; 49 | std::string gadget2; 50 | struct GadgetHeader header; 51 | format = GADGET_1; 52 | swap = false; 53 | 54 | std::ifstream igStr(fn.c_str(), std::ios::in | std::ios::binary); 55 | std::ifstream* gStr = &igStr; 56 | if (gStr->fail()) 57 | { 58 | std::cout << "File: " << fn << " cannot be opened" << std::endl; 59 | exit(-1); 60 | } 61 | 62 | // Set the gadget format type by reading the first 4 byte integer 63 | // If it is not "256" or "65536" then gadget-2 format with 16 bytes in front 64 | readData(swap, (void*) &blockSize, GADGET_SKIP, 1, gStr); 65 | if (blockSize != GADGET_HEADER_SIZE && blockSize != GADGET_HEADER_SIZE_SWP) 66 | { 67 | format = GADGET_2; 68 | gadget2 = readString(gStr, GADGET_2_SKIP - GADGET_SKIP); 69 | readData(swap, (void*) &blockSize, GADGET_SKIP, 1, gStr); 70 | } 71 | 72 | // Set the swap type 73 | if (blockSize != GADGET_HEADER_SIZE) 74 | { 75 | swap = true; 76 | blockSize = GADGET_HEADER_SIZE; 77 | } 78 | 79 | // Read the Gadget header 80 | readData(swap, (void*) &header.npart[0], 81 | sizeof(int), NUM_GADGET_TYPES, gStr); 82 | readData(swap, (void*) &header.mass[0], 83 | sizeof(double), NUM_GADGET_TYPES, gStr); 84 | readData(swap, (void*) &header.time, sizeof(double), 1, gStr); 85 | readData(swap, (void*) &header.redshift, sizeof(double), 1, gStr); 86 | readData(swap, (void*) &header.flag_sfr, sizeof(int), 1, gStr); 87 | readData(swap, (void*) &header.flag_feedback, sizeof(int), 1, gStr); 88 | readData(swap, (void*) &header.npartTotal[0], 89 | sizeof(int), NUM_GADGET_TYPES, gStr); 90 | readData(swap, (void*) &header.flag_cooling, sizeof(int), 1, gStr); 91 | readData(swap, (void*) &header.num_files, sizeof(int), 1, gStr); 92 | readData(swap, (void*) &header.BoxSize, sizeof(double), 1, gStr); 93 | readData(swap, (void*) &header.Omega0, sizeof(double), 1, gStr); 94 | readData(swap, (void*) &header.OmegaLambda, sizeof(double), 1, gStr); 95 | readData(swap, (void*) &header.HubbleParam, sizeof(double), 1, gStr); 96 | readData(swap, (void*) &header.flag_stellarage, sizeof(int), 1, gStr); 97 | readData(swap, (void*) &header.flag_metals, sizeof(int), 1, gStr); 98 | readData(swap, (void*) &header.HighWord[0], 99 | sizeof(int), NUM_GADGET_TYPES, gStr); 100 | readData(swap, (void*) &header.flag_entropy, sizeof(int), 1, gStr); 101 | std::string fill = readString(gStr, 60); 102 | strcpy(&header.fill[0], fill.c_str()); 103 | 104 | // Read the Gadget header size to verify block 105 | readData(swap, (void*) &blockSize2, GADGET_SKIP, 1, gStr); 106 | if (blockSize != blockSize2) 107 | throw std::runtime_error("Error reading header: end position is wrong"); 108 | 109 | // Every type particle will have location, velocity and tag so sum up 110 | long int particleCount = 0; 111 | for (int i = 0; i < NUM_GADGET_TYPES; i++) 112 | particleCount += header.npart[i]; 113 | 114 | individual_count_.push_back(particleCount); 115 | } 116 | 117 | diy::MemoryBuffer bb; 118 | diy::save(bb, data_files_); 119 | diy::save(bb, individual_count_); 120 | diy::save(bb, format); 121 | diy::save(bb, swap); 122 | diy::save(bb, blockSize); 123 | diy::save(bb, blockSize2); 124 | diy::mpi::broadcast(comm, bb.buffer, 0); 125 | } else 126 | { 127 | diy::MemoryBuffer bb; 128 | diy::mpi::broadcast(comm, bb.buffer, 0); 129 | diy::load(bb, data_files_); 130 | diy::load(bb, individual_count_); 131 | diy::load(bb, format); 132 | diy::load(bb, swap); 133 | diy::load(bb, blockSize); 134 | diy::load(bb, blockSize2); 135 | } 136 | 137 | size_t total = 0; 138 | for(size_t i = 0; i < individual_count_.size(); ++i) 139 | { 140 | cumulative_count_.push_back(total); 141 | total += individual_count_[i]; 142 | } 143 | 144 | 145 | size_t offset = total/size*rank; 146 | size_t count = (rank != size - 1 ? total/size : total - total/size*rank); 147 | 148 | size_t o_offset = offset; 149 | 150 | particles.resize(count * DIMENSION); 151 | 152 | size_t grd = boost::lower_bound(cumulative_count_, offset) - cumulative_count_.begin(); 153 | if (grd == cumulative_count_.size() || offset < cumulative_count_[grd]) 154 | --grd; 155 | offset -= cumulative_count_[grd]; 156 | 157 | size_t i = 0; 158 | while (count > 0) 159 | { 160 | std::ifstream ParticleFile(data_files_[grd].c_str(), std::ios::in | std::ios::binary); 161 | std::ifstream* gStr = &ParticleFile; 162 | 163 | size_t particleCount = individual_count_[grd]; 164 | 165 | size_t to_read; 166 | if (individual_count_[grd] - offset <= count) 167 | to_read = individual_count_[grd] - offset; 168 | else 169 | to_read = count; 170 | 171 | if (offset + to_read > individual_count_[grd]) 172 | throw std::runtime_error("Cannot read more particles than are in the file"); 173 | 174 | readString(gStr, GADGET_HEADER_SIZE + sizeof(blockSize) + sizeof(blockSize2)); 175 | if (format == GADGET_2) 176 | readString(gStr, GADGET_2_SKIP); 177 | 178 | std::vector location(particleCount * DIMENSION); 179 | 180 | // XXX: reading everything and then extracting a subset is not the most efficient strategy 181 | readData(swap, (void*) &blockSize, GADGET_SKIP, 1, gStr); 182 | readData(swap, (void*) &location[0], 183 | sizeof(POSVEL_T), particleCount*DIMENSION, gStr); 184 | readData(swap, (void*) &blockSize2, GADGET_SKIP, 1, gStr); 185 | if (blockSize != blockSize2) 186 | throw std::runtime_error("Error reading locations: end position is wrong " + 187 | boost::lexical_cast(blockSize) + 188 | " vs " + 189 | boost::lexical_cast(blockSize2)); 190 | 191 | for (size_t j = 0; j < to_read; ++j, ++i) 192 | for (size_t k = 0; k < DIMENSION; ++k) 193 | particles[i*DIMENSION + k] = location[(offset + j)*DIMENSION + coordinates_[k]]; 194 | 195 | offset = 0; 196 | count -= to_read; 197 | ++grd; 198 | } 199 | 200 | return total; 201 | } 202 | 203 | std::string 204 | io::gadget::detail:: 205 | readString(std::ifstream* inStr, int size) 206 | { 207 | char* buffer = new char[size + 1]; 208 | inStr->read(buffer, size); 209 | buffer[size] = '\0'; 210 | 211 | // Make sure string has legal values 212 | if (isalnum(buffer[0]) == 0) 213 | buffer[0] = '\0'; 214 | for (int i = 1; i < size; i++) 215 | if (isprint(buffer[i]) == 0) 216 | buffer[i] = '\0'; 217 | 218 | std::string retString = buffer; 219 | delete [] buffer; 220 | return retString; 221 | } 222 | 223 | void 224 | io::gadget::detail:: 225 | readData( 226 | bool swap, 227 | void* data, 228 | unsigned long dataSize, 229 | unsigned long dataCount, 230 | std::ifstream* inStr) 231 | { 232 | // Read all the data from the file 233 | inStr->read(reinterpret_cast(data), dataSize*dataCount); 234 | 235 | if (swap == true) { 236 | 237 | // Byte swap each integer 238 | char* dataPtr = (char*) data; 239 | char temp; 240 | for (unsigned long item = 0; item < dataCount; item++) { 241 | 242 | // Do a byte-by-byte swap, reversing the order. 243 | for (unsigned int i = 0; i < dataSize / 2; i++) { 244 | temp = dataPtr[i]; 245 | dataPtr[i] = dataPtr[dataSize - 1 - i]; 246 | dataPtr[dataSize - 1 - i] = temp; 247 | } 248 | dataPtr += dataSize; 249 | } 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /examples/pread-voronoi/io/gadget/particles.h: -------------------------------------------------------------------------------- 1 | #ifndef __IO_GADGET_PARTICLES_H__ 2 | #define __IO_GADGET_PARTICLES_H__ 3 | 4 | #include 5 | #include 6 | 7 | #include "mpi.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace io 15 | { 16 | 17 | namespace gadget 18 | { 19 | 20 | ssize_t read_particles(MPI_Comm comm, 21 | const char *infile, int rank, int size, 22 | std::vector& particles, 23 | const std::vector& coordinates); 24 | 25 | namespace bf = boost::filesystem; 26 | 27 | namespace detail 28 | { 29 | #ifdef ID_64 30 | typedef int64_t ID_T; // Particle and halo ids 31 | #else 32 | typedef int32_t ID_T; // Particle and halo ids 33 | #endif 34 | 35 | #ifdef POSVEL_64 36 | typedef double POSVEL_T; // Position,velocity 37 | #else 38 | typedef float POSVEL_T; // Position,velocity 39 | #endif 40 | 41 | const int DIMENSION = 3; 42 | 43 | const int NUM_GADGET_TYPES = 6; // Types of gadget particles 44 | 45 | const int GADGET_HEADER_SIZE = 256; // Size when the endian matches 46 | const int GADGET_HEADER_SIZE_SWP= 65536;// Size when the endian doesn't match 47 | const int GADGET_FILL = 60; // Current fill to HEADER SIZE 48 | const int GADGET_SKIP = 4; // Bytes the indicate block size 49 | const int GADGET_2_SKIP = 16; // Extra bytes in gadget-2 50 | 51 | const int GADGET_1 = 1; 52 | const int GADGET_2 = 2; 53 | 54 | 55 | struct GadgetHeader { 56 | int npart[NUM_GADGET_TYPES]; 57 | double mass[NUM_GADGET_TYPES]; 58 | double time; 59 | double redshift; 60 | int flag_sfr; 61 | int flag_feedback; 62 | int npartTotal[NUM_GADGET_TYPES]; 63 | int flag_cooling; 64 | int num_files; 65 | double BoxSize; 66 | double Omega0; 67 | double OmegaLambda; 68 | double HubbleParam; 69 | int flag_stellarage; 70 | int flag_metals; 71 | int HighWord[NUM_GADGET_TYPES]; 72 | int flag_entropy; 73 | char fill[GADGET_FILL]; 74 | }; 75 | 76 | void readData( 77 | bool swap, 78 | void* data, 79 | unsigned long dataSize, 80 | unsigned long dataCount, 81 | std::ifstream* inStr); 82 | 83 | std::string readString(std::ifstream* inStr, int size); 84 | 85 | } 86 | 87 | } 88 | } 89 | #endif // __IO_GADGET_PARTICLES_H__ 90 | -------------------------------------------------------------------------------- /examples/pread-voronoi/io/hacc/particles.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "particles.h" 4 | 5 | #include 6 | #include 7 | 8 | void 9 | io::hacc:: 10 | read_domain(MPI_Comm comm_, // MPI comm 11 | const char* infile, // input file name 12 | Bounds& domain) // output global domain bounds 13 | { 14 | gio::GenericIOReader *reader = new gio::GenericIOMPIReader(); 15 | reader->SetFileName(infile); 16 | reader->SetCommunicator(comm_); 17 | reader->OpenAndReadHeader(); 18 | 19 | double origin[3], scale[3]; // global min and max as doubles 20 | reader->GetPhysOrigin(origin); 21 | reader->GetPhysScale(scale); 22 | domain.min[0] = origin[0]; 23 | domain.min[1] = origin[1]; 24 | domain.min[2] = origin[2]; 25 | domain.max[0] = scale[0]; 26 | domain.max[1] = scale[1]; 27 | domain.max[2] = scale[2]; 28 | 29 | reader->Close(); 30 | delete reader; 31 | } 32 | 33 | void 34 | io::hacc:: 35 | read_particles(MPI_Comm comm_, // MPI comm 36 | const char* infile, // input file name 37 | std::vector& particles, // output particles 38 | int sample_rate) // output sample rate 39 | { 40 | // intialize reader 41 | gio::GenericIOReader *reader = new gio::GenericIOMPIReader(); 42 | reader->SetFileName(infile); 43 | reader->SetCommunicator(comm_); 44 | reader->OpenAndReadHeader(); 45 | 46 | // read generic I/O data 47 | vector x, y, z; 48 | vector id; 49 | size_t num_particles = detail::read_gio(comm_, reader, x, y, z, id); 50 | 51 | // unique_ids is used to weed out duplicate particles, which sometimes happens in hacc 52 | set unique_ids; 53 | 54 | // package particles, sampling as specified and filtering out duplicates 55 | num_particles /= sample_rate; 56 | particles.resize(num_particles * 3); 57 | size_t nu = 0; // number of unique points 58 | for (size_t i = 0; i < num_particles; i++) { 59 | if (unique_ids.find(id[i * sample_rate]) == unique_ids.end()) { 60 | particles[3 * nu] = x[i * sample_rate]; 61 | particles[3 * nu + 1] = y[i * sample_rate]; 62 | particles[3 * nu + 2] = z[i * sample_rate]; 63 | unique_ids.insert(id[i * sample_rate]); 64 | nu++; 65 | } 66 | } 67 | 68 | // cleanup 69 | reader->Close(); 70 | delete reader; 71 | } 72 | 73 | size_t 74 | io::hacc::detail:: 75 | read_gio(MPI_Comm comm_, // MPI comm 76 | gio::GenericIOReader* reader, // generic io reader 77 | vector& x, // output x coords 78 | vector& y, // output y coords 79 | vector& z, // output z coords 80 | vector& id) // output particle ids 81 | { 82 | diy::mpi::communicator comm(comm_); 83 | 84 | // genericio can only handle one block per process 85 | // even though in theory I should be able to concatenate multiple genericio block reads 86 | // it returns 0 particles when there are not enough mpi ranks for genericio blocks 87 | assert(comm.size() == reader->GetTotalNumberOfBlocks()); 88 | 89 | // read local genericio blocks 90 | reader->ClearVariables(); // clear reader variables 91 | 92 | // number of particles in this block 93 | size_t num_particles = reader->GetNumberOfElements(comm.rank()); 94 | 95 | // padsize CRC for floats 96 | int floatpadsize = gio::CRCSize / sizeof(float); 97 | int idpadsize = gio::CRCSize / sizeof(int64_t); 98 | 99 | // particles 100 | x.resize(num_particles + floatpadsize); 101 | y.resize(num_particles + floatpadsize); 102 | z.resize(num_particles + floatpadsize); 103 | id.resize(num_particles + idpadsize); 104 | reader->AddVariable("x", &x[0], gio::GenericIOBase::ValueHasExtraSpace); 105 | reader->AddVariable("y", &y[0], gio::GenericIOBase::ValueHasExtraSpace); 106 | reader->AddVariable("z", &z[0], gio::GenericIOBase::ValueHasExtraSpace); 107 | reader->AddVariable("id", &id[0], gio::GenericIOBase::ValueHasExtraSpace); 108 | 109 | reader->ReadBlock(comm.rank()); // read the particles 110 | 111 | return num_particles; 112 | } 113 | -------------------------------------------------------------------------------- /examples/pread-voronoi/io/hacc/particles.h: -------------------------------------------------------------------------------- 1 | #ifndef __IO_HACC_PARTICLES_H__ 2 | #define __IO_HACC_PARTICLES_H__ 3 | 4 | #include "mpi.h" 5 | #include 6 | #include 7 | #include 8 | #include "GenericIODefinitions.hpp" 9 | #include "GenericIOReader.h" 10 | #include "GenericIOMPIReader.h" 11 | #include "GenericIOPosixReader.h" 12 | #include 13 | 14 | #include 15 | 16 | typedef diy::ContinuousBounds Bounds; 17 | 18 | using namespace std; 19 | 20 | namespace io 21 | { 22 | 23 | namespace hacc 24 | { 25 | void read_domain(MPI_Comm comm_, 26 | const char* infile, 27 | Bounds& domain); 28 | 29 | void read_particles(MPI_Comm comm_, 30 | const char* infile, 31 | std::vector& particles, 32 | int sample_rate); 33 | 34 | namespace detail 35 | { 36 | size_t read_gio(MPI_Comm comm_, 37 | gio::GenericIOReader* reader, 38 | vector& x, 39 | vector& y, 40 | vector& z, 41 | vector& id); 42 | } 43 | } 44 | } 45 | 46 | #endif // __IO_HACC_PARTICLES_H__ 47 | -------------------------------------------------------------------------------- /examples/pread-voronoi/io/hdf5/pread-hdf5.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "pread.h" 7 | #include 8 | 9 | #ifndef H5_HAVE_PARALLEL 10 | #warning Parallel HDF5 not available, using serial version 11 | #endif 12 | 13 | size_t 14 | io::hdf5:: 15 | read_particles(MPI_Comm comm, 16 | const char *infile, int rank, int size, 17 | std::vector &particles, 18 | const std::vector & coordinates) 19 | { 20 | herr_t status; 21 | 22 | #ifdef H5_HAVE_PARALLEL 23 | MPI_Info info = MPI_INFO_NULL; 24 | hid_t acc_tpl1 = H5Pcreate (H5P_FILE_ACCESS); 25 | assert(acc_tpl1 != -1); 26 | herr_t ret = H5Pset_fapl_mpio(acc_tpl1, comm, info); // set up parallel access with communicator 27 | assert(ret != -1); 28 | 29 | hid_t file_id = H5Fopen(infile, H5F_ACC_RDONLY, acc_tpl1); 30 | #else 31 | hid_t file_id = H5Fopen(infile, H5F_ACC_RDONLY, H5P_DEFAULT); 32 | #endif 33 | hid_t dataset_id = H5Dopen2(file_id, coordinates[0].c_str(), H5P_DEFAULT); 34 | hid_t dataspace_id = H5Dget_space(dataset_id); 35 | 36 | int r = H5Sget_simple_extent_ndims(dataspace_id); // should be 1 37 | std::vector dims(r); 38 | int ndims = H5Sget_simple_extent_dims(dataspace_id, &dims[0], NULL); 39 | hsize_t count = dims[0]; 40 | 41 | status = H5Sclose(dataspace_id); 42 | status = H5Dclose(dataset_id); 43 | 44 | hsize_t offset = count/size*rank; 45 | hsize_t local_count = (rank != size - 1 ? count/size : count - count/size*rank); 46 | 47 | std::vector tmp(local_count); // optimizes reading 48 | particles.resize(3*local_count); 49 | 50 | for (size_t i = 0; i < 3; ++i) 51 | { 52 | std::string c = coordinates[i]; 53 | hid_t dataset_id = H5Dopen2(file_id, c.c_str(), H5P_DEFAULT); 54 | hid_t dataspace_id = H5Dget_space(dataset_id); 55 | 56 | status = H5Sselect_hyperslab (dataspace_id, H5S_SELECT_SET, &offset, NULL, &local_count, NULL); 57 | 58 | hid_t memspace_id = H5Screate_simple(1, &local_count, NULL); 59 | status = H5Dread (dataset_id, H5T_NATIVE_FLOAT, memspace_id, dataspace_id, H5P_DEFAULT, &tmp[0]); 60 | 61 | // stripe into particles 62 | for (size_t j = 0; j < local_count; ++j) 63 | particles[3*j + i] = tmp[j]; // probably wildly cache inefficient 64 | 65 | status = H5Sclose(memspace_id); 66 | status = H5Sclose(dataspace_id); 67 | status = H5Dclose(dataset_id); 68 | } 69 | 70 | status = H5Fclose(file_id); 71 | #ifdef H5_HAVE_PARALLEL 72 | status = H5Pclose(acc_tpl1); 73 | #endif 74 | 75 | return count; 76 | } 77 | -------------------------------------------------------------------------------- /examples/pread-voronoi/io/hdf5/pread.h: -------------------------------------------------------------------------------- 1 | #ifndef _PREAD_H 2 | #define _PREAD_H 3 | 4 | #include "mpi.h" 5 | #include 6 | #include 7 | 8 | namespace io 9 | { 10 | namespace hdf5 11 | { 12 | 13 | size_t read_particles(MPI_Comm comm, 14 | const char *infile, int rank, int size, 15 | std::vector &particles, 16 | const std::vector& coordinates); 17 | 18 | } 19 | } 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /examples/pread-voronoi/io/moab/particles.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "particles.h" 4 | 5 | #include 6 | #include 7 | 8 | 9 | 10 | // read global domain bounds 11 | void 12 | io::moab:: 13 | read_domain(MPI_Comm comm_, // MPI comm 14 | const char* infile, // input file name 15 | Bounds& domain) // output global domain bounds 16 | { 17 | // TODO: read domain bounds 18 | 19 | double origin[3], scale[3]; // global min and max as doubles 20 | 21 | // some hard-coded values for now 22 | domain.min[0] = -1.0; 23 | domain.min[1] = -1.0; 24 | domain.min[2] = -1.0; 25 | domain.max[0] = 4.0; 26 | domain.max[1] = 4.0; 27 | domain.max[2] = 4.0; 28 | } 29 | 30 | void 31 | io::moab:: 32 | read_particles(MPI_Comm comm_, // MPI comm 33 | const char* infile, // input file name 34 | std::vector& particles) // output particles 35 | { 36 | ErrorCode rval; // moab return value 37 | 38 | // load mesh 39 | Interface *mb = new Core(); 40 | const char* options = ";;PARALLEL=READ_PART;PARALLEL_RESOLVE_SHARED_ENTS;" 41 | "PARTITION=MATERIAL_SET;PARTITION_DISTRIBUTE"; 42 | EntityHandle file_set; 43 | rval = mb->create_meshset(MESHSET_SET, file_set); ERR; 44 | rval = mb->load_file(infile, &file_set, options); ERR; 45 | 46 | // get vertices (0-dimensional entities) 47 | Range pts; 48 | rval = mb->get_entities_by_dimension(file_set, 0, pts); ERR; 49 | 50 | // the point coordinates could be be extracted in one line, except that we 51 | // need to convert double to float for each coordinate; hence need to loop over the vertices 52 | // and copy each coordinate separately 53 | particles.reserve(pts.size() * 3); 54 | double pt[3]; 55 | for (Range::iterator it = pts.begin(); it != pts.end(); it++) 56 | { 57 | rval = mb->get_coords(&(*it), 1, pt); ERR; 58 | particles.push_back(pt[0]); 59 | particles.push_back(pt[1]); 60 | particles.push_back(pt[2]); 61 | } 62 | 63 | // cleanup 64 | delete mb; 65 | } 66 | -------------------------------------------------------------------------------- /examples/pread-voronoi/io/moab/particles.h: -------------------------------------------------------------------------------- 1 | #ifndef __IO_MOAB_PARTICLES_H__ 2 | #define __IO_MOAB_PARTICLES_H__ 3 | 4 | #include "mpi.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // TODO: which of the following do I actually need? 12 | #include "iMesh.h" 13 | #include "MBiMesh.hpp" 14 | #include "moab/Core.hpp" 15 | #include "moab/Range.hpp" 16 | #include "MBTagConventions.hpp" 17 | #include "moab/ParallelComm.hpp" 18 | #include "moab/HomXform.hpp" 19 | #include "moab/ReadUtilIface.hpp" 20 | #include "Coupler.hpp" 21 | 22 | #include 23 | 24 | #define ERR {if(rval!=MB_SUCCESS)printf("MOAB error at line %d in %s\n", __LINE__, __FILE__);} 25 | 26 | typedef diy::ContinuousBounds Bounds; 27 | 28 | using namespace std; 29 | 30 | namespace io 31 | { 32 | 33 | namespace moab 34 | { 35 | void read_domain(MPI_Comm comm_, 36 | const char* infile, 37 | Bounds& domain); 38 | 39 | void read_particles(MPI_Comm comm_, 40 | const char* infile, 41 | std::vector& particles); 42 | } 43 | } 44 | 45 | #endif // __IO_MOAB_PARTICLES_H__ 46 | -------------------------------------------------------------------------------- /examples/pread-voronoi/pread-voronoi.cpp: -------------------------------------------------------------------------------- 1 | #include "mpi.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "tess/tess.h" 10 | #include "tess/tess.hpp" 11 | 12 | #include "io/hdf5/pread.h" 13 | #ifdef TESS_GADGET_IO 14 | #include "io/gadget/particles.h" 15 | #endif 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "../opts.h" 25 | #include "../memory.h" 26 | 27 | #include "common.h" 28 | 29 | typedef diy::ContinuousBounds Bounds; 30 | 31 | struct AddAndRead: public AddBlock 32 | { 33 | AddAndRead(diy::Master& m, 34 | int nblocks_, 35 | const char* infile_, 36 | const std::vector& coordinates_): 37 | AddBlock(m), 38 | nblocks(nblocks_), 39 | infile(infile_), 40 | coordinates(coordinates_) {} 41 | 42 | void operator()(int gid, const Bounds& core, const Bounds& bounds, const Bounds& domain, 43 | const RCLink& link) const 44 | { 45 | DBlock* b = AddBlock::operator()(gid, core, bounds, domain, link); 46 | 47 | // read points 48 | std::vector particles; 49 | 50 | #if defined TESS_GADGET_IO 51 | std::string infn(infile); 52 | if (infn.size() > 7 && infn.substr(0,7) == "gadget:") 53 | { 54 | std::string infilename = std::string(infile).substr(7); 55 | io::gadget::read_particles(master.communicator(), 56 | infilename.c_str(), 57 | gid, 58 | nblocks, 59 | particles, 60 | coordinates); 61 | } else // assume HDF5 62 | #endif 63 | io::hdf5::read_particles(master.communicator(), 64 | infile, 65 | gid, 66 | nblocks, 67 | particles, 68 | coordinates); 69 | 70 | b->num_particles = particles.size()/3; 71 | b->num_orig_particles = b->num_particles; 72 | b->particles = (float *)malloc(particles.size() * sizeof(float)); 73 | for (size_t i = 0; i < particles.size(); ++i) 74 | b->particles[i] = particles[i]; 75 | 76 | for (int i = 0; i < 3; ++i) 77 | { 78 | b->box.min[i] = domain.min[i]; 79 | b->box.max[i] = domain.max[i]; 80 | } 81 | } 82 | 83 | int nblocks; 84 | const char* infile; 85 | const std::vector& coordinates; 86 | Bounds* data_bounds; // global data bounds (for hacc only) 87 | }; 88 | 89 | 90 | int main(int argc, char *argv[]) 91 | { 92 | int tot_blocks; // total number of blocks in the domain 93 | int num_threads; // number of threads diy can use 94 | int mem_blocks; // number of blocks to keep in memory 95 | string infile; // input file name 96 | string outfile; // output file name 97 | float minvol, maxvol; // volume range, -1.0 = unused 98 | int wrap_; // whether wraparound neighbors are used 99 | int rank,size; // MPI usual 100 | std::vector coordinates; // coordinates to read 101 | double times[TESS_MAX_TIMES]; // timing 102 | quants_t quants; // quantity stats 103 | 104 | diy::mpi::environment env(argc, argv); 105 | diy::mpi::communicator world; 106 | 107 | rank = world.rank(); 108 | size = world.size(); 109 | 110 | typedef diy::ContinuousBounds Bounds; 111 | Bounds domain {3}; 112 | 113 | using namespace opts; 114 | 115 | // defaults 116 | tot_blocks = size; 117 | num_threads = 4; 118 | mem_blocks = -1; 119 | string prefix = "./DIY.XXXXXX"; 120 | minvol = 0; 121 | maxvol = 0; 122 | 123 | Options ops(argc, argv); 124 | 125 | ops 126 | >> Option('b', "blocks", tot_blocks, "Total number of blocks to use") 127 | >> Option('t', "threads", num_threads, "Number of threads to use") 128 | >> Option('m', "in-memory", mem_blocks, "Number of blocks to keep in memory") 129 | >> Option('s', "storage", prefix, "Path for out-of-core storage") 130 | >> Option( "minvol", minvol, "minvol cutoff") 131 | >> Option( "maxvol", maxvol, "minvol cutoff") 132 | ; 133 | wrap_ = ops >> Present('w', "wrap", "Use periodic boundary conditions"); 134 | bool kdtree = ops >> Present( "kdtree", "use kdtree decomposition"); 135 | 136 | coordinates.resize(3); 137 | if ( ops >> Present('h', "help", "show help") || 138 | !(ops >> PosOption(infile) >> PosOption(outfile) 139 | >> PosOption(coordinates[0]) >> PosOption(coordinates[1]) >> PosOption(coordinates[2]) 140 | >> PosOption(domain.min[0]) >> PosOption(domain.min[1]) >> PosOption(domain.min[2]) 141 | >> PosOption(domain.max[0]) >> PosOption(domain.max[1]) >> PosOption(domain.max[2]) 142 | ) 143 | ) 144 | { 145 | if (rank == 0) 146 | { 147 | fprintf(stderr, "Usage: %s [OPTIONS] infile outfile coordinates mins maxs\n", argv[0]); 148 | std::cout << ops; 149 | } 150 | return 1; 151 | } 152 | 153 | if (kdtree) 154 | { 155 | if (mem_blocks != -1) 156 | { 157 | if (rank == 0) 158 | std::cout << "kdtree doesn't yet support the out-of-core mode\n"; 159 | return 1; 160 | } 161 | 162 | if (wrap_ && tot_blocks < 64 && rank == 0) 163 | fprintf(stderr, "Warning: using k-d tree with wrap on and fewer than 64 blocks is likely to fail\n"); 164 | } 165 | 166 | if (outfile == "!") 167 | outfile = ""; 168 | 169 | timing(times, -1, -1, world); 170 | timing(times, TOT_TIME, -1, world); 171 | 172 | // initialize DIY and decompose domain 173 | diy::FileStorage storage(prefix); 174 | diy::Master master(world, 175 | num_threads, 176 | mem_blocks, 177 | &create_block, 178 | &destroy_block, 179 | &storage, 180 | &save_block, 181 | &load_block); 182 | // NB: AddAndRead for hacc assumes contiguous; don't switch to round robin 183 | diy::ContiguousAssigner assigner(world.size(), tot_blocks); 184 | 185 | AddAndRead create_and_read(master, 186 | tot_blocks, 187 | infile.c_str(), 188 | coordinates); 189 | 190 | // decompose 191 | std::vector my_gids; 192 | assigner.local_gids(rank, my_gids); 193 | diy::RegularDecomposer::BoolVector wrap; 194 | diy::RegularDecomposer::BoolVector share_face; 195 | diy::RegularDecomposer::CoordinateVector ghosts; 196 | if (wrap_) 197 | wrap.assign(3, true); 198 | diy::decompose(3, rank, domain, assigner, create_and_read, share_face, wrap, ghosts); 199 | 200 | // sort and distribute particles to all blocks 201 | if (kdtree) 202 | tess_kdtree_exchange(master, assigner, times, wrap_); 203 | else 204 | tess_exchange(master, assigner, times); 205 | if (rank == 0) 206 | printf("particles exchanged\n"); 207 | 208 | DuplicateCountMap count; 209 | master.foreach([&count](DBlock* b, const diy::Master::ProxyWithLink& cp) { deduplicate(b,cp,count); }); 210 | 211 | // debug purposes only: checks if the particles got into the right blocks 212 | // master.foreach(&verify_particles); 213 | 214 | size_t rounds = tess(master, quants, times); 215 | if (rank == 0) 216 | fprintf(stderr, "Done in %lu rounds\n", rounds); 217 | 218 | tess_save(master, outfile.c_str(), times); 219 | 220 | timing(times, -1, TOT_TIME, world); 221 | tess_stats(master, quants, times); 222 | 223 | // Storage + memory stats 224 | size_t max_storage = storage.max_size(), 225 | sum_max_storage; 226 | diy::mpi::reduce(world, max_storage, sum_max_storage, 0, std::plus()); 227 | 228 | size_t hwm = proc_status_value("VmHWM"), 229 | max_hwm; 230 | diy::mpi::reduce(world, hwm, max_hwm, 0, diy::mpi::maximum()); 231 | 232 | if (rank == 0) 233 | { 234 | fprintf(stderr, "Sum of max storage: %lu\n", sum_max_storage); 235 | fprintf(stderr, "Max high water mark: %lu\n", max_hwm); 236 | } 237 | 238 | return 0; 239 | } 240 | -------------------------------------------------------------------------------- /examples/pread-voronoi/unit-cube.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diatomic/tess2/7a3543652bc19a85a2ebe16a13b624570ad8591f/examples/pread-voronoi/unit-cube.h5 -------------------------------------------------------------------------------- /examples/tess-dense/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable (tess-dense main.cpp) 2 | target_link_libraries (tess-dense tess ${libraries}) 3 | 4 | install (TARGETS tess-dense 5 | DESTINATION ${CMAKE_INSTALL_PREFIX}/examples/tess-dense/ 6 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE 7 | GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) 8 | 9 | install (FILES TESS_DENSE_TEST 10 | DESTINATION ${CMAKE_INSTALL_PREFIX}/examples/tess-dense/ 11 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE 12 | GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) 13 | -------------------------------------------------------------------------------- /examples/tess-dense/TESS_DENSE_TEST: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ##PBS -A CSC033 4 | #PBS -A CI-CCR000086 5 | #PBS -N t 6 | #PBS -j oe 7 | #PBS -l walltime=0:10:00,size=12 8 | 9 | #---------------------------------------------------------------------------- 10 | # 11 | # mpi run script 12 | # 13 | # Tom Peterka 14 | # Argonne National Laboratory 15 | # 9700 S. Cass Ave. 16 | # Argonne, IL 60439 17 | # tpeterka@mcs.anl.gov 18 | # 19 | #---------------------------------------------------------------------------- 20 | ARCH=MAC_OSX 21 | #ARCH=LINUX 22 | #ARCH=BGQ 23 | #ARCH=FUSION 24 | #ARCH=XT 25 | #ARCH=XE 26 | 27 | # number of procs 28 | num_procs=8 29 | 30 | # procs per node 31 | ppn=8 # adjustable for BG/Q, allowed 1, 2, 4, 8, 16, 32, 64 32 | 33 | # number of BG/P nodes for vn mode 34 | num_nodes=$[$num_procs / $ppn] 35 | if [ $num_nodes -lt 1 ]; then 36 | num_nodes=1 37 | fi 38 | 39 | # executable 40 | exe=./tess-dense 41 | 42 | # total number of blocks in the domain 43 | tb=$[$num_procs * 1] 44 | #tb=4 45 | 46 | # algorithm (0=tess, 1 = cic) 47 | alg=0 48 | 49 | # data size x y z (always 3D) 50 | #dsize="3 2 2" 51 | #dsize="3 3 3" 52 | #dsize="4 4 4" 53 | #dsize="5 5 5" 54 | #dsize="6 6 6" 55 | #dsize="8 8 8" 56 | #dsize="10 10 10" 57 | #dsize="16 16 16" 58 | dsize="32 32 32" 59 | #dsize="64 64 64" 60 | #dsize="128 128 128" 61 | #dsize="256 256 256" 62 | 63 | jitter=2.0 64 | 65 | # volume range (-1.0: unused) 66 | minv=-1.0 67 | maxv=-1.0 68 | 69 | # wrapped neighbors 0 or 1 70 | wrap=0 71 | 72 | # walls 0 or 1 (wrap should be 0) 73 | walls=0 74 | 75 | # output file name 76 | outfile="dense.raw" 77 | 78 | # sample grid size (number of points) x y z 79 | #gsize="16 16 16" 80 | #gsize="32 32 32" 81 | #gsize="64 64 64" 82 | #gsize="128 128 128" 83 | #gsize="256 256 256" 84 | gsize="512 512 512" 85 | #gsize="1024 1024 1024" 86 | #gsize="2048 2048 2048" 87 | #gsize="4096 4096 4096" 88 | #gsize="8192 8192 8192" 89 | 90 | #projection plane 91 | #project=! 92 | project="0.0 0.0 1.0" #normal to plane, xy plane is the only one supported so far" 93 | 94 | # particle mass 95 | mass=1 96 | 97 | # given bounds 98 | ng=0 99 | gmin="-1.5 -1.5" 100 | gmax="1.5 1.5" 101 | 102 | #------ 103 | # 104 | # program arguments 105 | # 106 | args="$alg $tb $dsize $jitter $minv $maxv $wrap $walls $outfile $gsize $project $mass $ng $gmin $gmax" 107 | 108 | #------ 109 | # 110 | # run commands 111 | # 112 | 113 | if [ "$ARCH" = "MAC_OSX" ]; then 114 | 115 | mpiexec -l -n $num_procs $exe $args 116 | 117 | #dsymutil $exe ; mpiexec -l -n $num_procs xterm -e gdb -x gdb.run --args $exe $args 118 | 119 | #dsymutil $exe ; mpiexec -l -n $num_procs valgrind -q $exe $args 120 | 121 | #dsymutil $exe ; mpiexec -n $num_procs valgrind -q --leak-check=yes $exe $args 122 | 123 | fi 124 | 125 | if [ "$ARCH" = "LINUX" ]; then 126 | 127 | mpiexec -n $num_procs $exe $args 128 | 129 | #mpiexec -n $num_procs xterm -e gdb -x gdb.run --args $exe $args 130 | 131 | #mpiexec -n $num_procs valgrind -q $exe $args 132 | 133 | #mpiexec -n $num_procs valgrind -q --leak-check=yes $exe $args 134 | 135 | fi 136 | 137 | if [ "$ARCH" = "BGQ" ]; then 138 | 139 | qsub -n $num_nodes --mode c$ppn -A SDAV -t 60 $exe $args 140 | 141 | fi 142 | 143 | if [ "$ARCH" = "FUSION" ]; then 144 | 145 | mpiexec $exe $args 146 | 147 | fi 148 | 149 | if [ "$ARCH" = "XT" ]; then 150 | 151 | cd /tmp/work/$USER 152 | aprun -n $num_procs $exe $args 153 | 154 | fi 155 | 156 | if [ "$ARCH" = "XE" ]; then 157 | 158 | aprun -n $num_procs $exe $args 159 | 160 | fi 161 | -------------------------------------------------------------------------------- /examples/tess-dense/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // driver for tess_test coupled directly to dense 3 | // 4 | // 5 | #include "mpi.h" 6 | #include 7 | #include 8 | #include "tess/tess.h" 9 | #include "tess/tess.hpp" 10 | #include "tess/dense.hpp" 11 | 12 | void GetArgs(int argc, 13 | char **argv, 14 | alg& alg_type, 15 | int &tb, 16 | int *dsize, 17 | float *jitter, 18 | float *minvol, 19 | float *maxvol, 20 | int *wrap, 21 | int *walls, 22 | char *outfile, 23 | int *num_given_bounds, 24 | float *given_mins, 25 | float *given_maxs, 26 | bool &project, 27 | float *proj_plane, 28 | float &mass, 29 | int *glo_num_idx) 30 | { 31 | assert(argc >= 18); 32 | 33 | if (atoi(argv[1]) == 0) 34 | alg_type = DENSE_TESS; 35 | else 36 | alg_type = DENSE_CIC; 37 | tb = atoi(argv[2]); 38 | dsize[0] = atoi(argv[3]); 39 | dsize[1] = atoi(argv[4]); 40 | dsize[2] = atoi(argv[5]); 41 | *jitter = atof(argv[6]); 42 | *minvol = atof(argv[7]); 43 | *maxvol = atof(argv[8]); 44 | *wrap = atoi(argv[9]); 45 | *walls = atoi(argv[10]); 46 | if (argv[11][0] =='!') 47 | strcpy(outfile, ""); 48 | else 49 | strcpy(outfile, argv[11]); 50 | 51 | glo_num_idx[0] = atoi(argv[12]); 52 | glo_num_idx[1] = atoi(argv[13]); 53 | glo_num_idx[2] = atoi(argv[14]); 54 | if (!strcmp(argv[15], "!")) 55 | { 56 | project = false; 57 | mass = atof(argv[16]); 58 | *num_given_bounds = atoi(argv[17]); 59 | if (*num_given_bounds == 1) 60 | { 61 | given_mins[0] = atof(argv[18]); 62 | given_maxs[0] = atof(argv[19]); 63 | } 64 | else if (*num_given_bounds == 2) 65 | { 66 | given_mins[0] = atof(argv[18]); 67 | given_mins[1] = atof(argv[19]); 68 | given_maxs[0] = atof(argv[20]); 69 | given_maxs[1] = atof(argv[21]); 70 | } 71 | else if (*num_given_bounds == 3) 72 | { 73 | given_mins[0] = atof(argv[18]); 74 | given_mins[1] = atof(argv[19]); 75 | given_mins[2] = atof(argv[20]); 76 | given_maxs[0] = atof(argv[21]); 77 | given_maxs[1] = atof(argv[22]); 78 | given_maxs[2] = atof(argv[23]); 79 | } 80 | } 81 | if (strcmp(argv[15], "!")) 82 | { 83 | project = true; 84 | proj_plane[0] = atof(argv[15]); 85 | proj_plane[1] = atof(argv[16]); 86 | proj_plane[2] = atof(argv[17]); 87 | mass = atof(argv[18]); 88 | *num_given_bounds = atoi(argv[19]); 89 | if (*num_given_bounds == 1) 90 | { 91 | given_mins[0] = atof(argv[20]); 92 | given_maxs[0] = atof(argv[21]); 93 | } 94 | else if (*num_given_bounds == 2) 95 | { 96 | given_mins[0] = atof(argv[20]); 97 | given_mins[1] = atof(argv[21]); 98 | given_maxs[0] = atof(argv[22]); 99 | given_maxs[1] = atof(argv[23]); 100 | } 101 | else if (*num_given_bounds == 3) 102 | { 103 | given_mins[0] = atof(argv[20]); 104 | given_mins[1] = atof(argv[21]); 105 | given_mins[2] = atof(argv[22]); 106 | given_maxs[0] = atof(argv[23]); 107 | given_maxs[1] = atof(argv[24]); 108 | given_maxs[2] = atof(argv[25]); 109 | } 110 | } 111 | } 112 | 113 | int main(int argc, char *argv[]) 114 | { 115 | int tot_blocks; // total number of blocks in the domain 116 | int nblocks; // my local number of blocks 117 | int dsize[3]; // domain grid size 118 | float jitter; // max amount to randomly displace particles 119 | float minvol, maxvol; // volume range, -1.0 = unused 120 | int wrap; // wraparound neighbors flag 121 | int walls; // apply walls to simulation (wrap must be off) 122 | char outfile[256]; // output file name 123 | float eps = 0.0001; // epsilon for floating point values to be equal 124 | float data_mins[3], data_maxs[3]; // data global bounds 125 | MPI_Comm comm = MPI_COMM_WORLD; 126 | alg alg_type; // TESS or CIC 127 | 128 | // grid bounds 129 | int num_given_bounds; // number of given bounds 130 | float given_mins[3], given_maxs[3]; // the given bounds 131 | int glo_num_idx[3]; // global grid number of points 132 | float grid_phys_mins[3], grid_phys_maxs[3]; // grid physical bounds 133 | float grid_step_size[3]; // physical size of one grid space 134 | 135 | // 2D projection 136 | bool project; // whether to project to 2D 137 | float proj_plane[3]; // normal to projection plane 138 | 139 | // particle mass 140 | float mass; 141 | 142 | MPI_Init(&argc, &argv); 143 | 144 | // timing 145 | double tess_times[TESS_MAX_TIMES]; // tessllation timing 146 | double dense_times[DENSE_MAX_TIMES]; // density timing 147 | double tess_time; // overall tess time 148 | double dense_time; // overall dense time 149 | double overall_time; // overall timing (tess + dense) 150 | for (int i = 0; i < TESS_MAX_TIMES; i++) 151 | tess_times[i] = 0.0; 152 | for (int i = 0; i < DENSE_MAX_TIMES; i++) 153 | dense_times[i] = 0.0; 154 | MPI_Barrier(comm); 155 | overall_time = MPI_Wtime(); 156 | dense_times[TESS_TIME] = MPI_Wtime(); 157 | 158 | GetArgs(argc, argv, alg_type, tot_blocks, dsize, &jitter, &minvol, &maxvol, &wrap, 159 | &walls, outfile, &num_given_bounds, given_mins, given_maxs, project, 160 | proj_plane, mass, glo_num_idx); 161 | 162 | // data extents 163 | typedef diy::ContinuousBounds Bounds; 164 | Bounds domain { 3 }; 165 | for(int i = 0; i < 3; i++) 166 | { 167 | domain.min[i] = 0; 168 | domain.max[i] = dsize[i] - 1.0; 169 | } 170 | 171 | // init diy 172 | int num_threads = 1; 173 | int mem_blocks = -1; 174 | diy::mpi::communicator world(comm); 175 | diy::FileStorage storage("./DIY.XXXXXX"); 176 | diy::Master master(world, 177 | num_threads, 178 | mem_blocks, 179 | &create_block, 180 | &destroy_block, 181 | &storage, 182 | &save_block, 183 | &load_block); 184 | diy::RoundRobinAssigner assigner(world.size(), tot_blocks); 185 | AddAndGenerate create(master, jitter); 186 | 187 | // decompose 188 | std::vector my_gids; 189 | assigner.local_gids(world.rank(), my_gids); 190 | diy::RegularDecomposer::BoolVector wraps; 191 | diy::RegularDecomposer::BoolVector share_face; 192 | diy::RegularDecomposer::CoordinateVector ghosts; 193 | if (wrap) 194 | wraps.assign(3, true); 195 | diy::decompose(3, world.rank(), domain, assigner, create, share_face, wraps, ghosts); 196 | nblocks = master.size(); 197 | 198 | // tessellate 199 | MPI_Barrier(comm); 200 | tess_time = MPI_Wtime(); 201 | quants_t quants; 202 | timing(tess_times, -1, -1, world); 203 | timing(tess_times, TOT_TIME, -1, world); 204 | tess(master, quants, tess_times); 205 | timing(tess_times, -1, TOT_TIME, world); 206 | tess_stats(master, quants, tess_times); 207 | 208 | MPI_Barrier(comm); 209 | tess_time = MPI_Wtime() - tess_time; 210 | dense_times[TESS_TIME] = MPI_Wtime() - dense_times[TESS_TIME]; 211 | dense_time = MPI_Wtime(); 212 | dense_times[TOTAL_TIME] = MPI_Wtime(); 213 | dense_times[COMP_TIME] = MPI_Wtime(); 214 | 215 | // compute the density 216 | dense(alg_type, num_given_bounds, given_mins, given_maxs, project, proj_plane, mass, 217 | data_mins, data_maxs, grid_phys_mins, grid_phys_maxs, grid_step_size, eps, glo_num_idx, 218 | master); 219 | MPI_Barrier(comm); 220 | dense_times[COMP_TIME] = MPI_Wtime() - dense_times[COMP_TIME]; 221 | dense_times[OUTPUT_TIME] = MPI_Wtime(); 222 | 223 | int maxblocks; // max blocks in any process 224 | MPI_Allreduce(&nblocks, &maxblocks, 1, MPI_INT, MPI_MAX, comm); 225 | 226 | // write file 227 | // NB: all blocks need to be in memory; WriteGrid is not diy2'ed yet 228 | MPI_Barrier(comm); 229 | dense_times[OUTPUT_TIME] = MPI_Wtime(); 230 | WriteGrid(maxblocks, tot_blocks, outfile, project, glo_num_idx, eps, data_mins, data_maxs, 231 | num_given_bounds, given_mins, given_maxs, master, assigner); 232 | MPI_Barrier(comm); 233 | dense_times[OUTPUT_TIME] = MPI_Wtime() - dense_times[OUTPUT_TIME]; 234 | dense_times[TOTAL_TIME] = MPI_Wtime() - dense_times[TOTAL_TIME]; 235 | dense_time = MPI_Wtime() - dense_time; 236 | overall_time = MPI_Wtime() - overall_time; 237 | 238 | dense_stats(dense_times, master, grid_step_size, grid_phys_mins, glo_num_idx); 239 | 240 | // overall timing 241 | if (world.rank() == 0) 242 | fprintf(stderr, "Overall time = %.3lf s = %.3lf s tess + %.3lf s dense\n", 243 | overall_time, tess_time, dense_time); 244 | 245 | MPI_Finalize(); 246 | 247 | return 0; 248 | } 249 | 250 | -------------------------------------------------------------------------------- /examples/tess/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable (delaunay main.cpp) 2 | target_link_libraries (delaunay tess ${libraries}) 3 | 4 | install (TARGETS delaunay 5 | DESTINATION ${CMAKE_INSTALL_PREFIX}/examples/tess/ 6 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE 7 | GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) 8 | 9 | install (FILES TESS_TEST 10 | DESTINATION ${CMAKE_INSTALL_PREFIX}/examples/tess/ 11 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE 12 | GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) 13 | -------------------------------------------------------------------------------- /examples/tess/TESS_TEST: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ##PBS -A CSC033 4 | #PBS -A CI-CCR000086 5 | #PBS -N t 6 | #PBS -j oe 7 | #PBS -l walltime=0:10:00,size=12 8 | 9 | #---------------------------------------------------------------------------- 10 | # 11 | # mpi run script 12 | # 13 | # Tom Peterka 14 | # Argonne National Laboratory 15 | # 9700 S. Cass Ave. 16 | # Argonne, IL 60439 17 | # tpeterka@mcs.anl.gov 18 | # 19 | #---------------------------------------------------------------------------- 20 | ARCH=MAC_OSX 21 | #ARCH=LINUX 22 | #ARCH=BGQ 23 | #ARCH=FUSION 24 | #ARCH=XT 25 | #ARCH=XE 26 | 27 | # number of procs 28 | num_procs=4 29 | 30 | # procs per node 31 | ppn=8 # adjustable for BG/Q, allowed 1, 2, 4, 8, 16, 32, 64 32 | 33 | # number of BG/P nodes for vn mode 34 | num_nodes=$[$num_procs / $ppn] 35 | if [ $num_nodes -lt 1 ]; then 36 | num_nodes=1 37 | fi 38 | 39 | # executable 40 | exe=./delaunay 41 | 42 | # total number of blocks in the domain 43 | tb=$[$num_procs * 1] 44 | #tb=8 45 | 46 | # max number of blocks in memory (-1 for no limit) 47 | mb=-1 48 | 49 | # data size x y z (always 3D) 50 | #dsize="3 2 2" 51 | #dsize="3 3 3" 52 | #dsize="4 4 4" 53 | #dsize="5 5 5" 54 | #dsize="6 6 6" 55 | #dsize="7 7 7" 56 | #dsize="8 8 8" 57 | #dsize="9 9 9" 58 | #dsize="10 10 10" 59 | dsize="11 11 11" 60 | #dsize="13 13 13" 61 | #dsize="16 16 16" 62 | #dsize="32 32 32" 63 | #dsize="33 33 33" 64 | #dsize="64 64 64" 65 | #dsize="128 128 128" 66 | #dsize="129 129 129" 67 | #dsize="256 256 256" 68 | 69 | jitter=2.0 70 | 71 | # volume range (-1.0: unused) 72 | minv=-1.0 73 | maxv=-1.0 74 | 75 | # wrapped neighbors 0 or 1 76 | wrap=0 77 | 78 | # walls 0 or 1 (wrap should be 0) 79 | walls=0 80 | 81 | # output file name, "!" to disable output 82 | #outfile="vor.out" 83 | outfile="del.out" 84 | #outfile="!" 85 | 86 | #------ 87 | # 88 | # program arguments 89 | # 90 | args="$tb $mb $dsize $jitter $minv $maxv $wrap $walls $outfile" 91 | 92 | #------ 93 | # 94 | # run commands 95 | # 96 | 97 | if [ "$ARCH" = "MAC_OSX" ]; then 98 | 99 | mpiexec -l -n $num_procs $exe $args 100 | 101 | #dsymutil $exe ; mpiexec -l -n $num_procs xterm -e gdb -x gdb.run --args $exe $args 102 | 103 | #dsymutil $exe ; mpiexec -l -n $num_procs valgrind -q $exe $args 104 | 105 | #dsymutil $exe ; mpiexec -l -n $num_procs valgrind -q --tool=massif $exe $args 106 | 107 | #dsymutil $exe ; mpiexec -n $num_procs valgrind -q --leak-check=yes $exe $args 108 | 109 | fi 110 | 111 | if [ "$ARCH" = "LINUX" ]; then 112 | 113 | #mpiexec -n $num_procs $exe $args 114 | 115 | #mpiexec -n $num_procs xterm -e gdb -x gdb.run --args $exe $args 116 | 117 | #mpiexec -n $num_procs valgrind -q $exe $args 118 | 119 | mpiexec -n $num_procs valgrind -q --leak-check=yes $exe $args 120 | 121 | fi 122 | 123 | if [ "$ARCH" = "BGQ" ]; then 124 | 125 | qsub -n $num_nodes --mode c$ppn -A SDAV -t 60 $exe $args 126 | 127 | fi 128 | 129 | if [ "$ARCH" = "FUSION" ]; then 130 | 131 | mpiexec $exe $args 132 | 133 | fi 134 | 135 | if [ "$ARCH" = "XT" ]; then 136 | 137 | cd /tmp/work/$USER 138 | aprun -n $num_procs $exe $args 139 | 140 | fi 141 | 142 | if [ "$ARCH" = "XE" ]; then 143 | 144 | aprun -n $num_procs $exe $args 145 | 146 | fi 147 | -------------------------------------------------------------------------------- /examples/tess/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mpi.h" 2 | #include 3 | #include 4 | #include "tess/tess.h" 5 | #include "tess/tess.hpp" 6 | 7 | void GetArgs(int argc, 8 | char **argv, 9 | int &tb, 10 | int &mb, 11 | int *dsize, 12 | float *jitter, 13 | float *minvol, 14 | float *maxvol, 15 | int *wrap, 16 | int *walls, 17 | char *outfile) 18 | { 19 | assert(argc >= 11); 20 | 21 | tb = atoi(argv[1]); 22 | mb = atoi(argv[2]); 23 | dsize[0] = atoi(argv[3]); 24 | dsize[1] = atoi(argv[4]); 25 | dsize[2] = atoi(argv[5]); 26 | *jitter = atof(argv[6]); 27 | *minvol = atof(argv[7]); 28 | *maxvol = atof(argv[8]); 29 | *wrap = atoi(argv[9]); 30 | *walls = atoi(argv[10]); 31 | if (argv[11][0] =='!') 32 | strcpy(outfile, ""); 33 | else 34 | strcpy(outfile, argv[11]); 35 | } 36 | 37 | int main(int argc, char *argv[]) 38 | { 39 | int tot_blocks; // total number of blocks in the domain 40 | int mem_blocks; // max blocks in memory 41 | int dsize[3]; // domain grid size 42 | float jitter; // max amount to randomly displace particles 43 | float minvol, maxvol; // volume range, -1.0 = unused 44 | double times[TESS_MAX_TIMES]; // timing 45 | int wrap; // wraparound neighbors flag 46 | int walls; // apply walls to simulation (wrap must be off) 47 | char outfile[256]; // output file name 48 | int num_threads = 1; // threads diy can use 49 | 50 | // init MPI 51 | MPI_Comm comm = MPI_COMM_WORLD; 52 | MPI_Init(&argc, &argv); 53 | 54 | GetArgs(argc, argv, tot_blocks, mem_blocks, dsize, &jitter, &minvol, &maxvol, &wrap, &walls, 55 | outfile); 56 | 57 | // data extents 58 | typedef diy::ContinuousBounds Bounds; 59 | Bounds domain { 3 }; 60 | for(int i = 0; i < 3; i++) 61 | { 62 | domain.min[i] = 0; 63 | domain.max[i] = dsize[i] - 1.0; 64 | } 65 | 66 | // init diy 67 | diy::mpi::communicator world(comm); 68 | diy::FileStorage storage("./DIY.XXXXXX"); 69 | diy::Master master(world, 70 | num_threads, 71 | mem_blocks, 72 | &create_block, 73 | &destroy_block, 74 | &storage, 75 | &save_block, 76 | &load_block); 77 | diy::RoundRobinAssigner assigner(world.size(), tot_blocks); 78 | AddAndGenerate create(master, jitter); 79 | 80 | // decompose 81 | std::vector my_gids; 82 | assigner.local_gids(world.rank(), my_gids); 83 | diy::RegularDecomposer::BoolVector wraps; 84 | diy::RegularDecomposer::BoolVector share_face; 85 | diy::RegularDecomposer::CoordinateVector ghosts; 86 | if (wrap) 87 | wraps.assign(3, true); 88 | diy::decompose(3, world.rank(), domain, assigner, create, share_face, wraps, ghosts); 89 | 90 | // tessellate 91 | quants_t quants; 92 | timing(times, -1, -1, world); 93 | timing(times, TOT_TIME, -1, world); 94 | tess(master, quants, times); 95 | 96 | // output 97 | tess_save(master, outfile, times); 98 | timing(times, -1, TOT_TIME, world); 99 | tess_stats(master, quants, times); 100 | 101 | MPI_Finalize(); 102 | 103 | return 0; 104 | } 105 | 106 | -------------------------------------------------------------------------------- /include/tess/delaunay.h: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------- 2 | * 3 | * delaunay block C version 4 | * 5 | * Tom Peterka 6 | * Argonne National Laboratory 7 | * 9700 S. Cass Ave. 8 | * Argonne, IL 60439 9 | * tpeterka@mcs.anl.gov 10 | * 11 | --------------------------------------------------------------------------*/ 12 | #ifndef _DELAUNAY_H 13 | #define _DELAUNAY_H 14 | 15 | #include "tet.h" 16 | #include 17 | 18 | #define MAX_HIST_BINS 256 /* maximum number of bins in cell volume histogram */ 19 | #define MAX_NEIGHBORS 27 /* maximum number of neighbor blocks */ 20 | 21 | /* remote particle */ 22 | struct point_t { 23 | float x, y, z; /* coordinates */ 24 | int gid; /* owner block global id */ 25 | int lid; /* point id within its block */ 26 | }; 27 | 28 | /* CLP - struct walls - using general equation of plane 29 | per http://mathworld.wolfram.com/Plane.html */ 30 | struct wall_t { 31 | float a; 32 | float b; 33 | float c; 34 | float d; 35 | }; 36 | 37 | /* delaunay tessellation for one DIY block */ 38 | struct dblock_t { 39 | 40 | int gid; /* global block id */ 41 | void* Dt; /* native delaunay data structure */ 42 | 43 | /* input particles */ 44 | int num_orig_particles; /* number of original particles in this block 45 | before any neighbor exchange */ 46 | int num_particles; /* current number of particles in this block after any 47 | neighbor exchange; original particles appear first 48 | followed by received particles */ 49 | float* particles; /* all particles, original plus those received from neighbors */ 50 | 51 | /* tets */ 52 | int num_tets; /* number of delaunay tetrahedra */ 53 | struct tet_t* tets; /* delaunay tets */ 54 | int* rem_gids; /* owners of remote particles */ 55 | int* rem_lids; /* "local ids" of the remote particles */ 56 | int* vert_to_tet; /* a tet that contains the vertex */ 57 | 58 | /* estimated density field */ 59 | float* density; /* density field */ 60 | int num_grid_pts; /* total number of density grid points */ 61 | 62 | int complete; 63 | }; 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /include/tess/delaunay.hpp: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // 3 | // delaunay block C++ version 4 | // 5 | // Tom Peterka 6 | // Argonne National Laboratory 7 | // 9700 S. Cass Ave. 8 | // Argonne, IL 60439 9 | // tpeterka@mcs.anl.gov 10 | // 11 | // --------------------------------------------------------------------------- 12 | 13 | #ifndef _DELAUNAY_HPP 14 | #define _DELAUNAY_HPP 15 | 16 | #include 17 | 18 | struct DBlock : dblock_t 19 | { 20 | diy::ContinuousBounds bounds { 3 }; // local block extents 21 | diy::ContinuousBounds data_bounds { 3 }; // global data extents 22 | diy::ContinuousBounds box { 3 }; // box in current round of point redistribution 23 | }; 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/tess/dense.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | // 3 | // density estimator header file 4 | // 5 | // Tom Peterka 6 | // Argonne National Laboratory 7 | // 9700 S. Cass Ave. 8 | // Argonne, IL 60439 9 | // tpeterka@mcs.anl.gov 10 | // 11 | //-------------------------------------------------------------------------- 12 | #ifndef _DENSE_H 13 | #define _DENSE_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "delaunay.h" 24 | #include "mpi.h" 25 | #include "tess/tet.hpp" 26 | #include "tess/tet-neighbors.h" 27 | #include "tess/tess.h" 28 | #include "tess/tess.hpp" 29 | 30 | using namespace std; 31 | 32 | // estimator algorithm 33 | enum alg 34 | { 35 | DENSE_TESS, 36 | DENSE_CIC, 37 | DENSE_NUM_ALGS, 38 | }; 39 | 40 | // grid point 41 | struct grid_pt_t 42 | { 43 | int idx[3]; // global grid point index 44 | double mass; // mass 45 | }; 46 | 47 | // auxiliary arguments for foreach block functions 48 | struct args_t 49 | { 50 | alg alg_type; 51 | bool project; 52 | float proj_plane[3]; 53 | float mass; 54 | float data_mins[3]; 55 | float data_maxs[3]; 56 | float grid_phys_mins[3]; 57 | float grid_step_size[3]; 58 | float eps; 59 | int glo_num_idx[3]; 60 | float div; 61 | }; 62 | 63 | // timing 64 | enum 65 | { 66 | INPUT_TIME, 67 | COMP_TIME, 68 | OUTPUT_TIME, 69 | TOTAL_TIME, 70 | TESS_TIME, 71 | DENSE_MAX_TIMES 72 | }; 73 | 74 | // function prototypes 75 | void dense(alg alg_type, 76 | int num_given_bounds, 77 | float *given_mins, 78 | float *given_maxs, 79 | bool project, 80 | float *proj_plane, 81 | float mass, 82 | float *data_mins, 83 | float *data_maxs, 84 | float *grid_phys_mins, 85 | float *grid_phys_maxs, 86 | float *grid_step_size, 87 | float eps, 88 | int *glo_num_idx, 89 | diy::Master& master); 90 | void init_dense(DBlock* b, 91 | const diy::Master::ProxyWithLink& cp, 92 | args_t* a); 93 | void est_dense(DBlock* b, 94 | const diy::Master::ProxyWithLink& cp, 95 | args_t* a); 96 | void recvd_pts(DBlock* b, 97 | const diy::Master::ProxyWithLink& cp, 98 | args_t* a); 99 | void BlockGridParams(DBlock *dblock, 100 | int *block_min_idx, 101 | int *block_max_idx, 102 | int *block_num_idx, 103 | float *grid_phys_mins, 104 | float *grid_step_size, 105 | float eps, 106 | float *data_mins, 107 | float *data_maxs, 108 | int *glo_num_idx); 109 | void IterateCells(DBlock *dblock, 110 | int *block_min_idx, 111 | int *block_num_idx, 112 | bool project, 113 | float *proj_plane, 114 | float *grid_phys_mins, 115 | float *grid_step_size, 116 | float *data_mins, 117 | float *data_maxs, 118 | float eps, 119 | float mass, 120 | const diy::Master::ProxyWithLink& cp); 121 | #ifndef TESS_NO_OPENMP 122 | void IterateCellsOMP(DBlock *dblock, 123 | int *block_min_idx, 124 | int *block_num_idx, 125 | bool project, 126 | float *proj_plane, 127 | float *grid_phys_mins, 128 | float *grid_step_size, 129 | float *data_mins, 130 | float *data_maxs, 131 | float eps, 132 | float mass, 133 | const diy::Master::ProxyWithLink& cp); 134 | #endif 135 | void IterateCellsCic(DBlock *dblock, 136 | int *block_min_idx, 137 | int *block_num_idx, 138 | bool project, 139 | float *proj_plane, 140 | float *grid_phys_mins, 141 | float *grid_step_size, 142 | float *data_maxs, 143 | float eps, 144 | float mass, 145 | const diy::Master::ProxyWithLink& cp); 146 | void CellBounds(DBlock *dblock, 147 | int cell, 148 | float *cell_min, 149 | float *cell_max, 150 | vector &normals, 151 | vector > &face_verts); 152 | int CellGridPts(float *cell_mins, 153 | float *cell_maxs, 154 | grid_pt_t* &grid_pts, 155 | int * &border, 156 | int& alloc_grid_pts, 157 | vector &normals, 158 | vector > &face_verts, 159 | float *data_mins, 160 | float *data_maxs, 161 | float *grid_phys_mins, 162 | float *grid_step_size, 163 | float mass, 164 | float eps, 165 | float* site); 166 | int CellInteriorGridPts(int *cell_grid_pts, 167 | int *cell_min_grid_idx, 168 | float *cell_min_grid_pos, 169 | grid_pt_t *grid_pts, 170 | int *border, 171 | vector &normals, 172 | vector > &face_verts, 173 | float *grid_step_size, 174 | float eps, 175 | float mass); 176 | bool PtInCell(float *pt, 177 | vector &normals, 178 | vector > &face_verts, 179 | float eps); 180 | void NewellNormal(float *verts, 181 | int num_verts, 182 | float *normal); 183 | void Global2LocalIdx(int *global_idx, 184 | int *local_idx, 185 | int *block_min_idx); 186 | void GridStepParams(int num_given_bounds, 187 | float *given_mins, 188 | float *given_maxs, 189 | float *data_mins, 190 | float *data_maxs, 191 | float *grid_phys_mins, 192 | float *grid_phys_maxs, 193 | float *grid_step_size, 194 | int *glo_num_idx); 195 | void WriteGrid(int mblocks, 196 | int tblocks, 197 | char *outfile, 198 | bool project, 199 | int *glo_num_idx, 200 | float eps, 201 | float *data_mins, 202 | float *data_maxs, 203 | int num_given_bounds, 204 | float *given_mins, 205 | float *given_maxs, 206 | diy::Master& master, 207 | diy::Assigner& assigner); 208 | void ProjectGrid(int gnblocks, 209 | int *glo_num_idx, 210 | float eps, 211 | float *data_mins, 212 | float *data_maxs, 213 | float *grid_phys_mins, 214 | float *grid_step_size, 215 | diy::Master& master, 216 | diy::Assigner& assigner); 217 | void handle_error(int errcode, 218 | char *str, 219 | MPI_Comm comm); 220 | int index(int *block_grid_idx, 221 | int *block_num_idx, 222 | bool project, 223 | float *proj_plane); 224 | void idx2phys(int *grid_idx, 225 | float *pos, 226 | float *grid_step_size, 227 | float *grid_phys_mins); 228 | void phys2idx(float* pos, 229 | int *grid_idx, 230 | float *grid_step_size, 231 | float *grid_phys_mins); 232 | void DataBounds(float *data_mins, 233 | float *data_maxs, 234 | diy::Master& master); 235 | void dense_stats(double *times, 236 | diy::Master& master, 237 | float *grid_step_size, 238 | float *grid_phys_mins, 239 | int *glo_num_idx); 240 | void DistributeScalarCIC(float *pt, 241 | float scalar, 242 | vector &grid_idxs, 243 | vector &grid_scalars, 244 | float *grid_step_size, 245 | float *grid_phys_mins, 246 | float eps); 247 | 248 | #endif 249 | -------------------------------------------------------------------------------- /include/tess/swap.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | // 3 | // swap utilities 4 | // 5 | // Tom Peterka 6 | // Argonne National Laboratory 7 | // 9700 S. Cass Ave. 8 | // Argonne, IL 60439 9 | // tpeterka@mcs.anl.gov 10 | // 11 | //--------------------------------------------------------------------------- 12 | 13 | #ifndef __SWAP 14 | #define __SWAP 15 | 16 | void Swap(char *n, int nitems, int item_size); 17 | void Swap8(char *n); 18 | void Swap4(char *n); 19 | void Swap2(char *n); 20 | 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /include/tess/tess-cgal.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "tet.h" 8 | #include 9 | 10 | typedef CGAL::Exact_predicates_inexact_constructions_kernel K; 11 | 12 | typedef CGAL::Triangulation_vertex_base_with_info_3 Vb; 13 | typedef CGAL::Triangulation_cell_base_with_info_3 Cb; 14 | typedef CGAL::Triangulation_data_structure_3 Tds; 15 | //Use the Fast_location tag. Default or Compact_location works too. 16 | //typedef CGAL::Delaunay_triangulation_3 Delaunay3D; 17 | typedef CGAL::Delaunay_triangulation_3 Delaunay3D; 18 | //typedef CGAL::Delaunay_triangulation_3 Delaunay3D; 19 | typedef Delaunay3D::Point Point; 20 | typedef Delaunay3D::Vertex_handle Vertex_handle; 21 | typedef Delaunay3D::Cell_handle Cell_handle; 22 | typedef K::FT RealValue; 23 | 24 | typedef Delaunay3D::Finite_vertices_iterator Vertex_iterator; 25 | typedef Delaunay3D::Finite_edges_iterator Edge_iterator; 26 | typedef Delaunay3D::Finite_facets_iterator Facet_iterator; 27 | typedef Delaunay3D::Finite_cells_iterator Cell_iterator; 28 | typedef Delaunay3D::All_cells_iterator All_cell_iterator; 29 | typedef Delaunay3D::Cell_circulator Cell_circulator; 30 | typedef Delaunay3D::Facet_circulator Facet_circulator; 31 | 32 | 33 | int gen_voronoi_output(Delaunay3D &Dt, struct vblock_t *vblock, 34 | int num_particles); 35 | int gen_delaunay_output(Delaunay3D &Dt, int **tet_verts); 36 | void construct_delaunay(Delaunay3D &Dt, int num_particles, float *particles); 37 | 38 | void gen_tets(Delaunay3D& Dt, tet_t* tets); 39 | 40 | 41 | #include 42 | 43 | namespace diy 44 | { 45 | template<> 46 | struct Serialization 47 | { 48 | static void save(BinaryBuffer& bb, const Delaunay3D& Dt) 49 | { 50 | std::ostringstream out; 51 | CGAL::set_mode(out, CGAL::IO::BINARY); 52 | out << Dt; 53 | 54 | size_t s = out.str().size(); 55 | diy::save(bb, s); 56 | bb.save_binary(out.str().c_str(), out.str().size()); 57 | } 58 | 59 | static void load(BinaryBuffer& bb, Delaunay3D& Dt) 60 | { 61 | size_t s; 62 | diy::load(bb, s); 63 | 64 | // This is not pretty, but portable. 65 | // Double copying is annoying. Perhaps, it's worth implementing an 66 | // iostream wrapper around BinaryBuffer. 67 | std::vector in_vec(s); 68 | bb.load_binary(&in_vec[0], s); 69 | 70 | std::string in_str(in_vec.begin(), in_vec.end()); 71 | std::istringstream in(in_str); 72 | CGAL::set_mode(in, CGAL::IO::BINARY); 73 | in >> Dt; 74 | 75 | // NB: this shouldn't be necessary, but CGAL doesn't save vertex info, 76 | // so we reset it here. This works because we don't need vertex ids to 77 | // be consistent across iterations. 78 | unsigned idx = 0; 79 | for(Vertex_iterator vit = Dt.finite_vertices_begin(); vit != Dt.finite_vertices_end(); ++vit) 80 | vit->info() = idx++; 81 | } 82 | }; 83 | } 84 | -------------------------------------------------------------------------------- /include/tess/tess-qhull.h: -------------------------------------------------------------------------------- 1 | #define qh_QHimport 2 | #include "qhull_a.h" 3 | #include "tet.h" 4 | #include "delaunay.h" 5 | 6 | void gen_delaunay_output(facetT *facetlist, struct dblock_t *dblock); 7 | void reorder_neighbors(struct dblock_t *dblock); 8 | 9 | -------------------------------------------------------------------------------- /include/tess/tess.h: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------- 2 | * 3 | * parallel tesselation 4 | * 5 | * Tom Peterka 6 | * Argonne National Laboratory 7 | * 9700 S. Cass Ave. 8 | * Argonne, IL 60439 9 | * tpeterka@mcs.anl.gov 10 | * 11 | --------------------------------------------------------------------------*/ 12 | #ifndef _TESS_H 13 | #define _TESS_H 14 | 15 | #include "mpi.h" 16 | #include 17 | #include "delaunay.h" 18 | #include "swap.hpp" 19 | 20 | /* timing and quantity stats */ 21 | enum 22 | { 23 | TOT_TIME, 24 | EXCH_TIME, 25 | DEL_TIME, 26 | OUT_TIME, 27 | TESS_MAX_TIMES 28 | }; 29 | 30 | enum 31 | { 32 | NUM_ORIG_PTS, 33 | NUM_FINAL_PTS, 34 | NUM_TETS, 35 | NUM_LOC_BLOCKS, 36 | MAX_QUANTS 37 | }; 38 | 39 | /* private */ 40 | 41 | #ifdef __cplusplus 42 | extern "C" 43 | #endif 44 | void local_cells(struct dblock_t *b); 45 | 46 | #ifdef __cplusplus 47 | extern "C" 48 | #endif 49 | void* init_delaunay_data_structures(int nblocks); 50 | #ifdef __cplusplus 51 | extern "C" 52 | #endif 53 | void init_delaunay_data_structure(struct dblock_t* b); 54 | #ifdef __cplusplus 55 | extern "C" 56 | #endif 57 | void clean_delaunay_data_structures(void* ds); 58 | #ifdef __cplusplus 59 | extern "C" 60 | #endif 61 | void clean_delaunay_data_structure(struct dblock_t* b); 62 | 63 | #ifdef __cplusplus 64 | extern "C" 65 | #endif 66 | void fill_vert_to_tet(struct dblock_t *dblock); 67 | 68 | #ifdef __cplusplus 69 | extern "C" 70 | #endif 71 | void get_mem(int breakpoint, MPI_Comm comm); 72 | 73 | void print_block(struct dblock_t *dblock, int gid); 74 | void print_particles(float *particles, int num_particles, int gid); 75 | void write_particles(int nblocks, float **particles, int *num_particles, char *outfile); 76 | void handle_error(int errcode, MPI_Comm comm, char *str); 77 | void create_walls(int *num_walls, struct wall_t **walls); 78 | void destroy_walls(int num_walls, struct wall_t *walls); 79 | int test_outside(const float * pt, const struct wall_t *wall); 80 | void generate_mirror(float *rpt, const float *pt, const struct wall_t *wall); 81 | void add_mirror_particles(int nblocks, float **mirror_particles, int *num_mirror_particles, 82 | float **particles, int *num_particles, int *num_orig_particles, 83 | int **gids, int **nids, unsigned char **dirs); 84 | void timing(double* times, int start, int stop, MPI_Comm comm); 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /include/tess/tess.hpp: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // 3 | // tess2 header 4 | // 5 | // Tom Peterka 6 | // Argonne National Laboratory 7 | // 9700 S. Cass Ave. 8 | // Argonne, IL 60439 9 | // tpeterka@mcs.anl.gov 10 | // 11 | // -------------------------------------------------------------------------- 12 | #ifndef _TESS_HPP 13 | #define _TESS_HPP 14 | 15 | #ifdef BGQ 16 | #include 17 | #else 18 | #include 19 | #endif 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #ifdef TESS_USE_CGAL 34 | #include "tess-cgal.h" 35 | #endif 36 | 37 | #include "tess.h" 38 | #include "delaunay.hpp" 39 | 40 | using namespace std; 41 | 42 | // quantity stats per process 43 | struct quants_t 44 | { 45 | int min_quants[MAX_QUANTS]; // min of quantities 46 | int max_quants[MAX_QUANTS]; // max of quantities 47 | int sum_quants[MAX_QUANTS]; // sum of quantities 48 | }; 49 | 50 | typedef diy::RegularContinuousLink RCLink; 51 | typedef vector LinkVector; 52 | typedef vector LastNeighbors; 53 | 54 | size_t tess(diy::Master& master); 55 | size_t tess(diy::Master& master, 56 | quants_t& quants, 57 | double* times); 58 | void tess_exchange(diy::Master& master, 59 | const diy::Assigner& assigner); 60 | void tess_exchange(diy::Master& master, 61 | const diy::Assigner& assigner, 62 | double* times); 63 | void tess_kdtree_exchange(diy::Master& master, 64 | const diy::Assigner& assigner, 65 | bool wrap, 66 | bool sampling = false); 67 | void tess_kdtree_exchange(diy::Master& master, 68 | const diy::Assigner& assigner, 69 | double* times, 70 | bool wrap, 71 | bool sampling = false); 72 | void tess_save(diy::Master& master, 73 | const char* outfile, 74 | const diy::MemoryBuffer& extra = diy::MemoryBuffer()); 75 | void tess_save(diy::Master& master, 76 | const char* outfile, 77 | double* times, 78 | const diy::MemoryBuffer& extra = diy::MemoryBuffer()); 79 | void tess_load(diy::Master& master, 80 | diy::StaticAssigner& assigner, 81 | const char* infile); 82 | void tess_load(diy::Master& master, 83 | diy::StaticAssigner& assigner, 84 | const char* infile, 85 | diy::MemoryBuffer& extra); 86 | void tess_stats(diy::Master& master, 87 | quants_t& quants, 88 | double* times); 89 | void* create_block(); 90 | void destroy_block(void* b); 91 | void save_block(const void* b, 92 | diy::BinaryBuffer& bb); 93 | void load_block(void* b, 94 | diy::BinaryBuffer& bb); 95 | void save_block_light(const void* b, 96 | diy::BinaryBuffer& bb); 97 | void load_block_light(void* b, 98 | diy::BinaryBuffer& bb); 99 | void create(int gid, 100 | const diy::ContinuousBounds& core, 101 | const diy::ContinuousBounds& bounds, 102 | const diy::Link& link); 103 | int gen_particles(DBlock* b, 104 | float jitter); 105 | void delaunay(DBlock* b, 106 | const diy::Master::ProxyWithLink& cp, 107 | const LinkVector& links, 108 | LastNeighbors& neighbors, 109 | bool first); 110 | void finalize(DBlock* b, 111 | const diy::Master::ProxyWithLink& cp, 112 | quants_t& quants); 113 | void neighbor_particles(DBlock* b, 114 | const diy::Master::ProxyWithLink& cp); 115 | size_t incomplete_cells(struct DBlock *dblock, 116 | const diy::Master::ProxyWithLink& cp, 117 | size_t last_neighbor); 118 | void reset_block(struct DBlock* &dblock); 119 | void fill_vert_to_tet(DBlock* dblock); 120 | void fill_vert_to_tet(dblock_t* dblock); 121 | void wall_particles(struct DBlock *dblock); 122 | void sample_particles(float *particles, 123 | int &num_particles, 124 | int sample_rate); 125 | void wrap_pt(point_t& rp, 126 | diy::Direction wrap_dir, 127 | diy::ContinuousBounds& domain); 128 | int compare(const void *a, 129 | const void *b); 130 | 131 | // add block to a master 132 | // user should not instantiate AddBlock; use AddAndGenerafet or AddEmpty (see below) 133 | struct AddBlock 134 | { 135 | AddBlock(diy::Master& master_): 136 | master(master_) {} 137 | 138 | DBlock* operator()(int gid, 139 | const diy::ContinuousBounds& core, 140 | const diy::ContinuousBounds& bounds, 141 | const diy::ContinuousBounds& domain, 142 | const RCLink& link) const 143 | { 144 | DBlock* b = static_cast(create_block()); 145 | RCLink* l = new RCLink(link); 146 | diy::Master& m = const_cast(master); 147 | 148 | m.add(gid, b, l); 149 | 150 | // init block fields 151 | b->gid = gid; 152 | b->bounds = core; 153 | b->data_bounds = domain; 154 | b->num_orig_particles = 0; 155 | b->num_particles = 0; 156 | b->particles = NULL; 157 | b->num_tets = 0; 158 | b->tets = NULL; 159 | b->rem_gids = NULL; 160 | b->rem_lids = NULL; 161 | b->vert_to_tet = NULL; 162 | b->num_grid_pts = 0; 163 | b->density = NULL; 164 | 165 | return b; 166 | } 167 | 168 | diy::Master& master; 169 | }; 170 | 171 | // add block to master and generate test particles 172 | struct AddAndGenerate: public AddBlock 173 | { 174 | AddAndGenerate(diy::Master& master_, 175 | float jitter_): 176 | AddBlock(master_), 177 | jitter(jitter_) {} 178 | 179 | void operator()(int gid, 180 | const diy::ContinuousBounds& core, 181 | const diy::ContinuousBounds& bounds, 182 | const diy::ContinuousBounds& domain, 183 | const RCLink& link) const 184 | { 185 | DBlock* b = AddBlock::operator()(gid, core, bounds, domain, link); 186 | b->num_particles = gen_particles(b, jitter); 187 | b->num_orig_particles = b->num_particles; 188 | } 189 | 190 | float jitter; 191 | }; 192 | 193 | // add empty block to master 194 | struct AddEmpty: public AddBlock 195 | { 196 | AddEmpty(diy::Master& master_) : 197 | AddBlock(master_) {} 198 | 199 | void operator()(int gid, 200 | const diy::ContinuousBounds& core, 201 | const diy::ContinuousBounds& bounds, 202 | const diy::ContinuousBounds& domain, 203 | const RCLink& link) const 204 | { 205 | DBlock* b = AddBlock::operator()(gid, core, bounds, domain, link); 206 | b->num_particles = 0; 207 | b->num_orig_particles = 0; 208 | } 209 | }; 210 | 211 | // serialize a block 212 | namespace diy 213 | { 214 | template<> 215 | struct Serialization 216 | { 217 | static void save(BinaryBuffer& bb, const DBlock& d) 218 | { 219 | // debug 220 | // fprintf(stderr, "Saving block gid %d\n", d.gid); 221 | diy::save(bb, d.gid); 222 | diy::save(bb, d.bounds); 223 | diy::save(bb, d.box); 224 | diy::save(bb, d.data_bounds); 225 | diy::save(bb, d.num_orig_particles); 226 | diy::save(bb, d.num_particles); 227 | diy::save(bb, d.particles, 3 * d.num_particles); 228 | diy::save(bb, d.rem_gids, d.num_particles - d.num_orig_particles); 229 | diy::save(bb, d.rem_lids, d.num_particles - d.num_orig_particles); 230 | diy::save(bb, d.num_grid_pts); 231 | diy::save(bb, d.density, d.num_grid_pts); 232 | // NB tets and vert_to_tet get recreated in each phase; not saved and reloaded 233 | 234 | diy::save(bb, d.complete); 235 | 236 | #ifdef TESS_USE_CGAL 237 | if (!d.complete) 238 | { 239 | #if 0 240 | const Delaunay3D* Dt = static_cast(d.Dt); 241 | diy::save(bb, *Dt); 242 | #endif 243 | } 244 | //fprintf(stderr, "Delaunay saved with %lu vertices\n", Dt->number_of_vertices()); 245 | #endif 246 | 247 | if (d.complete) 248 | { 249 | diy::save(bb, d.num_tets); 250 | diy::save(bb, d.tets, d.num_tets); 251 | diy::save(bb, d.vert_to_tet, d.num_particles); 252 | } 253 | 254 | // debug 255 | // fprintf(stderr, "Done saving block gid %d\n", d.gid); 256 | } 257 | 258 | static void load(BinaryBuffer& bb, DBlock& d) 259 | { 260 | diy::load(bb, d.gid); 261 | // debug 262 | // fprintf(stderr, "Loading block gid %d\n", d.gid); 263 | diy::load(bb, d.bounds); 264 | diy::load(bb, d.box); 265 | diy::load(bb, d.data_bounds); 266 | diy::load(bb, d.num_orig_particles); 267 | diy::load(bb, d.num_particles); 268 | d.particles = NULL; 269 | if (d.num_particles) 270 | d.particles = (float*)malloc(d.num_particles * 3 * sizeof(float)); 271 | diy::load(bb, d.particles, 3 * d.num_particles); 272 | d.rem_gids = NULL; 273 | d.rem_lids = NULL; 274 | if (d.num_particles - d.num_orig_particles) 275 | { 276 | d.rem_gids = 277 | (int*)malloc((d.num_particles - d.num_orig_particles) * sizeof(int)); 278 | d.rem_lids = 279 | (int*)malloc((d.num_particles - d.num_orig_particles) * sizeof(int)); 280 | } 281 | diy::load(bb, d.rem_gids, d.num_particles - d.num_orig_particles); 282 | diy::load(bb, d.rem_lids, d.num_particles - d.num_orig_particles); 283 | diy::load(bb, d.num_grid_pts); 284 | d.density = (float*)malloc(d.num_grid_pts * sizeof(float)); 285 | diy::load(bb, d.density, d.num_grid_pts); 286 | // NB tets and vert_to_tet get recreated in each phase; not saved and reloaded 287 | d.num_tets = 0; 288 | d.tets = NULL; 289 | d.vert_to_tet = NULL; 290 | if (d.num_particles) 291 | d.vert_to_tet = (int*)malloc(d.num_particles * sizeof(int)); 292 | 293 | diy::load(bb, d.complete); 294 | 295 | #ifdef TESS_USE_CGAL 296 | if (!d.complete) 297 | { 298 | #if 0 299 | Delaunay3D* Dt = static_cast(d.Dt); 300 | diy::load(bb, *Dt); 301 | //fprintf(stderr, "Delaunay loaded with %lu vertices\n", Dt->number_of_vertices()); 302 | #endif 303 | } 304 | #endif 305 | 306 | if (d.complete) 307 | { 308 | diy::load(bb, d.num_tets); 309 | d.tets = (tet_t*)malloc(d.num_tets * sizeof(tet_t)); 310 | diy::load(bb, d.tets, d.num_tets); 311 | d.vert_to_tet = NULL; 312 | if (d.num_particles) 313 | d.vert_to_tet = (int*)malloc(d.num_particles * sizeof(int)); 314 | diy::load(bb, d.vert_to_tet, d.num_particles); 315 | } 316 | 317 | // debug 318 | // fprintf(stderr, "Done loading block gid %d\n", d.gid); 319 | } 320 | }; 321 | } 322 | 323 | #endif 324 | -------------------------------------------------------------------------------- /include/tess/tet-neighbors.h: -------------------------------------------------------------------------------- 1 | // C++ only header to use vector 2 | 3 | #include 4 | 5 | bool neighbor_edges(std::vector< std::pair >& nbrs, 6 | int v, 7 | tet_t* tets, 8 | int t 9 | ); 10 | 11 | bool neighbor_tets(std::vector& nbrs, 12 | int v, 13 | tet_t* tets, 14 | int num_tets, 15 | int t 16 | ); 17 | 18 | // returns int instead of bool so that C can call complete 19 | #ifdef __cplusplus 20 | extern "C" 21 | #endif 22 | int complete(int v, 23 | tet_t* tets, 24 | int num_tets, 25 | int t 26 | ); 27 | 28 | void fill_edge_link(std::vector& edge_link, 29 | int v, 30 | int u, 31 | int ut, 32 | tet_t* tets); 33 | -------------------------------------------------------------------------------- /include/tess/tet.h: -------------------------------------------------------------------------------- 1 | #ifndef _TESS_TET_H 2 | #define _TESS_TET_H 3 | 4 | struct tet_t { 5 | int verts[4]; /* indices of the vertices */ 6 | int tets[4]; /* indices of the neighbors; tets[i] lies opposite verts[i] */ 7 | }; 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /include/tess/tet.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _TESS_TET_HPP 2 | #define _TESS_TET_HPP 3 | 4 | #include "tess/tet.h" 5 | #include 6 | 7 | int find(struct tet_t* tet, 8 | int v); 9 | float dot(float* u, 10 | float* p, 11 | float* x); 12 | void circumcenter(float* c, 13 | struct tet_t* tet, 14 | float* particles); 15 | int side_of_plane(diy::ContinuousBounds box, 16 | struct tet_t* tet, 17 | float* particles, 18 | int j); 19 | int circulate_start(struct tet_t* tets, 20 | int t, 21 | int x, 22 | int y); 23 | void circulate_next(int* next_t, 24 | int* next_v, 25 | struct tet_t* tets, 26 | int t, 27 | int v, 28 | int x, 29 | int y); 30 | float distance(float* u, 31 | float* v); 32 | void cross(float* res, 33 | float* u, 34 | float* v); 35 | float norm(float* x); 36 | float determinant(float* t, 37 | float* u, 38 | float* v); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /include/tess/volume.h: -------------------------------------------------------------------------------- 1 | #ifndef _TESS_VOLUME_H 2 | #define _TESS_VOLUME_H 3 | 4 | #include 5 | #include "tet.hpp" 6 | 7 | void fill_circumcenters(std::vector& circumcenters, 8 | tet_t* tets, 9 | int num_tets, 10 | float* particles); 11 | 12 | float volume(int v, 13 | int* verts_to_tets, 14 | tet_t* tets, int num_tets, 15 | float* particles, 16 | const std::vector& circumcenters); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Buld tess library 2 | 3 | set (TESS_SOURCES tess.cpp tess-regular.cpp tess-kdtree.cpp swap.cpp tet.cpp dense.cpp volume.cpp) 4 | 5 | if (${serial} MATCHES "CGAL") 6 | # add_library (tess SHARED ${TESS_SOURCES} tess-cgal.cpp) 7 | add_library (tess ${TESS_SOURCES} tess-cgal.cpp) 8 | target_link_libraries (tess ${libraries}) 9 | elseif (${serial} MATCHES "QHull") 10 | # add_library (tess SHARED ${TESS_SOURCES} tess-qhull.c) 11 | add_library (tess ${TESS_SOURCES} tess-qhull.c) 12 | target_link_libraries (tess ${libraries}) 13 | else () 14 | message ("Uknown serial library: ${serial}") 15 | endif () 16 | 17 | install (TARGETS tess 18 | DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/ 19 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE 20 | GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) 21 | -------------------------------------------------------------------------------- /src/backward.cpp: -------------------------------------------------------------------------------- 1 | // Pick your poison. 2 | // 3 | // On GNU/Linux, you have few choices to get the most out of your stack trace. 4 | // 5 | // By default you get: 6 | // - object filename 7 | // - function name 8 | // 9 | // In order to add: 10 | // - source filename 11 | // - line and column numbers 12 | // - source code snippet (assuming the file is accessible) 13 | 14 | // Install one of the following library then uncomment one of the macro (or 15 | // better, add the detection of the lib and the macro definition in your build 16 | // system) 17 | 18 | // - apt-get install libdw-dev ... 19 | // - g++/clang++ -ldw ... 20 | // #define BACKWARD_HAS_DW 1 21 | 22 | // - apt-get install binutils-dev ... 23 | // - g++/clang++ -lbfd ... 24 | // #define BACKWARD_HAS_BFD 1 25 | 26 | #include "backward.hpp" 27 | 28 | namespace backward { 29 | 30 | backward::SignalHandling sh; 31 | 32 | } // namespace backward 33 | -------------------------------------------------------------------------------- /src/swap.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | // 3 | // swap utilities 4 | // 5 | // Tom Peterka 6 | // Argonne National Laboratory 7 | // 9700 S. Cass Ave. 8 | // Argonne, IL 60439 9 | // tpeterka@mcs.anl.gov 10 | // 11 | // (C) 2011 by Argonne National Laboratory. 12 | // See COPYRIGHT in top-level directory. 13 | // 14 | //--------------------------------------------------------------------------- 15 | #include 16 | #include "tess/swap.hpp" 17 | 18 | // 19 | // swaps bytes 20 | // 21 | // n: address of items 22 | // nitems: number of items 23 | // item_size: either 2, 4, or 8 bytes 24 | // returns quietly if item_size is 1 25 | // 26 | void Swap(char *n, int nitems, int item_size) { 27 | 28 | int i; 29 | 30 | switch(item_size) { 31 | case 1: 32 | break; 33 | case 2: 34 | for (i = 0; i < nitems; i++) { 35 | Swap2(n); 36 | n += 2; 37 | } 38 | break; 39 | case 4: 40 | for (i = 0; i < nitems; i++) { 41 | Swap4(n); 42 | n += 4; 43 | } 44 | break; 45 | case 8: 46 | for (i = 0; i < nitems; i++) { 47 | Swap8(n); 48 | n += 8; 49 | } 50 | break; 51 | default: 52 | fprintf(stderr, "Error: size of data must be either 1, 2, 4, or 8 bytes per item\n"); 53 | //MPI_Abort(MPI_COMM_WORLD, 0); 54 | 55 | } 56 | 57 | } 58 | //----------------------------------------------------------------------- 59 | // 60 | // Swaps 8 bytes from 1-2-3-4-5-6-7-8 to 8-7-6-5-4-3-2-1 order. 61 | // cast the input as a char and use on any 8 byte variable 62 | // 63 | void Swap8(char *n) { 64 | 65 | char *n1; 66 | char c; 67 | 68 | n1 = n + 7; 69 | c = *n; 70 | *n = *n1; 71 | *n1 = c; 72 | 73 | n++; 74 | n1--; 75 | c = *n; 76 | *n = *n1; 77 | *n1 = c; 78 | 79 | n++; 80 | n1--; 81 | c = *n; 82 | *n = *n1; 83 | *n1 = c; 84 | 85 | n++; 86 | n1--; 87 | c = *n; 88 | *n = *n1; 89 | *n1 = c; 90 | 91 | } 92 | //----------------------------------------------------------------------------- 93 | // 94 | // Swaps 4 bytes from 1-2-3-4 to 4-3-2-1 order. 95 | // cast the input as a char and use on any 4 byte variable 96 | // 97 | void Swap4(char *n) { 98 | 99 | char *n1; 100 | char c; 101 | 102 | n1 = n + 3; 103 | c = *n; 104 | *n = *n1; 105 | *n1 = c; 106 | 107 | n++; 108 | n1--; 109 | c = *n; 110 | *n = *n1; 111 | *n1 = c; 112 | 113 | } 114 | //---------------------------------------------------------------------------- 115 | // 116 | // Swaps 2 bytes from 1-2 to 2-1 order. 117 | // cast the input as a char and use on any 2 byte variable 118 | // 119 | void Swap2(char *n){ 120 | 121 | char c; 122 | 123 | c = *n; 124 | *n = n[1]; 125 | n[1] = c; 126 | 127 | } 128 | //---------------------------------------------------------------------------- 129 | 130 | -------------------------------------------------------------------------------- /src/tess-cgal.cpp: -------------------------------------------------------------------------------- 1 | #include "tess/tess.h" 2 | #include "tess/tess-cgal.h" 3 | #include "tess/tet-neighbors.h" 4 | #include 5 | 6 | //---------------------------------------------------------------------------- 7 | // Initialize and destroy Delaunay data structures used by CGAL. 8 | // We keep them persistent for later incremental insertion of additional points. 9 | // 10 | 11 | void init_delaunay_data_structure(dblock_t* b) 12 | { 13 | b->Dt = static_cast(new Delaunay3D); 14 | } 15 | 16 | void clean_delaunay_data_structure(dblock_t* b) 17 | { 18 | delete static_cast(b->Dt); 19 | } 20 | //---------------------------------------------------------------------------- 21 | // 22 | // creates local delaunay cells in one block 23 | // 24 | // b: local block 25 | // 26 | void local_cells(struct dblock_t *b) 27 | { 28 | Delaunay3D* d = (Delaunay3D*)b->Dt; 29 | construct_delaunay(*d, b->num_particles, b->particles); 30 | int ntets = d->number_of_finite_cells(); 31 | b->num_tets = ntets; 32 | b->tets = (struct tet_t*)malloc(ntets * sizeof(struct tet_t)); 33 | gen_tets(*d, b->tets); 34 | fill_vert_to_tet(b); 35 | } 36 | //---------------------------------------------------------------------------- 37 | // 38 | // generates delaunay output 39 | // 40 | // facetlist: qhull list of convex hull facets 41 | // tet_verts: pointer to array of tet vertex indeices for this block 42 | // 43 | // returns: number of tets found 44 | // 45 | int gen_delaunay_output(Delaunay3D &Dt, int** tet_verts) { 46 | 47 | int numfacets = Dt.number_of_finite_cells(); 48 | int v = 0; // index in tets 49 | 50 | *tet_verts = (int *)malloc(numfacets * 4 * sizeof(int)); 51 | 52 | // process the tets 53 | for(Cell_iterator cit = Dt.finite_cells_begin(); 54 | cit != Dt.finite_cells_end(); ++cit) { 55 | 56 | for (int i = 0; i < 4; ++i) 57 | (*tet_verts)[v++] = cit->vertex(i)->info(); 58 | 59 | } 60 | 61 | assert(numfacets == v / 4); // sanity 62 | 63 | return numfacets; 64 | 65 | } 66 | //---------------------------------------------------------------------------- 67 | // 68 | // compute Delaunay 69 | // 70 | void construct_delaunay(Delaunay3D &Dt, int num_particles, float *particles) 71 | { 72 | int n = Dt.number_of_vertices(); 73 | 74 | #ifdef TESS_CGAL_ALLOW_SPATIAL_SORT 75 | std::vector< std::pair > points; points.reserve(num_particles); 76 | for (unsigned j = n; j < (unsigned)num_particles; j++) 77 | { 78 | Point p(particles[3*j], 79 | particles[3*j+1], 80 | particles[3*j+2]); 81 | points.push_back(std::make_pair(p,j)); 82 | } 83 | Dt.insert(points.begin(), points.end()); 84 | #else 85 | for (unsigned j = n; j < (unsigned)num_particles; j++) 86 | { 87 | Point p(particles[3*j], 88 | particles[3*j+1], 89 | particles[3*j+2]); 90 | Dt.insert(p)->info() = j; 91 | } 92 | #endif 93 | } 94 | //---------------------------------------------------------------------------- 95 | // 96 | // Convert Delaunay3D to a vector of tets 97 | // 98 | void gen_tets(Delaunay3D& Dt, tet_t* tets) 99 | { 100 | // Initialize all cell info to -1 (infinite cells will keep -1) 101 | for(All_cell_iterator cit = Dt.all_cells_begin(); 102 | cit != Dt.all_cells_end(); ++cit) 103 | cit->info() = -1; 104 | 105 | // Record tet vertices (and indices in info()) 106 | int idx = 0; 107 | for(Cell_iterator cit = Dt.finite_cells_begin(); 108 | cit != Dt.finite_cells_end(); ++cit) 109 | { 110 | cit->info() = idx; // record indices in cells' info() 111 | for (int i = 0; i < 4; ++i) 112 | tets[idx].verts[i] = cit->vertex(i)->info(); 113 | ++idx; 114 | } 115 | 116 | // Record tet adjacency information 117 | idx = 0; 118 | for(Cell_iterator cit = Dt.finite_cells_begin(); 119 | cit != Dt.finite_cells_end(); ++cit) 120 | { 121 | for (int i = 0; i < 4; ++i) 122 | tets[idx].tets[i] = cit->neighbor(i)->info(); 123 | ++idx; 124 | } 125 | } 126 | //---------------------------------------------------------------------------- 127 | -------------------------------------------------------------------------------- /src/tess-kdtree.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "tess/tess.h" 7 | #include "tess/delaunay.hpp" 8 | 9 | struct KDTreeBlock 10 | { 11 | struct Point 12 | { 13 | float& operator[](unsigned i) { return data[i]; } 14 | const float& operator[](unsigned i) const { return data[i]; } 15 | float data[3]; 16 | }; 17 | std::vector points; 18 | }; 19 | 20 | struct WrapMaster 21 | { 22 | diy::Master* master; 23 | bool wrap; 24 | }; 25 | 26 | void populate_kdtree_block(DBlock* d, 27 | const diy::Master::ProxyWithLink& cp, 28 | diy::Master& kdtree_master, 29 | bool wrap) 30 | { 31 | diy::ContinuousBounds domain = d->data_bounds; 32 | 33 | KDTreeBlock* b = new KDTreeBlock; 34 | diy::RegularContinuousLink* l = new diy::RegularContinuousLink(3, domain, domain); 35 | kdtree_master.add(cp.gid(), b, l); 36 | 37 | // copy the particles over 38 | b->points.resize(d->num_orig_particles); 39 | for (size_t i = 0; i < d->num_orig_particles; ++i) 40 | { 41 | b->points[i][0] = d->particles[3*i + 0]; 42 | b->points[i][1] = d->particles[3*i + 1]; 43 | b->points[i][2] = d->particles[3*i + 2]; 44 | } 45 | } 46 | 47 | void extract_kdtree_block(KDTreeBlock* b, 48 | const diy::Master::ProxyWithLink& cp, 49 | diy::Master& tess_master) 50 | { 51 | int tess_lid = tess_master.lid(cp.gid()); 52 | DBlock* d = (DBlock*) tess_master.block(tess_lid); // assumes all blocks in memory 53 | 54 | // copy out the particles 55 | d->num_particles = d->num_orig_particles = b->points.size(); 56 | d->particles = (float *)realloc(d->particles, b->points.size() * 3 * sizeof(float)); 57 | for (size_t i = 0; i < d->num_orig_particles; ++i) 58 | { 59 | d->particles[3*i + 0] = b->points[i][0]; 60 | d->particles[3*i + 1] = b->points[i][1]; 61 | d->particles[3*i + 2] = b->points[i][2]; 62 | } 63 | 64 | //fprintf(stderr, "[%d]: %d particles copied out\n", cp.gid(), d->num_orig_particles); 65 | 66 | // steal the link 67 | diy::RegularContinuousLink* tess_link = 68 | static_cast(tess_master.link(tess_lid)); 69 | diy::RegularContinuousLink* kdtree_link = 70 | static_cast(cp.link()); 71 | tess_link->swap(*kdtree_link); 72 | 73 | d->box = tess_link->bounds(); 74 | for (int i = 0; i < 3; ++i) 75 | { 76 | d->bounds.min[i] = tess_link->bounds().min[i]; 77 | d->bounds.max[i] = tess_link->bounds().max[i]; 78 | } 79 | 80 | delete b; // safe to do since kdtree_master doesn't own the blocks (no create/destroy supplied) 81 | } 82 | 83 | void tess_kdtree_exchange(diy::Master& master, 84 | const diy::Assigner& assigner, 85 | double* times, 86 | bool wrap, 87 | bool sampling) 88 | { 89 | timing(times, EXCH_TIME, -1, master.communicator()); 90 | 91 | diy::Master kdtree_master(master.communicator(), master.threads(), -1); 92 | master.foreach([&](DBlock* b, const diy::Master::ProxyWithLink& cp) 93 | { populate_kdtree_block(b, cp, kdtree_master, wrap); }); 94 | 95 | int bins = 1024; // histogram bins; TODO: make a function argument 96 | diy::ContinuousBounds domain = master.block(master.loaded_block())->data_bounds; 97 | if (sampling) 98 | diy::kdtree_sampling(kdtree_master, assigner, 3, domain, &KDTreeBlock::points, bins, wrap); 99 | else 100 | diy::kdtree(kdtree_master, assigner, 3, domain, &KDTreeBlock::points, bins, wrap); 101 | 102 | kdtree_master.foreach([&](KDTreeBlock* b, const diy::Master::ProxyWithLink& cp) 103 | { extract_kdtree_block(b, cp, master); }); 104 | master.set_expected(kdtree_master.expected()); 105 | 106 | timing(times, -1, EXCH_TIME, master.communicator()); 107 | } 108 | 109 | void tess_kdtree_exchange(diy::Master& master, 110 | const diy::Assigner& assigner, 111 | bool wrap, 112 | bool sampling) 113 | { 114 | double times[TESS_MAX_TIMES]; 115 | tess_kdtree_exchange(master, assigner, times, wrap, sampling); 116 | } 117 | -------------------------------------------------------------------------------- /src/tess-qhull.c: -------------------------------------------------------------------------------- 1 | #include "tess/tess-qhull.h" 2 | #include "tess/tess.h" 3 | #include 4 | 5 | /*--------------------------------------------------------------------------*/ 6 | /* Initialization and destruction of Delaunay data structures is not used with 7 | * qhull, since it doesn't support incremental updates. 8 | */ 9 | void init_delaunay_data_structure(struct dblock_t* b) 10 | { 11 | b->Dt = NULL; 12 | } 13 | 14 | void clean_delaunay_data_structure(struct dblock_t* b) 15 | { 16 | b->Dt = NULL; 17 | } 18 | /*--------------------------------------------------------------------------*/ 19 | /* 20 | creates local delaunay cells 21 | 22 | dblock: local block 23 | */ 24 | void local_cells(struct dblock_t *dblock) 25 | { 26 | boolT ismalloc = False; /* True if qhull should free points in 27 | qh_freeqhull() or reallocation */ 28 | char flags[250]; /* option flags for qhull, see qh-quick.htm */ 29 | int exitcode; /* 0 if no error from qhull */ 30 | int curlong, totlong; /* memory remaining after qh_memfreeshort */ 31 | FILE *dev_null; /* file descriptor for writing to /dev/null */ 32 | int i, j; 33 | int dim = 3; /* 3d */ 34 | 35 | dev_null = fopen("/dev/null", "w"); 36 | assert(dev_null != NULL); 37 | 38 | /* deep copy from float to double (qhull API is double) */ 39 | double *pts = 40 | (double *)malloc(dblock->num_particles * 3 * sizeof(double)); 41 | for (j = 0; j < 3 * dblock->num_particles; j++) 42 | pts[j] = dblock->particles[j]; 43 | 44 | /* compute delaunay */ 45 | /* sprintf(flags, "qhull v d o Fv Fo"); /\* Fv prints voronoi faces *\/ */ 46 | sprintf (flags, "qhull d Qt"); /* print delaunay cells */ 47 | 48 | /* eat qhull output by sending it to dev/null */ 49 | exitcode = qh_new_qhull(dim, dblock->num_particles, pts, ismalloc, 50 | flags, dev_null, stderr); 51 | 52 | free(pts); 53 | 54 | /* process delaunay output */ 55 | if (!exitcode) 56 | gen_delaunay_output(qh facet_list, dblock); 57 | 58 | /* qhull does not order verts and neighbor tets such that the ith 59 | neighbor is opposite the ith vertex; so need to reorder neighbors */ 60 | reorder_neighbors(dblock); 61 | 62 | fill_vert_to_tet(dblock); 63 | 64 | /* clean up qhull */ 65 | qh_freeqhull(!qh_ALL); /* free long memory */ 66 | qh_memfreeshort(&curlong, &totlong); /* free short memory */ 67 | if (curlong || totlong) 68 | fprintf (stderr, "qhull internal warning: did not free %d bytes of " 69 | "long memory (%d pieces)\n", totlong, curlong); 70 | 71 | fclose(dev_null); 72 | 73 | } 74 | /*--------------------------------------------------------------------------*/ 75 | /* 76 | generates delaunay output from qhull 77 | 78 | facetlist: qhull list of convex hull facets 79 | tet_verts: pointer to array of tet vertex indices for this block 80 | (allocated by this function, user's responsibility to free) 81 | 82 | */ 83 | void gen_delaunay_output(facetT *facetlist, struct dblock_t *dblock) { 84 | 85 | facetT *facet, *neighbor, **neighborp; 86 | vertexT *vertex, **vertexp; 87 | int numfacets = 0; 88 | int t, v, n; /* index in tets, tet verts, tet neighbors */ 89 | 90 | /* count number of tets (facets to qhull) */ 91 | FORALLfacet_(facetlist) { 92 | if ((facet->visible && qh NEWfacets) || (qh_skipfacet(facet))) 93 | facet->visitid= 0; 94 | else 95 | facet->visitid= ++numfacets; 96 | } 97 | 98 | dblock->num_tets = numfacets; 99 | dblock->tets = (struct tet_t *)malloc(numfacets * sizeof(struct tet_t)); 100 | 101 | /* for all tets, get vertices */ 102 | t = 0; 103 | FORALLfacet_(facetlist) { 104 | 105 | if (qh_skipfacet(facet) || (facet->visible && qh NEWfacets)) 106 | continue; 107 | 108 | if (qh_setsize(facet->vertices) != 4) { 109 | fprintf(stderr, "tet has %d vertices; skipping.\n", 110 | qh_setsize(facet->vertices)); 111 | continue; 112 | } 113 | 114 | /* for all vertices */ 115 | v = 0; 116 | if ((facet->toporient ^ qh_ORIENTclock) 117 | || (qh hull_dim > 2 && !facet->simplicial)) { 118 | FOREACHvertex_(facet->vertices) 119 | dblock->tets[t].verts[v++] = qh_pointid(vertex->point); 120 | } else { 121 | FOREACHvertexreverse12_(facet->vertices) 122 | dblock->tets[t].verts[v++] = qh_pointid(vertex->point); 123 | } 124 | ++t; 125 | } 126 | 127 | /* for all tets, get neighbors */ 128 | t = 0; 129 | FORALLfacet_(facetlist) { 130 | 131 | if (qh_skipfacet(facet) || (facet->visible && qh NEWfacets)) 132 | continue; 133 | 134 | if (qh_setsize(facet->vertices) != 4) { 135 | fprintf(stderr, "tet has %d vertices; skipping.\n", 136 | qh_setsize(facet->vertices)); 137 | continue; 138 | } 139 | 140 | /* for all neighbor tets */ 141 | n = 0; 142 | FOREACHneighbor_(facet) { 143 | if (neighbor->visitid) 144 | dblock->tets[t].tets[n++] = neighbor->visitid - 1; 145 | else 146 | dblock->tets[t].tets[n++] = -1; 147 | } 148 | assert(n == 4); /* sanity */ 149 | 150 | t++; 151 | 152 | } /* for all tets */ 153 | 154 | assert(numfacets == t); /* sanity */ 155 | 156 | } 157 | /*--------------------------------------------------------------------------*/ 158 | /* 159 | reorders neighbors in dblock such that ith neighbor is opposite ith vertex 160 | */ 161 | void reorder_neighbors(struct dblock_t *dblock) { 162 | 163 | int t, v, n, nv; /*indices into tets, verts, neighbors, neighbor verts */ 164 | int nbr; /* one neighbor tet */ 165 | int done; /* this neighbor is done */ 166 | int tets[4]; /* newly ordered neighbors */ 167 | 168 | /* tets */ 169 | for (t = 0; t < dblock->num_tets; t++) { 170 | 171 | /* verts */ 172 | for (v = 0; v < 4; v++) { 173 | 174 | done = 0; 175 | 176 | /* neighbor tets */ 177 | for (n = 0; n < 4; n++) { 178 | 179 | nbr = dblock->tets[t].tets[n]; 180 | if (nbr == -2) /* done already */ 181 | continue; 182 | 183 | /* neighbor tet verts */ 184 | if (nbr > -1) { 185 | for (nv = 0; nv < 4; nv++) { 186 | if (dblock->tets[nbr].verts[nv] == dblock->tets[t].verts[v]) 187 | break; /* nbr is the wrong neighbor */ 188 | } 189 | } 190 | 191 | if (nbr > -1 && nv == 4) {/* nbr is the right neighbor */ 192 | done = 1; 193 | break; 194 | } 195 | 196 | } 197 | 198 | if (done) { 199 | tets[v] = nbr; 200 | dblock->tets[t].tets[n] = -2; /* mark this neighbor as done */ 201 | } 202 | else 203 | tets[v] = -1; 204 | 205 | } /* verts */ 206 | 207 | /* copy reordered neighbors back to dblock */ 208 | for (n = 0; n < 4; n++) 209 | dblock->tets[t].tets[n] = tets[n]; 210 | 211 | /* sanity check */ 212 | for (v = 0; v < 4; ++v) { 213 | int nbr = dblock->tets[t].tets[v]; /* opposite neighbor */ 214 | if (nbr > -1) { 215 | for (nv = 0; nv < 4; nv++) { /* verts in opposite neighbor */ 216 | if (dblock->tets[nbr].verts[nv] == dblock->tets[t].verts[v]) 217 | fprintf(stderr, "Neighbor of tet %d can't have a vertex opposite " 218 | "to it\n", t); 219 | } 220 | } 221 | } 222 | 223 | } /* tets */ 224 | 225 | } 226 | /*--------------------------------------------------------------------------*/ 227 | -------------------------------------------------------------------------------- /src/tess-regular.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "tess/tess.hpp" 7 | 8 | void redistribute(void* b_, 9 | const diy::ReduceProxy& srp, 10 | const diy::RegularSwapPartners& partners) 11 | { 12 | DBlock* b = static_cast(b_); 13 | unsigned round = srp.round(); 14 | 15 | //fprintf(stderr, "in_link.size(): %d\n", srp.in_link().size()); 16 | //fprintf(stderr, "out_link.size(): %d\n", srp.out_link().size()); 17 | 18 | // step 1: dequeue and merge 19 | // dequeue all the incoming points and add them to this block's vector 20 | // could use srp.incoming() instead 21 | for (unsigned i = 0; i < srp.in_link().size(); ++i) 22 | { 23 | int nbr_gid = srp.in_link().target(i).gid; 24 | if (nbr_gid == srp.gid()) 25 | continue; 26 | 27 | std::vector in_points; 28 | srp.dequeue(nbr_gid, in_points); 29 | int npts = in_points.size() / 3; 30 | 31 | //fprintf(stderr, "[%d] Received %d points from [%d]\n", srp.gid(), npts, nbr_gid); 32 | b->particles = (float *)realloc(b->particles, (b->num_particles + npts) * 3 * sizeof(float)); 33 | size_t o = b->num_particles * 3; 34 | for (size_t j = 0; j < in_points.size(); ++j) 35 | b->particles[o++] = in_points[j]; 36 | b->num_particles += npts; 37 | } 38 | b->num_orig_particles = b->num_particles; 39 | 40 | // step 2: subset and enqueue 41 | //fprintf(stderr, "[%d] out_link().size(): %d\n", srp.gid(), srp.out_link().size()); 42 | if (srp.out_link().size() == 0) // final round; nothing needs to be sent 43 | return; 44 | 45 | std::vector< std::vector > out_points(srp.out_link().size()); 46 | int group_size = srp.out_link().size(); 47 | int cur_dim = partners.dim(round); 48 | for (size_t i = 0; i < b->num_particles; ++i) 49 | { 50 | int loc = floor((b->particles[3*i + cur_dim] - b->box.min[cur_dim]) / 51 | (b->box.max[cur_dim] - b->box.min[cur_dim]) * 52 | group_size); 53 | if ((loc >= out_points.size() && b->particles[3*i + cur_dim] > b->box.max[cur_dim]) || 54 | loc < 0) 55 | fprintf(stderr, "Warning: loc=%d < 0 || loc >= %lu : %f vs [%f,%f]\n", 56 | loc, out_points.size(), 57 | b->particles[3*i + cur_dim], 58 | b->box.min[cur_dim], b->box.max[cur_dim] 59 | ); 60 | if (loc == out_points.size()) 61 | loc -= 1; 62 | if (loc < 0) 63 | loc = 0; 64 | 65 | out_points[loc].push_back(b->particles[3*i]); 66 | out_points[loc].push_back(b->particles[3*i + 1]); 67 | out_points[loc].push_back(b->particles[3*i + 2]); 68 | } 69 | int pos = -1; 70 | for (int i = 0; i < group_size; ++i) 71 | { 72 | if (srp.out_link().target(i).gid == srp.gid()) 73 | { 74 | b->particles = (float *)realloc(b->particles, out_points[i].size() * sizeof(float)); 75 | for (size_t j = 0; j < out_points[i].size(); ++j) 76 | b->particles[j] = out_points[i][j]; 77 | b->num_particles = out_points[i].size() / 3; 78 | b->num_orig_particles = b->num_particles; 79 | pos = i; 80 | } 81 | else 82 | { 83 | srp.enqueue(srp.out_link().target(i), out_points[i]); 84 | //fprintf(stderr, "[%d] Sent %d points to [%d]\n", srp.gid(), (int) out_points[i].size() / 3, srp.out_link().target(i).gid); 85 | } 86 | } 87 | float new_min = b->box.min[cur_dim] + (b->box.max[cur_dim] - b->box.min[cur_dim])/group_size*pos; 88 | float new_max = b->box.min[cur_dim] + (b->box.max[cur_dim] - b->box.min[cur_dim])/group_size*(pos + 1); 89 | b->box.min[cur_dim] = new_min; 90 | b->box.max[cur_dim] = new_max; 91 | } 92 | 93 | void tess_exchange(diy::Master& master, 94 | const diy::Assigner& assigner, 95 | double* times) 96 | { 97 | int rank, size; 98 | MPI_Comm_rank(master.communicator(), &rank); 99 | MPI_Comm_size(master.communicator(), &size); 100 | timing(times, EXCH_TIME, -1, master.communicator()); 101 | int k = 2; 102 | 103 | diy::ContinuousBounds domain = 104 | master.block(master.loaded_block())->data_bounds; 105 | diy::RegularDecomposer decomposer(3, domain, assigner.nblocks()); 106 | diy::RegularSwapPartners partners(decomposer, k, false); 107 | 108 | diy::reduce(master, assigner, partners, &redistribute); 109 | timing(times, -1, EXCH_TIME, master.communicator()); 110 | } 111 | 112 | void tess_exchange(diy::Master& master, 113 | const diy::Assigner& assigner) 114 | { 115 | double times[TESS_MAX_TIMES]; 116 | tess_exchange(master, assigner, times); 117 | } 118 | -------------------------------------------------------------------------------- /src/tet.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "tess/tet.hpp" 9 | #include "tess/tet-neighbors.h" 10 | 11 | // finds index of v in tet->verts 12 | int find(tet_t* tet, int v) 13 | { 14 | for (int i = 0; i < 4; ++i) 15 | if (tet->verts[i] == v) 16 | return i; 17 | assert(0); 18 | return -1; 19 | } 20 | 21 | // dot product: u * (p - x) 22 | float dot(float* u, float* p, float* x) 23 | { 24 | float res = 0; 25 | for (int i = 0; i < 3; ++i) 26 | res += u[i] * (p[i] - x[i]); 27 | return res; 28 | } 29 | 30 | /** 31 | * computes circumcenter of a tetrahedron 32 | * 33 | * center: array of 3 floats that will store the result 34 | * tet: the tetrahedron 35 | * particles: array of particles (x1,y1,z1,x2,y2,z2,...) 36 | */ 37 | void circumcenter(float* center, tet_t* tet, float* particles) 38 | { 39 | float *a = &particles[3*tet->verts[0]], 40 | *b = &particles[3*tet->verts[1]], 41 | *c = &particles[3*tet->verts[2]], 42 | *d = &particles[3*tet->verts[3]]; 43 | 44 | // center everything at d 45 | float t[3], u[3], v[3]; 46 | for (int i = 0; i < 3; ++i) { 47 | t[i] = a[i] - d[i]; 48 | u[i] = b[i] - d[i]; 49 | v[i] = c[i] - d[i]; 50 | } 51 | float norm_t = norm(t), 52 | norm_u = norm(u), 53 | norm_v = norm(v); 54 | 55 | // God help us if den is close to 0 56 | // (this will happen for nearly flat tets) 57 | float den = 2*determinant(t,u,v); 58 | 59 | float uv[3], vt[3], tu[3]; 60 | cross(uv, u, v); 61 | cross(vt, v, t); 62 | cross(tu, t, u); 63 | 64 | for (int i = 0; i < 3; ++i) 65 | center[i] = d[i] + (norm_t*uv[i] + norm_u*vt[i] + norm_v*tu[i])/den; 66 | } 67 | 68 | // determine if any point in the box [min, max] lies on the opposite side of the 69 | // facet opposite to vertex j 70 | int side_of_plane(diy::ContinuousBounds box, 71 | struct tet_t* tet, 72 | float* particles, 73 | int j) 74 | { 75 | float one[3] = { 1., 1., 1. }; 76 | 77 | int idx[4] = { 0, 1, 2, 3 }; 78 | std::swap(idx[j], idx[3]); 79 | 80 | float *x = &particles[3*tet->verts[idx[0]]], 81 | *y = &particles[3*tet->verts[idx[1]]], 82 | *z = &particles[3*tet->verts[idx[2]]], 83 | *p = &particles[3*tet->verts[idx[3]]]; 84 | 85 | float u[3], v[3], w[3], n[3]; 86 | for (int i = 0; i < 3; ++i) 87 | { 88 | u[i] = y[i] - x[i]; 89 | v[i] = z[i] - x[i]; 90 | } 91 | cross(n, u, v); 92 | 93 | // plane through x, normal to n 94 | float res = dot(n, p, x); 95 | int sign = 0; 96 | if (res > 0) 97 | sign = 1; 98 | else if (res < 0) 99 | sign = -1; 100 | 101 | if (sign == 0) 102 | { 103 | // Warning: got a degenerate convex hull tet in side_of_plane computation 104 | // Play it safe and report the box as intersecting 105 | return 1; 106 | } 107 | 108 | // find the sign of the extreme-most point of the box 109 | res = 0; 110 | for (int i = 0; i < 3; ++i) 111 | { 112 | if ((sign > 0) != (n[i] < 0)) 113 | res += n[i] * (box.min[i] - x[i]); 114 | else 115 | res += n[i] * (box.max[i] - x[i]); 116 | } 117 | 118 | return ((sign > 0) != (res > 0)); 119 | } 120 | 121 | // returns |x|^2 122 | float norm(float* x) 123 | { 124 | float res = 0; 125 | for (int i = 0; i < 3; ++i) 126 | res += x[i]*x[i]; 127 | return res; 128 | } 129 | 130 | // res = u x v 131 | void cross(float* res, float* u, float* v) 132 | { 133 | res[0] = u[1]*v[2] - u[2]*v[1]; 134 | res[1] = u[2]*v[0] - u[0]*v[2]; 135 | res[2] = u[0]*v[1] - u[1]*v[0]; 136 | } 137 | 138 | // returns determinant of |t u v| 139 | float determinant(float* t, float* u, float* v) 140 | { 141 | return t[0]*u[1]*v[2] + u[0]*v[1]*t[2] + v[0]*t[1]*u[2] - 142 | v[0]*u[1]*t[2] - u[0]*t[1]*v[2] - t[0]*v[1]*u[2]; 143 | } 144 | 145 | // returns |u - v| 146 | float distance(float* u, float* v) 147 | { 148 | float n = 0; 149 | for (int i = 0; i < 3; ++i) { 150 | n += (u[i] - v[i])*(u[i] - v[i]); 151 | } 152 | return sqrt(n); 153 | } 154 | 155 | /** 156 | * find initial vertex to circulate from, tets[t] must contain edge (x,y) 157 | * 158 | * tets: array of all tetrahedra 159 | * t: index into tets (initial tetrahedron) 160 | * x,y: edge we are circulating around 161 | * 162 | * returns: index into tets[t]->verts (neither x, nor y) 163 | */ 164 | int circulate_start(tet_t* tets, int t, int x, int y) 165 | { 166 | for (int i = 0; i < 4; ++i) { 167 | int v = tets[t].verts[i]; 168 | if (v != x && v != y) 169 | return i; 170 | } 171 | assert(0); // can't get to this point 172 | return -1; 173 | } 174 | 175 | /** 176 | * find the next tetrahedron around edge (x,y) and the next vertex in it 177 | * 178 | * next_t: will store the index of the next tetrahedron 179 | * next_v: will store the index of the next vertex 180 | * tets: array of all tetrahedra 181 | * t: index into tets (current tetrahedron) 182 | * v: index of the current vertex in tets->verts 183 | * x,y: edge we are circulating around 184 | */ 185 | void circulate_next(int* next_t, int* next_v, tet_t* tets, int t, int v, int x, int y) 186 | { 187 | tet_t* cur = tets + t; 188 | 189 | // Find the unused vertex (i.e., the entry in tets[t]->verts that's not x,y, or tets[t]->verts[v]) 190 | int nv = -1; 191 | for (int i = 0; i < 4; ++i) { 192 | int inv = cur->verts[i]; 193 | if (i != v && 194 | inv != x && 195 | inv != y) { 196 | nv = inv; 197 | break; 198 | } 199 | } 200 | assert(nv != -1); 201 | 202 | // take a step 203 | *next_t = cur->tets[v]; 204 | tet_t* next = tets + *next_t; 205 | 206 | // find the index of nv in next->verts 207 | for (int i = 0; i < 4; ++i) { 208 | int inv = next->verts[i]; 209 | if (inv == nv) { 210 | *next_v = i; 211 | return; 212 | } 213 | } 214 | assert(false); // must have found everything 215 | } 216 | 217 | /** 218 | * fills a vector of u, ut pairs with Delaunay neighbors and tetratehedra 219 | * that conain edge (v,u) 220 | * 221 | * nbrs: vector of pairs (u, ut) 222 | * v: vertex 223 | * tets: array of all tetrahedra 224 | * t: tet that contains v 225 | * 226 | * returns whether the Voronoi cell is finite 227 | */ 228 | bool neighbor_edges(std::vector< std::pair >& nbrs, 229 | int v, 230 | tet_t* tets, 231 | int t 232 | ) 233 | { 234 | bool finite = true; 235 | std::queue q; 236 | std::set visited_tets, 237 | visited_verts; 238 | 239 | q.push(t); 240 | 241 | // BFS in the star of v 242 | while (!q.empty()) 243 | { 244 | int t = q.front(); 245 | q.pop(); 246 | 247 | // already visited, continue 248 | if (visited_tets.find(t) != visited_tets.end()) 249 | continue; 250 | visited_tets.insert(t); 251 | 252 | // insert vertices, not yet inserted, and queue neighbors 253 | for (int i = 0; i < 4; ++i) { 254 | int u = tets[t].verts[i]; 255 | if (u != v) { 256 | if (visited_verts.find(u) == visited_verts.end()) { 257 | visited_verts.insert(u); 258 | nbrs.push_back(std::make_pair(u,t)); 259 | } 260 | int next = tets[t].tets[i]; 261 | if (next == -1) 262 | finite = false; 263 | else 264 | q.push(next); 265 | } 266 | } 267 | } 268 | 269 | return finite; 270 | } 271 | 272 | /** 273 | * fills a vector with tetrahedra that contain the given vertex 274 | * 275 | * nbrs: vector to be filled with tet indices 276 | * v: vertex 277 | * tets: array of all tetrahedra 278 | * t: tet that contains v 279 | * 280 | * returns whether the Voronoi cell is finite 281 | */ 282 | bool neighbor_tets(std::vector& nbrs, 283 | int v, 284 | tet_t* tets, 285 | int num_tets, 286 | int t 287 | ) 288 | { 289 | if (t < 0) 290 | fprintf(stderr, "Warning in neighbor_tets(): cannot start with negative tet %d\n", t); 291 | 292 | bool finite = true; 293 | std::queue q; 294 | std::set visited_tets; 295 | q.push(t); 296 | 297 | // BFS in the star of v 298 | while (!q.empty()) 299 | { 300 | int t = q.front(); 301 | q.pop(); 302 | 303 | if (t >= num_tets) 304 | fprintf(stderr, "Warning in neighbor_tets(): tet %d exceeds total %d tets\n", t, num_tets); 305 | 306 | // already visited, continue 307 | if (visited_tets.find(t) != visited_tets.end()) 308 | continue; 309 | visited_tets.insert(t); 310 | nbrs.push_back(t); 311 | 312 | // queue neighbors 313 | for (int i = 0; i < 4; ++i) { 314 | int u = tets[t].verts[i]; 315 | if (u != v) { 316 | int next = tets[t].tets[i]; 317 | if (next == -1) 318 | finite = false; 319 | else 320 | q.push(next); 321 | } 322 | } 323 | } 324 | 325 | return finite; 326 | } 327 | 328 | /** 329 | * determines if v's Voronoi cell is finite 330 | * (equivalently, whether v lies on the convex hull) 331 | * returning int instead of bool so that C can call complete 332 | * 333 | * v: vertex 334 | * tets: array of all tetrahedra 335 | * t: tet that contains v 336 | */ 337 | int complete(int v, 338 | tet_t* tets, 339 | int num_tets, 340 | int t 341 | ) 342 | { 343 | if (t < 0) 344 | fprintf(stderr, "Warning in complete(): cannot start with a negative tet %d\n", t); 345 | 346 | std::queue q; 347 | std::set visited_tets; 348 | q.push(t); 349 | 350 | // BFS in the star of v 351 | while (!q.empty()) 352 | { 353 | int t = q.front(); 354 | q.pop(); 355 | 356 | if (t >= num_tets) 357 | fprintf(stderr, "Warning in complete(): tet %d exceeds total %d tets\n", t, num_tets); 358 | 359 | // already visited, continue 360 | if (visited_tets.find(t) != visited_tets.end()) 361 | continue; 362 | visited_tets.insert(t); 363 | 364 | // queue neighbors 365 | for (int i = 0; i < 4; ++i) { 366 | int u = tets[t].verts[i]; 367 | if (u != v) { 368 | int next = tets[t].tets[i]; 369 | if (next == -1) 370 | return 0; // returning int instead of bool 371 | else 372 | q.push(next); 373 | } 374 | } 375 | } 376 | 377 | return 1; // returning int instead of bool 378 | } 379 | 380 | /** 381 | * Fills edge_link with all the tets containing edge (u,v), 382 | * the tets are inserted in circular order, starting from ut 383 | * 384 | * edge_link: output vector 385 | * v,u: vertices 386 | * ut: a tet that contains edge (v,u) 387 | * tets: array of tets 388 | */ 389 | void fill_edge_link(std::vector& edge_link, 390 | int v, 391 | int u, 392 | int ut, 393 | tet_t* tets) 394 | { 395 | int wi = circulate_start(tets, ut, v, u); 396 | int t = ut; 397 | while (true) 398 | { 399 | edge_link.push_back(t); 400 | 401 | int next_t, next_wi; 402 | circulate_next(&next_t, &next_wi, tets, t, wi, v, u); 403 | if (next_t == ut || next_t == -1) 404 | break; 405 | 406 | t = next_t; 407 | wi = next_wi; 408 | } 409 | } 410 | -------------------------------------------------------------------------------- /src/volume.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "tess/volume.h" 4 | #include "tess/tet-neighbors.h" 5 | 6 | void fill_circumcenters(std::vector& circumcenters, tet_t* tets, int num_tets, float* particles) 7 | { 8 | circumcenters.resize(num_tets*3); 9 | for (int i = 0; i < num_tets; ++i) 10 | circumcenter(&circumcenters[3*i], &tets[i], particles); 11 | } 12 | 13 | float volume(int v, int* verts_to_tets, tet_t* tets, int num_tets, float* particles, const std::vector& circumcenters) 14 | { 15 | int vt = verts_to_tets[v]; 16 | 17 | std::vector< std::pair > nbrs; 18 | bool finite = neighbor_edges(nbrs, v, tets, vt); 19 | 20 | if (!finite) 21 | return -1; // don't compute infinite volumes 22 | 23 | float vol = 0; 24 | for (int i = 0; i < nbrs.size(); ++i) 25 | { 26 | std::vector edge_link; 27 | int u = nbrs[i].first; 28 | int ut = nbrs[i].second; 29 | fill_edge_link(edge_link,v,u,ut,tets); 30 | 31 | // area of the Voronoi facet dual to (u,v) 32 | float area = 0; 33 | int a = edge_link[0]; 34 | for (int i = 1; i < edge_link.size() - 1; ++i) { 35 | int b = edge_link[i]; 36 | int c = edge_link[i+1]; 37 | 38 | float ab[3], ac[3]; 39 | for (int j = 0; j < 3; ++j) { 40 | ab[j] = circumcenters[3*b + j] - circumcenters[3*a + j]; 41 | ac[j] = circumcenters[3*c + j] - circumcenters[3*a + j]; 42 | } 43 | float cp[3]; 44 | cross(cp, ab, ac); 45 | area += sqrt(norm(cp))/2; 46 | } 47 | 48 | // distance between u and v 49 | float dist = distance(&particles[3*u], &particles[3*v]); 50 | vol += area*dist/6; 51 | } 52 | 53 | return vol; 54 | } 55 | -------------------------------------------------------------------------------- /tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (draw) 2 | 3 | include_directories (${OPENGL_INCLUDE_DIR} ${GLUT_INCLUDE_DIR}) 4 | add_executable (draw draw.cpp) 5 | target_link_libraries (draw tess ${libraries} ${OPENGL_LIBRARIES} ${GLUT_LIBRARIES}) 6 | install (TARGETS draw 7 | DESTINATION ${CMAKE_INSTALL_PREFIX}/tools/ 8 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE 9 | GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) 10 | 11 | endif (draw) 12 | 13 | add_executable (stats stats.cpp) 14 | target_link_libraries (stats tess ${libraries}) 15 | 16 | install (TARGETS stats 17 | DESTINATION ${CMAKE_INSTALL_PREFIX}/tools/ 18 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE 19 | GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) 20 | 21 | install (FILES dense-plot.py 22 | DESTINATION ${CMAKE_INSTALL_PREFIX}/tools/ 23 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE 24 | GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) 25 | -------------------------------------------------------------------------------- /tools/dense-plot.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | 3 | #--------------------------------------------------------------------------- 4 | # 5 | # plots 2D density 6 | # 7 | # Tom Peterka 8 | # Argonne National Laboratory 9 | # 9700 S. Cass Ave. 10 | # Argonne, IL 60439 11 | # tpeterka@mcs.anl.gov 12 | # 13 | # (C) 2013 by Argonne National Laboratory. 14 | # See COPYRIGHT in top-level directory. 15 | # 16 | #-------------------------------------------------------------------------- 17 | 18 | import numpy as np 19 | import argparse 20 | import struct 21 | import matplotlib 22 | 23 | #----- 24 | 25 | # parse arguments 26 | parser = argparse.ArgumentParser() 27 | parser.add_argument("-i", "--in", dest="infile", help="raw (binary) density input file") 28 | parser.add_argument("-n", "--numpts", dest="numpts", 29 | help="number of grid point position in one axis (same for x and y)") 30 | parser.add_argument("-o", "--out", dest="out", type=str, 31 | choices=["pdf", "svg", "png"], default="png", 32 | help="Output format: pdf, svg, none or png also runs interactive display.") 33 | parser.add_argument("-c", "--color", dest="color", type=str, 34 | choices=["lin", "log"], default="lin", 35 | help="Color map: lin (linear, default) or log (logarithmic)") 36 | parser.add_argument("-min", "--min", dest="min", type=float, 37 | default=-1.0, help="Color map minimum value (optional)") 38 | parser.add_argument("-max", "--max", dest="max", type=float, 39 | default=-1.0, help="Color map maximum value (optional)") 40 | args = parser.parse_args() 41 | 42 | # output format (default = png + interactive display) 43 | if args.out == 'svg': 44 | print "Output will be in myfig.svg" 45 | matplotlib.use('svg') 46 | elif args.out == 'pdf': 47 | print "Output will be in myfig.pdf" 48 | matplotlib.use('pdf') 49 | else: 50 | print "Output will be in myfig.png as well as in interactive disply" 51 | import matplotlib.pyplot as plt 52 | 53 | #----- 54 | 55 | # construct raw density x, y, d arrays 56 | 57 | # using np.fromfile() does not work correctly on my mac, so I need to read 58 | # the file one value at a time and append to the array 59 | 60 | f1 = open(args.infile, "rb") 61 | s = int(args.numpts) # size in one dimension 62 | ss = s * s # total size in two dimensions 63 | d = np.zeros((s, s), dtype = np.float32) # empty array for the density 64 | for i in range(s): 65 | for j in range(s): 66 | d1 = f1.read(4) 67 | dense = struct.unpack('f', d1) 68 | d[i, j] = dense[0] 69 | f1.close() 70 | 71 | #----- 72 | 73 | # plot array 74 | 75 | # get extents 76 | dmin = np.amin(d) 77 | dmax = np.amax(d) 78 | print 'data min =', dmin, 'max =', dmax 79 | if args.min >= 0.0: 80 | dmin = args.min 81 | print 'setting min for colormap to', dmin 82 | if args.max >= 0.0: 83 | dmax = args.max 84 | print 'setting max for colormap to', dmax 85 | 86 | # plot it 87 | font = {'family' : 'sans-serif', 88 | 'weight' : 'normal', 89 | 'size' : 24} 90 | matplotlib.rc('font', **font) 91 | if args.color == 'log': # log scale 92 | if dmin <= 0.0: 93 | img = plt.imshow(d, interpolation = 'nearest', cmap = 'Greys', 94 | norm=matplotlib.colors.LogNorm(), vmax=dmax) 95 | else: 96 | img = plt.imshow(d, interpolation = 'nearest', cmap = 'Greys', 97 | norm=matplotlib.colors.LogNorm(), vmin=dmin, vmax=dmax) 98 | else: # or linear scale 99 | img = plt.imshow(d, interpolation = 'nearest', cmap = 'pink', vmin=dmin, vmax=dmax) 100 | 101 | ax = plt.gca() # current graph axes 102 | ax.set_xticks([]) # no x ticks or labels 103 | ax.set_yticks([]) # no y ticks or labels 104 | plt.colorbar(img) 105 | plt.savefig('myfig', bbox_inches='tight') 106 | plt.show() # only runs interactive display when ouput mode is default=png 107 | -------------------------------------------------------------------------------- /tools/stats.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tess/tess.hpp" 3 | #include "tess/tet-neighbors.h" 4 | 5 | void print_block_info(void* b_, const diy::Master::ProxyWithLink& cp) 6 | { 7 | dblock_t* b = static_cast(b_); 8 | 9 | printf("%d: num_orig_particles = %d, num_particles = %d, num_tets = %d\n", 10 | b->gid, b->num_orig_particles, b->num_particles, b->num_tets); 11 | } 12 | 13 | void sum_edges(void* b_, const diy::Master::ProxyWithLink& cp) 14 | { 15 | dblock_t* b = static_cast(b_); 16 | 17 | std::vector< std::pair > nbrs; 18 | size_t total_edges = 0; 19 | for (size_t v = 0; v < b->num_orig_particles; ++v) 20 | { 21 | neighbor_edges(nbrs, v, b->tets, b->vert_to_tet[v]); 22 | total_edges += nbrs.size(); 23 | nbrs.clear(); 24 | } 25 | 26 | cp.all_reduce(total_edges, std::plus()); 27 | size_t nparticles = b->num_orig_particles; 28 | cp.all_reduce(nparticles, std::plus()); 29 | } 30 | 31 | 32 | int main(int argc, char** argv) { 33 | 34 | if (argc < 2) { 35 | fprintf(stderr, "Usage: stats \n"); 36 | exit(0); 37 | } 38 | 39 | diy::mpi::environment env(argc, argv); 40 | diy::mpi::communicator world; 41 | 42 | diy::Master master(world, -1, -1, 43 | &create_block, 44 | &destroy_block); 45 | 46 | diy::ContiguousAssigner assigner(world.size(), -1); // number of blocks will be set by read_blocks() 47 | diy::io::read_blocks(argv[1], world, assigner, master, &load_block_light); 48 | 49 | printf("Blocks read: %d\n", master.size()); 50 | 51 | master.foreach(print_block_info); 52 | 53 | master.foreach(sum_edges); 54 | master.exchange(); // process collectives 55 | 56 | if (world.rank() == 0) 57 | { 58 | size_t total_edges = master.proxy(0).get(); 59 | size_t total_particles = master.proxy(0).get(); 60 | 61 | printf("Total edges: %lu\n", total_edges); 62 | printf("Total points: %lu\n", total_particles); 63 | } 64 | } 65 | --------------------------------------------------------------------------------