├── .travis.yml ├── ARCHER.md ├── CMakeLists.txt ├── LICENSE ├── README.md ├── bin └── .gitignore ├── data └── Berea.conf ├── docs └── Tarantula.txt ├── include ├── CTImage.h ├── mesh_conversion.h └── writers.h ├── python ├── convert_dolfin_new_xml.py ├── stokes-dolfin-snes.py └── stokes-dolfin.py ├── src ├── CTImage.cpp ├── convert_microct.cpp ├── create_hourglass.cpp ├── mesh_conversion.cpp ├── mesh_microct.cpp ├── tarantula2gmsh.cpp ├── vtk2gmsh.cpp └── writers.cpp └── tutorial.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | before_install: 3 | - sudo apt-get update -qq 4 | - sudo apt-get install -y libvtk5-dev libvtk5.8 libcgal-dev libboost-system-dev libboost-filesystem-dev 5 | compiler: 6 | - gcc 7 | # Change this to your needs 8 | script: 9 | - mkdir build 10 | - cd build 11 | - cmake .. 12 | - make 13 | 14 | -------------------------------------------------------------------------------- /ARCHER.md: -------------------------------------------------------------------------------- 1 | Tutorial for running on ARCHER (www.archer.ac.uk) 2 | ========================== 3 | 4 | If you have not already done so then read the [basic tutorial]{https://github.com/ggorman/poreflow/blob/master/tutorial.md} first. 5 | 6 | ~/.bashrc file setup 7 | -------------------- 8 | I recommend appending these lines to your ~/.bashrc file if they are not already there: 9 | ```bash 10 | export MODULEPATH=$PWD/modules:/work/e319/shared/modules:$MODULEPATH 11 | export WORK=/work/n01/n01/$USER/ # This path will vary depending on what project you are on. The important part is that you are operating from /work and not /home. 12 | 13 | export PACKAGE_VERSION="dev" 14 | export INSTANT_CACHE_DIR=$WORK/.instant 15 | ``` 16 | 17 | Download and compile 18 | -------------------- 19 | 20 | ```bash 21 | mkdir -p $WORK/projects 22 | cd $WORK/projects 23 | git clone https://github.com/ggorman/poreflow.git 24 | cd poreflow 25 | ``` 26 | As it turns out we do not have to build any code on ARCHER - we are only going to use the python code that uses Dolfin. 27 | 28 | Prepare data sample 29 | ------------------- 30 | This should already have been completed following the instructions from the [basic tutorial]{https://github.com/ggorman/poreflow/blob/master/tutorial.md}. So create a suitable directory on ARCHER (within /work of course) and scp across the dolfin .xml data file. 31 | 32 | Running a simulation 33 | -------------------- 34 | When using ARCHER (or indeed any cluster/supercomputer) you typically do not run interactively like on a workstation. Instead you use a batch queuing system - such as PBS in this case. Here is an example PBS file for submitting a job to the queue - cut and paste from here into an editor on ARCHER. 35 | ``` 36 | # Parallel script produced by bolt 37 | # Resource: ARCHER (Cray XC30 (24-core per node)) 38 | # Batch system: PBSPro_select 39 | # 40 | # bolt is written by EPCC (http://www.epcc.ed.ac.uk) 41 | # 42 | #PBS -l select=4 43 | #PBS -N pore 44 | # The resources account this simulation is charged to. 45 | #PBS -A n02-ic1 46 | # The maximum wall time this simulation will run to. 47 | #PBS -l walltime=8:00:00 48 | 49 | # Switch to the directory where you submitted the pbs script to the queue. 50 | cd $PBS_O_WORKDIR 51 | 52 | # Load the fenics environment lovingly maintained on ARCHER by Chris Richardson and Garth Wells (thanks guys). 53 | module load fenics/dev-64bit 54 | 55 | # Use an alternative gcc version to avoid some pesky bugs. 56 | module switch gcc gcc/4.9.0 57 | 58 | # -n specifies the total number of processing elements (pes - i.e. cores) 59 | # -N number of pes that we are using per node 60 | # -S number of pes we are using per NUMA region. 61 | # -d number of cpu's per pe 62 | # In general you just want to specify -l select=x to specify the number of compute nodes you want, and then update the -n option on the next line to 24*number_of_nodes. You can leave the other options as they are. 63 | aprun -n 96 -N 24 -S 12 -d 1 python $WORK/projects/poreflow/stokes-dolfin-snes.py Berea.xml 64 | ``` 65 | 66 | To submit this "job" to the queue execute the command: 67 | ```bash 68 | qsub mysimulation.pbs 69 | ``` 70 | You monitor whether it is sitting in the queue or actually running using the command: 71 | ```bash 72 | qstat -u $USER 73 | ``` 74 | If you do not get anything back that means that your job has completed. At this point you should look at your output files: 75 | ```bash 76 | ls -ltr 77 | ``` 78 | You will see files of the pattern pore.o... and pore.e... which are the standard output and standard error of your job respectively. 79 | 80 | Possible problems 81 | ----------------- 82 | Sometimes the "instant" run compiler fails if it runs in parallel. The way around this problem is to first run a small problem in serial so that all the runtime compiling completes. Afterward you can run in parallel without issue. 83 | 84 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8.5) 2 | PROJECT(poreflow) 3 | 4 | # The version number. 5 | set (POREFLOW_VERSION_MAJOR 1) 6 | set (POREFLOW_VERSION_MINOR 0) 7 | 8 | set (CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/CMake/Modules ${CMAKE_MODULE_PATH}) 9 | 10 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 11 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 12 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 13 | 14 | enable_language(CXX) 15 | 16 | find_program (INTEL_COMPILER icpc) 17 | if (INTEL_COMPILER) 18 | include (CMakeForceCompiler) 19 | cmake_force_cxx_compiler (icpc "Intel C++ Compiler") 20 | endif (INTEL_COMPILER) 21 | 22 | set (POREFLOW_LIBRARIES) 23 | 24 | FIND_PACKAGE(VTK REQUIRED NO_MODULE) 25 | if(VTK_FOUND) 26 | message(STATUS "Found VTK: ${VTK_DIR} (found version \"${VTK_VERSION}\")") 27 | 28 | add_definitions(-DHAVE_VTK) 29 | include(${VTK_USE_FILE}) 30 | set (POREFLOW_LIBRARIES ${VTK_LIBRARIES} ${POREFLOW_LIBRARIES}) 31 | endif() 32 | 33 | include(CheckCXXCompilerFlag) 34 | CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) 35 | CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) 36 | if(COMPILER_SUPPORTS_CXX11) 37 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 38 | elseif(COMPILER_SUPPORTS_CXX0X) 39 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") 40 | else() 41 | message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") 42 | endif() 43 | 44 | find_package(OpenMP) 45 | if (OPENMP_FOUND) 46 | add_definitions(-DHAVE_OPENMP) 47 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") 48 | endif() 49 | 50 | FIND_PACKAGE(CGAL REQUIRED) 51 | if(CGAL_FOUND) 52 | include(${CGAL_USE_FILE}) 53 | set (POREFLOW_LIBRARIES CGAL CGAL_ImageIO mpfr boost_thread pthread ${POREFLOW_LIBRARIES}) 54 | endif() 55 | 56 | FIND_PACKAGE(Boost REQUIRED) 57 | if(Boost_FOUND) 58 | include(${Boost_INCLUDE_DIRS}) 59 | set (POREFLOW_LIBRARIES boost_system boost_filesystem ${POREFLOW_LIBRARIES}) 60 | endif() 61 | 62 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-literal-suffix -Wno-deprecated -std=c++0x") 63 | 64 | include_directories(include) 65 | 66 | file(GLOB CXX_SOURCES src/CTImage.cpp src/writers.cpp src/mesh_conversion.cpp) 67 | 68 | ADD_EXECUTABLE(convert_microct src/convert_microct.cpp ${CXX_SOURCES}) 69 | TARGET_LINK_LIBRARIES(convert_microct ${POREFLOW_LIBRARIES}) 70 | 71 | ADD_EXECUTABLE(create_hourglass ./src/create_hourglass.cpp ${CXX_SOURCES}) 72 | TARGET_LINK_LIBRARIES(create_hourglass ${POREFLOW_LIBRARIES}) 73 | 74 | ADD_EXECUTABLE(mesh_microct ./src/mesh_microct.cpp ${CXX_SOURCES}) 75 | TARGET_LINK_LIBRARIES(mesh_microct ${POREFLOW_LIBRARIES}) 76 | 77 | ADD_EXECUTABLE(tarantula2gmsh ./src/tarantula2gmsh.cpp ${CXX_SOURCES}) 78 | TARGET_LINK_LIBRARIES(tarantula2gmsh ${POREFLOW_LIBRARIES}) 79 | 80 | ADD_EXECUTABLE(vtk2gmsh ./src/vtk2gmsh.cpp ${CXX_SOURCES}) 81 | TARGET_LINK_LIBRARIES(vtk2gmsh ${POREFLOW_LIBRARIES}) 82 | 83 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2010 Imperial College London and others. 2 | 3 | Please see the AUTHORS file in the main source directory for a 4 | full list of copyright holders. 5 | 6 | Gerard Gorman 7 | Applied Modelling and Computation Group 8 | Department of Earth Science and Engineering 9 | Imperial College London 10 | 11 | g.gorman@imperial.ac.uk 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions 15 | are met: 16 | 1. Redistributions of source code must retain the above copyright 17 | notice, this list of conditions and the following disclaimer. 18 | 2. Redistributions in binary form must reproduce the above 19 | copyright notice, this list of conditions and the following 20 | disclaimer in the documentation and/or other materials provided 21 | with the distribution. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 24 | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 25 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 26 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 28 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 29 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 30 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 32 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 33 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 34 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 | SUCH DAMAGE. 36 | 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Poreflow 2 | ======== 3 | 4 | Modelling flow at the rock pore scale 5 | 6 | Compilation 7 | --------- 8 | ``` 9 | cd src/ 10 | make 11 | ``` 12 | The binaries are stored in bin/ 13 | 14 | Hourglass example 15 | ----------------- 16 | In this simple example we: 17 | * Create a simple image of a pore channel with a hourglass profile. 18 | * Generate a tetrahedral mesh using the Tarantula meshing code. 19 | * Use dolfin to model stokes flow through the channel and calculate the flux and permeability. 20 | 21 | ``` 22 | # Create an image 47^3 voxels in size, norrowest width of hourglass. Write file in vox image format. 23 | ./bin/create_hourglass -s 47 -t 8 -c vox -o hourglass.vox 24 | 25 | # Mesh image using tarantula's voxmesher 26 | voxmesher hourglass.conf 27 | 28 | # Convert Tarantula's .spm file into GMSH format. 29 | ./bin/tarantula2gmsh -v hourglass.spm 30 | 31 | # Convert GMSH file into Dolfin's xml format. 32 | dolfin-convert hourglass.msh hourglass.xml 33 | 34 | # Run Stokes FEM model using a direct solver and the Taylor-Hood element pair. 35 | python python/stokes-dolfin.py -D -e 0 hourglass.xml 36 | ``` 37 | You will see that the file velocity000000.vtu has been created. Use ParaView to visualise the velocity profile. 38 | 39 | Small Berea Sandstone example 40 | ----------------------------- 41 | In this simple example we: 42 | * Extract 64x64x64 section out of the Berea sandstone sample from http://www3.imperial.ac.uk/earthscienceandengineering/research/perm/porescalemodelling/micro-ct%20images%20and%20networks/berea%20sandstone 43 | * Generate a tetrahedral mesh using the Tarantula meshing code. 44 | * Use dolfin to model stokes flow through the channel and calculate the flux and permeability. 45 | 46 | ``` 47 | # Extract 64^3 voxels from micro-CT image. Write out file in vox image format. 48 | cd data/ 49 | unzip 33505696.ZIP 50 | mv Image Berea 51 | ./bin/convert_microct -c vox -s 64 Berea 52 | 53 | # Mesh image using tarantula's voxmesher 54 | voxmesher Berea.conf 55 | 56 | # Convert Tarantula's .spm file into GMSH format. 57 | ../bin/tarantula2gmsh -v Berea.spm 58 | 59 | # Convert GMSH file into Dolfin's xml format. 60 | dolfin-convert Berea.msh Berea.xml 61 | 62 | # Run Stokes FEM model using a direct solver and the Taylor-Hood element pair. 63 | python python/stokes-dolfin.py -D -e 4 Berea.xml 64 | ``` 65 | You will see that the file velocity000000.vtu has been created. Use ParaView to visualise the velocity profile. 66 | 67 | -------------------------------------------------------------------------------- /bin/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | 6 | -------------------------------------------------------------------------------- /data/Berea.conf: -------------------------------------------------------------------------------- 1 | MESHFILENAME Berea.spm 2 | VOXFILENAME Berea.vox 3 | ISO_REFINEMENT_LEVEL .5 2 4 | INTERVAL_REFINEMENT_LEVEL .5 1.5 3 5 | BASEH 8 6 | SMOOTHING_ITERATIONS 5 7 | NOSHRINK_ITERATIONS 5 8 | CUTTING_VALUE .5 9 | TETRAHEDRIZE 10 | END 11 | -------------------------------------------------------------------------------- /docs/Tarantula.txt: -------------------------------------------------------------------------------- 1 | Notes on how to use Tarantula: 2 | 3 | Example taken from http://www.biomedsearch.com/nih/Development-Anatomically-Detailed-MRI-Derived/19933417.html 4 | 5 | Parameter Description Value 6 | ISO_REFINEMENT_LEVEL Refines surface 0.5 (isosurface), 3 (levels) 7 | INTERVAL_REFINEMENT_LEVEL Refines inner mesh volume 0.5, 1.5 (isosurface lower & upper), 3 (levels) 8 | BASEH Maximal cell width 8 9 | NO_SHRINK_ITERATIONS Number of smoothing steps 1 10 | SMOOTHING_ITERATIONS Defines smoothing steps 5 11 | TAGGING Maps tags from segmentation to mesh ON 12 | TETRAHEDRALISE Constructs purely tetrahedral mesh ON 13 | 14 | Example usage is: 15 | 16 | $ voxmesher hourglass.conf 17 | 18 | where hourglass.conf is in data/ 19 | 20 | -------------------------------------------------------------------------------- /include/CTImage.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2010 Imperial College London and others. 2 | * 3 | * Please see the AUTHORS file in the main source directory for a 4 | * full list of copyright holders. 5 | * 6 | * Gerard Gorman 7 | * Applied Modelling and Computation Group 8 | * Department of Earth Science and Engineering 9 | * Imperial College London 10 | * 11 | * g.gorman@imperial.ac.uk 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions 15 | * are met: 16 | * 1. Redistributions of source code must retain the above copyright 17 | * notice, this list of conditions and the following disclaimer. 18 | * 2. Redistributions in binary form must reproduce the above 19 | * copyright notice, this list of conditions and the following 20 | * disclaimer in the documentation and/or other materials provided 21 | * with the distribution. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 24 | * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 25 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 26 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 28 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 29 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 30 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 32 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 33 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 34 | * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 | * SUCH DAMAGE. 36 | */ 37 | 38 | #ifndef CTIMAGE_H 39 | #define CTIMAGE_H 40 | 41 | #include 42 | 43 | #include 44 | #include 45 | #include 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | // Domain 52 | typedef CGAL::Exact_predicates_inexact_constructions_kernel K; 53 | typedef CGAL::Labeled_image_mesh_domain_3 Mesh_domain; 54 | 55 | // Triangulation 56 | typedef CGAL::Mesh_triangulation_3::type Tr; 57 | typedef CGAL::Mesh_complex_3_in_triangulation_3 C3t3; 58 | 59 | typedef Tr::Point Point; 60 | 61 | // Criteria 62 | typedef CGAL::Mesh_criteria_3 Mesh_criteria; 63 | typedef CGAL::Mesh_constant_domain_field_3 Sizing_field; 65 | 66 | #define BOOST_NO_CXX11_SCOPED_ENUMS 67 | #include 68 | #undef BOOST_NO_CXX11_SCOPED_ENUMS 69 | 70 | #include "boost/filesystem/fstream.hpp" 71 | #include "boost/filesystem/operations.hpp" 72 | 73 | class CTImage{ 74 | public: 75 | CTImage(); 76 | ~CTImage(); 77 | 78 | void verbose_on(); 79 | 80 | double get_porosity(); 81 | size_t get_NNodes(); 82 | size_t get_NElements(); 83 | size_t get_NFacets(); 84 | 85 | int read(std::string filename, const int offsets[], int slab_size); 86 | int read_nhdr(std::string filename, const int offsets[], int slab_size); 87 | int read_raw(std::string filename, const int offsets[], int slab_size); 88 | int create_hourglass(int size, int throat_width); 89 | 90 | void mesh(); 91 | 92 | void set_basename(std::string basename); 93 | void set_resolution(double resolution); 94 | 95 | void trim_channels(int in_boundary, int out_boundary); 96 | 97 | // Write INR file. 98 | void write_inr(const char *filename=NULL); 99 | 100 | // Write NRRD file. 101 | void write_nhdr(const char *filename=NULL); 102 | 103 | // Write vox file. 104 | void write_vox(const char *filename=NULL); 105 | 106 | // Write VTK unstructured grid file (*.vtu) 107 | void write_vtu(const char *filename=NULL); 108 | 109 | // Write GMSH file. 110 | int write_gmsh(const char *filename=NULL); 111 | 112 | private: 113 | double volume(const double *x0, const double *x1, const double *x2, const double *x3) const; 114 | 115 | bool verbose; 116 | unsigned char *raw_image; 117 | int image_size, dims[3]; 118 | double resolution; 119 | CGAL::Image_3 *image; 120 | Mesh_domain *domain; 121 | std::string basename; 122 | 123 | std::vector xyz; 124 | std::vector tets; 125 | std::vector facets; 126 | std::vector facet_ids; 127 | }; 128 | 129 | #endif 130 | -------------------------------------------------------------------------------- /include/mesh_conversion.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2010 Imperial College London and others. 2 | * 3 | * Please see the AUTHORS file in the main source directory for a 4 | * full list of copyright holders. 5 | * 6 | * Gerard Gorman 7 | * Applied Modelling and Computation Group 8 | * Department of Earth Science and Engineering 9 | * Imperial College London 10 | * 11 | * g.gorman@imperial.ac.uk 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions 15 | * are met: 16 | * 1. Redistributions of source code must retain the above copyright 17 | * notice, this list of conditions and the following disclaimer. 18 | * 2. Redistributions in binary form must reproduce the above 19 | * copyright notice, this list of conditions and the following 20 | * disclaimer in the documentation and/or other materials provided 21 | * with the distribution. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 24 | * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 25 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 26 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 28 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 29 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 30 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 32 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 33 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 34 | * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 | * SUCH DAMAGE. 36 | */ 37 | 38 | #ifndef MESH_CONVERSION_H 39 | #define MESH_CONVERSION_H 40 | 41 | #include 42 | #include 43 | 44 | int create_domain(int axis, std::vector &xyz, std::vector &tets, std::vector &facets, std::vector &facet_ids); 45 | 46 | double read_resolution_from_nhdr(std::string filename); 47 | 48 | void read_tarantula_mesh_file(std::string filename, std::string nhdr_filename, bool toggle_material, std::vector &xyz, std::vector &tets); 49 | 50 | void read_vtk_mesh_file(std::string filename, std::string nhdr_filename, std::vector &xyz, std::vector &tets); 51 | 52 | double volume(const double *x0, const double *x1, const double *x2, const double *x3); 53 | 54 | #endif 55 | 56 | -------------------------------------------------------------------------------- /include/writers.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2010 Imperial College London and others. 2 | * 3 | * Please see the AUTHORS file in the main source directory for a 4 | * full list of copyright holders. 5 | * 6 | * Gerard Gorman 7 | * Applied Modelling and Computation Group 8 | * Department of Earth Science and Engineering 9 | * Imperial College London 10 | * 11 | * g.gorman@imperial.ac.uk 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions 15 | * are met: 16 | * 1. Redistributions of source code must retain the above copyright 17 | * notice, this list of conditions and the following disclaimer. 18 | * 2. Redistributions in binary form must reproduce the above 19 | * copyright notice, this list of conditions and the following 20 | * disclaimer in the documentation and/or other materials provided 21 | * with the distribution. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 24 | * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 25 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 26 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 28 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 29 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 30 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 32 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 33 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 34 | * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 | * SUCH DAMAGE. 36 | */ 37 | #ifndef WRITERS_H 38 | #define WRITERS_H 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #include 48 | #include 49 | 50 | int write_vtk_file(std::string filename, 51 | std::vector &xyz, 52 | std::vector &tets, 53 | std::vector &facets, 54 | std::vector &facet_ids); 55 | 56 | int write_triangle_file(std::string basename, 57 | std::vector &xyz, 58 | std::vector &tets, 59 | std::vector &facets, 60 | std::vector &facet_ids); 61 | 62 | int write_gmsh_file(std::string basename, 63 | std::vector &xyz, 64 | std::vector &tets, 65 | std::vector &facets, 66 | std::vector &facet_ids); 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /python/convert_dolfin_new_xml.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from dolfin import * 3 | import sys 4 | if len(sys.argv) > 1: 5 | meshfile = sys.argv[1] 6 | else: 7 | meshfile = "mesh.xml" 8 | mesh = Mesh(meshfile) 9 | mesh_function = MeshFunction('size_t', mesh, meshfile[:-4] + "_facet_region.xml") 10 | file = File(meshfile[:-4] + '_facet_region.xml') 11 | file << mesh_function 12 | 13 | try: 14 | mesh_function = MeshFunction('size_t', mesh, meshfile[:-4] + "_physical_region.xml") 15 | file = File(meshfile[:-4] + '_physical_region.xml') 16 | file << mesh_function 17 | except IOError: 18 | print "Could not find a physical region file. Skipping." 19 | -------------------------------------------------------------------------------- /python/stokes-dolfin-snes.py: -------------------------------------------------------------------------------- 1 | # Solves the Stokes equation. This is adapted from the fenics example: 2 | # http://fenicsproject.org/documentation/dolfin/dev/python/demo/pde/stokes-iterative/python/documentation.html 3 | # 4 | # Note that the sign for the pressure has been flipped for symmetry.""" 5 | 6 | from dolfin import * 7 | from numpy import array 8 | from petsc4py import PETSc 9 | 10 | import getopt 11 | 12 | import sys 13 | 14 | def usage(): 15 | print sys.argv[0]+""" [options] dolfin_mesh.xml 16 | options: 17 | -a [xyz] Specifies the axis along which to apply the pressure gradient. By default it is applied along the x-axis. 18 | -h Prints this help message. 19 | -d Enable debugging mode. 20 | -v Enable verbose mode. 21 | -e 0-4 where: 22 | 0 P2 for velocity and P1 for pressure, i.e. Taylor-Hood. 23 | 1 p3-p1. 24 | 2 Crouzeix-Raviart. 25 | 3 p3-DG P1, CD. 26 | 4 MINI (default) 27 | -w L Zidth of domain. 28 | """ 29 | 30 | # Test for PETSc 31 | if not has_linear_algebra_backend("PETSc"): 32 | info("DOLFIN has not been configured with PETSc. Exiting.") 33 | exit() 34 | 35 | optlist, args = getopt.getopt(sys.argv[1:], 'a:dhve:') 36 | 37 | verbose = False 38 | element_pair = 4 39 | sample_width = None 40 | direct_solver = False 41 | axis = "x" 42 | for opt in optlist: 43 | if opt[0] == '-a': 44 | axis = opt[1] 45 | elif opt[0] == '-d': 46 | set_log_level(DEBUG) 47 | elif opt[0] == '-h': 48 | usage() 49 | sys.exit(0) 50 | elif opt[0] == '-v': 51 | verbose = True 52 | elif opt[0] == '-e': 53 | element_pair = int(opt[1]) 54 | elif opt[0] == '-w': 55 | sample_width = float(opt[1]) 56 | 57 | filename = args[-1] 58 | 59 | if axis == "x": 60 | axis = 0 61 | bc_in = 1 62 | bc_out = 2 63 | elif axis == "y": 64 | axis = 1 65 | bc_in = 3 66 | bc_out = 4 67 | elif axis == "z": 68 | axis = 2 69 | bc_in = 5 70 | bc_out = 6 71 | else: 72 | raise ValueError("Invalid argument for -a option") 73 | 74 | lu_args = [sys.argv[0]] + """ 75 | --petsc.se_snes_monitor 76 | --petsc.se_snes_converged_reason 77 | --petsc.se_snes_stol 0.0 78 | --petsc.se_snes_atol 1.0e-9 79 | --petsc.se_snes_rtol 1.0e-20 80 | --petsc.se_snes_max_it 200 81 | --petsc.se_ksp_type preonly 82 | --petsc.se_pc_type lu 83 | --petsc.se_pc_factor_mat_solver_package mumps 84 | """.split() 85 | 86 | fieldsplit_args = [sys.argv[0]] + """ 87 | --petsc.se_snes_monitor 88 | --petsc.se_snes_max_it 30 89 | --petsc.se_snes_converged_reason 90 | --petsc.se_snes_rtol 1.0e-5 91 | --petsc.se_snes_atol 1.0e-16 92 | --petsc.se_snes_divtol 1.0e6 93 | 94 | --petsc.se_ksp_converged_reason 95 | --petsc.se_ksp_type bcgs 96 | --petsc.se_ksp_monitor_true_residual 97 | --petsc.se_ksp_rtol 1.0e-5 98 | --petsc.se_ksp_atol 1.0e-16 99 | --petsc.se_ksp_divtol 1.0e6 100 | 101 | --petsc.se_pc_type fieldsplit 102 | --petsc.se_pc_fieldsplit_type schur 103 | --petsc.se_pc_fieldsplit_schur_factorization_type upper 104 | 105 | --petsc.se_fieldsplit_0_ksp_type preonly 106 | --petsc.se_fieldsplit_0_ksp_max_it 1 107 | --petsc.se_fieldsplit_0_pc_type hypre 108 | --petsc.se_fieldsplit_0_pc_hypre_type boomeramg 109 | 110 | --petsc.se_fieldsplit_1_ksp_type preonly 111 | --petsc.se_fieldsplit_1_ksp_max_it 1 112 | --petsc.se_fieldsplit_1_pc_type jacobi 113 | """.split() 114 | 115 | parameters["std_out_all_processes"] = False 116 | args = fieldsplit_args 117 | parameters.parse(args) 118 | 119 | mesh = Mesh(filename) 120 | 121 | n = FacetNormal(mesh) 122 | 123 | # Define function spaces 124 | Z = None 125 | if element_pair == 0: 126 | # - Taylor-Hood 127 | p = 2 128 | V = VectorFunctionSpace(mesh, "Lagrange", p) 129 | Q = FunctionSpace(mesh, "Lagrange", p-1) 130 | Z = V * Q 131 | elif element_pair == 1: 132 | V = VectorFunctionSpace(mesh, "Lagrange", 3) 133 | Q = FunctionSpace(mesh, "Lagrange", 1) 134 | Z = V * Q 135 | elif element_pair == 2: 136 | # - Crouzeix-Raviart 137 | V = VectorFunctionSpace(mesh, "Crouzeix-Raviart", 1) 138 | Q = FunctionSpace(mesh, "Discontinuous Lagrange", 0) 139 | Z = V * Q 140 | elif element_pair == 3: 141 | # - CD 142 | V = VectorFunctionSpace(mesh, "Lagrange", 3) 143 | Q = FunctionSpace(mesh, "Discontinuous Lagrange", 1) 144 | Z = V * Q 145 | elif element_pair == 4: 146 | # - MINI 147 | p = 1 148 | P1 = VectorFunctionSpace(mesh, "Lagrange", p) 149 | B = VectorFunctionSpace(mesh, "Bubble", p+3) 150 | Q = FunctionSpace(mesh, "Lagrange", p) 151 | Z = (P1+B)*Q 152 | 153 | # Boundary 154 | boundaries = MeshFunction('size_t', mesh, filename[0:-4] + "_facet_region.xml") 155 | 156 | ds = Measure('ds')[boundaries] 157 | 158 | # A few physical paramerters 159 | dP = 1.0 160 | 161 | # Boundary conditions 162 | noslip = Constant((0.0, 0.0, 0.0)) 163 | p_in = Constant(dP) 164 | p_out = Constant(0.0) 165 | bcs = [] 166 | for i in range(1, 8): 167 | if i != bc_in and i != bc_out: 168 | bcs.append(DirichletBC(Z.sub(0), noslip, boundaries, i)) 169 | 170 | # Define variational problem 171 | z = Function(Z) 172 | (u, p) = split(z) 173 | (v, q) = TestFunctions(Z) 174 | 175 | F = (inner(grad(u), grad(v)) - div(v)*p + q*div(u))*dx + p_in*dot(v, n)*ds(bc_in) 176 | 177 | # Class for interacting with Newton solver. 178 | class GeneralProblem(NonlinearProblem): 179 | def __init__(self, F, z, bcs): 180 | NonlinearProblem.__init__(self) 181 | self.fform = F 182 | self.z = z 183 | self.bcs = bcs 184 | self.jacobian = derivative(F, z) 185 | 186 | def F(self, b, x): 187 | assemble(self.fform, tensor=b) 188 | [bc.apply(b, x) for bc in self.bcs] 189 | 190 | def J(self, A, x): 191 | assemble(self.jacobian, tensor=A) 192 | [bc.apply(A) for bc in self.bcs] 193 | 194 | problem = GeneralProblem(F, z, bcs=bcs) 195 | solver = PETScSNESSolver() 196 | 197 | solver.parameters["relative_tolerance"] = 1e-6 198 | solver.parameters["report"] = False 199 | solver.parameters["options_prefix"] = "se" 200 | solver.parameters["error_on_nonconvergence"] = False 201 | s 202 | solver.init(problem, z.vector()) 203 | snes = solver.snes() 204 | snes.setFromOptions() 205 | 206 | # Configure the FIELDSPLIT stuff. 207 | if args != lu_args: 208 | pc = snes.ksp.pc 209 | 210 | fields = [] 211 | for i in range(2): 212 | subspace = SubSpace(Z, i) 213 | subdofs = subspace.dofmap().dofs() 214 | IS = PETSc.IS() 215 | IS.createGeneral(subdofs) 216 | name = str(i) 217 | fields.append((name, IS)) 218 | 219 | pc.setFieldSplitIS(*fields) 220 | 221 | def extract_sub_matrix(A, subspace_in, subspace_out): 222 | Amat = as_backend_type(A).mat() 223 | 224 | subis_in = PETSc.IS() 225 | subdofs_in = Z.sub(subspace_in).dofmap().dofs() 226 | subis_in.createGeneral(subdofs_in) 227 | 228 | subis_out = PETSc.IS() 229 | subdofs_out = Z.sub(subspace_out).dofmap().dofs() 230 | subis_out.createGeneral(subdofs_out) 231 | 232 | submat = Amat.getSubMatrix(subis_out, subis_in) 233 | 234 | return submat 235 | 236 | def extract_sub_vector(V, subspace): 237 | Vvec = as_backend_type(V.vector()).vec() 238 | 239 | subis = PETSc.IS() 240 | subdofs = Z.sub(subspace).dofmap().dofs() 241 | subis.createGeneral(subdofs) 242 | 243 | subvec = Vvec.getSubVector(subis) 244 | dupe = subvec.duplicate() 245 | Vvec.restoreSubVector(subis, subvec) 246 | 247 | return dupe 248 | 249 | (u, p) = TrialFunctions(Z) 250 | (v, q) = TestFunctions(Z) 251 | 252 | schur_D = assemble(inner(p, q)*dx) 253 | schur = extract_sub_matrix(schur_D, 1, 1) 254 | 255 | def monitor(snes, its, norm): 256 | pc = snes.ksp.pc 257 | pc.setFieldSplitSchurPreType(PETSc.PC.SchurPreType.USER, schur) 258 | 259 | snes.setMonitor(monitor) 260 | 261 | snes.solve(None, as_backend_type(z.vector()).vec()) 262 | 263 | # File("solution_stokes.xml.gz") << z 264 | 265 | (u, p) = z.split() 266 | 267 | # Darcy's law 268 | # U = -k/mu dP/L 269 | # Darcy velocity U = flux/area = flux/L^2 270 | # Therefore, permability, k = UL/dP = (flux mu)/(L dP) (here mu==1) 271 | 272 | flux = [assemble(dot(u, n)*ds(i)) for i in range(1, 8)] 273 | mean_flux = (-flux[bc_in-1]+flux[bc_out-1])*0.5 274 | 275 | bbox = array([MPI.min(mesh.mpi_comm(), mesh.coordinates()[:,0].min()), MPI.max(mesh.mpi_comm(), mesh.coordinates()[:,0].max()), 276 | MPI.min(mesh.mpi_comm(), mesh.coordinates()[:,1].min()), MPI.max(mesh.mpi_comm(), mesh.coordinates()[:,1].max()), 277 | MPI.min(mesh.mpi_comm(), mesh.coordinates()[:,2].min()), MPI.max(mesh.mpi_comm(), mesh.coordinates()[:,2].max())]) 278 | 279 | L = (bbox[1]-bbox[0] + bbox[3]-bbox[2] + bbox[5]-bbox[4])/3.0 280 | 281 | if sample_width: 282 | mean_flux*=(sample_width/L)**3 283 | L = sample_width 284 | 285 | permability = (mean_flux)/(dP*L) 286 | 287 | if MPI.rank(mesh.mpi_comm()) == 0: 288 | print flux 289 | print """################################# 290 | ## In-flux = %g 291 | ## Out-flux = %g 292 | ## Permability = %g m^2 (%g D) 293 | #################################"""%(flux[bc_in-1], flux[bc_out-1], permability, permability*1e12) 294 | 295 | # Save solution in VTK format 296 | # ufile_pvd = File("velocity.pvd") 297 | # ufile_pvd << u 298 | # pfile_pvd = File("pressure.pvd") 299 | # pfile_pvd << p 300 | 301 | -------------------------------------------------------------------------------- /python/stokes-dolfin.py: -------------------------------------------------------------------------------- 1 | # Solves the Stokes equation. This is adapted from the fenics example: 2 | # http://fenicsproject.org/documentation/dolfin/dev/python/demo/pde/stokes-iterative/python/documentation.html 3 | 4 | from dolfin import * 5 | from numpy import array 6 | 7 | import getopt 8 | import sys 9 | import os.path 10 | 11 | def usage(): 12 | print sys.argv[0]+""" [options] dolfin_mesh.xml 13 | options: 14 | -h Prints this help message. 15 | -d Enable debugging mode. 16 | -v Enable verbose mode. 17 | -e 0-4 where: 18 | 0 P2 for velocity and P1 for pressure, i.e. Taylor-Hood. 19 | 1 p3-p1. 20 | 2 Crouzeix-Raviart. 21 | 3 p3-DG P1, CD. 22 | 4 MINI (default) 23 | """ 24 | 25 | # Test for PETSc 26 | if not has_linear_algebra_backend("PETSc"): 27 | info("DOLFIN has not been configured with PETSc. Exiting.") 28 | exit() 29 | 30 | # Set defaults for options. 31 | verbose = False 32 | element_pair = 4 33 | 34 | # Parse commandline options. 35 | optlist, args = getopt.getopt(sys.argv[1:], 'dhve:') 36 | for opt in optlist: 37 | if opt[0] == '-d': 38 | set_log_level(DEBUG) 39 | elif opt[0] == '-h': 40 | usage() 41 | sys.exit(0) 42 | elif opt[0] == '-v': 43 | verbose = True 44 | elif opt[0] == '-e': 45 | element_pair = int(opt[1]) 46 | 47 | filename = args[-1] 48 | if not os.path.isfile(filename): 49 | raise IOError("No such file: %s"%filename) 50 | usage() 51 | exit() 52 | 53 | axis = 0 54 | bc_in = 1 55 | bc_out = 2 56 | 57 | mesh = Mesh(filename) 58 | 59 | n = FacetNormal(mesh) 60 | 61 | # Define function spaces. 62 | W = None 63 | if element_pair == 0: 64 | # - Taylor-Hood 65 | p = 2 66 | V = VectorFunctionSpace(mesh, "Lagrange", p) 67 | Q = FunctionSpace(mesh, "Lagrange", p-1) 68 | W = V * Q 69 | elif element_pair == 1: 70 | V = VectorFunctionSpace(mesh, "Lagrange", 3) 71 | Q = FunctionSpace(mesh, "Lagrange", 1) 72 | W = V * Q 73 | elif element_pair == 2: 74 | # - Crouzeix-Raviart 75 | V = VectorFunctionSpace(mesh, "Crouzeix-Raviart", 1) 76 | Q = FunctionSpace(mesh, "Discontinuous Lagrange", 0) 77 | W = V * Q 78 | elif element_pair == 3: 79 | # - CD 80 | V = VectorFunctionSpace(mesh, "Lagrange", 3) 81 | Q = FunctionSpace(mesh, "Discontinuous Lagrange", 1) 82 | W = V * Q 83 | elif element_pair == 4: 84 | # - MINI 85 | p = 1 86 | P1 = VectorFunctionSpace(mesh, "Lagrange", p) 87 | B = VectorFunctionSpace(mesh, "Bubble", p+3) 88 | Q = FunctionSpace(mesh, "Lagrange", p) 89 | W = (P1+B)*Q 90 | 91 | # Boundary 92 | boundaries = MeshFunction('size_t', mesh, filename[0:-4] + "_facet_region.xml") 93 | 94 | ds = Measure('ds')[boundaries] 95 | 96 | # Boundary conditions 97 | dP = 1.0 98 | noslip = Constant((0.0, 0.0, 0.0)) 99 | p_in = Constant(dP) 100 | p_out = Constant(0.0) 101 | bcs = [] 102 | for i in range(1, 8): 103 | if i != bc_in and i != bc_out: 104 | bcs.append(DirichletBC(W.sub(0), noslip, boundaries, i)) 105 | 106 | # Define variational problem 107 | (u, p) = TrialFunctions(W) 108 | (v, q) = TestFunctions(W) 109 | 110 | a = (inner(grad(u), grad(v)) - div(v)*p + q*div(u))*dx 111 | A = assemble(a) 112 | 113 | L = inner(Constant((0.0, 0.0, 0.0)), v)*dx - p_in*dot(v, n)*ds(bc_in) 114 | b = assemble(L) 115 | 116 | for bc in bcs: 117 | bc.apply(A) 118 | bc.apply(b) 119 | 120 | solver = LUSolver("mumps") 121 | solver.set_operator(A) 122 | 123 | # Solve 124 | U = Function(W) 125 | solver.solve(U.vector(), b) 126 | 127 | # Get sub-functions 128 | u, p = U.split() 129 | 130 | # Darcy's law 131 | # U = -k/mu dP/L 132 | # Here the visocity, mu, and dP are both set to unity. The Darcy velocity U = flux/area = flux/L^2 133 | # Therefore, permability, k = UL = flux/L 134 | 135 | flux = [assemble(dot(u, n)*ds(i)) for i in range(1, 8)] 136 | mean_flux = (-flux[bc_in-1]+flux[bc_out-1])*0.5 137 | 138 | bbox = array([MPI.min(mesh.mpi_comm(), mesh.coordinates()[:,0].min()), MPI.max(mesh.mpi_comm(), mesh.coordinates()[:,0].max()), 139 | MPI.min(mesh.mpi_comm(), mesh.coordinates()[:,1].min()), MPI.max(mesh.mpi_comm(), mesh.coordinates()[:,1].max()), 140 | MPI.min(mesh.mpi_comm(), mesh.coordinates()[:,2].min()), MPI.max(mesh.mpi_comm(), mesh.coordinates()[:,2].max())]) 141 | 142 | L = (bbox[1]-bbox[0] + bbox[3]-bbox[2] + bbox[5]-bbox[4])/3.0 143 | 144 | permability = mean_flux/L 145 | 146 | if MPI.rank(mesh.mpi_comm()) == 0: 147 | print flux 148 | print """################################# 149 | ## In-flux = %g 150 | ## Out-flux = %g 151 | ## Permability = %g 152 | #################################"""%(flux[bc_in-1], flux[bc_out-1], permability) 153 | 154 | # Save solution in VTK format 155 | ufile_pvd = File("velocity.pvd") 156 | ufile_pvd << u 157 | pfile_pvd = File("pressure.pvd") 158 | pfile_pvd << p 159 | 160 | -------------------------------------------------------------------------------- /src/CTImage.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2010 Imperial College London and others. 2 | * 3 | * Please see the AUTHORS file in the main source directory for a 4 | * full list of copyright holders. 5 | * 6 | * Gerard Gorman 7 | * Applied Modelling and Computation Group 8 | * Department of Earth Science and Engineering 9 | * Imperial College London 10 | * 11 | * g.gorman@imperial.ac.uk 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions 15 | * are met: 16 | * 1. Redistributions of source code must retain the above copyright 17 | * notice, this list of conditions and the following disclaimer. 18 | * 2. Redistributions in binary form must reproduce the above 19 | * copyright notice, this list of conditions and the following 20 | * disclaimer in the documentation and/or other materials provided 21 | * with the distribution. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 24 | * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 25 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 26 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 28 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 29 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 30 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 32 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 33 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 34 | * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 | * SUCH DAMAGE. 36 | */ 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | 54 | #include 55 | #include 56 | #include 57 | 58 | #include 59 | #include 60 | 61 | #include "CTImage.h" 62 | 63 | // To avoid verbose function and named parameters call 64 | using namespace CGAL::parameters; 65 | 66 | CTImage::CTImage(){ 67 | verbose = false; 68 | for(int i=0;i<3;i++) 69 | dims[i] = -1; 70 | resolution=1.0; 71 | image = NULL; 72 | raw_image = NULL; 73 | domain = NULL; 74 | } 75 | 76 | CTImage::~CTImage(){ 77 | if(raw_image!=NULL) 78 | delete raw_image; 79 | if(domain!=NULL) 80 | delete domain; 81 | } 82 | 83 | void CTImage::verbose_on(){ 84 | verbose = true; 85 | } 86 | 87 | double CTImage::get_porosity(){ 88 | int cnt=0; 89 | for(int i=0;i nrrd_dict; 153 | while(std::getline(nhdr, line)){ 154 | // Skip comments. 155 | if(line.compare(0, 1, "#")==0){ 156 | continue; 157 | } 158 | 159 | int delimiter_pos = line.find(":"); 160 | std::string key = line.substr(0, delimiter_pos); 161 | 162 | int offset = line.substr(delimiter_pos+1).find_first_not_of(" \t"); 163 | std::string value = line.substr(delimiter_pos+1+offset); 164 | value = value.substr(0, value.find_last_not_of(" \t")); 165 | nrrd_dict[key] = value; 166 | } 167 | 168 | std::string filename_raw; 169 | double units=1.0; 170 | for(std::map::const_iterator it=nrrd_dict.begin(); it!=nrrd_dict.end();++it){ 171 | if(it->first=="data file"){ 172 | // Add a path if necessary. 173 | int slash = filename.find_last_of("/\\"); 174 | if(slash!=std::string::npos){ 175 | // filename_raw = filename.substr(0, slash+1)+it->second.substr(0, it->second.find_last_not_of(" ")); 176 | filename_raw = filename.substr(0, slash+1)+it->second; 177 | }else{ 178 | filename_raw = it->second; 179 | } 180 | }else if(it->first=="dimension"){ 181 | if(atoi(it->second.c_str())!=3){ 182 | std::cerr<<"ERROR: Expected dimension 3, but got ->"<second<<"<-"<second.c_str())<first=="encoding"){ 186 | if(it->second!="raw"){ 187 | std::cerr<<"ERROR: Expected raw encoding, but got ->"<second<<"<-"<first=="endian"){ 191 | if(it->second!="little"){ 192 | std::cerr<<"ERROR: Expected little endian, but got ->"<second<<"<-"<first=="type"){ 196 | if(it->second!="uchar"){ 197 | std::cerr<<"ERROR: Expected date dype of image data to be uchar, but got ->"<second<<"<-"<first=="sizes"){ 201 | int loc = it->second.find(" "); 202 | dims[0] = atoi(it->second.substr(0, loc).c_str()); 203 | 204 | // Going to assume for now all the dimensions are the same as I do not have a use case where this is otherwise. 205 | dims[1] = dims[0]; 206 | dims[2] = dims[0]; 207 | }else if(it->first=="space directions"){ 208 | int loc = it->second.find("("); 209 | int length = it->second.substr(loc+1).find(","); 210 | resolution = atof(it->second.substr(loc+1, length).c_str()); 211 | }else if(it->first=="space units"){ 212 | int loc = it->second.find('"'); 213 | int length = it->second.substr(loc+1).find('"'); 214 | if(it->second.substr(loc+1, length)=="µm"){ 215 | units = 1.0e-6; 216 | }else{ 217 | std::cerr<<"WARNING: units not handled. Set manually."<"<first<<"<- :: ->"<second<dims[i]){ 253 | std::cerr<<"ERROR: slab_size larger than original image.\n"; 254 | exit(-1); 255 | } 256 | } 257 | 258 | raw_image = new unsigned char[image_size]; 259 | 260 | std::ifstream image_file; 261 | image_file.open(filename, std::ios::binary); 262 | image_file>>raw_image; 263 | image_file.close(); 264 | 265 | if(slab_size>0){ 266 | int dim2 = dims[2]*dims[1]; 267 | int slab_size2 = slab_size*slab_size; 268 | int image_size_new = slab_size*slab_size*slab_size; 269 | unsigned char *raw_image_new = new unsigned char[image_size_new]; 270 | for(int k=0;kdata()); 332 | image->set_data((void*)raw_image); 333 | 334 | // Domain 335 | domain = new Mesh_domain(*image); 336 | 337 | // Mesh criteria 338 | Mesh_criteria criteria(facet_angle=25.0, 339 | facet_size=1.0, 340 | cell_size=2.0, 341 | facet_distance=0.1); 342 | 343 | // Mesh_criteria criteria(facet_angle=25, facet_size=subsample*resolution, 344 | // cell_radius_edge_ratio=3, cell_size=subsample*resolution); 345 | 346 | //Mesh_criteria criteria(facet_angle=30, facet_size=0.1, facet_distance=0.025, 347 | // cell_radius_edge_ratio=2, cell_size=5); 348 | 349 | // Mesh generation and optimization in one call 350 | C3t3 c3t3 = CGAL::make_mesh_3(*domain, criteria, 351 | lloyd(time_limit=60), 352 | // odt(time_limit=60), 353 | exude(time_limit=60, sliver_bound=10)); 354 | 355 | // C3t3 c3t3 = CGAL::make_mesh_3(*domain, criteria); 356 | 357 | // Mesh generation and optimization in several call 358 | // C3t3 c3t3 = CGAL::make_mesh_3(domain, criteria, 359 | // no_perturb(), no_exude()); 360 | 361 | // Output 362 | // 363 | 364 | // Export points 365 | Tr t = c3t3.triangulation(); // get triangulation (needed below) 366 | 367 | size_t index = 0; 368 | std::map coordToId; 369 | for(Tr::Point_iterator it=t.points_begin();it!=t.points_end();it++, index++){ 370 | xyz.push_back(resolution*CGAL::to_double(it->x())); 371 | xyz.push_back(resolution*CGAL::to_double(it->y())); 372 | xyz.push_back(resolution*CGAL::to_double(it->z())); 373 | coordToId[*it] = index; 374 | } 375 | 376 | // Export elements 377 | for (C3t3::Cells_in_complex_iterator it = c3t3.cells_in_complex_begin(); it != c3t3.cells_in_complex_end(); ++it){ 378 | tets.push_back(coordToId[it->vertex(0)->point()]); 379 | tets.push_back(coordToId[it->vertex(1)->point()]); 380 | tets.push_back(coordToId[it->vertex(2)->point()]); 381 | tets.push_back(coordToId[it->vertex(3)->point()]); 382 | } 383 | 384 | // Find boundary facets 385 | size_t NNodes = get_NNodes(); 386 | std::vector< std::set > NEList(NNodes); 387 | 388 | size_t NElements = get_NElements(); 389 | std::vector EEList(NElements*4, -1); 390 | 391 | for(int i=0;i::const_iterator it=NEList[n1].begin();it!=NEList[n1].end();++it){ 403 | if(*it==i) 404 | continue; 405 | if(NEList[n2].find(*it)!=NEList[n2].end() && NEList[n3].find(*it)!=NEList[n3].end()){ 406 | EEList[i*4+j] = *it; 407 | break; 408 | } 409 | } 410 | } 411 | } 412 | for(int i=0;i(NFacets, 7); 439 | double dx=0.05*resolution; 440 | double dy=0.05*resolution; 441 | double dz=0.05*resolution; 442 | for(int i=0;iresolution = resolution; 470 | } 471 | 472 | void CTImage::trim_channels(int in_boundary, int out_boundary){ 473 | if(verbose) 474 | std::cout<<"void trim_channels(int in_boundary, int out_boundary)"< > NEList(NNodes); 481 | int count_positive=0, count_negative=0; 482 | for(int i=0;i EEList(NElements*4, -1); 504 | for(int i=0;i edge_neighbours; 510 | set_intersection(NEList[tets[i*4+(j+1)%4]].begin(), NEList[tets[i*4+(j+1)%4]].end(), 511 | NEList[tets[i*4+(j+2)%4]].begin(), NEList[tets[i*4+(j+2)%4]].end(), 512 | inserter(edge_neighbours, edge_neighbours.begin())); 513 | 514 | std::set neighbours; 515 | set_intersection(NEList[tets[i*4+(j+3)%4]].begin(), NEList[tets[i*4+(j+3)%4]].end(), 516 | edge_neighbours.begin(), edge_neighbours.end(), 517 | inserter(neighbours, neighbours.begin())); 518 | 519 | if(neighbours.size()==2){ 520 | if(*neighbours.begin()==i) 521 | EEList[i*4+j] = *neighbours.rbegin(); 522 | else 523 | EEList[i*4+j] = *neighbours.begin(); 524 | } 525 | #ifndef NDEBUG 526 | else{ 527 | assert(neighbours.size()==1); 528 | assert(*neighbours.begin()==i); 529 | } 530 | #endif 531 | } 532 | } 533 | 534 | // Create full facet ID list. Also, create the initial fronts for 535 | // the active region detection. 536 | std::map< std::set, int> facet_id_lut; 537 | int NFacets = facet_ids.size(); 538 | for(int i=0;i facet; 540 | for(int j=0;j<3;j++){ 541 | facet.insert(facets[i*3+j]); 542 | } 543 | assert(facet.size()==3); 544 | assert(facet_id_lut.find(facet)==facet_id_lut.end()); 545 | facet_id_lut[facet] = facet_ids[i]; 546 | } 547 | 548 | std::set front0, front1; 549 | std::map< std::set, int> facet_element_lut; 550 | for(int i=0;i facet; 557 | for(int k=1;k<4;k++) 558 | facet.insert(tets[i*4+(j+k)%4]); 559 | assert(facet.size()==3); 560 | assert(facet_element_lut.find(facet)==facet_element_lut.end()); 561 | facet_element_lut[facet] = i; 562 | 563 | std::map< std::set, int>::iterator facet_id_pair = facet_id_lut.find(facet); 564 | if(facet_id_pair==facet_id_lut.end()){ 565 | facet_ids.push_back(7); 566 | 567 | if(j==0){ 568 | facets.push_back(tets[i*4+1]); facets.push_back(tets[i*4+3]); facets.push_back(tets[i*4+2]); 569 | }else if(j==1){ 570 | facets.push_back(tets[i*4]); facets.push_back(tets[i*4+3]); facets.push_back(tets[i*4+2]); 571 | }else if(j==2){ 572 | facets.push_back(tets[i*4]); facets.push_back(tets[i*4+3]); facets.push_back(tets[i*4+1]); 573 | }else if(j==3){ 574 | facets.push_back(tets[i*4]); facets.push_back(tets[i*4+2]); facets.push_back(tets[i*4+1]); 575 | } 576 | }else{ 577 | if(facet_id_pair->second==in_boundary) 578 | front0.insert(i); 579 | else if(facet_id_pair->second==out_boundary) 580 | front1.insert(i); 581 | } 582 | } 583 | } 584 | } 585 | 586 | // Advance front0 587 | std::vector label(NElements, 0); 588 | while(!front0.empty()){ 589 | // Get the next unprocessed element in the set. 590 | int seed = *front0.begin(); 591 | front0.erase(front0.begin()); 592 | if(label[seed]==1) 593 | continue; 594 | label[seed] = 1; 595 | 596 | for(int i=0;i<4;i++){ 597 | int eid = EEList[seed*4+i]; 598 | if(eid!=-1 && label[eid]!=1){ 599 | front0.insert(eid); 600 | } 601 | } 602 | } 603 | 604 | // Advance back sweep using front1. 605 | while(!front1.empty()){ 606 | // Get the next unprocessed element in the set. 607 | int seed = *front1.begin(); 608 | front1.erase(front1.begin()); 609 | if(label[seed]!=1) // ie was either never of interest or has been processed in the backsweep. 610 | continue; 611 | label[seed] = 2; 612 | 613 | for(int i=0;i<4;i++){ 614 | int eid = EEList[seed*4+i]; 615 | if(eid!=-1 && label[eid]==1){ 616 | front1.insert(eid); 617 | } 618 | } 619 | } 620 | 621 | // Find active vertex set and create renumbering. 622 | std::map renumbering; 623 | for(int i=0;i(tets[i*4+j], -1)); 630 | } 631 | } 632 | 633 | // Create new compressed mesh. 634 | std::vector xyz_new; 635 | std::vector tets_new; 636 | std::vector facets_new; 637 | std::vector facet_ids_new; 638 | int cnt=0; 639 | for(std::map::iterator it=renumbering.begin();it!=renumbering.end();++it){ 640 | it->second = cnt++; 641 | 642 | xyz_new.push_back(xyz[(it->first)*3]); 643 | xyz_new.push_back(xyz[(it->first)*3+1]); 644 | xyz_new.push_back(xyz[(it->first)*3+2]); 645 | } 646 | for(int i=0;i facet; 659 | for(int j=0;j<3;j++) 660 | facet.insert(facets[i*3+j]); 661 | 662 | // Check if this is an orphaned facet from previous purge. 663 | if(facet_element_lut.find(facet)==facet_element_lut.end()) 664 | continue; 665 | 666 | // Check if this is a newly orphaned facet. 667 | if(label[facet_element_lut[facet]]!=2){ 668 | continue; 669 | } 670 | 671 | for(int j=0;j<3;j++){ 672 | std::map::iterator it=renumbering.find(facets[i*3+j]); 673 | assert(it!=renumbering.end()); 674 | facets_new.push_back(it->second); 675 | } 676 | 677 | facet_ids_new.push_back(facet_ids[i]); 678 | } 679 | xyz.swap(xyz_new); 680 | tets.swap(tets_new); 681 | facets.swap(facets_new); 682 | facet_ids.swap(facet_ids_new); 683 | } 684 | 685 | // Write INR file. 686 | void CTImage::write_inr(const char *filename){ 687 | if(verbose) 688 | std::cout<<"void write_inr()"<image(), std::string(basename+".inr").c_str()); 692 | else 693 | _writeImage(image->image(), filename); 694 | } 695 | 696 | // Write NHDR file. 697 | void CTImage::write_nhdr(const char *filename){ 698 | if(verbose) 699 | std::cout<<"void write_nhdr()"< ug_tets = vtkSmartPointer::New(); 765 | 766 | // Write out points 767 | size_t NNodes = get_NNodes(); 768 | vtkSmartPointer pts = vtkSmartPointer::New(); 769 | pts->SetNumberOfPoints(NNodes); 770 | for(int i=0;iSetPoint(i, &(xyz[i*3])); 772 | } 773 | ug_tets->SetPoints(pts); 774 | 775 | size_t NElements = get_NElements(); 776 | for(int i=0;i idlist = vtkSmartPointer::New(); 778 | for(int j=0;j<4;j++) 779 | idlist->InsertNextId(tets[i*4+j]); 780 | ug_tets->InsertNextCell(10, idlist); 781 | } 782 | 783 | vtkSmartPointer tet_writer = vtkSmartPointer::New(); 784 | if(filename==NULL) 785 | tet_writer->SetFileName(std::string(basename+".vtu").c_str()); 786 | else 787 | tet_writer->SetFileName(filename); 788 | #if VTK_MAJOR_VERSION < 6 789 | tet_writer->SetInput(ug_tets); 790 | #else 791 | tet_writer->SetInputData(ug_tets); 792 | #endif 793 | tet_writer->Write(); 794 | 795 | // Initalise the vtk mesh 796 | vtkSmartPointer ug_tris = vtkSmartPointer::New(); 797 | ug_tris->SetPoints(pts); 798 | 799 | // Write facet 800 | size_t NFacets = get_NFacets(); 801 | for(int i=0;i idlist = vtkSmartPointer::New(); 803 | for(int j=0;j<3;j++) 804 | idlist->InsertNextId(facets[i*3+j]); 805 | ug_tris->InsertNextCell(5, idlist); 806 | } 807 | 808 | // Write facet_ids 809 | vtkSmartPointer vtk_facet_ids = vtkSmartPointer::New(); 810 | vtk_facet_ids->SetName("Boundary label"); 811 | vtk_facet_ids->SetNumberOfComponents(1); 812 | vtk_facet_ids->SetNumberOfTuples(NFacets); 813 | for(int i=0;iSetValue(i, facet_ids[i]); 815 | ug_tris->GetCellData()->AddArray(vtk_facet_ids); 816 | 817 | vtkSmartPointer tri_writer = vtkSmartPointer::New(); 818 | if(filename==NULL) 819 | tri_writer->SetFileName(std::string(basename+"_facets.vtu").c_str()); 820 | else 821 | tri_writer->SetFileName((std::string(filename, strlen(filename)-4)+"_facets.vtu").c_str()); 822 | #if VTK_MAJOR_VERSION < 6 823 | tri_writer->SetInput(ug_tris); 824 | #else 825 | tri_writer->SetInputData(ug_tris); 826 | #endif 827 | tri_writer->Write(); 828 | 829 | } 830 | 831 | int CTImage::write_gmsh(const char *filename){ 832 | if(verbose) 833 | std::cout<<"int write_gmsh()"<::digits10+1); 851 | for(size_t i=0;i 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include "CTImage.h" 45 | 46 | void usage(char *cmd){ 47 | std::cout<<"Usage: "<0){ 170 | image.set_resolution(resolution); 171 | } 172 | 173 | if(convert==std::string("vox")){ 174 | if(verbose) 175 | std::cout<<"INFO: Write VOX file\n"; 176 | 177 | image.write_vox(); 178 | }else if(convert==std::string("nhrd")){ 179 | if(verbose) 180 | std::cout<<"INFO: Write NHDR file\n"; 181 | 182 | image.write_nhdr(); 183 | }else if(convert==std::string("inr")){ 184 | if(verbose) 185 | std::cout<<"INFO: Write INR file\n"; 186 | 187 | image.write_inr(); 188 | } 189 | 190 | return 0; 191 | } 192 | -------------------------------------------------------------------------------- /src/create_hourglass.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2010 Imperial College London and others. 2 | * 3 | * Please see the AUTHORS file in the main source directory for a 4 | * full list of copyright holders. 5 | * 6 | * Gerard Gorman 7 | * Applied Modelling and Computation Group 8 | * Department of Earth Science and Engineering 9 | * Imperial College London 10 | * 11 | * g.gorman@imperial.ac.uk 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions 15 | * are met: 16 | * 1. Redistributions of source code must retain the above copyright 17 | * notice, this list of conditions and the following disclaimer. 18 | * 2. Redistributions in binary form must reproduce the above 19 | * copyright notice, this list of conditions and the following 20 | * disclaimer in the documentation and/or other materials provided 21 | * with the distribution. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 24 | * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 25 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 26 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 28 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 29 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 30 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 32 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 33 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 34 | * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 | * SUCH DAMAGE. 36 | */ 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include "CTImage.h" 44 | 45 | void usage(char *cmd){ 46 | std::cout<<"Usage: "< 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | 58 | #include "writers.h" 59 | #include "mesh_conversion.h" 60 | 61 | int create_domain(int axis, 62 | std::vector &xyz, 63 | std::vector &tets, 64 | std::vector &facets, 65 | std::vector &facet_ids){ 66 | 67 | // Create node-element adjancy list. 68 | size_t NNodes = xyz.size()/3; 69 | 70 | std::vector< std::unordered_set > NEList(NNodes); 71 | int NTetra = tets.size()/4; 72 | 73 | for(int i=0;i EEList(NTetra*4); 90 | #pragma omp parallel 91 | { 92 | // Initialise and ensure 1st touch placement. 93 | #pragma omp for 94 | for(int i=0;i<4*NTetra;i++) 95 | EEList[i] = -1; 96 | 97 | #pragma omp for 98 | for(int i=0;i front0, front1; 181 | for(size_t i=0;i label(NTetra, 0); 231 | while(!front0.empty()){ 232 | // Get the next unprocessed element in the set. 233 | int seed = *front0.begin(); 234 | front0.erase(front0.begin()); 235 | if(label[seed]==1) 236 | continue; 237 | 238 | label[seed] = 1; 239 | 240 | for(int i=0;i<4;i++){ 241 | int eid = EEList[seed*4+i]; 242 | if(eid!=-1 && label[eid]!=1){ 243 | front0.insert(eid); 244 | } 245 | } 246 | } 247 | 248 | // Advance back sweep using front1. 249 | while(!front1.empty()){ 250 | // Get the next unprocessed element in the set. 251 | int seed = *front1.begin(); 252 | front1.erase(front1.begin()); 253 | 254 | if(label[seed]!=1) // i.e. was either never of interest or has been processed in the backsweep. 255 | continue; 256 | 257 | label[seed] = 2; 258 | for(int i=0;i<4;i++){ 259 | int eid = EEList[seed*4+i]; 260 | if(eid!=-1 && label[eid]==1){ 261 | front1.insert(eid); 262 | } 263 | } 264 | } 265 | 266 | // Find active vertex set and create renumbering. 267 | std::map renumbering; 268 | for(int i=0;i(tets[i*4+j], -1)); 272 | } 273 | } 274 | 275 | // Create new compressed mesh. 276 | std::vector xyz_new; 277 | std::vector tets_new; 278 | int cnt=0; 279 | for(auto& it : renumbering){ 280 | it.second = cnt++; 281 | 282 | xyz_new.push_back(xyz[(it.first)*3]); 283 | xyz_new.push_back(xyz[(it.first)*3+1]); 284 | xyz_new.push_back(xyz[(it.first)*3+2]); 285 | } 286 | for(int i=0;i nrrd_dict; 385 | while(std::getline(nhdr, line)){ 386 | // Skip comments. 387 | if(line.compare(0, 1, "#")==0){ 388 | continue; 389 | } 390 | 391 | int delimiter_pos = line.find(":"); 392 | std::string key = line.substr(0, delimiter_pos); 393 | 394 | int offset = line.substr(delimiter_pos+1).find_first_not_of(" \t"); 395 | std::string value = line.substr(delimiter_pos+1+offset); 396 | 397 | if(key=="space directions"){ 398 | int loc = value.find("("); 399 | int length = value.substr(loc+1).find(","); 400 | resolution *= atof(value.substr(loc+1, length).c_str()); 401 | break; 402 | }else if(key=="space units"){ 403 | int loc = value.find('"'); 404 | int length = value.substr(loc+1).find('"'); 405 | if(value.substr(loc+1, length)=="µm"){ 406 | resolution *= 1.0e-6; 407 | }else{ 408 | std::cerr<<"WARNING: units not handled. Set manually."< &xyz, 423 | std::vector &tets){ 424 | 425 | double resolution = 1.0; 426 | 427 | // Get meta-data from NHDR file if specified. 428 | if(!nhdr_filename.empty()){ 429 | resolution = read_resolution_from_nhdr(nhdr_filename); 430 | } 431 | 432 | // Read Tarantuala file 433 | std::ifstream infile; 434 | infile.open(filename.c_str()); 435 | 436 | // Read header 437 | std::string throwaway; 438 | std::getline(infile, throwaway); // line 1 439 | std::getline(infile, throwaway); // line 2 440 | int NNodes; 441 | infile>>NNodes; 442 | 443 | // Read vertices 444 | xyz.resize(NNodes*3); 445 | for(int i=0;i>xyz[i*3]; 447 | infile>>xyz[i*3+1]; 448 | infile>>xyz[i*3+2]; 449 | } 450 | 451 | // Rescale if necessary. 452 | if(resolution!=1.0){ 453 | for(int i=0;i>NTetra; 466 | tets.resize(NTetra*4); 467 | for(int i=0;i>nloc; 469 | assert(nloc==4); 470 | infile>>tets[i*4]; 471 | infile>>tets[i*4+1]; 472 | infile>>tets[i*4+2]; 473 | infile>>tets[i*4+3]; 474 | } 475 | 476 | // Read materials 477 | std::vector< std::vector > materials; 478 | while(infile.good()){ 479 | // Stream through file until we find material data. 480 | std::getline(infile, throwaway); 481 | if(throwaway.substr(0, 4)!="mat1" && throwaway.substr(0, 4)!="mat2") 482 | continue; 483 | 484 | // Junk next line. 485 | std::getline(infile, throwaway); 486 | 487 | // Get number of cells of this material 488 | size_t cnt; 489 | infile>>cnt; 490 | std::vector cells(cnt); 491 | for(int i=0;i>cells[i]; 493 | materials.push_back(cells); 494 | } 495 | 496 | // Junking the rest of the file. 497 | infile.close(); 498 | 499 | if(materials.size()>1){ 500 | assert(materials.size()==2); 501 | size_t select=0; 502 | if(toggle_material) 503 | select = 1; 504 | 505 | // Create the mask. 506 | std::vector mask(NTetra, false); 507 | for(std::vector::const_iterator it=materials[select].begin();it!=materials[select].end();++it){ 508 | mask[*it] = true; 509 | } 510 | 511 | // Turn off masked tets. 512 | for(size_t i=0;i &xyz, 521 | std::vector &tets){ 522 | 523 | double resolution = 1.0; 524 | 525 | // Get meta-data from NHDR file if specified. 526 | if(!nhdr_filename.empty()){ 527 | resolution = read_resolution_from_nhdr(nhdr_filename); 528 | } 529 | 530 | // Read VTK file 531 | vtkSmartPointer reader = vtkSmartPointer::New(); 532 | reader->SetFileName(filename.c_str()); 533 | reader->Update(); 534 | 535 | vtkSmartPointer pd = vtkSmartPointer::New(); 536 | pd->DeepCopy(reader->GetOutput()); 537 | 538 | // Read vertices 539 | int NNodes = pd->GetNumberOfPoints(); 540 | xyz.resize(NNodes*3); 541 | for(int i=0;iGetPoints()->GetPoint(i, xyz.data()+i*3); 543 | } 544 | 545 | // Rescale if necessary. 546 | if(resolution!=1.0){ 547 | for(int i=0;iGetNumberOfCells(); 554 | for(int i=0;iGetCell(i+j)->GetCellType(); 557 | if(cell_type!=VTK_TRIANGLE){ 558 | std::cerr<<"ERROR("<<__FILE__<<"): unsupported element type.\n"; 559 | exit(-1); 560 | } 561 | } 562 | 563 | vtkCell *cell0 = pd->GetCell(i); 564 | for(int j=0;j<3;j++){ 565 | tets.push_back(cell0->GetPointId(j)); 566 | } 567 | 568 | vtkCell *cell1 = pd->GetCell(i+1); 569 | tets.push_back(cell1->GetPointId(2)); 570 | } 571 | } 572 | 573 | double volume(const double *x0, const double *x1, const double *x2, const double *x3){ 574 | 575 | double x01 = (x0[0] - x1[0]); 576 | double x02 = (x0[0] - x2[0]); 577 | double x03 = (x0[0] - x3[0]); 578 | 579 | double y01 = (x0[1] - x1[1]); 580 | double y02 = (x0[1] - x2[1]); 581 | double y03 = (x0[1] - x3[1]); 582 | 583 | double z01 = (x0[2] - x1[2]); 584 | double z02 = (x0[2] - x2[2]); 585 | double z03 = (x0[2] - x3[2]); 586 | 587 | return (-x03*(z02*y01 - z01*y02) + x02*(z03*y01 - z01*y03) - x01*(z03*y02 - z02*y03))/6; 588 | } 589 | 590 | 591 | -------------------------------------------------------------------------------- /src/mesh_microct.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2010 Imperial College London and others. 2 | * 3 | * Please see the AUTHORS file in the main source directory for a 4 | * full list of copyright holders. 5 | * 6 | * Gerard Gorman 7 | * Applied Modelling and Computation Group 8 | * Department of Earth Science and Engineering 9 | * Imperial College London 10 | * 11 | * g.gorman@imperial.ac.uk 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions 15 | * are met: 16 | * 1. Redistributions of source code must retain the above copyright 17 | * notice, this list of conditions and the following disclaimer. 18 | * 2. Redistributions in binary form must reproduce the above 19 | * copyright notice, this list of conditions and the following 20 | * disclaimer in the documentation and/or other materials provided 21 | * with the distribution. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 24 | * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 25 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 26 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 28 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 29 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 30 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 32 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 33 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 34 | * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 | * SUCH DAMAGE. 36 | */ 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include "CTImage.h" 44 | 45 | void usage(char *cmd){ 46 | std::cout<<"Usage: "< 39 | 40 | #include "writers.h" 41 | #include "mesh_conversion.h" 42 | 43 | 44 | void usage(char *cmd){ 45 | std::cerr<< 46 | "Converts the Tarantula generated mesh into a GMSH file. In order to " 47 | "avoid having disconnected domains we sweep across adjacent elements " 48 | "between two parallel sides of the domain and only keep mesh elements " 49 | "that are visited.\n" 50 | 51 | "Usage: "< xyz; 166 | std::vector tets; 167 | read_tarantula_mesh_file(filename, nhdr_filename, toggle_material, xyz, tets); 168 | 169 | if(verbose) 170 | std::cout<<"INFO: Finished reading "< facets, facet_ids; 174 | if(verbose){ 175 | std::cout<<"INFO: Create the active domain."< 42 | 43 | void usage(char *cmd){ 44 | std::cerr<< 45 | "Converts the VTK mesh output from Cleaver2 into a GMSH file. In order to " 46 | "avoid having disconnected domains we sweep across adjacent elements " 47 | "between two parallel sides of the domain and only keep mesh elements " 48 | "that are visited.\n" 49 | 50 | "Usage: "< xyz; 158 | std::vector tets; 159 | read_vtk_mesh_file(filename, nhdr_filename, xyz, tets); 160 | 161 | if(verbose) 162 | std::cout<<"INFO: Finished reading "< facets, facet_ids; 166 | if(verbose){ 167 | std::cout<<"INFO: Create the active domain."< 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include 47 | #include 48 | #include 49 | 50 | #include "writers.h" 51 | 52 | int write_vtk_file(std::string filename, 53 | std::vector &xyz, 54 | std::vector &tets, 55 | std::vector &facets, 56 | std::vector &facet_ids){ 57 | 58 | // Write out points 59 | int NNodes = xyz.size()/3; 60 | vtkSmartPointer pts = vtkSmartPointer::New(); 61 | pts->SetNumberOfPoints(NNodes); 62 | for(int i=0;iSetPoint(i, &(xyz[i*3])); 64 | 65 | // Initalise the vtk mesh 66 | vtkSmartPointer ug_tets = vtkSmartPointer::New(); 67 | ug_tets->SetPoints(pts); 68 | 69 | int NTetra = tets.size()/4; 70 | for(int i=0;i idlist = vtkSmartPointer::New(); 75 | for(int j=0;j<4;j++) 76 | idlist->InsertNextId(tets[i*4+j]); 77 | ug_tets->InsertNextCell(10, idlist); 78 | } 79 | 80 | vtkSmartPointer tet_writer = vtkSmartPointer::New(); 81 | tet_writer->SetFileName(std::string(filename+".vtu").c_str()); 82 | #if VTK_MAJOR_VERSION < 6 83 | tet_writer->SetInput(ug_tets); 84 | #else 85 | tet_writer->SetInputData(ug_tets); 86 | #endif 87 | tet_writer->Write(); 88 | 89 | if(facets.empty()) 90 | return 0; 91 | 92 | // Write out facets 93 | vtkSmartPointer ug_facets = vtkSmartPointer::New(); 94 | ug_facets->SetPoints(pts); 95 | int NFacets = facet_ids.size(); 96 | for(int i=0;i idlist = vtkSmartPointer::New(); 98 | for(int j=0;j<3;j++){ 99 | idlist->InsertNextId(facets[i*3+j]); 100 | } 101 | ug_facets->InsertNextCell(5, idlist); 102 | } 103 | 104 | vtkSmartPointer vtk_facet_ids = vtkSmartPointer::New(); 105 | vtk_facet_ids->SetNumberOfTuples(NFacets); 106 | vtk_facet_ids->SetNumberOfComponents(1); 107 | vtk_facet_ids->SetName("Facet IDs"); 108 | for(int i=0;iSetValue(i, facet_ids[i]); 110 | } 111 | ug_facets->GetCellData()->AddArray(vtk_facet_ids); 112 | 113 | vtkSmartPointer tri_writer = vtkSmartPointer::New(); 114 | tri_writer->SetFileName(std::string(filename+"_facets.vtu").c_str()); 115 | #if VTK_MAJOR_VERSION < 6 116 | tri_writer->SetInput(ug_facets); 117 | #else 118 | tri_writer->SetInputData(ug_facets); 119 | #endif 120 | tri_writer->Write(); 121 | 122 | return 0; 123 | } 124 | 125 | int write_triangle_file(std::string basename, 126 | std::vector &xyz, 127 | std::vector &tets, 128 | std::vector &facets, 129 | std::vector &facet_ids){ 130 | std::string filename_node = basename+".node"; 131 | std::string filename_face = basename+".face"; 132 | std::string filename_ele = basename+".ele"; 133 | 134 | int NNodes = xyz.size()/3; 135 | int NTetra = tets.size()/4; 136 | int NFacets = facet_ids.size(); 137 | assert(NFacets==facets.size()/3); 138 | 139 | ofstream nodefile; 140 | nodefile.open(std::string(basename+".node").c_str()); 141 | nodefile<::digits10+1); 143 | 144 | for(int i=0;i &xyz, 168 | std::vector &tets, 169 | std::vector &facets, 170 | std::vector &facet_ids){ 171 | 172 | int NNodes = xyz.size()/3; 173 | int NTetra = tets.size()/4; 174 | int NFacets = facet_ids.size(); 175 | assert(NFacets==facets.size()/3); 176 | 177 | ofstream file; 178 | file.open(std::string(basename+".msh").c_str()); 179 | file<<"$MeshFormat"<::digits10+1); 185 | for(size_t i=0;i> .bashrc 16 | export PATH=$HOME/projects/poreflow/bin:$PATH 17 | ``` 18 | 19 | Prepare data sample 20 | ------------------- 21 | *THIS SECTION IS OUT OF DATE UNTIL WE FINISH UPLOADING NEW NDDR FILES* 22 | All the data is available [here][http://www3.imperial.ac.uk/earthscienceandengineering/research/perm/porescalemodelling/micro-ct%20images%20and%20networks]. For this example click on [Berea Sandstone][http://www3.imperial.ac.uk/earthscienceandengineering/research/perm/porescalemodelling/micro-ct%20images%20and%20networks/berea%20sandstone]. You can see a full description of the data sample. Download the image by either clicking on [Download image (binary and ASCII)][http://www3.imperial.ac.uk/pls/portallive/docs/1/33505696.ZIP] or it may be easier to use wget: 23 | ```bash 24 | wget http://www3.imperial.ac.uk/pls/portallive/docs/1/33505696.ZIP 25 | unzip 33505696.ZIP 26 | mv Image ~/projects/Berea 27 | ``` 28 | 29 | Next you have to convert the nhdr/raw format to something that the mesh generator can read (there is going to be a lot of converting so get used to it). 30 | ```bash 31 | convert_microct -v -c vox -s 64 Berea.nhdr 32 | ``` 33 | 34 | If you just type the command by itself it will give you an example of its usage. The *-v* option enables verbose mode. As well as outputting messages to the stdout it may also output VTK files for diagnostic/visualisation purposes. *-c vox* specifies the output format as VOX and the optional argument *-s * specifies that only a sample of the data is selected. 35 | 36 | These files can be huge so always compress them after generation or you will quickly run out of space and patience. 37 | 38 | ```bash 39 | gzip Berea.vox 40 | ``` 41 | 42 | Mesh generation 43 | --------------- 44 | Grab a template configuration file for Tarantula: 45 | 46 | ```bash 47 | cp $HOME/projects/poreflow/data/Berea.conf . 48 | ``` 49 | 50 | Although obviously not required here, rename this to the rename this file to correspond to the dataset you are working on, and update the first 2 lines of the conf file so that the vox and mesh names are correct. I strongly recommend you use a different directory for each data sample or you will continuously clobber old results/data. Next, run the mesh generator: 51 | 52 | ```bash 53 | /usr/local/tarantula/bin/linux/voxmesher Berea.conf 54 | ``` 55 | 56 | This can sometimes fail. When this happens try modifying the sample size with the -s option above. Powers of 2 are good. If this runs successfully you should see a Tarantula .spm file: 57 | 58 | ```bash 59 | ls -ltr | tail -1 60 | -rw-r--r-- 1 ggorman esestaff 65685433 Sep 8 11:23 Berea.spm 61 | ``` 62 | 63 | This us a rather unique format that not many codes will recognise so we will convert this to GMSH format. For this we will use tarantula2gmsh. Tarantuala also drops the resolution parameter at the top of the VOX file. Therefore you should also specify the original nhdr file so that the meta-data can be re-read. 64 | 65 | ```bash 66 | tarantula2gmsh -v -n Berea.nhdr Berea.spm 67 | ``` 68 | 69 | As well as converting the file format, this command does the following: 70 | 71 | * Images will typically have two materials (rock and void indicated by 1 and 0), you can toggle which material mesh it extracts using the *-t* flag. 72 | * Extracts only the active region - it throws away any connected region that is not connected to both sides of the domain along the X-axis. 73 | * Applies boundary labels: -x, +x, -y, +y, -z, +z, grain boundaries labelled as 1, 2, 3, 4, 5, 6, 7 respectively. 74 | * Add the *-v* option if you want verbose messaging and VTK files to admire your beautiful mesh! 75 | 76 | Use paraview to take a look at the data. Does it look ok? Is it "fit for purpose"? 77 | 78 | Running a simulation 79 | -------------------- 80 | We are going to use caloris.ese.ic.ac.uk because this has the master version of FEniCS and PETSc installed (complements of Patrick Farrell) which is required for the split field preconditioners used. 81 | 82 | First set environment variables so you are using the right versions of everything: 83 | 84 | ```bash 85 | source /data/pfarrell/src/local/install_fenics_opt.sh 86 | ``` 87 | 88 | Convert GMSH to Dolfin XML format so that dolfin can read it. Ideally we should convert to XDMF but I am having some trouble getting that working: 89 | 90 | ```bash 91 | dolfin-convert Berea.msh Berea.xml 92 | python ~/projects/poreflow/python/convert_dolfin_new_xml.py Berea.xml 93 | ``` 94 | 95 | Finally we are ready to run the Stokes flow solver in parallel using MPI: 96 | 97 | ```bash 98 | mpiexec -n 24 python ~/projects/poreflow/python/stokes-dolfin-snes.py Berea.xml 99 | 100 | # the tail of the output will look something like this: 101 | 211 KSP preconditioned resid norm 5.766382019060e-09 true resid norm 6.166533331081e-23 ||r(i)||/||b|| 6.843859574820e-14 102 | 212 KSP preconditioned resid norm 5.523493457232e-09 true resid norm 1.886341229798e-23 ||r(i)||/||b|| 2.093535183190e-14 103 | 213 KSP preconditioned resid norm 2.250052402294e-09 true resid norm 1.575254855321e-22 ||r(i)||/||b|| 1.748279372793e-13 104 | Linear solve converged due to CONVERGED_RTOL iterations 213 105 | 1 SNES Function norm 1.575287828239e-22 106 | Nonlinear solve converged due to CONVERGED_FNORM_ABS iterations 1 107 | [-1.0791655061523711e-16, 1.0791655010304546e-16, 0.0, 0.0, 0.0, 0.0, 0.0] 108 | ################################# 109 | ## In-flux = -1.07917e-16 110 | ## Out-flux = 1.07917e-16 111 | ## Permability = 3.20407e-13 m^2 (0.320407 D) 112 | ################################# 113 | ``` 114 | 115 | Note the permability above. Does it look the value you were expecting? 116 | 117 | Finally have a look at the solution using paraview: 118 | 119 | ```bash 120 | paraview --data=velocity.pvd 121 | paraview --data=pressure.pvd 122 | ``` 123 | --------------------------------------------------------------------------------